I wrote a fairly simple program that grades theory-based quizzes. It is fairly straightforward. I would love to get insights and ideas on how it might be improved.


"""This module uses consine distance to check the similarity between two sentences"""

from nltk.corpus import stopwords
from nltk.cluster.util import cosine_distance
from nltk.tokenize import word_tokenize

def consine_similarity(sent1: str, sent2: str) -> float:
    """Consine similarity between two sentences

        sent1: str
        sent2: str

    sent1_list = word_tokenize(sent1)
    sent2_list = word_tokenize(sent2)
    stop_words = stopwords.words('english')
    all_words = list(set(sent1_list + sent2_list))

    vector1 = [0] * len(all_words)
    vector2 = [0] * len(all_words)

    for word in sent1_list:
        if word in stop_words:
        vector1[all_words.index(word)] += 1

    for word in sent2_list:
        if word in stop_words:
        vector2[all_words.index(word)] += 1

    return 1 - cosine_distance(vector1, vector2)


"""Theory-based quiz application"""

from dataclasses import dataclass
from random import shuffle
from consine_similarity import consine_similarity

class Quiz:
    """Quiz data"""

    quiz: str
    answer: str

    def __str__(self):
        return '{}'.format(self.quiz)

def start_quiz(quiz_list: list) -> None:
    """Start the quiz application"""

    consine_list = []

    for quiz in quiz_list:
        answer = input('> ')
        consine_list.append(consine_similarity(quiz.answer, answer))

    grade = sum(consine_list) / len(consine_list) * 100

    print('Your grade is {:.2f}%'.format(grade))

if __name__ == "__main__":
    QUIZ_LIST = [
        Quiz(quiz='What is a computer?',
             answer='An electronic device for storing and processing data, \
                 typically in binary form, according to instructions given to it'),
        Quiz(quiz='What are logic gates?',
             answer='These are the basic building blocks of any digital system. It is an \
                  electronic circuit having one or more than one input and only one output'),
        Quiz(quiz='What is BIOS',
             answer='This is firmware used to perform hardware initialization during the booting \
                 process and to provide runtime services for operating systems and programs'),


There is no need to include stop words in the dictionary if they are filtered out of the vectors anyway. Thus, tokenization and stop words removal can be considered as a single step. Secondly, the vectors can be initialized with the correct values immediately by taking advantage of collections.Counter

from collections import Counter
from typing import List
from nltk.corpus import stopwords
from nltk.cluster.util import cosine_distance
from nltk.tokenize import word_tokenize

def tokenize_without_stop_words(sentence:str) -> List[str]:
    return [
        for word in word_tokenize(sentence)
        if word not in stopwords.words('english')

def cosine_similarity(first_sentence: str, second_sentence: str) -> float:
    first_words = tokenize_without_stop_words(first_sentence)
    second_words = tokenize_without_stop_words(second_sentence)

    dictionary = list(set(first_words+second_words))

    def encode(words: List[str]) -> List[int]:
        word_counts = Counter(words)
        return [
            for word in dictionary

    first_vector = encode(first_words)
    second_vector = encode(second_words)

    return 1 - cosine_distance(first_vector, second_vector)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.