AutoTypo / autocorrect_package /autocorrection.py
PixiRus's picture
Update autocorrect_package/autocorrection.py
17e84da verified
Raw
History Blame Contribute Delete
4.36 kB
# 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]