之前写的词频统计有点low(【自然语言处理】最简单的词频统计),给许老师看了以后提了许多改进意见,这里记录一下。
1 原代码
import operator
import string
contents = []
path = r"C:\Users\Lenovo\Desktop\corpus.tc.en"
with open(path, encoding='gb18030', errors='ignore') as f:
for line in f.readlines():
line.encode('latin-1', 'ignore')
contents.append(line)
dic = {}
t = []
for line in contents:
for i in line:
if i in string.punctuation:
line = line.replace(i, " ")
t = line.lower().split()
for word in t:
if word not in dic:
dic[word] = 1
else:
dic[word] = dic[word] + 1
res = sorted(dic.items(), key=operator.itemgetter(1), reverse=True)
path2 = r'C:\Users\Lenovo\Desktop\result.txt'
file = open(path2, 'w', encoding='utf-8')
for item in res:
file.write(item[0] + " ")
file.write(str(item[1]))
file.write("\n")
print("写入完成")
2 存在的问题
2.1 内存
我的思路是将该文件全部存到内存contents 中,然后再从内存中一行一行取。当文件很大时很容易爆内存,因此最好的解决方式是读一行处理一行。 ----------------------------20220323补充:-------------------------------------- 这个真的是很重要的一点,不要想着一下先读内存再处理,这真不中。
2.2 with open
打开文件最好选择用类似with open() as f 的方式,而不是file = open() 。因为前者更安全。 补充一点,用前者不需要加f.close() ,但后者需要加。
2.3 标点符号
在NLP 中标点符号不要去掉,这也算在tokens 里。
2.4 小写问题
不用转小写,因为很多专有名词(比如Apple )和转小写之后的意思(比如apple )截然不同。
2.5 get方法
get 方法是python 字典的方法,使用方式:
dict.get(key[, value])
value 可选,当key 不存在时,返回指定的value 值(充当设定默认值的作用)。
因此,下面这段代码:
if word not in dic:
dic[word] = 1
else:
dic[word] = dic[word] + 1
可以精简为:
dic[word] = dic.get(word, 0) + 1
2.6 字符串拼接
for item in res:
file.write(item[0] + " ")
file.write(str(item[1]))
file.write("\n")
上面的代码中,python 中字符串拼接的+ 号速度是很慢的。建议用类似的C 语言的形式去输出:(具体语法暂时没查到,但是tuple 类型确实可以这么用)
for item in res:
file.write("%s %d\n" % item)
2.7 编码
一会儿用国标码gb 一会儿用utf-8 太乱了,国际上一般使用的就是utf-8 。
2.8 接口与传参
建议处理成一个可以直接调用的函数接口,以后直接传入文件就可以得到结果。
if __name__ == "__main__":
/*
*/
假设我们有了handle() 和save() 函数,那我们可以这样写main 函数:
if __name__ == "__main__":
print(sys.argv)
save(sys.argv[2], handle(sys.argv[1]))
关于这个部分不理解的可以参考下面两篇文章: 如何简单地理解Python中的if __ name __ == ‘__ main __’ Python中 sys.argv[]的用法简明解释
2.9 关于r 、rb 、w 、wb
这一点老师并没有指出来,可能不是大问题。但老师给的代码里还是用到了rb 、wb 。查阅资料后发现这两个打开文件的方式适合于处理二进制文件。 更多参考:[Python] 详细解答 open 函数中 r 和 rb 的区别
2.10 tqdm(非必要)
当文件较大时,我们可以给处理过程加入进度条。 比如运行如下代码:
from time import sleep
from tqdm import tqdm
for i in tqdm(range(1000)):
sleep(0.01)
就可以得到这样的结果:(脑部进度条.gif) 如果涉及对某个数组的循环可以这样:
pbar = tqdm(["a", "b", "c", "d"])
for char in pbar:
sleep(1)
pbar.set_description("Processing %s" % char)
我们把需要执行循环的数组用tqdm() 一下,就可以显示出进度条了。 (可恶,我被上面这些操作误导了) 正确的思路可以参考我的另一篇文章:tqdm显示逐行读取文件并处理
3 修改代码
在修改了上面的问题后,词频统计的代码修改如下:
import sys
from tqdm import tqdm
def handle(srcf):
vocab = {}
count = 0
with open(srcf, "rb") as frd:
for line in frd:
count = count + 1
pbar = tqdm(total=count)
with open(srcf, "rb") as frd:
for line in frd:
tmp = line.strip()
if tmp:
tmp = tmp.decode("utf-8")
for word in tmp.split():
if word:
vocab[word] = vocab.get(word, 0) + 1
pbar.update(1)
pbar.close()
return vocab
def save(fname, obj):
with open(fname, "wb") as fwrt:
fwrt.write(repr(obj).encode("utf-8"))
def load(fname):
with open(fname, "rb") as frd:
tmp = frd.read().strip()
rs = eval(tmp.decode("utf-8"))
return rs
if __name__ == "__main__":
print(sys.argv)
save(sys.argv[2], handle(sys.argv[1]))
这个代码在编辑器上是跑不通的,需要使用cmd ,第一个参数是要处理的文件地址,第二个参数是保存的文件地址。我的使用截图如下:
4 补充
补充一下和老师的讨论记录,稍做记录:
|