一、前言
在日常工作或生活中,有时会遇到词频分析的场景。如果要词频分析,首先要对句子进行分词,切割句子中的单词,按词性进行分类。 在Python有一个第三方库叫jieba(结巴),可以对文章或句子进行分词。我不得不佩服这个图书馆的作者,他真的是个名字天才:)
二、分词
2.1 安装库
jieba库github地址 jieba库官方给出了3中安装库的方法,分别如下: easy_install jieba 或者 pip install jieba / pip3 install jieba 先下载jieba · PyPI,解压后运行 python setup.py install 将 jieba 目录放置在当前目录或目录中 site-packages 目录
2.2 方法介绍
2.2.1 jieba.cut
该方法接受四个输入参数: 1.需要分词的字符串; 2、cut_all 用于控制是否采用全模式的参数; 3、HMM 用参数控制是否使用 HMM 模型; 4、use_paddle 用参数控制是否使用paddle模式下的分词模式,paddle该模式采用延迟加载模式enable_paddle接口安装paddlepaddle-tiny,并且import相关代码; 注意: 1、实测paddle无法打开模式(jieba.enable_paddle(),会报错。 2.该方法返回generator,如需返回list,则可以通过list或使用转换结果jieba.lcut方法 3、待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。不建议直接输入。 GBK 字符串可能会被意想不到的错误解码 UTF-8。
2.2.1 jieba.cut_for_search
该方法接受两个参数: 1.需要分词的字符串; 2、是否使用 HMM 模型。 注意: 该方法适用于搜索引擎构建倒排索引的分词,粒度较细。 2.该方法返回generator,如需返回list,则可以通过list或使用转换结果jieba.lcut_for_search方法。
2.2.2 jieba.Tokenizer(dictionary=DEFAULT_DICT)
该方法用于新建自定义分词器,可同时使用不同的词典。jieba.dt 对于默认分词器,所有与全局分词相关的函数都是分词器的映射。
2.3 代码示例
2.3.1 分词
# encoding=utf-8 |
|
import jieba |
|
strs = ["我来到北京清华大学", "乒乓球拍卖结束了", "中国科技大学"] |
|
for s in strs: |
|
seg_list = jieba.cut(s, use_paddle=False) # 使用paddle模式 |
|
print("Paddle Mode: " '/'.join(list(seg_list))) |
|
seg_list = jieba.cut("我来到北京清华大学", cut_all=True) |
|
print("Full Mode: " "/ ".join(seg_list)) # 全模式 |
|
seg_list = jieba.cut("我来到北京清华大学", cut_all=False) |
|
print("Default Mode: " "/ ".join(seg_list)) # 精确模式 |
|
seg_list = jieba.cut("他来到网易杭研大厦") # 默认是一种精确的模式 |
|
print(", ".join(seg_list)) |
|
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后来在日本京都大学深造") # 搜索引擎模式 |
|
print(", ".join(seg_list)) |
Building prefix dict from the default dictionary ... Loading model from cache C:\Users\FURONG~1\AppData\Local\Temp\jieba.cache Loading model cost 0.705 seconds. Prefix dict has been built successfully. Paddle Mode: 我/来/北京/清华大学 Paddle Mode: 乒乓球/拍卖/结束/ Paddle Mode: 中国/科技/大学 Full Mode: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学 Default Mode: 我/ 来到/ 北京/ 清华大学 他, 来到, 了, 网易, 杭研, 大厦 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, ,, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造
2.3.2 载入词典
开发者可以指定自己定义的词典,以便包含它 jieba 尽管没有词库。 jieba 有识别新词的能力,但自添加新词可以保证更高的准确性。 用法:jieba.load_userdict(file_name) # file_name 路径是文件对象或自定义词典。 词典格式和 dict.txt 一样(dict.txt为安装jieba库时自带的词典,路径为:Python安装路径\Lib\site-packages\jieba\dict.txt),一行占一行;每行分为三部分:
- 词语
- 词频(可省略)
- 词性(可省略) 用空间隔开,顺序不能颠倒。file_name 如果打开路径或二进制的文件,文件必须是 UTF-8 编码。 省略词频时,使用自动计算可以保证词频的分离。 例如:
创新办 3 i 云计算 5 凯特琳 nz 台中
更改分词器(默认为 jieba.dt)的 tmp_dir 和 cache_file 有限的文件系统可分别指定缓存文件所在的文件夹及其文件名。 范例: 自定义词典:jieba/userdict.txt at master · fxsjy/jieba · GitHub 用法示例:jieba/test_userdict.py at master · fxsjy/jieba · GitHub 在加载自定义词库之前: 李小福 / 是 / 创新 / 办 / 主任 / 也 / 是 / 云 / 计算 / 方面 / 的 / 专家 / 加载自定义词库后: 李小福 / 是 / 创新办 / 主任 / 也 / 是 / 云计算 / 方面 / 的 / 专家 / 使用 add_word(word, freq=None, tag=None) 和 del_word(word) 词典可以在程序中动态修改。 使用 suggest_freq(segment, tune=Tre) 可调节单个词语的词频,使其能(或不能)被分出来。 注意:自动计算的词频在使用 HMM 新词发现功能时可能无效。
print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False))) 如果/放到/post/中将/出错/。 jieba.suggest_freq(('中', '将'), True) 494 print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False))) 如果/放到/post/中/将/出错/。 print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False))) 「/台/中/」/正确/应该/不会/被/切开 jieba.suggest_freq('台中', True) 69 print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False))) 「/台中/」/正确/应该/不会/被/切开
2.3.3 词性标注
jieba.posseg.POSTokenizer(tokenizer=None) 新建自定义分词器,tokenizer 参数可指定内部使用的 jieba.Tokenizer 分词器。jieba.posseg.dt 为默认词性标注分词器。 标注句子分词后每个词的词性,采用和 ictclas 兼容的标记法。
import jieba.posseg as pseg |
|
words = pseg.cut("我爱北京天安门") |
|
for word, flag in words: |
|
print('%s %s' % (word, flag)) |
我 r 爱 v 北京 ns 天安门 ns
词性和专名类别标签集合如下表,其中词性标签 24 个(小写字母),专名类别标签 4 个(大写字母):
三、实例
3.1 分词
这里就拿笔者最近看的一本小说判官.txt来进行实例词频分析吧。目的是想要分析这本小说的人物、名词、地名,从而对这本小说有个大概的认识。 分词代码如下:
def m_get_content(self): |
|
""" |
|
获取给定txt或str中的内容 |
|
@return: {str:获取到的内容} |
|
""" |
|
if self.f_input.endswith('.txt'): |
|
with open(self.f_input, 'r', encoding='utf-8') as f: |
|
content = f.read() |
|
else: |
|
content = self.f_input |
|
return content |
|
def m_get_target_words_from_content(self, p_content): |
|
""" |
|
从目标内容中获取到指定词性的词语 |
|
@param p_content: 需要分词的内容 |
|
@return: {list:分词后的词语} |
|
""" |
|
return [ |
|
_.word |
|
for _ in psg.cut(p_content) |
|
if len(_.word) > 1 and _.flag in self.f_wordclass |
|
] |
3.2 按照词频排序
def words_frequency(p_list, p_reverse=True): |
|
""" |
|
返回给定的list中的所有元素按照频率和指定的顺序组成的list |
|
@param p_list: 给定的list |
|
@param p_reverse: 指定的排序顺序(倒序为True,正序为False) |
|
@return: {list[tuple]:经过排序后的内容} |
|
""" |
|
result = {} |
|
for word in p_list: |
|
result.setdefault(word, 0) |
|
result[word] += 1 |
|
return sorted(result.items(), key=lambda x: x[1], reverse=p_reverse) |
|
def m_words_sort(self, p_words: list): |
|
""" |
|
对获取到的分词的内容进行按照频率排序 |
|
@param p_words: 需要进行按频率排序的内容 |
|
@return: {list[tuple]:经过排序后的内容} |
|
""" |
|
return words_frequency(p_list=p_words, p_reverse=self.f_reverse) |
3.3 获取排序后的前N项内容制作柱图
def m_show_words_frequency(self, p_words_sort: list): |
|
""" |
|
将p_words_sort中的内容提取前p_ranknum项使用"-"符号展示柱图。 |
|
@param p_words_sort: 要用来展示柱图的dict内容 |
|
@return: {str:柱图} |
|
""" |
|
max_frequency = max(p_words_sort[0][1], p_words_sort[-1][1]) |
|
ratio = max_frequency / self.f_shownum |
|
print('%-5s\t%-5s\t%-5s\t%-15s' % ('序号', '词性', '词频', '柱图')) |
|
for i in range(self.f_ranknum): |
|
print(f'{i + 1:<5}\t{p_words_sort[i][0]:<5}\t{p_words_sort[i][1]:<5}\t{"-" * int(p_words_sort[i][1] / ratio):<15}') |
3.4 获取排序后的前N项内容制作词云图
用Python制作词云图需要安装wordcloud第三方库。参照如下:python词云制作(最全最详细的教程)
from wordcloud import WordCloud |
|
words_cloud = ' '.join([_[0] for _ in words_sort[:10]]) |
|
wordcloud = WordCloud(font_path="msyh.ttc").generate(words_cloud) |
|
wordcloud.to_file(self.f_wordcloud_fn) |
3.5 结果
序号 词性 词频 柱图 1 闻时 1405 ------------------------------ 2 谢问 1092 ----------------------- 3 周煦 762 ---------------- 4 卜宁 513 ---------- 5 张岚 466 --------- 6 张雅临 332 ------- 7 张碧灵 219 ---- 8 沈曼怡 216 ---- 9 张正初 185 --- 10 李先生 164 ---
序号 词性 词频 柱图 1 时候 777 ------------------------------ 2 有点 449 ----------------- 3 傀线 376 -------------- 4 手指 345 ------------- 5 声音 324 ------------ 6 地方 316 ------------ 7 感觉 293 ----------- 8 眼睛 255 --------- 9 老毛 209 -------- 10 对方 208 --------
序号 词性 词频 柱图 1 夏樵 803 ------------------------------ 2 东西 550 -------------------- 3 大东 231 -------- 4 沈家 184 ------ 5 云山 140 ----- 6 沈桥 80 -- 7 宁州 51 - 8 下山 45 - 9 天津 35 - 10 西屏园 32 -
3.6 完整代码
import jieba.posseg as psg |
|
def words_frequency(p_list, p_reverse=True): |
|
""" |
|
返回给定的list中的所有元素按照频率和指定的顺序组成的list |
|
@param p_list: 给定的list |
|
@param p_reverse: 指定的排序顺序(倒序为True,正序为False) |
|
@return: {list[tuple]:经过排序后的内容} |
|
""" |
|
result = {} |
|
for word in p_list: |
|
result.setdefault(word, 0) |
|
result[word] += 1 |
|
return sorted(result.items(), key=lambda x: x[1], reverse=p_reverse) |
|
class WordsFrequency: |
|
def __init__(self, p_input: str, p_wordclass: list[str], p_ranknum=10, p_reverse=True, p_shownum=30, p_wordcloud_fn='人名.jpg'): |
|
""" |
|
@param p_input: 要分析的文件名或字符串,文件必须为txt格式,编码为utf-8. |
|
@param p_wordclass: 要获取的词性。常见的类型有:普通名词n、人名nr、地名ns |
|
@param p_ranknum: 要获取前多少个目标词性的词语。 |
|
@param p_reverse: 目标词语通过频率指定的排序方向。(倒序为True,正序为False) |
|
@param p_shownum: 频率最多的元素使用的柱图标识"-"的个数。 |
|
@param p_wordcloud_fn: 生成词云图的图片文件名 |
|
""" |
|
self.f_input = p_input |
|
self.f_wordclass = p_wordclass |
|
self.f_ranknum = p_ranknum |
|
self.f_reverse = p_reverse |
|
self.f_shownum = p_shownum |
|
self.f_wordcloud_fn = p_wordcloud_fn |
|
def m_get_content(self): |
|
""" |
|
获取给定txt或str中的内容 |
|
@return: {str:获取到的内容} |
|
""" |
|
if self.f_input.endswith('.txt'): |
|
with open(self.f_input, 'r', encoding='utf-8') as f: |
|
content = f.read() |
|
else: |
|
content = self.f_input |
|
return content |
|
def m_get_target_words_from_content(self, p_content): |
|
""" |
|
从目标内容中获取到指定词性的词语 |
|
@param p_content: 需要分词的内容 |
|
@return: {list:分词后的词语} |
|
""" |
|
return [ |
|
_.word |
|
for _ in psg.cut(p_content) |
|
if len(_.word) > 1 and _.flag in self.f_wordclass |
|
] |
|
def m_words_sort(self, p_words: list): |
|
""" |
|
对获取到的分词的内容进行按照频率排序 |
|
@param p_words: 需要进行按频率排序的内容 |
|
@return: {list[tuple]:经过排序后的内容} |
|
""" |
|
return words_frequency(p_list=p_words, p_reverse=self.f_reverse) |
|
def m_show_words_frequency(self, p_words_sort: list): |
|
""" |
|
将p_words_sort中的内容提取前p_ranknum项使用"-"符号展示柱图。 |
|
@param p_words_sort: 要用来展示柱图的dict内容 |
|
@return: {str:柱图} |
|
""" |
|
max_frequency = max(p_words_sort[0][1], p_words_sort[-1][1]) |
|
ratio = max_frequency / self.f_shownum |
|
print('%-5s\t%-5s\t%-5s\t%-15s' % ('序号', '词性', '词频', '柱图')) |
|
for i in range(self.f_ranknum): |
|
print(f'{i + 1:<5}\t{p_words_sort[i][0]:<5}\t{p_words_sort[i][1]:<5}\t{"-" * int(p_words_sort[i][1] / ratio):<15}') |
|
def main(self): |
|
content = self.m_get_content() |
|
words = self.m_get_target_words_from_content(content) |
|
words_sort = self.m_words_sort(words) |
|
# 柱图部分 |
|
self.m_show_words_frequency(words_sort) |
|
# 生成词云图 |
|
from wordcloud import WordCloud |
|
words_cloud = ' '.join([_[0] for _ in words_sort[:10]]) |
|
wordcloud = WordCloud(font_path="msyh.ttc").generate(words_cloud) |
|
wordcloud.to_file(self.f_wordcloud_fn) |
|
if __name__ == '__main__': |
|
wf = WordsFrequency(p_input='判官.txt', p_wordclass=['ns'], p_wordcloud_fn='地名.jpg') |
|
wf.main() |
折叠
从自动化办公到智能化办公