WANG LH , Research & Development

NLP-考研英语真题词汇统计

2021.05.31 22:05

代码:https://github.com/Kingsea442/nlp_word/tree/master
生成词汇top1000:http://www.wanglh.top/post/blog/kao-yan-ying-yu-gao-pin-ci-hui

使用wordcloud分析前1000个重要词汇生成的词云图。

核心思想

想法:首先一般阅读中出现的不认识的词汇可能就那么几个不常见的,所以过滤掉常见的词,比如the,and,if,you等,剩下的应该就是比较重要的词汇。所以可以直接采用TF-IDF简单粗暴提取英语阅读中的重要词汇。

TF-IDF算法介绍

TF-IDF(term frequency–inverse document frequency,词频-逆向文件频率)是一种用于信息检索(information retrieval)与文本挖掘(text mining)的常用加权技术。
TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。

TF是词频(Term Frequency)

词频(TF)表示词条(关键字)在文本中出现的频率。这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。
向文件频率 (IDF) :某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。
![](./_image/2020-11-18/2020-11-19-00-04-03.jpg文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
TF-IDF=TF * IDF

python爬虫题库

用python直接爬取都学网的考研英语题库,2010到2020年共11年的题库。分析都学网网站,可以直接通过接口请求获取到文档数据,不需要解析html,比较方便。

清洗文档数据

爬取后的数据还是会有些特殊符号,标点符号,需要进行清洗,采用下面代码清洗数据。

class TxtUtils :
    def clean_txt(txt):
        import re
        p1 = re.compile(r'-\{.*?(zh-hans|zh-cn):([^;]*?)(;.*?)?\}-')
        p2 = re.compile(r'[(][: @ . , ?!\s][)]')
        p3 = re.compile(r'[「『]')
        p4 = re.compile(r'[\s+\.\!\/_,$%^*(+\"\')]+|[+——()?【】“”!,。?、~@#¥%……&*()0-9 , : ; \-\ \[\ \]\ ]')
        txt = p1.sub(r' ', txt)
        txt = p2.sub(r' ', txt)
        txt = p3.sub(r' ', txt)
        txt = p4.sub(r' ', txt)
        return txt

分词

采用nltk对文档进行分词。

nltk参考文档.

from nltk.tokenize import word_tokenize
tokenize_words = word_tokenize(txt, language='english', preserve_line=True)

自定义停用词库

使用nltk自带的停用词只能过滤掉一些语气词比如emm,还远不能过滤掉我们想过滤掉的词比如 the,and,he,you这些简单词汇。从两方面处理,假设文档出现次数最多的词肯定不是我们不认识的词,可以过滤掉,假设4个字母一下的单词我们也都认识,可以直接过滤掉。这样只保留长度在四个以上,出现次数并不是很多的单词。然后用自己准备的停用词库,再去分词。

分析词的原型

分完词之后,取词的原型进行后续的计算,有待优化。

 lemmatizer = WordNetLemmatizer()
 lemmatizer.lemmatize('cats')
# 输出cat

统计词频和TF和逆文档频率IDF

采用nltk的FreqDist生成词典,进一步分析单词,算出tf-idf,最终得到一个分值。

from __future__ import division

import math
import os

import nltk
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from nltk.tokenize import word_tokenize

import constant
from file_utils import FileUitls
from stopwords import PapperStopWords
from txt_utils import TxtUtils


def dist(txt):
    tokenize_words = word_tokenize(txt, language='english', preserve_line=True)
    filter_tokenize_word = [w for w in tokenize_words if not w in stop_words]
    return nltk.FreqDist(filter_tokenize_word)

def count_word_document(word, document_freq_dists):
    count = 0
    for fd in document_freq_dists:
        if fd.get(word) is not None:
            # count = count + 1
            count = count + fd.get(word)
    return count

def top200(word_dict):
    for k, v in word_dict.items():
        if v > 0.01 and v < 0.1:
            print(k, v)


if __name__ == '__main__':
    file_parent_path = constant.paper_data_file_path

    stop_words = set(stopwords.words('english'))
    for word in PapperStopWords.load_stop_words():
        stop_words.add(word)

    dir = os.listdir(file_parent_path)
    document_count = len(dir)
    print(document_count)

    freq_dist = []
    for f in dir:
        f_content = open(file_parent_path + f, 'r').read()
        f_content = TxtUtils.clean_txt(f_content)
        fd = dist(f_content)
        freq_dist.append(fd)

    word_tf_idf = {}
    word_freq = {}
    for fd in freq_dist:
        items = fd.items()
        total_word_count = fd.N()

        for k, v in items:
            tf = v / total_word_count

            word_in_document_count = count_word_document(k, freq_dist)
            idf = math.log((document_count / (word_in_document_count + 1)))

            tf_idf = tf * idf + math.log(min(10, len(k)))

            word_tf_idf[k] = [tf, idf, tf_idf]
    result = sorted(word_tf_idf.items(), key=lambda d: d[1][2])
    result.reverse()

    lemmatizer = WordNetLemmatizer()
    for k, v in result:
        k = lemmatizer.lemmatize(k)
        if len(k) >= 4:
            word_freq[k] = v[2]
            print(k,  v)

    final_result = ''
    for k, v in word_freq.items():
        line = str.format('{} {}\n', k, round(v, 7))
        final_result = final_result + line
    FileUitls.write(final_result, constant.result_file)

一般单词越长,一般会越陌生,所以这里可以把单词的长度作为一个影响因素计算到单词总分值中,这样最后排序,保存结果

生成词云

用wordcloud可以定制生成词语。

import src.constant
from file_utils import FileUitls

lines = FileUitls.reade_lines(src.constant.result_file)

result_cn = ''
word_freq = {}
for l in lines:
    l = str.replace(l, '\n', '')
    split = l.split(' ')
    word_freq[split[0]] = float(split[1])

w = WordCloud(background_color='white', width=800, height=450, scale=1.5)
w.generate_from_frequencies(word_freq)
w.to_file("top_word.jpg")

根据给定的图片生成词云

import FileUitls
from wordcloud import WordCloud, ImageColorGenerator
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image


lines = FileUitls.reade_lines(src.constant.result_file)

result_cn = ''
word_freq = {}
for l in lines[0:500]:
    l = str.replace(l, '\n', '')
    split = l.split(' ')
    word_freq[split[0]] = float(split[1])
print(word_freq)
color_mask = np.array(Image.open("/Users/wlh/asea/workspace/python/nlp_word/data/wlh.png"))

w = WordCloud(mask=color_mask, background_color='white', width=1000, height=1000)
w.generate_from_frequencies(word_freq)
image_colors = ImageColorGenerator(color_mask)

# 在只设置mask的情况下 会得到一个拥有图片形状的词云 axis默认为on 会开启边框
plt.imshow(w, interpolation="bilinear")
plt.axis("on")
plt.savefig("a.jpg")

# 直接在构造函数中直接给颜色 这种方式词云将会按照给定的图片颜色布局生成字体颜色策略
plt.imshow(w.recolor(color_func=image_colors), interpolation="bilinear")
plt.axis("on")
plt.savefig("w_i.jpg")
w.to_file("w.jpg")

单词翻译

直接调用百度api,翻译top n的单词即可。

待优化

使用nltk分析单词命名实体,去掉单词中人名,地名等专有名词。

代码

https://github.com/Kingsea442/nlp_word/tree/master