关闭

使用Python+NLTK实现英文单词词频统计

标签: pythonnltk词频统计词形还原
2920人阅读 评论(0) 收藏 举报
分类:

使用Python+NLTK实现英文单词词频统计

应用场景

本人近来想要提高英语水平,决定从直接看英文书籍开始做起,在选择英文书的时候,我需要了解这本书的词汇量以及词频,这样在遇到生词的时候,我可以有针对性的对那些出现频率高的单词着重记忆。

在真正开始做的时候,发现统计词频并不像想象中那样简单。需要考虑到词形变化,动词、名词、形容词、副词、代词等等,这些词都会在有若干种不同的形式,来表达不同的意思。例如move和moved,这显然应该归为一个词而不应该分为两个词来统计,于是,我们需要对各种形式的单词进行词形还原(lemmatization)。

Fork Me

本项目已在github上开源,大家可以随意fork、下载,欢迎大家指出程序中的错误和不足,共同学习!
https://github.com/Sailingboat1988/wordcounter

参考运行环境

  • Python 2.7.12
  • NLTK 3.2.2
  • Ubuntu 16.04 LTS

流程步骤图

操作流程如下图:

Created with Raphaël 2.1.0开始读取文件过滤特殊符号以及还原常见缩写单词分词词形还原统计词频结果写入文件结束

其中词形还原需要用到NLTK,同时也是本文着重描述的地方。

详细步骤

以下为详细步骤。每一步骤中所涉及到的代码仅作为举例说明,如需完整代码,请参见文章开始或结尾处的github地址。

读取文件

在Python中,可以使用with open (file) as f: 来进行文件操作,例如:

with open (file) as f:
    for line in f:
        do something...

过滤特殊符号以及还原常见缩写单词

因为我们要统计的是词频,因此像双引号、单引号、句点、逗号等等这些符号都要过滤掉,只留单词。这些符号被去掉之后采用空格符号来占位。

通过使用正则表达式,可以达到此目的:

pat_letter = re.compile(r'[^a-zA-Z \']+')
new_text = pat_letter.sub(' ', text).strip().lower()

这里并没有过滤掉单引号,因为单引号在英文中同时也是缩写符号,例如 He's, I'm, I'd 等等。

接下来要对这种缩写形式进行转换,情形比较多,但都可以使用正则表达式来一一处理:

import re
# to find the 's following the pronouns. re.I is refers to ignore case
pat_is = re.compile("(it|he|she|that|this|there|here)(\'s)", re.I)
# to find the 's following the letters
pat_s = re.compile("(?<=[a-zA-Z])\'s")
# to find the ' following the words ending by s
pat_s2 = re.compile("(?<=s)\'s?")
# to find the abbreviation of not
pat_not = re.compile("(?<=[a-zA-Z])n\'t")
# to find the abbreviation of would
pat_would = re.compile("(?<=[a-zA-Z])\'d")
# to find the abbreviation of will
pat_will = re.compile("(?<=[a-zA-Z])\'ll")
# to find the abbreviation of am
pat_am = re.compile("(?<=[I|i])\'m")
# to find the abbreviation of are
pat_are = re.compile("(?<=[a-zA-Z])\'re")
# to find the abbreviation of have
pat_ve = re.compile("(?<=[a-zA-Z])\'ve")

new_text = pat_is.sub(r"\1 is", new_text)
new_text = pat_s.sub("", new_text)
new_text = pat_s2.sub("", new_text)
new_text = pat_not.sub(" not", new_text)
new_text = pat_would.sub(" would", new_text)
new_text = pat_will.sub(" will", new_text)
new_text = pat_am.sub(" am", new_text)
new_text = pat_are.sub(" are", new_text)
new_text = pat_ve.sub(" have", new_text)
new_text = new_text.replace('\'', ' ')

分词

由于是英文,所以分词很容易,使用str.split()方法就可以实现分词,split()方法默认采用“空白字符”(WhiteSpace String)作为分隔符,参见python文档关于split()方法的描述:

split(…)
S.split([sep [,maxsplit]]) -> list of strings
Return a list of the words in the string S, using sep as the delimiter string. If maxsplit is given, at most maxsplit splits are done. If sep is not specified or is None, any whitespace string is a separator and empty strings are removed from the result.

词形还原

词形还原所涉及的具体情况还挺多的,例如,动词有一般式、过去式、过去分词、现在分词、单数第三人称一般式,名词有一般形式、可数名词复数形式,形容词和副词有一般式、比较级、最高级。通过自己的写规则的方式显然不太现实,那么有没有现成的工具可以完成这些词形的还原工作呢?答案是有的,这就是NLTK了。

NLTK

NLTK (Natural Language Toolkit) 是一个基于Python平台用于自然语言处理的工具集,包含许多很强大的功能,而我们现在要用的是其众多功能之一的词形还原(lemmatization)。
NLTK 的安装过程请参见官网上的步骤。
官网链接

单词的TAG

在nltk中,用来描述一个单词的词性(含词形、时态等概念)的就是tag,请看下面这个例子:

import nltk
from nltk.tokenize import word_tokenize
tag = nltk.pos_tag(word_tokenize("John's big idea isn't all that bad."))

word_tokenize返回的值是一个list类型,它的元素类型是str,如下:

['John', "'s", 'big', 'idea', 'is', "n't", 'all', 'that', 'bad', '.']

细心的朋友可能会看出来,这不就相当于分词吗?没错,这就是分词,但是它分完的结果并不能为我们所用,因为包含了像 's . 这一类符号,这不是我们统计词频想要的,所以我们要自己分词,以方便我们过滤掉这些特殊符号或者还原这些特定的缩写。
上面的list结果传入nltk.pos_tag方法,可以获得每一个单词的词性,如下:

[('John', 'NNP'), ("'s", 'POS'), ('big', 'JJ'), ('idea', 'NN'), ('is', 'VBZ'), ("n't", 'RB'), ('all', 'PDT'), ('that', 'DT'), ('bad', 'JJ'), ('.', '.')]

这是一个list,它的元素类型是tuple,('John', 'NNP')表示”John”的词性是NNP(Proper noun, singular),即专有名词单数。这个NNP就称为一个TAG,在下面做词形还原的时候会用到。
查看更多tag解释请参见 Alphabetical list of part-of-speech tags used in the Penn Treebank Project

词形还原

词形还原需要用到nltk.stem.wordnet.WordNetLemmatizer,请参考如下代码:

from nltk.stem.wordnet import WordNetLemmatizer
lmtzr = WordNetLemmatizer()
new_word = lmtzr.lemmatize('taken', 'v')

new_word 的值是u'take',该方法是将动词过去分词taken还原成一般式,第二个参数’v’是一个描述字符,表示单词是一个动词,这种用于词形还原的“描述词性的字符”称之为pos(与上面的tag有关系但不是一回事),其他pos可以参见help(nltk.corpus.wordnet),如下:

| Data and other attributes defined here:
| ADJ = u’a’
| ADJ_SAT = u’s’
| ADV = u’r’
| MORPHOLOGICAL_SUBSTITUTIONS = {u’a’: [(u’er’, u”), (u’est’, u”), (u’…
| NOUN = u’n’
| VERB = u’v’

这个ADJ_SAT是个什么东西,我也没闹明白,知道的同学还请告知一下。
这些pos和上面的tag有相似的地方,我也不明白为什么在nltk中要保持这两套描述体系,在它们之间我们可以发现对应关系,进而可以写出一个方法,通过tag来获得pos,如下:

def get_wordnet_pos(treebank_tag):
    if treebank_tag.startswith('J'):
        return nltk.corpus.wordnet.ADJ
    elif treebank_tag.startswith('V'):
        return nltk.corpus.wordnet.VERB
    elif treebank_tag.startswith('N'):
        return nltk.corpus.wordnet.NOUN
    elif treebank_tag.startswith('R'):
        return nltk.corpus.wordnet.ADV
    else:
        return ''

必须说明一点,这个方法并不是我的原创,而是来自stackoverflow上一位大神的回答,参见wordnet-lemmatization-and-pos-tagging-in-python

接下来我们就可以利用lmtzr.lemmatize(word, pos)以及刚刚获得的pos来对单词进行还原了

统计词频

统计词频主要是依靠collections.Counter(word_list).most_common()方法来自动统计并排序,如下:

import collections
collections.Counter("How tragic that most people had to get ill before they understood what a gift it was to be alive".split())

result:

[('to', 2), ('a', 1), ('be', 1), ('what', 1), ('gift', 1), ('most', 1), ('ill', 1), ('people', 1), ('had', 1), ('it', 1), ('alive', 1), ('How', 1), ('that', 1), ('tragic', 1), ('they', 1), ('understood', 1), ('get', 1), ('was', 1), ('before', 1)]

结果写入文件

这一步骤很简单,就不做过多说明了,请看代码:

def write_to_file(words, file='results.txt'):
    f = open(file, 'w')
    for item in words:
        for field in item:
            f.write(str(field)+',')
        f.write('\n')

完整代码已放到Github上开源,请移步 https://github.com/Sailingboat1988/wordcounter

转载请注明出处!

1
1
查看评论

NLTK 词频统计(一) 词频统计,绘图,词性标注

内容简介 代码一,笔记简略版本 代码二,词频统计与pandas集合,分词词性提取与词频统计结合 代码一import FontCN_NLPtools as fts引用的是我自己写的一个类,是对我常用的一些方法的封装,code已经上传# 解决乱码问题 import matplotlib as mpl ...
  • FontThrone
  • FontThrone
  • 2017-07-29 21:27
  • 2277

Python 3.6 利用NLTK 统计多个文本中的词频

#!/usr/bin/env python # encoding: utf-8""" @author: wg @software: PyCharm @file: word_frequency_statistics.py @time: 2017/3/16 0016 10:...
  • u011389474
  • u011389474
  • 2017-03-16 14:04
  • 2788

词频统计包----nltk

来自 python自然语言处理
  • fennvde007
  • fennvde007
  • 2014-05-16 21:19
  • 4080

nltk(2)——频率分布&词操作

频率分布 函数FreqDist 函数FreqDist方法获取在文本中每个出现的标识符的频率分布。通常情况下,函数得到的是每个标识符出现的次数与标识符的map映射 标识符 出现次数 are 209 the 660 people 550 ...
  • wang735019
  • wang735019
  • 2016-12-29 21:54
  • 1116

自己动手写word2vec (二):统计词频

在我之前写的word2vec的大概流程中,第一步的分词使用jieba来实现,感觉效果还不错。第二步. 统计词频统计词频,相对来讲比较简单一些,主要在Python自带的Counter类基础上稍作改进。值得注意的是需要去掉停用词。所谓停用词,就是出现频率太高的词,如逗号,句号等等,以至于没有区分度。停用...
  • u014595019
  • u014595019
  • 2016-07-14 10:55
  • 8494

Python分词并进行词频统计

strs='1、大专以上学历,年龄在18-28岁之间;2、计算机相关专业、自动化、测控、生仪、机电、数学、物理等等理工科专业优先;' \ '3、热爱软件开发事业、有较强的逻辑思维能力,对IT行业抱有浓厚的兴趣并有志于在IT行业长远发展,创造个人价值(非销售、非保险岗...
  • data_scientist
  • data_scientist
  • 2017-01-23 16:11
  • 4914

用python统计词频脚本

  • 2012-04-02 17:23
  • 1KB
  • 下载

利用NLTK进行分句分词

1.输入一个段落,分成句子(Punkt句子分割器)import nltk import nltk.data def splitSentence(paragraph): tokenizer = nltk.data.load('tokenizers/punkt/english.pick...
  • baidu_27438681
  • baidu_27438681
  • 2017-03-05 17:16
  • 4121

NLTK在去停用词、分词、分句以及词性标注的使用

因为实习的缘故,所以有机会接触到了自然语言处理的一些方面。 这里主要总结一下在python环境下进行自然语言处理的相关包和可能会出现的相关错误,目前接触的都比较Low,但是还是想要记录下来。 Nltk是python下处理语言的主要工具包,可以实现去除停用词、词性标注以及分词和分句等。 安装nl...
  • qq_27717921
  • qq_27717921
  • 2017-03-09 22:16
  • 5003

如何利用python统计英文文章词频

应用介绍: 统计英文文章词频是很常见的需求,本文利用python实现。 思路分析: 1、把英文文章的每个单词放到列表里,并统计列表长度; 2、遍历列表,对每个单词出现的次数进行统计,并将结果存储在字典中; 3、利用步骤1中获得的列表长度,求出每个单词出现的频率,并将结果存储在频率字典中; ...
  • suqi791776
  • suqi791776
  • 2017-08-10 11:40
  • 751
    个人资料
    • 访问:3158次
    • 积分:52
    • 等级:
    • 排名:千里之外
    • 原创:2篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档