NLTK之词频

FreqDist类

官方文档:nltk.probability.FreqDist

介绍:词的概率分布类,包含一些概率学的方法。

所在文件:probability.py ,大约在63~427行

创建

定义:__init__(self, samples=None)

1
2
3
4
5
6
7
import nltk
from nltk.book import *
fdist = FreqDist(gutenberg.words('chesterton-thursday.txt'))
print fdist

outputs:
<FreqDist with 6807 samples and 69213 outcomes>

上面的69213 outcomes是指有69213个词,而6807 samples是指有6807不重复的词。

通过下述命令可以确认这一点:

1
2
3
4
5
6
len(wds)
len(set(wds))

outputs:
69213
6807

词数量

1
2
3
4
fdist.N()

outputs:
69213

不重复词的数量(样本数量)

1
2
3
4
fdist.B()

outputs:
6807

生成副本

1
2
3
4
5
6
7
8
9
10
11
12
fdist2 = fdist
fdist3 = fdist.copy() #make copy
fdist == fdist2
fdist == fdist3
fdist is fdist2
fdist is fdist3

outputs:
True
True
True
False

词频率

定义:freq(sample)

sample是词,返回值∈[0, 1]

1
2
3
4
fdist.freq('the')

outputs:
0.04754887087685839

可见,the的词频是0.0475,也就是4.75%

词频度(出现次数)

1
2
3
4
fdist['the']

outputs:
3291

词和词频的对

1
2
3
4
5
items = fdist.items()
print items[:5]

outputs:
[(u'yellow', 16), (u'four', 18), (u'Does', 6), (u'conjuring', 2), (u'marching', 4)]

只出现一次的词

会一次性全部返回,下面只展示前5个。

1
2
3
4
5
hapaxes = fdist.hapaxes()
print hapaxes[:5]

outputs:
[u'disturb', u'buttonhole', u'himselves', u'woods', u'wavered']

出现次数最多的词

1
2
3
4
fdist.max()

outputs:
u','

u是unicode的标志,说明,出现的最多。

这明显不符合预期,因为,不应该被算作一个词,这会干扰我们分析文本,所以在我们创建FreqDist实例的时候,应该要把词集进行过滤,剔除掉标点符号。

词频分布介绍

定义:pformat(maxlen=10)

返回一个可以代表本FreqDist类的字符串,其实就是把高频词和频度列出来了。

maxlen 表示打印多少个高频词

1
2
3
4
fdist.pformat()

outputs:
u'FreqDist({u\',\': 3488, u\'the\': 3291, u\'.\': 2717, u\'a\': 1713, u\'of\': 1710, u\'and\': 1568, u\'"\': 1336, u\'to\': 1045, u\'in\': 888, u\'I\': 885, ...})'

频率分布图

定义:plot(args, \\*kwargs)

可以指定的参数有

  • title 表示图像的标题
  • cumulative 是否累计频度

如果第一参数是整数的话,它表示展示词的数量

1
fdist.plot(50)

这样看起来是不是别扭呢,因为曲线肯定会下降(频度越高的词越显示在左边)

所以有时候我们需要看这些高频词加起来占了多少,所以:

1
fdist.plot(50, cumulative=True)

可以发现,曲线越到后面越平缓。

频率分布表

1
2
3
4
5
fdist.tabulate(10)

outputs:
, the . a of and
3488 3291 2717 1713 1710 1568

累计表:

1
2
3
4
5
fdist.tabulate(10,cumulative=True)

outputs:
, the . a of and
3488 6779 9496 11209 12919 14487

FreqDist的应用

前面已经介绍了Text,结合本次的FreqDist类,我们已经可以做一些基础的NLP研究了。

理解文本的主题或风格

高频词

text1 变量保存的是《白鲸记》Text类实例,高频词是否可以帮助我们理解呢?

打印出它前50个词的频率分布图可以发现:

1
2
fdist1 = text1.vocab()
fdist1.plot(50, cumulative=True)

相比theof 这类助词,whale的频率相对靠后,而这个词恰恰是《白鲸记》中比较重要的词,可以体现出文本的主题。

低频词

既然高频率词没啥帮助,那只出现一次的词呢?

1
2
3
4
len(fdist1.hapaxes())

outputs:
9002

emmm,貌似低频词太多了,如果不看上下文,我们也很难借助低频词来理解文本主题。

细粒度的选择词

或许长词包含的信息比较多,是否有帮助呢?

我们考虑以下哪些成都大于15的词:

1
2
3
4
5
6
7
8
9
10
11
12
samples = set(text1)
print [w for w in samples if len(w) > 15]

outputs:
[u'hermaphroditical', u'subterraneousness', u'uninterpenetratingly',
u'irresistibleness', u'responsibilities', u'comprehensiveness',
u'uncompromisedness', u'superstitiousness', u'uncomfortableness',
u'supernaturalness', u'circumnavigating', u'cannibalistically',
u'circumnavigations', u'indispensableness', u'preternaturalness',
u'apprehensiveness', u'CIRCUMNAVIGATION', u'simultaneousness',
u'undiscriminating', u'characteristically', u'Physiognomically',
u'physiognomically', u'circumnavigation', u'indiscriminately']

似乎比低频词的数量要少很多了,可以发现的是,长词 大多也是 低频词,所以从这个角度来说我们忽略了短高频词和大部分低频词,也许寻找那些长高频词会比较有帮助。

长高频词

下面将选择出那些长度大于7,且出现的频度也大于7的词:

1
2
3
4
print [w for w in samples if len(w) > 7 and fdist1[w] > 7]

outputs:
[u'uncertain', u'bringing', u'substance', u'cannibal', u'therefore', u'violently', u'whalebone' ...]

其实得到的结果数量还是很多,但我们已经看到了whalebone 这个词,可以大致确定下来《白鲸记》的主题了,所以寻找长高频词可以有效的帮助我们理解文本的主题。

搭配词

文中出现的搭配词能非常明确的体现文本的类型。

下面寻找搭配词:

1
2
3
4
5
6
7
text1.collocations()

outputs:
Sperm Whale; Moby Dick; White Whale; old man; Captain Ahab; sperm
whale; Right Whale; Captain Peleg; New Bedford; Cape Horn; cried Ahab;
years ago; lower jaw; never mind; Father Mapple; cried Stubb; chief
mate; white whale; ivory leg; one hand

可以看到第一个词就是Sperm Whale

计算其他东西

词长分布

也就是计算每个长度的词出现的次数

1
2
3
4
5
6
lens = [len(w) for w in text1]
fdistw = FreqDist(lens)
fdistw.keys()

outputs:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20]

可以看到text1中的词,最短长度是1,最长长度是20

1
fdistw.plot()

可以发现长度大于8和之后的的词出现的频度已经很小了

1
2
3
4
fdistw.max()

outputs:
3

可以发现,最频繁出现的词长度是3。

ConditionalFreqDist类

官方文档:nltk.probability.ConditionalFreqDist

介绍:条件频率分布类

所在文件:probablity.py,大约在1734~2003

创建

定义:__init__(cond_samples=None):

1
2
3
4
5
6
7
8
9
import nltk
from nltk.book import *
from nltk.probability import ConditionalFreqDist
cond_samples = [(len(w), w) for w in text1]
cfdist = ConditionalFreqDist(cond_samples)
print cfdist

outputs:
<ConditionalFreqDist with 19 conditions>

cond_samples参数是元组集合,元组是(条件, 词),上述代码的条件是词长,下面看看效果你就懂了:

1
2
3
4
cfdist[3]

outputs:
FreqDist({u'the': 13721, u'and': 6024, u'his': 2459, u'was': 1632, u'all': 1462, u'for': 1414, u'but': 1113, u'not': 1103, u'him': 1058, u'one': 889, ...})

这列出了所有长度为3的词,这意味着元组的第一个元素条件被作为了cfdist的键,而它值是一个FreqDist对象。

查看所有条件

1
2
3
4
cfdist.conditions()

outputs:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20]

条件频率

1
2
3
4
cfdist[3].freq('but')

outputs:
0.02216116122095454

长度为3的条件下,but出现的频率是0.02216

条件频率分布图

定义:plot(args, \\*kwargs)

可以指定的参数有

  • samples list 要显示的样本集合
  • title str 标题
  • conditions list 要显示的条件,默认是全部

为了便于理解,下面把cfdist换成以文本为条件:

1
2
3
4
5
6
7
8
9
10
11
from nltk.corpus import brown
cond_samples = [
(genre, word)
for genre in ['news', 'romance']
for word in brown.words(categories=genre)
]
cfdist = ConditionalFreqDist(cond_samples)
print cfdist

outputs:
<ConditionalFreqDist with 2 conditions>

这里我们取了两个文体:newsromance,看看两者中情态动词的频率分布吧:

1
cfdist.plot(samples=['would', 'will', 'could', 'can', 'might', 'may', 'should'])

哈哈,大致符合预期呢,新闻中的will频率异常的高。

条件频率分布表

定义:tabulate(args, \\*kwargs)

可以指定的参数有

  • samples list 要显示的样本集合
  • title str 标题
  • conditions list 要显示的条件,默认是全部
1
2
3
4
5
6
cfdist.tabulate(samples=['would', 'will', 'could', 'can', 'might', 'may', 'should'])

outputs:
would will could can might may should
news 244 389 86 93 38 66 59
romance 244 43 193 74 51 11 32

ConditionalFreqDist的应用

用词分析

之前对比了newsromance两个文体中,情态动词的频率分布,发现了news 的will词用的频率很高。

生成随机文本

如果知道了一个词如living,也知道了living这个词的条件频率分布,那么可以找到最有可能出现在living后面的那个词,这样迭代的进行计算,可以获得一个随机文本。根据上述方法,可以定义一个函数用来生成随机文本:

1
2
3
4
def random_text(cfdist, word, num=15):
for i in range(num):
print word,
word = cfdist[word].max()

我们导入《创世纪》文本:

1
2
from nltk.corpus import genesis
text = genesis.words('english-kjv.txt')

为创建条件概率分布类,我们需要cond_samples,所以我们可以构造一些双连词bigrams 可以从数组 生成 双连词

1
2
3
4
5
6
7
from nltk import bigrams
cond_samples = bigrams(text)
cfdist = ConditionalFreqDist(cond_samples)
random_text(cfdist, 'living')

outputs:
living creature that he said , and the land of the land of the land

可以发现。。效果不怎么好,因为后面无限循环了。。

我们试试其他词:

1
2
3
4
random_text(cfdist, 'I')

outputs:
I will not be a son , and the land of the land of the

哈哈,最后还是无限循环了,不过挺有趣的!

坚持原创文章分享,您的支持将鼓励我继续创作!