python脚本实现英文单词纠错
GitHub地址:https://github.com/fanorfan/EnglishWordErrorCorrection
目录
- 单词纠错算法
- python实现
- 1. 英文单词纠错(CorrectWords.py)
- 2. word文档纠错(CorrectDocx.py)
- 编辑距离
- 动态规划算法
- python实现(DynamicProgramming.py)
单词纠错算法
- 准备一个语料库,里面包含的内容如下:
- Gutenberg语料库数据
- 维基词典
- 英国国家语料库中的最常用单词列表
下载地址:https://github.com/fanorfan/EnglishWordErrorCorrection/blob/master/big.txt
取出语料库中所有的单词,并统计其出现的次数。
对于一个给定的单词,不管其是否拼写错误,依次找到和它编辑距离为0、1、2的单词。
考虑以下三个优先顺序来挑选出该单词一个最佳纠正结果:
1). 编辑距离为0的单词(即该单词本身) > 编辑距离为1的单词 > 编辑距离为2的单词2). 是否在语料库中出现(最起码得是一个正确的存在的单词)3). 语料库中出现的次数
python实现
1. 英文单词纠错(CorrectWords.py)
- 语料库的处理
import re, collections# text中所有单词转为小写形式并匹配
# text示例:"The Project Gutenberg EBook of The Adventures of Sherlock Holmes\nby Sir Arthur Conan Doyle"
def tokens(text):return re.findall("[a-z]+", text.lower())# 打开语料库文件
with open('big.txt', 'r') as f:words = tokens(f.read())
- 取出语料库中所有的单词,并统计其出现的次数。
# 统计语料库中每个单词的个数
word_counts = collections.Counter(words)
- 单词纠正
创建一个单词纠正函数如下:
# text示例:'fianlly helloa cta'
def correct_text_generic(text):"""纠正匹配到的单词中所有拼写错误的单词"""return re.sub('[a-zA-Z]+', correct_match, text)
correct_match
参数是一个回调函数,来执行对匹配到的单词的操作,我们创建这个函数如下:
def correct_match(match):"""替换的回调函数"""# 匹配到的单词依次取出,第一次取'fianlly'word = match.group()def case_of(text):"""返回小写"""return (str.upper if text.isupper() elsestr.lower if text.islower() elsestr.title if text.istitle() elsestr)return case_of(word)(correct(word.lower()))
case_of(word)()
的第二个括号中需要一个函数,我们创建这个函数如下:
def correct(word):"""获得输入单词的最佳正确拼写"""# 1.是否在语料库中出现# 2.单词的优先顺序:编辑距离为0的单词(即该单词本身) > 编辑距离为1的单词 > 编辑距离为2的单词# 3.在语料库中的出现次数candidates = (known(edits0(word)) orknown(edits1(word)) orknown(edits2(word)) or{word})return max(candidates, key=word_counts.get)
candidates
变量来保存符合1). 2).两个条件的单词,最后返回其中在语料库中出现次数最多的单词。
2). 是否在语料库中出现对应的函数如下:
def known(words):"""判断words中的每一个单词是否在语料库中出现,若出现就返回此单词"""return {w for w in words if w in word_counts}
1). 编辑距离为0的单词(即该单词本身) > 编辑距离为1的单词 > 编辑距离为2的单词 对应的函数如下:
def edits0(word):"""返回跟输入单词是0距离的单词,也就是自己"""return {word}def edits1(word):"""返回跟输入单词是1距离的单词"""# 26个英文字母 ord():获取'a'的码 chr():通过码还原对应的字符# 'abcdefghijklmnopqrstuvwxyz'alphabet = ''.join([chr(ord('a') + i) for i in range(26)])def splits(word):"""分割单词 以cta为例: [('', 'cta'), ('c', 'ta'), ('ct', 'a'), ('cta', '')]"""return [(word[:i], word[i:])for i in range(len(word) + 1)]# 分割好的单词pairs = splits(word)# 删除某个字符 ['ta', 'ca', 'ct']deletes = [a + b[1:] for (a, b) in pairs if b]# 两个字符换位置 ['tca', 'cat']transposes = [a + b[1] + b[0] + b[2:] for (a, b) in pairs if len(b) > 1]# 替换某个字符 ['ata', 'bta', 'cta', 'dta', 'eta', 'fta', 'gta', 'hta', 'ita', 'jta', 'kta', 'lta', 'mta', 'nta', 'ota', 'pta', 'qta', 'rta', 'sta', 'tta', 'uta', 'vta', 'wta', 'xta', 'yta', 'zta', 'caa', 'cba', 'cca', 'cda', 'cea', 'cfa', 'cga', 'cha', 'cia', 'cja', 'cka', 'cla', 'cma', 'cna', 'coa', 'cpa', 'cqa', 'cra', 'csa', 'cta', 'cua', 'cva', 'cwa', 'cxa', 'cya', 'cza', 'cta', 'ctb', 'ctc', 'ctd', 'cte', 'ctf', 'ctg', 'cth', 'cti', 'ctj', 'ctk', 'ctl', 'ctm', 'ctn', 'cto', 'ctp', 'ctq', 'ctr', 'cts', 'ctt', 'ctu', 'ctv', 'ctw', 'ctx', 'cty', 'ctz']replaces = [a + c + b[1:] for (a, b) in pairs for c in alphabet if b]# 插入某个字符 ['acta', 'bcta', 'ccta', 'dcta', 'ecta', 'fcta', 'gcta', 'hcta', 'icta', 'jcta', 'kcta', 'lcta', 'mcta', 'ncta', 'octa', 'pcta', 'qcta', 'rcta', 'scta', 'tcta', 'ucta', 'vcta', 'wcta', 'xcta', 'ycta', 'zcta', 'cata', 'cbta', 'ccta', 'cdta', 'ceta', 'cfta', 'cgta', 'chta', 'cita', 'cjta', 'ckta', 'clta', 'cmta', 'cnta', 'cota', 'cpta', 'cqta', 'crta', 'csta', 'ctta', 'cuta', 'cvta', 'cwta', 'cxta', 'cyta', 'czta', 'ctaa', 'ctba', 'ctca', 'ctda', 'ctea', 'ctfa', 'ctga', 'ctha', 'ctia', 'ctja', 'ctka', 'ctla', 'ctma', 'ctna', 'ctoa', 'ctpa', 'ctqa', 'ctra', 'ctsa', 'ctta', 'ctua', 'ctva', 'ctwa', 'ctxa', 'ctya', 'ctza', 'ctaa', 'ctab', 'ctac', 'ctad', 'ctae', 'ctaf', 'ctag', 'ctah', 'ctai', 'ctaj', 'ctak', 'ctal', 'ctam', 'ctan', 'ctao', 'ctap', 'ctaq', 'ctar', 'ctas', 'ctat', 'ctau', 'ctav'...inserts = [a + c + b for (a, b) in pairs for c in alphabet]# 返回集合return set(deletes + transposes + replaces + inserts)def edits2(word):"""返回跟输入单词是2距离的单词寻找跟word编辑距离为1的单词的编辑距离为1的单词就是编辑距离为2的单词"""return {e2 for e1 in edits1(word) for e2 in edits1(e1)}
说明:与该单词编辑距离为2的单词,也就是与该单词的编辑距离为1的单词
的编辑距离为1的单词。
最后我们验证一下效果:
if __name__ == '__main__':original_word = 'fianlly helloa cta'correct_word = correct_text_generic(original_word)print('Original word:%s\nCorrect word:%s' % (original_word, correct_word))
输出结果如下:
Original word:fianlly helloa cta
Correct word:finally hello cat
2. word文档纠错(CorrectDocx.py)
1.导入相关模块,读取doc文档
from docx import Document
from nltk import sent_tokenize, word_tokenize
from CorrectWords import correct_text_generic
from docx.shared import RGBColor# 文档中修改的单词个数
count_correct = 0
# 获取文档对象
file = Document("ErrorDocument.docx")
# 各种标点符号
punkt_list = r",.?\"'!()/\\-<>:@#$%^&*~"
document = Document() # word文档句柄
2.创建修改每一段中错误单词的函数如下:
def write_correct_paragraph(i):"""修改一个段落中的错误"""global count_correct# 每一段的内容paragraph = file.paragraphs[i].text.strip()# 进行句子划分sentences = sent_tokenize(text=paragraph)# 词语划分words_list = [word_tokenize(sentence) for sentence in sentences]# 段落句柄p = document.add_paragraph(' ' * 7)for word_list in words_list:for word in word_list:if word not in punkt_list:p.add_run(' ')# 修改单词,如果单词正确,则返回原单词correct_word = correct_text_generic(word)# 每一句话第一个单词的第一个字母大写,并空两格if word_list.index(word) == 0 and words_list.index(word_list) == 0:correct_word = correct_word[0].upper() + correct_word[1:]# 如果单词有修改,则颜色为红色if correct_word != word:colored_word = p.add_run(correct_word)font = colored_word.fontfont.color.rgb = RGBColor(0xFF, 0x00, 0x00)count_correct += 1else:p.add_run(correct_word)else:p.add_run(word)
- 将
count_correct
设置为全局变量,将一段话划分为句子列表,再将每一句话划分为单词列表。 - 对每一个单词进行遍历,如果是punkt_list中的符号,直接添加;如果是单词,先加一个空格(英语句子中两个单词之间得有空格)。
- 接着纠正单词,调用的是CorrectWords.py中的
correct_text_generic()
函数。 - 如果是一句话的开头,第一个单词首字母大写。
- 如果单词有修改,设置其颜色为红色,文档中修改的单词个数加1。
最后我们验证一下效果:
if __name__ == '__main__':print("段落数:" + str(len(file.paragraphs)))for i in range(len(file.paragraphs)):write_correct_paragraph(i)document.save("CorrectDocument.docx")print("修改并保存文件完毕!")print("一共修改了%d处。" % count_correct)
输出结果如下:
段落数:4
修改并保存文件完毕!
一共修改了19处。
ErrorDocument.docx文档
CorrectDocument.docx文档
编辑距离
什么是两个字符串的编辑距离(edit distance)?给定字符串s1和s2,以及在s1上的如下操作:
- 删除(delete)一个字符
- 交换(transpose)两字符位置
- 替换(replace)一个字符
- 插入(insert)一个字符
最少需要多少次操作才能使s1转换为s2?
比如:
单词“cat”和“hat”,这样的操作最少需要1次,只需要把“cat”中的“c”替换为“h”即可。
单词“recall”和“call”,这样的操作最少需要2次,只需要把“recall”中的“r”和“e”去掉即可。
单词“Sunday”和“Saturday”,这样的操作最少需要3次,在“Sunday”的“S”和“u”中插入“a”和“t”,再把“n”替换成“r”即可。
以上操作对应的编辑距离分别为1,2,3。
那么,是否存在一种高效的算法,能够快速、准确地计算出两个字符串的编辑距离呢?
动态规划算法
我们使用动态规划算法(Dynamic Programming)来计算出两个字符串的编辑距离。
我们从两个字符串s1和s2的最末端向前遍历,假设s1的长度为m,s2的长度为n,算法如下:
1.如果两个字符串的最后一个字符一样,那么,我们就可以递归地计算长度为m-1和n-1的两个字符串的情形;
2.如果两个字符串的最后一个字符不一样,那么,进入以下三种情形(选择其后续总共需要操作步骤最少的):
- 删除:递归地计算长度为m-1和n的两个字符串的情形,这是在s1中的末端删除了一个字符;
- 替换:递归地计算长度为m-1和n-1的两个字符串的情形,这是因为把s1中末端字符替换成了s2的最后一个字符,这样s1和s2的末端字符一样,就是1中情形;
- 插入:递归地计算长度为m和n-1的两个字符串的情形,这是因为在s1中的末端插入了一个s2的最后一个字符,这样s1和s2的末端字符一样,就是1中情形;
如果m为0,则至少需要操作n次,即在s1中逐个添加s2的字符,一共是n次;如果n为0,则至少需要操作m次,即把s1的字符逐个删除即可,一共是m次。
python实现(DynamicProgramming.py)
计算编辑距离的函数如下:
# 计算编辑距离
def edit_distance(s1, s2):print("---------------------------------------")print("s1的值:", s1)print("s2的值:", s2)# s1 长度mm = len(s1)# s2 长度nn = len(s2)# 如果m = 0,则至少还需要操作n次,即在s1中逐个添加s2的字符if m == 0:if n > 0:return nelse:return 0# 如果n = 0,则至少还需要操作m次,即在s1中逐个删除字符# 如果m =0,n = 0,则不需要再操作if n == 0:if m > 0:return melse:return 0# s1和s2最后一个字符一样if s1[-1] == s2[-1]:return edit_distance(s1[:-1], s2[:-1])else: # 最后一个字符不一样# 插入 删除 替换return 1 + min(edit_distance(s1, s2[:-1]), edit_distance(s1[:-1], s2), edit_distance(s1[:-1], s2[:-1]))
- 如果s1长度m已经为0,s2长度n不为0,则还需要s2长度n的操作次数,即在s1中逐个添加s2的字符。同理,如果s2长度n已经为0,s1长度m不为0,则还需要s1长度m的操作次数,即把s1的字符逐个删除即可。
- 如果s1和s2最后一个字符一样,直接递归迭代长度为m-1和n-1的两个字符串的情形
- 如果s1和s2最后一个字符不一样,则操作数加1,且选择插入、删除、替换,三操作中
以后递归迭代完毕后需要的操作总次数最少
的那个操作。
最后我们验证一下效果:
if __name__ == '__main__':string1 = "Sunday"string2 = "Saturday"distance = edit_distance(string1, string2)print("编辑距离:", distance)
输出结果如下:
编辑距离: 3
参考博客:
- 用 Python 实现英文单词纠错功能
- 用动态规划算法计算字符串的编辑距离
python脚本实现英文单词纠错相关推荐
- 网页版python叫什么-python脚本和网页有何区别
Python是一种计算机程序设计语言,一种面向对象的动态类型语言,一种脚本语言.最初被设计用于编写自动化脚本(shell)的,常用于各种服务器的维护和自动化运行.它具有丰富和强大的库.它常被昵称为胶水 ...
- 网页运行python脚本_python脚本和网页有何区别
Python是一种计算机程序设计语言,一种面向对象的动态类型语言,一种脚本语言.最初被设计用于编写自动化脚本(shell)的,常用于各种服务器的维护和自动化运行.它具有丰富和强大的库.它常被昵称为胶水 ...
- 命令行运行Python脚本时传入参数的三种方式
三种常用的方式 如果在运行python脚本时需要传入一些参数,例如gpus与batch_size,可以使用如下三种方式. python script.py 0,1,2 10 python script ...
- Python脚本语言写法
Python脚本语言写法 脚本语言的开始行,是指文件中的代码用什么可执行程序去运行它,就这么简单. #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的pyth ...
- python 脚本撞库国内“某榴”账号
其实日常生活中我们的用户名和密码就那么几个,所以这给撞库带来了可能,本文主要给出python脚本撞库的一点粗浅代码.这里只讨论技术本生,代码中某榴的地址也已经改掉,避免被管理员误解禁言等发生,谢谢大家 ...
- python脚本编写_【PyQGIS】编写用于处理框架(QGIS3)的Python脚本
可以编写可通过QGIS中的Python控制台运行的独立pyqgis脚本.进行一些调整,即可使您的独立脚本通过处理框架运行.这具有几个优点.首先,获取用户输入和写入输出文件要容易得多,因为Process ...
- python脚本——图片重命名、图片合成视频、faster-rcnn画P-R曲线
调试faster rcnn算法实用的python脚本 目录 调试faster rcnn算法实用的python脚本 一.前言 二.常用python脚本 三.后记 一.前言 最近在做关于目标检测算法的研究 ...
- Blender中的Python脚本介绍学习教程
Blender中的Python脚本介绍学习教程 MP4 |视频:h264,1280×720 |音频:AAC,48000 Hz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:1.63 ...
- 【python】使用python脚本将LFW数据中1672组同一个人多张照片拷贝出来
使用python脚本将LFW数据中1672组同一个人多张照片拷贝出来 dataCleaning4multiple.py 源码如下: import os, random, shutil import s ...
最新文章
- Python--yield关键字的使用
- 【Flask项目2】定制统一的JSON返回格式(6)
- 【CentOS7】【docker】常用操作命令
- 几款Java开发者必备常用的工具,准点下班不在话下
- 13.相机和图像——介绍,太阳摄影机,成像系统,图像形成,光圈(Aperture)_1
- 计算机学术英语常见词汇短语总结
- 【Hadoop】HDFS数据复制
- 征集 | AAAI 2021线下论文预讲会讲者征集
- matlab里的deploy,MATLAB deploytool simulink未定义函数'load_system'
- Java项目开发规范参考
- Python爬虫实战 | (13) 爬取新浪滚动新闻
- java 调用gephi_Gephi可视化(一)——使用Gephi Toolkit创建Gephi应用
- 【CXY】JAVA基础 之 语法基础
- AnyviewC编程作业系统——支持程序可视化运行、调试和测评
- java blueprint_OSGI Blueprint入门之四
- 别踩白块_前端H5游戏毕设
- C++入门第一阶段——基础篇
- 使用Python和C++的写数据结构和算法
- SpringBoot 出现 Content type ‘application/x-www-form-urlencoded;charset=UTF-8‘ not supported
- oracle字符串函数(转)
热门文章
- arduino笔记26:8*8LED矩阵
- PX4的CMake解析
- 漫画图解JWT设计单点登录系统
- 0x00007FF9BF948828 (Qt5Core.dll) (QtGuiApplication3.exe 中)处有未经处理的异常: 请求了严重的程序退出——解决方案
- Vue2的路由和异步请求
- VScode设置字体大小
- 怎么解决长期戴口罩脸过敏的肌肤问题
- 一只奇鸽 for app —— 集工具、功能于一身的有趣应用
- 从作用域到作用域链,思维脑图+代码示例让知识点一目了然!系列(三)
- pipenv使用教程