66# source code from https://github.com/norvig/pytudes
77#https://yurichev.com/blog/fuzzy_string/
88
9+ '''
10+ The Levenshtein distance is a string metric for measuring difference between two
11+ sequences. Informally, the Levenshtein distance between two words is
12+ the minimum number of single-character edits
13+ (i.e. insertions, deletions or substitutions)
14+ required to change one word into the other.
15+ '''
16+
917################ Spelling Corrector
1018
1119import re
1220from collections import Counter
21+ from zipfile import ZipFile
22+
23+ words = lambda text : re .findall (r'\w+' , text .lower ())
1324
14- def words (text ): return re .findall (r'\w+' , text .lower ())
25+ with ZipFile ('big.zip' ) as archive , archive .open ('big.txt' , 'r' ) as f :
26+ WORDS = Counter (words (f .read ().decode ('utf-8' )))
1527
16- WORDS = Counter ( words ( open ( 'big.txt' ). read () ))
28+ COUNT_OF_WORDS = sum ( WORDS . values ( ))
1729
18- def P (word , N = sum ( WORDS . values ())):
19- " Probability of `word`."
30+ def P (word , N = COUNT_OF_WORDS ):
31+ ' Probability of `word`.'
2032 return WORDS [word ] / N
2133
22- def correction (word ):
23- " Most probable spelling correction for word."
34+ def correction (word ):
35+ ' Most probable spelling correction for word.'
2436 return max (candidates (word ), key = P )
2537
26- def candidates (word ):
27- " Generate possible spelling corrections for word."
38+ def candidates (word ):
39+ ' Generate possible spelling corrections for word.'
2840 return (known ([word ]) or known (edits1 (word )) or known (edits2 (word )) or [word ])
2941
30- def known (words ):
31- " The subset of `words` that appear in the dictionary of WORDS."
42+ def known (words ):
43+ ' The subset of `words` that appear in the dictionary of WORDS.'
3244 return set (w for w in words if w in WORDS )
3345
3446def edits1 (word ):
35- " All edits that are one edit away from `word`."
47+ ' All edits that are one edit away from `word`.'
3648 letters = 'abcdefghijklmnopqrstuvwxyz'
3749 splits = [(word [:i ], word [i :]) for i in range (len (word ) + 1 )]
3850 deletes = [L + R [1 :] for L , R in splits if R ]
@@ -41,8 +53,8 @@ def edits1(word):
4153 inserts = [L + c + R for L , R in splits for c in letters ]
4254 return set (deletes + transposes + replaces + inserts )
4355
44- def edits2 (word ):
45- " All edits that are two edits away from `word`."
56+ def edits2 (word ):
57+ ' All edits that are two edits away from `word`.'
4658 return (e2 for e1 in edits1 (word ) for e2 in edits1 (e1 ))
4759
4860## To rewrite
@@ -62,7 +74,7 @@ def unit_tests():
6274 assert Counter (words ('This is a test. 123; A TEST this is.' )) == (
6375 Counter ({'123' : 1 , 'a' : 2 , 'is' : 2 , 'test' : 2 , 'this' : 2 }))
6476 assert len (WORDS ) == 32198
65- assert sum ( WORDS . values ()) == 1115585
77+ assert COUNT_OF_WORDS == 1115585
6678 assert WORDS .most_common (10 ) == [
6779 ('the' , 79809 ),
6880 ('of' , 40024 ),
@@ -79,31 +91,5 @@ def unit_tests():
7991 assert 0.07 < P ('the' ) < 0.08
8092 return 'unit_tests pass'
8193
82- def spelltest (tests , verbose = False ):
83- "Run correction(wrong) on all (right, wrong) pairs; report results."
84- import time
85- start = time .clock ()
86- good , unknown = 0 , 0
87- n = len (tests )
88- for right , wrong in tests :
89- w = correction (wrong )
90- good += (w == right )
91- if w != right :
92- unknown += (right not in WORDS )
93- if verbose :
94- print ('correction({}) => {} ({}); expected {} ({})'
95- .format (wrong , w , WORDS [w ], right , WORDS [right ]))
96- dt = time .clock () - start
97- print ('{:.0%} of {} correct ({:.0%} unknown) at {:.0f} words per second '
98- .format (good / n , n , unknown / n , n / dt ))
99-
100- def Testset (lines ):
101- "Parse 'right: wrong1 wrong2' lines into [('right', 'wrong1'), ('right', 'wrong2')] pairs."
102- return [(right , wrong )
103- for (right , wrongs ) in (line .split (':' ) for line in lines )
104- for wrong in wrongs .split ()]
105-
10694if __name__ == '__main__' :
10795 print (unit_tests ())
108- spelltest (Testset (open ('spell-testset1.txt' )))
109- spelltest (Testset (open ('spell-testset2.txt' )))
0 commit comments