python上手--10行代码读懂红楼梦
取名10行代码看懂红楼梦,是将介绍使用python代码来读红楼梦获取其主要人物。这里的思想就是词频统计,通过分析红楼梦小说文字中出现最多的词语,来概括说明红楼梦的核心人物和事情。实际上如果你能跟着往下看,就开始进入了自然语言处理的一些基础知识。
在正式进入读红楼梦之前,需要先铺垫一些词频统计相关知识。因此首先从英文的词频统计操作开始,其中的思想用到了大数据分布式处理里的mapreduce框架,在该框架中主要包括两个任务:map(映射)和reduce(规约)。这里不具体讨论MapReduce的思想和处理流程,我们来看一下在python中实现wordcount词频统计任务,进而来体验一下其基本思路。后面再来实现中文的词频统计分析,进而看懂红楼梦。
英文文章的词频统计
词频统计任务是一个非常常见的任务,也是相对较为简单的程序。任务就是从一段文字中将单词出现的次数统计出来,例如从ChinaDaily英文网站上关注一段新闻:
CHENGDU -- Rescuers have located 14 miners trapped underground in a flooded coal mine in Southwest China's Sichuan province, local authorities said Sunday.
The rescuers are clearing the shaft and drilling a deep hole to reach the trapped miners. They are also trying to pump and block water in a bid to prevent the rising of underground water levels and are sending more oxygen down the underground shaft.
The accident occurred at 3:26 pm Saturday at the Shanmushu coal mine owned by Sichuan Coal Industry Group in Gongxian County when 347 miners were working underground. A total of 329 escaped and four were killed.
Nearly 200 rescuers are racing against the clock to reach the trapped miners.
Due to communication interruptions in some mining areas, the workers were not immediately located. But through their consistent efforts, the rescue workers have finally located the remaining miners。
那这段话有多少个单词呢?最笨的办法就是一个个的数,但这显然不是我们想要的方式。既然有python,我们可以尝试使用程序来解决这个问题。
我们先来理清一下思路顺序。
(1)首先需要将这段文字从网站上拷贝下来或者爬取下来保存成文本文件;
(2)然后在python中读取该文件开始处理这个段落。因为是单词统计,很明显单词与单词之间主要分割标记就是空格,如果使用空格来分割段落文字,就可以将段落打散为一个个的单词列表了。不过同时看到段落中除了空格外,还有标点符号以及数字,这些也都需要去除。在整理好单词列表后,就可以使用map方式将所有单词与其出现的次数构建成<单词,次数>这种key-value结构对。
(3)然后使用reduce规约思想将这种结构对进一步处理,即将相同单词的次数累加,获得每个单词出现的频率。
根据思路我们来组织程序代码:
第一步,简单点,将新闻段落复制粘贴到记事本里,保存为news.txt文件。这个部分就不需要代码了。
第二步,python读这个文件,将段落读出来。这里定义个函数为readPara,即读取段落文字,函数参数为filename。定义完函数后就可以测试一下。
#定义一个读取段落文字的函数
def getPara(filename):
with open(filename,'r') as f:
content=f.readlines()
return content
#给定文本文件的位置
file="news.txt"
print(getPara(file))
测试结果返回一个列表,具体如下:
["CHENGDU -- Rescuers have located 14 miners trapped underground in a flooded coal mine in Southwest China's Sichuan province, local authorities said Sunday.\n", 'The rescuers are clearing the shaft and drilling a deep hole to reach the trapped miners. They are also trying to pump and block water in a bid to prevent the rising of underground water levels and are sending more oxygen down the underground shaft.\n', 'The accident occurred at 3:26 pm Saturday at the Shanmushu coal mine owned by Sichuan Coal Industry Group in Gongxian County when 347 miners were working underground. A total of 329 escaped and four were killed.\n', 'Nearly 200 rescuers are racing against the clock to reach the trapped miners.\n', 'Due to communication interruptions in some mining areas, the workers were not immediately located. But through their consistent efforts, the rescue workers have finally located the remaining miners。']
第三步,开始分割段落为单词。这里的任务包括去除其中的非单词字符,如标点符号和数字。
这个段落稍微有点复杂,那就是最后一个单词miners那有个中文的句号,需要先将其清除。清除的办法采用分割方法split函数:split('。')。分割后获得的为两个列表,这里只需要取第一个列表即可,因为第二个列表为标点符号句号。
for para in content:
paraText=para.split('。')
paraText=para[0]
然后在剩下的段落文本paraText中采用英文的句号继续分割:split('.'),形成多个句子的列表。
for para in content:
paraText=para.split('。')
paraText=para[0]
paraList=paraText.split('.')
紧接着就可以将句子打散为单词了。不过其中还有一些换行符号和非英文单词字符,可以使用python自带的isalpha函数来判断,isalpha函数就是用于判断整个单词是否都是字母组成,如果判断为真,说明就是单词,如果不是,就说明不是单词。这样做问题都不大,不过在本次段落中出现了一个China's,被误杀了。这种连接拼写确实不是很好处理,这里也只能先舍弃掉。后面再想办法来处理。
打散后,然后将单词再一一的添加到一个新的列表中,这样形成整个段落的英文单词列表。所以可以先定义一个words_list空列表,然后后面使用append方法将打散的单词添加进去。
同时在单词处理的时候,大小写还是需要注意的,这里将所有大写都变成小写即可。使用方法就是单词作为字符串对象,使用其lower函数即可。
整个过程的代码组织如下:
def mapper(filename):
words_list=[] #定义一个空列表
with open(filename,'r') as f: #打开段落所在的文本文件
content=f.readlines() #从头读到尾并保存到content列表中。默认会按段落分割
for para in content: #对每一个段落列表进行处理
para=para.split('。') #如果存在中文句号,将整个段落按句号分割,形成两个大的列表
para0=para[0].split('.') #取第一个列表,并使用英文句号分割,形成多个不含句号的文本列表
for item in para0: #对每一个文本列表进行处理
words=item.split(' ') #采用空格分割方式将文本列表打散为单词或者其他字符
for word in words: #对每个单词或其他字符组合进行判断
if word.isalpha()==False: #如果不是单词时
continue #就不执行下面的操作
words_list.append(word.lower()) #将所有的英文单词变成小写后一个个添加到words_list列表中
print(words_list) #打印测试查看最终获得的单词列表
运行后结果如下:
['chengdu', 'rescuers', 'have', 'located', 'miners', 'trapped', 'underground', 'in', 'a', 'flooded', 'coal', 'mine', 'in', 'southwest', 'sichuan', 'local', 'authorities', 'said', 'sunday', 'the', 'rescuers', 'are', 'clearing', 'the', 'shaft', 'and', 'drilling', 'a', 'deep', 'hole', 'to', 'reach', 'the', 'trapped', 'miners', 'they', 'are', 'also', 'trying', 'to', 'pump', 'and', 'block', 'water', 'in', 'a', 'bid', 'to', 'prevent', 'the', 'rising', 'of', 'underground', 'water', 'levels', 'and', 'are', 'sending', 'more', 'oxygen', 'down', 'the', 'underground', 'shaft', 'the', 'accident', 'occurred', 'at', 'pm', 'saturday', 'at', 'the', 'shanmushu', 'coal', 'mine', 'owned', 'by', 'sichuan', 'coal', 'industry', 'group', 'in', 'gongxian', 'county', 'when', 'miners', 'were', 'working', 'underground', 'a', 'total', 'of', 'escaped', 'and', 'four', 'were', 'killed', 'nearly', 'rescuers', 'are', 'racing', 'against', 'the', 'clock', 'to', 'reach', 'the', 'trapped', 'miners', 'due', 'to', 'communication', 'interruptions', 'in', 'some', 'mining', 'the', 'workers', 'were', 'not', 'immediately', 'located', 'but', 'through', 'their', 'consistent', 'the', 'rescue', 'workers', 'have', 'finally', 'located', 'the', 'remaining', 'miners']
除了上述说的China's外,其他的单词都进入了列表。
第四步,开始统计单词出现的次数,处理的时候可以先定义个空字典,然后读取列表中的单词,如果在字典中已存在,则将其出现次数累加,如果不存在,则将其次数设定为1:
for word in words_list:
if word in words_dict:
words_dict[word]+=1
else:
words_dict[word]=1
这里我们可以单独定义一个函数如reduce,其输入为第三步的单词列表。
def reduce(words):
words_dict={}
for word in words:
if word in words_dict:
words_dict[word]+=1
else:
words_dict[word]=1
return words_dict
将第三步的单词列表传入reduce函数,打印一下处理结果如下:
{'chengdu': 1, 'rescuers': 3, 'have': 2, 'located': 3, 'miners': 5, 'trapped': 3, 'underground': 4, 'in': 5, 'a': 4, 'flooded': 1, 'coal': 3, 'mine': 2, 'southwest': 1, 'sichuan': 2, 'local': 1, 'authorities': 1, 'said': 1, 'sunday': 1, 'the': 12, 'are': 4, 'clearing': 1, 'shaft': 2, 'and': 4, 'drilling': 1, 'deep': 1, 'hole': 1, 'to': 5, 'reach': 2, 'they': 1, 'also': 1, 'trying': 1, 'pump': 1, 'block': 1, 'water': 2, 'bid': 1, 'prevent': 1, 'rising': 1, 'of': 2, 'levels': 1, 'sending': 1, 'more': 1, 'oxygen': 1, 'down': 1, 'accident': 1, 'occurred': 1, 'at': 2, 'pm': 1, 'saturday': 1, 'shanmushu': 1, 'owned': 1, 'by': 1, 'industry': 1, 'group': 1, 'gongxian': 1, 'county': 1, 'when': 1, 'were': 3, 'working': 1, 'total': 1, 'escaped': 1, 'four': 1, 'killed': 1, 'nearly': 1, 'racing': 1, 'against': 1, 'clock': 1, 'due': 1, 'communication': 1, 'interruptions': 1, 'some': 1, 'mining': 1, 'workers': 2, 'not': 1, 'immediately': 1, 'but': 1, 'through': 1, 'their': 1, 'consistent': 1, 'rescue': 1, 'finally': 1, 'remaining': 1}
第五步,由此我们将每个单词出现的次数都统计了一下,最后需要对该字典按value方式排序,出现次数多的排列在前面,少的排列在后面,代码为:
def reduce(words):
words_dict={}
for word in words:
if word in words_dict:
words_dict[word]+=1
else:
words_dict[word]=1
words_dict=sorted(words_dict.items(),key=lambda x:x[1],reverse=True) #字典按value排序
return dict(words_dict) #排序后为元组列表,使用dict函数将其转换为字典
再来测试结果就得到了:
{'the': 12, 'miners': 5, 'in': 5, 'to': 5, 'underground': 4, 'a': 4, 'are': 4, 'and': 4, 'rescuers': 3, 'located': 3, 'trapped': 3, 'coal': 3, 'were': 3, 'have': 2, 'mine': 2, 'sichuan': 2, 'shaft': 2, 'reach': 2, 'water': 2, 'of': 2, 'at': 2, 'workers': 2, 'chengdu': 1, 'flooded': 1, 'southwest': 1, 'local': 1, 'authorities': 1, 'said': 1, 'sunday': 1, 'clearing': 1, 'drilling': 1, 'deep': 1, 'hole': 1, 'they': 1, 'also': 1, 'trying': 1, 'pump': 1, 'block': 1, 'bid': 1, 'prevent': 1, 'rising': 1, 'levels': 1, 'sending': 1, 'more': 1, 'oxygen': 1, 'down': 1, 'accident': 1, 'occurred': 1, 'pm': 1, 'saturday': 1, 'shanmushu': 1, 'owned': 1, 'by': 1, 'industry': 1, 'group': 1, 'gongxian': 1, 'county': 1, 'when': 1, 'working': 1, 'total': 1, 'escaped': 1, 'four': 1, 'killed': 1, 'nearly': 1, 'racing': 1, 'against': 1, 'clock': 1, 'due': 1, 'communication': 1, 'interruptions': 1, 'some': 1, 'mining': 1, 'not': 1, 'immediately': 1, 'but': 1, 'through': 1, 'their': 1, 'consistent': 1, 'rescue': 1, 'finally': 1, 'remaining': 1}
上述五步整个代码完整组织如下:
def mapper(filename):
words_list=[]
with open(filename,'r') as f:
content=f.readlines()
for para in content:
para=para.split('。')
para0=para[0].split('.')
for item in para0:
words=item.split(' ')
for word in words:
if word.isalpha()==False:
continue
words_list.append(word.lower())
return words_list
def reduce(words):
words_dict={}
for word in words:
if word in words_dict:
words_dict[word]+=1
else:
words_dict[word]=1
words_dict=sorted(words_dict.items(),key=lambda x:x[1],reverse=True)
return dict(words_dict)
filename="new.txt" #文件资源位置
wordlist=mapper(filename) #获取单词列表
wordCount=reduce(wordlist) #对词频进行统计
print(wordCount) #打印结果
上述过程就是单文件词频统计,在段落中出现次数最多的是the单词,这个对于段落内容理解没有意义,第二多的是miners、in 和to,这个miners应该是有意义的,in和to也是没有意义的单词。第三多的是underground,a,are和and,这里underground也是有意义的,后两个都没有意义。所以从前面排序结果来看,大概可以知道这个段落里讲的是underground miners,也就是地下挖矿的人,和他们有关。如果进一步往下看的话,出现两次的单词里基本上都把整个段落的内容概括了。
由此可以将没有意义,但出现频率很高的单词过滤掉,这类词在NLP自然语言处理里称之为stop words终止词,过滤的时候在上述代码中循环处理添加进列表的时候就可以判断,如果是终止词,就不添加,即:
def mapper(filename):
words_list=[]
list_stopWords=['the','a','and','or','is', 'are','to','in','at','by','of','but']#常见终止词列表
with open(filename,'r') as f:
content=f.readlines()
for para in content:
para=para.split('。')
para0=para[0].split('.')
for item in para0:
words=item.split(' ')
for word in words:
if word.isalpha()==False:
continue
if word in list_stopWords: continue #过滤终止词
words_list.append(word.lower())
return words_list
再来看词频统计结果:
'miners': 5, 'underground': 4, 'rescuers': 3, 'located': 3, 'trapped': 3, 'coal': 3, 'were': 3, 'have': 2, 'mine': 2, 'sichuan': 2, 'the': 2, 'shaft': 2, 'reach': 2, 'water': 2, 'workers': 2, 'chengdu': 1, 'flooded': 1, 'southwest': 1, 'local': 1, 'authorities': 1, 'said': 1, 'sunday': 1, 'clearing': 1, 'drilling': 1, 'deep': 1, 'hole': 1, 'they': 1, 'also': 1, 'trying': 1, 'pump': 1, 'block': 1, 'bid': 1, 'prevent': 1, 'rising': 1, 'levels': 1, 'sending': 1, 'more': 1, 'oxygen': 1, 'down': 1, 'accident': 1, 'occurred': 1, 'pm': 1, 'saturday': 1, 'shanmushu': 1, 'owned': 1, 'industry':1
这样再来看,就明白了这个新闻里说的就是,四川挖煤矿的工人被困在井下,救援者进行施救。所以词频统计对理解段落含义很有意义。
最后再来一个词云展示,也就是将上述词频统计里出现的词用一种图来表示出来,比较直观。具体实现的时候需要先安装一个wordcloud词云第三方库,matplotlib绘图库,然后先设置绘图背景,然后将词频统计结果放置到背景上呈现效果。整个代码组织如下:
from wordcloud import WordCloud, STOPWORDS #生成词云、通用词
import matplotlib.pyplot as plt # 在线显示
def mapper(filename):
words_list=[]
list_stopWords=['the','a','and','or','is', 'are','to','in','at','by','of','but']#常见终止词列表
with open(filename,'r') as f:
content=f.readlines()
for para in content:
para=para.split('。')
para0=para[0].split('.')
for item in para0:
words=item.split(' ')
for word in words:
if word.isalpha()==False:
continue
if word in list_stopWords: continue #过滤终止词
words_list.append(word.lower())
return words_list
def reduce(words):
words_dict={}
for word in words:
if word in words_dict:
words_dict[word]+=1
else:
words_dict[word]=1
words_dict=sorted(words_dict.items(),key=lambda x:x[1],reverse=True)
return dict(words_dict)
filename="new.txt" #文件资源位置
wordlist=mapper(filename) #获取单词列表
wordCount=reduce(wordlist) #对词频进行统计
#准备绘制词云图
wc = WordCloud(background_color="white",width=600, height=400, margin=5) #准备一个背景
wc.generate_from_frequencies(wordCount) #根据词频统计结果产生词云
plt.imshow(wc) #显示出来
plt.axis("off")
plt.show()
执行后效果如下:
词频统计思路就是如上分步骤所述,但具体到每篇文章,由于文章的格式、标准等都不一样,所以还需要具体问题去分析,需要哪些步骤来实现。
上述的词频统计是自然语言处理的一个最基本阶段,即Tokenization标识化。这部分任务在nltk自然语言处理库里调用其word_tokenize方法就可以完成。用法为:
import nltk
text="I love China and I was born in Hubei Province"
tokens=nltk.word_tokenize(text)
print(tokens)
另外还可以使用python自带的collections库里的counter函数,直接获得词频统计结果。我们上述的代码实际上就是将这个词频统计Counter函数进行了详细解析。例如:
import collections
text = "I love China and I was born in Hubei Province"
words=collections.Counter(text.split(' '))
print(words)
打印结果为:
Counter({'I': 2, 'love': 1, 'China': 1, 'and': 1, 'was': 1, 'born': 1, 'in': 1, 'Hubei': 1, 'Province': 1})
10行代码读懂红楼梦
中文文章词频统计任务相对英文要稍微复杂一些,因为英文单词与单词之间天然就用空格空隔开,所以很容易处理;但中文就不一样了,一段中文话里每个字与每个字之间没有天然的分割标记,而且还有含义的理解。比如“我看他们在跳舞”这句话,“我”是一个词,“看”是一个词,“他们”是一个词,“在”是一个词,“跳舞”是一个词。也就是一个词可能是一个字,也有可能是多个字构成。这样在处理的时候就麻烦了。如何确定是一个词语呢?
这里就需要引入前人所做的工作,把所有词语都统计好了,形成一个字典库,名称叫结巴分词,直接从cmd窗口使用pip install jieba命令就可以下载到本地:
pip install jieba
为了了解结巴分词模块的用法,可以去python安装目录下找到site-packages里的jieba文件夹,如下:
使用文本编辑器打开其中的_ init _.py文件,查看其源代码,定位其中常用的cut方法,即分词方法。
def cut(self, sentence, cut_all=False, HMM=True):
The main function that segments an entire sentence that contains
Chinese characters into seperated words.
Parameter:
- sentence: The str(unicode) to be segmented.
- cut_all: Model type. True for full pattern, False for accurate pattern.
- HMM: Whether to use the Hidden Markov Model.
sentence = strdecode(sentence)
if cut_all:
re_han = re_han_cut_all
re_skip = re_skip_cut_all
else:
re_han = re_han_default
re_skip = re_skip_default
if cut_all:
cut_block = self.__cut_all
elif HMM:
cut_block = self.__cut_DAG
else:
cut_block = self.__cut_DAG_NO_HMM
blocks = re_han.split(sentence)
for blk in blocks:
if not blk:
continue
if re_han.match(blk):
for word in cut_block(blk):
yield word
else:
tmp = re_skip.split(blk)
for x in tmp:
if re_skip.match(x):
yield x
elif not cut_all:
for xx in x:
yield xx
else:
yield x
def cut_for_search(self, sentence, HMM=True):
Finer segmentation for search engines.
words = self.cut(sentence, HMM=HMM)
for w in words:
if len(w) > 2:
for i in xrange(len(w) - 1):
gram2 = w[i:i + 2]
if self.FREQ.get(gram2):
yield gram2
if len(w) > 3:
for i in xrange(len(w) - 2):
gram3 = w[i:i + 3]
if self.FREQ.get(gram3):
yield gram3
yield w
代码中有关cut方法的参数说明:cut_all: 如果设置为true则为全模式分词,如果为false,就为精确分词,如果使用HMM就使用隐层马尔科夫模型。具体效果如何,使用代码来实践看看:
import jieba
text="我在看他们跳舞,我心里高兴得不得了。有时候我也想也许我也可以这样随便跳起舞来"
#设置参数cut_all=True,即全模式分词
word_sep1=list(jieba.cut(text,cut_all=True))
print("全模式分词效果为:",word_sep1)
#设置参数cut_all=False,即精确模式分词
word_sep2=list(jieba.cut(text,cut_all=False))
print("精确模式分词效果为:",word_sep2)
运行后结果返回列表结果如下:
全模式分词效果为 :['我', '在', '看', '他们', '跳舞', '', '', '我心', '心里', '高兴', '得', '不得', '不得了', '', '', '有时', '有时候', '时候', '我', '也', '想', '也许', '我', '也', '可以', '这样', '随便', '跳起', '起舞', '来']
精确模式分词效果为: ['我', '在', '看', '他们', '跳舞', ',', '我', '心里', '高兴', '得', '不得了', '。', '有时候', '我', '也', '想', '也许', '我', '也', '可以', '这样', '随便', '跳', '起舞', '来']
对比而言,精确分词更为准确,全模式分词还有词的联想效果。
有了这个非常好用的分词方法,那对于大段中文文章的词频统计过程就与上述的英文文章类似了。也是分:首先分词处理,获得词语的列表,然后进行map操作,构建字典,每个词语出现过,就给次数1,最后再进行reduce操作,将相同词语出现的次数相加,获得出现频率结果。
下面我对红楼梦相关章节进行分词处理。这个难度还是比较大的,主要是红楼梦属于古典小说,有许多文言文表达,而不是白话文。所以分词效果并不是非常准确。不过对于练习已经足够有吸引力了。
第一步,从网上下载红楼梦小说txt文本,保存成一个文本文件。
第二步,开始在python中编写代码,首先读取这个文本文件,获得红楼梦中文文本。并使用分词结果构建一个字典,字典内容为<词语,次数1>。由于全部回数的文本很长,在练习时可以选择其中一部分来进行测试。代码参考如下:
import jieba
#定义函数mapper,用于构建字典
def mapper(file):
word_sep=[]
word_map={}
punctuation=['?','!',',','。',';',':','“','”','\n','\u3000','(',')']
stopwords=["之","的","一","他","她","我","我们","可以","你","里","去",
"来","那","在","上","下"]
with open(file,'r') as f:
text=f.readlines()
for i in range(50):
words=list(jieba.cut(text[i],cut_all=False))
for word in words:
if word in punctuation:continue #去除标点符号
if word in stopwords:continue #去除终止词
word_sep.append(word)
for word in word_sep:
word_map[word]=1
return word_map
if __name__=="__main__":
file='红楼梦.txt'
print(mapper(file))
在这一步中使用了标点符号和终止词,当分词后的字符是标点符号和终止词时,就不加入字典。不过这里终止词是我自己构建的列表,内容相对较少。这块可以自行增加。
运行上述代码后就可以获得如下结果(这里受限篇幅仅显示部分结果):
{'甄士隐': 1, '梦幻': 1, '识通灵': 1, '贾雨村': 1, '风尘': 1, '怀': 1, '闺秀': 1, '1': 1, '列位': 1, '看官': 1, '道': 1, '此书': 1, '从何而来': 1, '说起': 1, '根由': 1, '虽近': 1, '荒唐': 1, '细': 1, '按': 1, '则': 1, '深有': 1, '趣味': 1, '待': 1, '将': 1, '此': 1, '来历': 1, '注明': 1, '方使': 1, '阅者': 1, '了然': 1, '不惑': 1, '原来': 1, '女娲': 1, '氏': 1, '炼石补天': 1, '时': 1, '于': 1, '大': 1, '荒山': 1, '无稽': 1, '崖': 1, '炼成': 1, '高经': 1, '十二': 1, '丈': 1, '、': 1, '方经': 1, '二十四丈': 1, '顽石': 1, '三万': 1, '六千五百': 1, '零': 1, '一块': 1, '娲': 1, '皇氏': 1, '只用': 1, '了': 1, '块': 1, '只': 1, '单单': 1, '剩': 1, '未': 1, '用': 1, '便弃': 1, '此山': 1, '青埂峰': 1, '谁知': 1, '此石': 1, '自经': 1, '煅炼': 1, '之后': 1, '灵性': 1, '已通': 1, '因见': 1, '众': 1, '石俱得': 1, '补天': 1, '独': 1, '自己': 1, '无材': 1, '不堪': 1, '入选': 1, '遂': 1, '自怨': 1, '自叹': 1, '日夜': 1, '悲号': 1, '惭愧': 1, '一日': 1, '正当': 1, '嗟悼': 1, '之际': 1, '俄见': 1, '一僧': 1, '一道': 1, '远远': 1, '而': 1, '生得': 1, '骨格': 1, '不凡': 1, '丰神': 1, '迥别': 1, '说说笑笑': 1, '至峰': 1, '坐于': 1, '石边': 1, '高谈': 1, '快论': 1, '先是': 1, '说些': 1, '云山': 1, '雾海': 1, '神仙': 1, '玄幻': 1, '之事': 1, '后': 1, '便': 1, '说': 1, '到': 1, '红尘': 1, '中': 1, '荣华富贵': 1, '听': 1, '不觉': 1, '打动': 1, '凡心': 1, '也': 1, '想要': 1, '人间': 1, '享一享': 1, '这': 1, '但': 1, '自恨': 1, '粗蠢': 1, '不得已': 1}
第三步,有了上述的词语字典后,就可以进行词频统计了。此时增加一个reducer函数,专门用于处理统计。
def reducer(word_dict):
word_freq={}
for key in word_dict:
if key in word_freq:
word_freq[key]+=1
else:
word_freq[key]=1
word_freq=sorted(word_freq.items(),key=lambda x:x[1],reverse=True)
return dict(word_freq)
输出词频统计结果,如下示例:
文字出现的频率为: {'道': 8, '弟子': 6, '一块': 4, '便': 4, '到': 4, '红尘': 4, '听': 4, '不知': 4, '补天': 3, '说': 3, '不能': 3, '却': 3, '如此': 3, '自然': 3, '则': 2, '将': 2, '此': 2, '不惑': 2, '原来': 2, '时': 2, '荒山': 2, '无稽': 2, '崖': 2, '三万': 2, '六千五百': 2, '只': 2, '青埂峰': 2, '此石': 2, '无材': 2, '一日': 2, '一僧': 2, '一道': 2, '而': 2, '荣华富贵': 2, '凡心': 2, '但': 2, '粗蠢': 2, '繁华': 2, '富贵': 2, '善哉': 2, '好': 2, '这石': 2, '再': 2, '那僧': 2, '助': 2, '还': 2, '石头': 2, '个': 2, '携': 2, '空空': 2, '道人': 2, '甄士隐': 1, '梦幻': 1, '识通灵': 1, '贾雨村': 1, '风尘': 1, '怀': 1, '闺秀': 1, '1': 1, '列位': 1, '看官': 1, '此书': 1, '从何而来': 1, '说起': 1, '根由': 1, '虽近': 1, '荒唐': 1, '细': 1, '按': 1, '深有': 1, '趣味': 1, '待': 1, '来历': 1, '注明': 1, '方使': 1, '阅者': 1, '了然': 1, '女娲': 1, '氏': 1, '炼石补天': 1, '于': 1, '大': 1, '炼成': 1, '高经': 1, '十二': 1, '丈': 1, '方经': 1, '二十四丈': 1, '顽石': 1, '零': 1, '娲': 1, '皇氏': 1, '只用': 1, '块': 1, '单单': 1, '剩': 1, '未': 1, '用': 1, '便弃': 1, '此山': 1, '谁知': 1, '自经': 1, '煅炼': 1, '之后': 1, '灵性': 1, '已通': 1, '因见': 1, '众': 1, '石俱得': 1, '独': 1, '自己': 1, '不堪': 1, '入选': 1, '遂': 1, '自怨': 1, '自叹': 1, '日夜': 1, '悲号': 1, '惭愧':1}
细看统计结果,里面出现许多一个字的词语,一般情况下中文单个文字表达的意思还是很有限的,多以词组的形式来表示含义。因此需要将单个文字从词语统计中剔除。
另外为了统计结果更直观,代码中也增加词云库,使用词云来显示词频统计的结果。词云显示的时候由于是汉字,所以需要增加汉字字库。即给定font_path,代码中直接调用windows系统的字体库中的宋体。
wc = WordCloud(background_color="white",width=600,
height=400, margin=5,font_path="C:/Windows/Fonts/simsun.ttc")
将上述三步合起来,代码整体组织如下:
import jieba
from wordcloud import WordCloud, STOPWORDS #生成词云、通用词
import matplotlib.pyplot as plt # 在线显示
#定义函数mapper,用于构建词语列表
def mapper(file):
word_sep=[]
word_map={}
punctuation=['?','!',',','。',';',':','“','”','’','‘','\n','\u3000','(',')','、']
stopwords=["之","的","一","他","她","我","我们","可以","你","里","去",
"来","那","在","上","下","了","又","是","这","着","也","人",
'不','有']
with open(file,'r') as f:
text=f.readlines()
for i in range(50): #取前50列表测试
words=list(jieba.cut(text[i],cut_all=False)) #结巴分词
for word in words:
if word in punctuation:continue #去除标点符号
if word in stopwords:continue #去除终止词
if len(word)<2:continue #去除单个字
word_sep.append(word) #将分好的词语添加到空列表
return word_sep
#定义函数,用于词频统计
def reducer(word_dict):
word_freq={}
for key in word_dict:
if key in word_freq:
word_freq[key]+=1
else:
word_freq[key]=1
word_freq=sorted(word_freq.items(),key=lambda x:x[1],reverse=True)
return dict(word_freq)
if __name__=="__main__":
file='红楼梦.txt'
word_dict=mapper(file)
word_freq=reducer(word_dict)