前言

标签云或文字云是关键词的视觉化描述,用于汇总用户生成的标签或一个网站的文字内容。标签一般是独立的词汇,常常按字母顺序排列,其重要程度又能通过改变字体大小或颜色来表现,所以标签云可以灵活地依照字序或热门程度来检索一个标签。 大多数标签本身就是超级链接,直接指向与标签相联的一系列条目。 —维基百科: 标签云

在做文本数据可视化的时候, 词云是一个非常火热的选择, 而且展现效果也十分不错, 本文主要介绍使用 Python 中的 wordcloud 和 jieba 制作中文词云.

准备工作

在进行中文词云制作之前, 需要安装两个 Python 第三方包, 一个负责词云的绘制, 另一个是用于对中文进行分词处理. 在网上你可以找到很多关于这两项工作的第三方包, 本文选择的是 amueller/word_cloud: A little word cloud generator in Pythonfxsjy/jieba: 结巴中文分词 两个模块, 你需要事先安装这两个模块:

1
2
pip install wordcloud
pip install jieba

需要注意的是, 由于种种原因你可能会遇到安装失败的问题 (尤其是 wordcloud 很容易遇到 C 编译器的版本问题, 网上有一些解决方案, 你可以自行搜索), 请到前文中超链接指向的 GitHub 页面寻求帮助, 并使用其他方式安装.

当完成准备工作之后, 就可以开始制作你的词云了, 如果文中有什么不清楚的地方, 你可以评论或者 联系我.

那么现在假定你已经有了一个需要绘制词云的文本文件 text.txt, 一个带有中文的字体 font.ttf, 一个停止词列表 stop_words.txt, 并且这三者都存放在你的项目文件夹目录下. 如果你还没有这些内容的话, 请不要着急, 接下来我会详细说明如何获取后两者的.

中文分词

中文相比英文的一个重要特征就是词与词之间没有空格隔开, 这就导致了在文本分析中无法直接处理中文句子, 此时就需要使用到分词工具来提取词组, 常见的开源分词工具有很多, 想要了解更多你可以参考 这里, 目前 jieba 分词是比较常见的分词工具, 网上的教程也大多基于它, 当然, 大部分分词工具其实都大同小异, 你可以随意选择你自己喜欢的工具.

分词

首先在 Python 中导入 jieba:

1
import jieba

前文提到, 现在你已经拥有一个文本文件 text.txt, 读取这个文件并将内容保存在 data 变量中, 在这一步中, 你可以使用 re 模块或者其他方式滤去一些你不想要的语句或词组, 例如当你处理一个表格时, 只有第四列是你需要的文本, 那么你可以做进一步处理.

1
data = open('text.txt', 'r').read()

有时候当你对输入文本进行处理之后, 可能获得的结果是一个列表 ‘list0’, 使用 ' '.join(str(l) for l in list0) 将列表转换为字符串. (这行代码的意思是, 遍历列表, 将列表的每个元素都转换为 string 并用空格将它们拼接起来)

读取文件成功后, 可以直接对文本进行分词, 使用 jieba.cut(data) 即可获得一个分词后的列表, 此时分词就已经完成了.

1
cut_text = jieba.cut(data)

去除停止词 (Stopwords)

2018.5.29 更新

你可以直接使用 jieba.analyse.set_stop_words 来设置停用词, 用法如下:

1
jieba.analyse.set_stop_words("stop_words.txt")

现在你就可以去掉这一步的内容, 直接跳转到下一步绘制词云了.

参考: jieba/extract_tags_stop_words.py at master · fxsjy/jieba

历史内容

停止词是指语句中存在比较多歧义或本身没有实际意义的词语, 例如一些冠词和语气助词, 在进行自然语言处理的时候这些词组不能提供任何有效信息, 因此一般都会在处理前去掉这些词语.

首先, 请右击 这里 并选择另存为文件以下载停止词列表, 将其放在你的项目目录下. 在 Python 中使用前文类似的方法将其读取. 如果有必要的话, 你可以临时额外添加停止词以适应你的文本文件, 此时 ‘stop_words.txt’ 文件不会被修改.

1
2
3
4
stop_words = open('stop_words.txt', 'r').read()

# add stop words
stop_words.extend(['word1', 'word2', ...])

接下来使用一个简单的循环遍历 ‘cut_text’, 判断词组是否需要停用, 并将需要保留的词组添加到 ‘clean_text’中:

1
2
3
4
5
6
7
clean_text = []
for w in cut_text:
if w not in stop_words:
clean_text.append(w)

# 一个等价但更精炼的实现
clean_text = [w for w in cut_text if w not in stop_words]

有时你可能会想, 为什么不直接在循环中使用 ‘del’ 语句删除元素来实现去除停止词呢, 这是因为 ‘del’ 语句会修改迭代对象本身, 如果在循环中使用这条语句的话有可能会导致出现错误, 例如发生访问越界而报错, 所以请慎重使用 ‘del’ 语法.

词云绘制

选择字体

由于 wordcloud 模块默认字体不支持中文, 所以你如果直接生成词云的话, 中文会变成方块, 所以你需要选择一个支持中文的字体. 在搜索引擎中搜索 ‘字体’ 并进入一个能够下载字体的网站, 下载你喜欢的字体并保存在你的项目文件夹中. 下载 ‘.ttf’ 格式的字体即可. 接下来本文将假定你下载的字体名称为 ‘font.ttf’, 请灵活修改代码.

计算词频

为了使得词云中字体的大小能够体现词组的重要性, 需要对词频进行统计. jieba 库自身就带有一个关键词分析工具 ‘jieba.analyse’, 你可以直接使用它, 或者自己编写词频计数代码.

请注意, 网上有一些教程会使用 ‘set()’ 函数把词组列表转换为集合, 从而达到去重的作用, 如果你不打算在最终生成的词云中体现词组的词频, 那么你可以使用这个方法并跳过这一步, 否则请避免将词组列表转换为集合. 当然, 如果你并不需要去重的话, 你也可以跳过这一步 (这样你最后得到的结果将会是一张存在重复词语的图片, 且各个词组的大小与其在文本中的重要性没有联系).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 由于 extract_tags 函数仅接收字符串作为输入,
# 因此你需要在此之前将 clean_text 转换为字符串
word_list = ' '.join(clean_text)

import jieba.analyse
# 计算词频
frequencies = jieba.analyse.extract_tags(word_list, topK=100, withWeight=True)
# 将结果转换为字典
freq_dict = dict()
for w in frequencies:
freq_dict[w[0]] = w[1]

# 等价实现: 将嵌套列表转换为字典可以直接使用 dict()
freq_dict = dict(frequencies)
  • 语句原型: jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
    • sentence 为待提取的文本, 这个变量应当是字符串
    • topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20 (意思是这个函数只返回最重要的一部分词语, 由这一参数控制数量)
    • withWeight 为是否一并返回关键词权重值,默认值为 False, 如果你需要得到词频的话应当设置这一项为 True
    • allowPOS 仅包括指定词性的词,默认值为空,即不筛选

绘制

当得到词频字典后, 就可以正式开始绘制词云了, 首先从 wordcloud 包中导入 WordCloud 类, 并初始化 WordCloud 类, 请注意区分大小写.

1
2
3
4
5
6
7
from wordcloud import WordCloud
cloud = WordCloud(
font_path='font.ttf', # 字体路径(带有中文的字体)
background_color='white', # 背景颜色
max_words=40, # 最多显示词组个数
max_font_size=40 # 最大字号
)

接下来你只需要一句话就能生成词云了:

1
word_cloud = cloud.generate_from_frequencies(freq_dict)

当然你会发现计算机什么都没有返回, 使用下一行语句保存词云到文件:

1
word_cloud.to_file('word_cloud.jpg')

接下来你就可以在你的项目文件夹中找到 ‘word_cloud.jpg’ 了, 这就是最终生成的词云文件了.

完整参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import jieba
import jieba.analyse
from wordcloud import WordCloud

# readfiles
data = open('text.txt', 'r').read()
stop_words = open('stop_words.txt', 'r').read()

# add stop words
# stop_words.extend(['word1', 'word2', ...])

# cut words
cut_text = jieba.cut(data)

# remove stop words
clean_text = [w for w in cut_text if w not in stop_words]
# 你可以使用下面这句代码替代除去停用词的作用, 你可能需要对代码其他地方进行适当修改
# jieba.analyse.set_stop_words("stop_words.txt")

# generate word frequencies
word_list = ' '.join(clean_text)
frequencies = jieba.analyse.extract_tags(word_list, topK=100, withWeight=True)

# converts the results to the dictionary
freq_dict = dict(frequencies)

# init WordCloud
cloud = WordCloud(
font_path='font.ttf', # 字体路径(带有中文的字体)
background_color='white', # 背景颜色
max_words=40, # 最多显示词组个数
max_font_size=40 # 最大字号
)

# generate and save wordcloud
word_cloud = cloud.generate_from_frequencies(freq_dict)
word_cloud.to_file('word_cloud.jpg')

进一步探索