A Spelling Corrector in 21 Lines of Python


In [1]:
!pwd


/cygdrive/c/Users/shusli/Source/IntroPy/Section_01

In [2]:
!ls -lh


total 6.5M
-rwxr-xr-x 1 shusli Domain Users  14K Apr 28 20:32 00_Intro.ipynb
-rwxr-xr-x 1 shusli Domain Users  18K Apr 29 09:45 01_SpellingCorrector.ipynb
-rwxr-xr-x 1 shusli Domain Users 178K Apr 28 18:49 02_MSDN_Spider.ipynb
-rwxr-xr-x 1 shusli Domain Users  77K Apr 28 16:07 Debug.html
-rwxr-xr-x 1 shusli Domain Users 6.2M Apr 28 11:49 big.txt

From a corpus(big.txt), count the frequence of each word.


In [3]:
import re, collections

def words(text): return re.findall('[a-z]+', text.lower())

def train(features):
    model = collections.defaultdict(lambda: 1)
    for f in features:
        model[f] += 1
    return model

NWORDS = train(words(file('big.txt').read()))

In [4]:
len(NWORDS)


Out[4]:
29157

Find Words That The Edit Distance Is 1


In [5]:
alphabet = 'abcdefghijklmnopqrstuvwxyz'

def edits1(word):
   splits     = [(word[:i], word[i:]) for i in range(len(word) + 1)]
   deletes    = [a + b[1:] for a, b in splits if b]
   transposes = [a + b[1] + b[0] + b[2:] for a, b in splits if len(b)>1]
   replaces   = [a + c + b[1:] for a, b in splits for c in alphabet if b]
   inserts    = [a + c + b     for a, b in splits for c in alphabet]
   return set(deletes + transposes + replaces + inserts)

In [6]:
edits1('speling')


Out[6]:
{'apeling',
 'aspeling',
 'bpeling',
 'bspeling',
 'cpeling',
 'cspeling',
 'dpeling',
 'dspeling',
 'epeling',
 'espeling',
 'fpeling',
 'fspeling',
 'gpeling',
 'gspeling',
 'hpeling',
 'hspeling',
 'ipeling',
 'ispeling',
 'jpeling',
 'jspeling',
 'kpeling',
 'kspeling',
 'lpeling',
 'lspeling',
 'mpeling',
 'mspeling',
 'npeling',
 'nspeling',
 'opeling',
 'ospeling',
 'peling',
 'ppeling',
 'pseling',
 'pspeling',
 'qpeling',
 'qspeling',
 'rpeling',
 'rspeling',
 'saeling',
 'sapeling',
 'sbeling',
 'sbpeling',
 'sceling',
 'scpeling',
 'sdeling',
 'sdpeling',
 'seeling',
 'seling',
 'sepeling',
 'sepling',
 'sfeling',
 'sfpeling',
 'sgeling',
 'sgpeling',
 'sheling',
 'shpeling',
 'sieling',
 'sipeling',
 'sjeling',
 'sjpeling',
 'skeling',
 'skpeling',
 'sleling',
 'slpeling',
 'smeling',
 'smpeling',
 'sneling',
 'snpeling',
 'soeling',
 'sopeling',
 'spaeling',
 'spaling',
 'spbeling',
 'spbling',
 'spceling',
 'spcling',
 'spdeling',
 'spdling',
 'speaing',
 'spealing',
 'spebing',
 'spebling',
 'specing',
 'specling',
 'speding',
 'spedling',
 'speeing',
 'speeling',
 'spefing',
 'spefling',
 'speging',
 'spegling',
 'spehing',
 'spehling',
 'speiing',
 'speiling',
 'speilng',
 'speing',
 'spejing',
 'spejling',
 'speking',
 'spekling',
 'spelaing',
 'spelang',
 'spelbing',
 'spelbng',
 'spelcing',
 'spelcng',
 'spelding',
 'speldng',
 'speleing',
 'speleng',
 'spelfing',
 'spelfng',
 'spelging',
 'spelgng',
 'spelhing',
 'spelhng',
 'speliag',
 'speliang',
 'spelibg',
 'spelibng',
 'spelicg',
 'spelicng',
 'spelidg',
 'spelidng',
 'spelieg',
 'spelieng',
 'spelifg',
 'spelifng',
 'spelig',
 'speligg',
 'spelign',
 'speligng',
 'spelihg',
 'spelihng',
 'speliig',
 'speliing',
 'spelijg',
 'spelijng',
 'spelikg',
 'spelikng',
 'spelilg',
 'spelilng',
 'spelimg',
 'spelimng',
 'spelin',
 'spelina',
 'spelinag',
 'spelinb',
 'spelinbg',
 'spelinc',
 'spelincg',
 'spelind',
 'spelindg',
 'speline',
 'spelineg',
 'spelinf',
 'spelinfg',
 'speling',
 'spelinga',
 'spelingb',
 'spelingc',
 'spelingd',
 'spelinge',
 'spelingf',
 'spelingg',
 'spelingh',
 'spelingi',
 'spelingj',
 'spelingk',
 'spelingl',
 'spelingm',
 'spelingn',
 'spelingo',
 'spelingp',
 'spelingq',
 'spelingr',
 'spelings',
 'spelingt',
 'spelingu',
 'spelingv',
 'spelingw',
 'spelingx',
 'spelingy',
 'spelingz',
 'spelinh',
 'spelinhg',
 'spelini',
 'spelinig',
 'spelinj',
 'spelinjg',
 'spelink',
 'spelinkg',
 'spelinl',
 'spelinlg',
 'spelinm',
 'spelinmg',
 'spelinn',
 'spelinng',
 'spelino',
 'spelinog',
 'spelinp',
 'spelinpg',
 'spelinq',
 'spelinqg',
 'spelinr',
 'spelinrg',
 'spelins',
 'spelinsg',
 'spelint',
 'spelintg',
 'spelinu',
 'spelinug',
 'spelinv',
 'spelinvg',
 'spelinw',
 'spelinwg',
 'spelinx',
 'spelinxg',
 'speliny',
 'spelinyg',
 'spelinz',
 'spelinzg',
 'speliog',
 'speliong',
 'spelipg',
 'spelipng',
 'speliqg',
 'speliqng',
 'spelirg',
 'spelirng',
 'spelisg',
 'spelisng',
 'spelitg',
 'spelitng',
 'speliug',
 'speliung',
 'spelivg',
 'spelivng',
 'speliwg',
 'speliwng',
 'spelixg',
 'spelixng',
 'speliyg',
 'speliyng',
 'spelizg',
 'spelizng',
 'speljing',
 'speljng',
 'spelking',
 'spelkng',
 'spelling',
 'spellng',
 'spelming',
 'spelmng',
 'spelng',
 'spelnig',
 'spelning',
 'spelnng',
 'speloing',
 'spelong',
 'spelping',
 'spelpng',
 'spelqing',
 'spelqng',
 'spelring',
 'spelrng',
 'spelsing',
 'spelsng',
 'spelting',
 'speltng',
 'speluing',
 'spelung',
 'spelving',
 'spelvng',
 'spelwing',
 'spelwng',
 'spelxing',
 'spelxng',
 'spelying',
 'spelyng',
 'spelzing',
 'spelzng',
 'speming',
 'spemling',
 'spening',
 'spenling',
 'speoing',
 'speoling',
 'speping',
 'spepling',
 'speqing',
 'speqling',
 'spering',
 'sperling',
 'spesing',
 'spesling',
 'speting',
 'spetling',
 'speuing',
 'speuling',
 'speving',
 'spevling',
 'spewing',
 'spewling',
 'spexing',
 'spexling',
 'speying',
 'speyling',
 'spezing',
 'spezling',
 'spfeling',
 'spfling',
 'spgeling',
 'spgling',
 'spheling',
 'sphling',
 'spieling',
 'spiling',
 'spjeling',
 'spjling',
 'spkeling',
 'spkling',
 'spleing',
 'spleling',
 'spling',
 'splling',
 'spmeling',
 'spmling',
 'spneling',
 'spnling',
 'spoeling',
 'spoling',
 'sppeling',
 'sppling',
 'spqeling',
 'spqling',
 'spreling',
 'sprling',
 'spseling',
 'spsling',
 'spteling',
 'sptling',
 'spueling',
 'spuling',
 'spveling',
 'spvling',
 'spweling',
 'spwling',
 'spxeling',
 'spxling',
 'spyeling',
 'spyling',
 'spzeling',
 'spzling',
 'sqeling',
 'sqpeling',
 'sreling',
 'srpeling',
 'sseling',
 'sspeling',
 'steling',
 'stpeling',
 'sueling',
 'supeling',
 'sveling',
 'svpeling',
 'sweling',
 'swpeling',
 'sxeling',
 'sxpeling',
 'syeling',
 'sypeling',
 'szeling',
 'szpeling',
 'tpeling',
 'tspeling',
 'upeling',
 'uspeling',
 'vpeling',
 'vspeling',
 'wpeling',
 'wspeling',
 'xpeling',
 'xspeling',
 'ypeling',
 'yspeling',
 'zpeling',
 'zspeling'}

Find Words That The Edit Distance Is Less Or Equal Than 2


In [7]:
def known_edits2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)

def known(words): return set(w for w in words if w in NWORDS)

Find The Words With Maximum Likelihood


In [8]:
def correct(word):
    candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
    return max(candidates, key=NWORDS.get)

In [9]:
correct('speling')


Out[9]:
'spelling'

In [10]:
NWORDS.get(correct('speling'))


Out[10]:
5

In [11]:
correct('korrecter')


Out[11]:
'corrected'

Python Remote Object


In [12]:
class Corrector:
    def __init__(self, correct):
        self.correct = correct

import Pyro4
daemon = Pyro4.Daemon(host="localhost", port=51515)
local_object = Corrector(correct)
uri = daemon.register(local_object)

In [13]:
uri


Out[13]:
<Pyro4.core.URI at 0x4276b48, PYRO:obj_47d5723cd35341f1bafe040728e06b30@localhost:51515>

In [ ]:
daemon.requestLoop()

Python let you focus on What To Do, rather than How To Do