# Import necessary libraries import string # For lowercase English letters import re # For regular expression operations from collections import Counter # For counting word occurrences import editdistance # For calculating edit distance import numpy as np # Not used in code (can be removed) class Autocorrection(object): def __init__(self, filename): # Read the file and extract all lowercase words with open(filename, "r", encoding="UTF-8") as file: word = [] lines = file.readlines() for line in lines: # Extract words using regex and convert to lowercase word += re.findall(r'\w+', line.lower()) # Create a vocabulary from the unique words self.vocabulary = set(word) # Count occurrences of each word self.counts_of_word = Counter(word) # Total number of words for probability calculation self.total_words = float(sum(self.counts_of_word.values())) # Compute the probability of each word self.prob_of_word = { w: self.counts_of_word[w] / self.total_words for w in self.counts_of_word.keys() } def edit1(self, word): # All letters from a-z # letter = string.ascii_lowercase letter = 'abcdefghijklmnopqrstuvwxyzàâçéèêëîïôùûüÿœæ' # All possible split positions in the word splits = [(word[:i], word[i:]) for i in range(len(word) + 1)] # Generate all one-edit-distance words: # Insertion: insert a letter at any position insert = [l + c + r for l, r in splits for c in letter] # Deletion: remove one letter delete = [l + r[1:] for l, r in splits if r] # Replacement: change one letter replace = [l + c + r[1:] for l, r in splits if r for c in letter] # Transposition (swap): swap adjacent letters swap = [l + r[1] + r[0] + r[2:] for l, r in splits if len(r) > 1] # Return all variations as a set to avoid duplicates return set(replace + insert + delete + swap) def edit2(self, word): # Generate all words that are two edits away return [e2 for e1 in self.edit1(word) for e2 in self.edit1(e1)] def common_prefix_length(self, word1, word2): # Find the length of the common prefix between two words common_len = 0 for i, (c1, c2) in enumerate(zip(word1, word2)): if c1 == c2: common_len += 1 else: break return common_len def custom_score(self, suggestion, original_word): # Weights for different operations replace_weight = 1 insert_weight = 2 delete_weight = 3 swap_weight = 4 # Get the edit distance between suggestion and original word distance = editdistance.eval(suggestion, original_word) # Compute a custom score based on the type of edit if len(suggestion) == len(original_word): # Likely replace common_prefix_len = self.common_prefix_length(suggestion, original_word) score = distance * replace_weight + (len(original_word) - common_prefix_len) elif len(suggestion) == len(original_word) + 1: # Likely insertion common_prefix_len = self.common_prefix_length(suggestion, original_word) score = distance * insert_weight + (len(original_word) - common_prefix_len) elif len(suggestion) == len(original_word) - 1: # Likely deletion score = distance * delete_weight else: # Likely swap or other score = distance * swap_weight return score def correct_spelling(self, word): # Return if word is already in the vocabulary if word in self.vocabulary: print(f"{word} is already correctly spelt") return # Get edit distance candidates suggestions = self.edit1(word) or self.edit2(word) or [word] # Filter candidates that are real words best_guesses = [w for w in suggestions if w in self.vocabulary] # Sort suggestions by custom score best_guesses.sort(key=lambda w: self.custom_score(w, word)) # Return suggestions with their probabilities return [(w, self.prob_of_word[w]) for w in best_guesses]