Python生成静态词云及Echarts动态渲染词云

  1. 1. 基本介绍
    1. 1.1 词云简介
    2. 1.2 常见技术方案
  2. 2. Python生成静态词云
    1. 2.1 依赖环境安装
    2. 2.2 NLP相关库的使用
      1. 2.2.1 jieba分词库
      2. 2.2.2 snownlp文本分析库
    3. 2.3 基本词云示例
      1. 2.3.1 最简版词云
      2. 2.3.2 美化词云样式
      3. 2.3.3 按照指定剪影生成
      4. 2.3.4 勾勒轮廓线
      5. 2.3.5 按照模板着色
      6. 2.3.6 情感分析词云
      7. 2.3.7 人物阵营分色词云
  3. 3. Echarts动态渲染词云
    1. 3.1 echarts-wordcloud介绍
    2. 3.2 动态渲染词云示例
  4. 4. 参考资料

1. 基本介绍

1.1 词云简介

词云,又称文字云,是文本数据的视觉表示,由词汇组成类似云的彩色图形,用于展示大量文本数据。 通常用于描述网站上的关键字元数据,或可视化自由格式文本,每个词的重要性以字体大小或颜色显示。在数据可视化方面,词云一直是一种视觉冲击力很强的方式。

词云示例

1.2 常见技术方案

对输入的一段文字进行语义分割,得到不同频度的词汇,然后以正比于词频的字体大小无规则的集中显示高频词,简洁直观高效。其中获取词汇我们可以使用 jieba 分词等 NLP 库。渲染部分通常有两种方式,一种是使用 Python 直接渲染成静态图片,另一种是以 RESTful API 的形式返回给前端,使用Echarts进行渲染,这样得到的就是动态词云,鼠标悬浮上去会有特效,会更好看一些。

本文的完整示例代码已在Github上开源:https://github.com/Logistic98/word-cloud

2. Python生成静态词云

2.1 依赖环境安装

wordCloud:https://github.com/fuqiuai/wordCloud(词云)

imageio:https://github.com/imageio/imageio(指定形状)

jieba:https://github.com/fxsjy/jieba(分词)

snownlp:https://github.com/isnowfy/snownlp(中文NLP)

1
2
3
4
$ pip intsall Pillow numpy matplotlib wordcloud
$ pip install imageio
$ pip install jieba
$ pip install snownlp

注:wordcloud包依赖于Pillow、numpy、matplotlib。

2.2 NLP相关库的使用

2.2.1 jieba分词库

精确模式(最常用,生成词云一般用这个):每个字只用一遍,不存在冗余词汇。jieba.lcut('动力学和电磁学')

全模式:把每个字可能形成的词汇都提取出来,存在冗余。jieba.lcut('动力学和电磁学',cut_all=True)

搜索引擎模式:将全模式分词的结果从短到长排列好。jieba.lcut_for_search('动力学和电磁学')

jieba-demo.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding: utf-8 -*-

import jieba

text = '动力学和电磁学'

print('{:-^50}'.format('精确模式:每个字只用一遍,不存在冗余词汇'))
textlist = jieba.lcut(text)
print('分词之后生成的列表为', textlist)

print('{:-^50}'.format('全模式:把每个字可能形成的词汇都提取出来,存在冗余'))
textlist = jieba.lcut(text, cut_all=True)
print('分词之后生成的列表为', textlist)

print('{:-^50}'.format('搜索引擎模式:将全模式分词的结果从短到长排列好'))
textlist = jieba.lcut_for_search(text)
print('分词之后生成的列表为', textlist)

>>> ---------------精确模式:每个字只用一遍,不存在冗余词汇---------------
>>> 分词之后生成的列表为 ['动力学', '和', '电磁学']
>>> ------------全模式:把每个字可能形成的词汇都提取出来,存在冗余-------------
>>> 分词之后生成的列表为 ['动力', '动力学', '力学', '和', '电磁', '电磁学', '磁学']
>>> -------------搜索引擎模式:将全模式分词的结果从短到长排列好--------------
>>> 分词之后生成的列表为 ['动力', '力学', '动力学', '和', '电磁', '磁学', '电磁学']

2.2.2 snownlp文本分析库

snownlp的语料库是淘宝等电商网站的评论,所以对购物类的文本情感分析准确度很高。

snownlp-demo.py

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
# -*- coding: utf-8 -*-

import snownlp

text1 = '中华民族伟大复兴'
print('{:-^50}'.format('测试文本:'+text1))
s = snownlp.SnowNLP(text1)
print('情感分析', s.sentiments)
print('中文分词', s.words)
print('转成拼音', s.pinyin)
print('词频', s.tf)
print('提取三个关键词', s.keywords(3))

text2 = '快递慢到死,客服态度不好,退款!'
print('{:-^50}'.format('测试文本:'+text2))
s = snownlp.SnowNLP(text2)
print('情感分析', s.sentiments)
print('中文分词', s.words)
print('转成拼音', s.pinyin)
print('词频', s.tf)
print('提取三个关键词', s.keywords(3))

>>> ------------------测试文本:中华民族伟大复兴-------------------
>>> 情感分析 0.9935086411278989
>>> 中文分词 ['中华民族', '伟大', '复兴']
>>> 转成拼音 ['zhong', 'hua', 'min', 'zu', 'wei', 'da', 'fu', 'xing']
>>> 词频 [{'中': 1}, {'华': 1}, {'民': 1}, {'族': 1}, {'伟': 1}, {'大': 1}, {'复': 1}, {'兴': 1}]
>>> 提取三个关键词 ['复兴', '中华民族']
>>> --------------测试文本:快递慢到死,客服态度不好,退款!---------------
>>> 情感分析 0.00012171645785852281
>>> 中文分词 ['快递', '慢', '到', '死', ',', '客', '服', '态度', '不好', ',', '退款', '!']
>>> 转成拼音 ['kuai', 'di', 'man', 'dao', 'si', ',', 'ke', 'fu', 'tai', 'du', 'bu', 'hao', ',', 'tui', 'kuan', '!']
>>> 词频 [{'快': 1}, {'递': 1}, {'慢': 1}, {'到': 1}, {'死': 1}, {',': 1}, {'客': 1}, {'服': 1}, {'态': 1}, {'度': 1}, {'不': 1}, {'好': 1}, {',': 1}, {'退': 1}, {'款': 1}, {'!': 1}]
>>> 提取三个关键词 ['服', '不好', '态度']

2.3 基本词云示例

2.3.1 最简版词云

simple-word-cloud.py

1
2
3
4
5
6
7
# -*- coding: utf-8 -*-

import wordcloud

w = wordcloud.WordCloud()
w.generate('and that government of the people, by the people, for the people, shall not perish from the earth.')
w.to_file('./output/simple-word-cloud.png')

讲解说明:

  • wordcloud库为每一个词云生成一个WordCloud对象,wordcloud.WordCloud()代表一个词云对象,我们将它赋值给w
  • 我们可以在WordCloud()括号里填入各种参数,控制词云的字体、字号、字的颜色、背景颜色等。wordcloud库会非常智能地按空格进行分词及词频统计,出现次数多的词就大。

生成效果:

simple-word-cloud

2.3.2 美化词云样式

beautify-word-cloud.py

1
2
3
4
5
6
7
8
9
10
11
12
# -*- coding: utf-8 -*-

import wordcloud

# 构建词云对象w,设置词云图片宽、高、字体、背景颜色等参数
w = wordcloud.WordCloud(width=1000,
height=700,
background_color='white',
font_path='msyh.ttc')

w.generate('从明天起,做一个幸福的人。喂马、劈柴,周游世界。从明天起,关心粮食和蔬菜。我有一所房子,面朝大海,春暖花开')
w.to_file('./output/beautify-word-cloud.png')

参数说明:

  • width 词云图片宽度,默认400像素
  • height 词云图片高度,默认200像素
  • background_color 词云图片的背景颜色,默认为黑色background_color='white'
  • font_step 字号增大的步进间隔,默认1号
  • font_path 指定字体路径,默认None,对于中文可用font_path='msyh.ttc'
  • mini_font_size 最小字号,默认4号
  • max_font_size 最大字号,根据高度自动调节
  • max_words 最大词数,默认200
  • stop_words 不显示的单词 stop_words={"python","java"}
  • Scale:默认值1。值越大,图像密度越大越清晰
  • prefer_horizontal:默认值0.90,浮点数类型。表示在水平如果不合适,就旋转为垂直方向,水平放置的词数占0.9?
  • relative_scaling:默认值0.5,浮点型。设定按词频倒序排列,上一个词相对下一位词的大小倍数。有如下取值:“0”表示大小标准只参考频率排名,“1”如果词频是2倍,大小也是2倍
  • mask 指定词云形状图片,默认为矩形

生成效果:

beautify-word-cloud

2.3.3 按照指定剪影生成

china-word-cloud.py

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
# -*- coding: utf-8 -*-

# 导入词云制作库wordcloud和中文分词库jieba
import jieba
import wordcloud

# 导入imageio库中的imread函数,并用这个函数读取本地图片,作为词云形状图片
import imageio

mk = imageio.imread("./input/chinamap.png")

# 构建并配置词云对象w,注意要加scale参数,提高清晰度。将不想展示在词云中的词放在stopwords集合里。
w = wordcloud.WordCloud(width=1000,
height=700,
background_color='white',
font_path='msyh.ttc',
mask=mk,
scale=15,
stopwords={'和', '的', '是'})

# 对来自外部文件的文本进行中文分词,得到string
f = open('./input/新时代中国特色社会主义.txt', encoding='utf-8')
txt = f.read()
txtlist = jieba.lcut(txt)
string = " ".join(txtlist)

# 将string变量传入w的generate()方法,给词云输入文字
w.generate(string)

# 将词云图片导出到当前文件夹
w.to_file('./output/china-word-cloud.png')

说明:可以把大量stopwords写入到txt文件中,用如下方式读取成set()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: utf-8 -*-

import codecs


def read_stopwords(path):
stop_words = set()
for word in codecs.open(path, 'r', 'utf-8', 'ignore'):
stop_words.add(word.strip())
return stop_words


if __name__ == '__main__':
path = './stopwords.txt'
stop_words = read_stopwords(path)
print(stop_words)

生成效果:

china-word-cloud

2.3.4 勾勒轮廓线

contour-word-cloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding: utf-8 -*-

# 导入词云制作库wordcloud
import wordcloud

# 导入imageio库中的imread函数,并用这个函数读取本地图片,作为词云形状图片
import imageio

mk = imageio.imread("./input/alice.png")

# 将外部文件包含的文本保存在string变量中
string = open('./input/hamlet.txt').read()

# 构建词云对象w,注意增加参数contour_width和contour_color设置轮廓宽度和颜色
w = wordcloud.WordCloud(background_color="white",
mask=mk,
contour_width=1,
contour_color='steelblue')

# # 将string变量传入w的generate()方法,给词云输入文字
w.generate(string)

# 将词云图片导出到当前文件夹
w.to_file('./output/contour-word-cloud.png')

生成效果:

contour-word-cloud

2.3.5 按照模板着色

color-word-cloud.py

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
38
# -*- coding: utf-8 -*-

# 导入绘图库matplotlib和词云制作库wordcloud
import matplotlib.pyplot as plt
from wordcloud import WordCloud, ImageColorGenerator

# 导入imageio库中的imread函数,并用这个函数读取本地图片,作为词云形状图片
import imageio
mk = imageio.imread("./input/alice_color.png")

# 将外部文件包含的文本保存在text变量中
text = open('./input/alice.txt').read()

# 构建词云对象w
wc = WordCloud(background_color="white",
mask=mk,)
# 将text字符串变量传入w的generate()方法,给词云输入文字
wc.generate(text)

# 调用wordcloud库中的ImageColorGenerator()函数,提取模板图片各部分的颜色
image_colors = ImageColorGenerator(mk)

# 显示原生词云图、按模板图片颜色的词云图和模板图片,按左、中、右显示
fig, axes = plt.subplots(1, 3)
# 最左边的图片显示原生词云图
axes[0].imshow(wc)
# 中间的图片显示按模板图片颜色生成的词云图,采用双线性插值的方法显示颜色
axes[1].imshow(wc.recolor(color_func=image_colors), interpolation="bilinear")
# 右边的图片显示模板图片
axes[2].imshow(mk, cmap=plt.cm.gray)
for ax in axes:
ax.set_axis_off()
plt.show()

# 给词云对象按模板图片的颜色重新上色
wc_color = wc.recolor(color_func=image_colors)
# 将词云图片导出到当前文件夹
wc_color.to_file('./output/color-word-cloud.png')

注意事项:

  • 定制剪影图片可以这么获得:先使用remove.bg工具去除背景,然后用画图工具打开,截图出来即可。

生成效果:

color-word-cloud

2.3.6 情感分析词云

emotion-word-cloud.py

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# -*- coding: utf-8 -*-

# 导入词云制作库wordcloud和中文分词库jieba
import jieba
import wordcloud

# 导入imageio库中的imread函数,并用这个函数读取本地图片,作为词云形状图片
import imageio
mk = imageio.imread("./input/chinamap.png")

# 构建并配置两个词云对象w1和w2,分别存放积极词和消极词
w1 = wordcloud.WordCloud(width=1000,
height=700,
background_color='white',
font_path='msyh.ttc',
mask=mk,
scale=15)
w2 = wordcloud.WordCloud(width=1000,
height=700,
background_color='white',
font_path='msyh.ttc',
mask=mk,
scale=15)

# 对来自外部文件的文本进行中文分词,得到积极词汇和消极词汇的两个列表
f = open('./input/三国演义.txt',encoding='utf-8')
txt = f.read()
txtlist = jieba.lcut(txt)
positivelist = []
negativelist = []

# 下面对文本中的每个词进行情感分析,情感>0.96判为积极词,情感<0.06判为消极词
print('开始进行情感分析,请稍等...')
# 导入自然语言处理第三方库snownlp
import snownlp
for each in txtlist:
each_word = snownlp.SnowNLP(each)
feeling = each_word.sentiments
if feeling > 0.96:
positivelist.append(each)
elif feeling < 0.06:
negativelist.append(each)
else:
pass
# 将积极和消极的两个列表各自合并成积极字符串和消极字符串,字符串中的词用空格分隔
positive_string = " ".join(positivelist)
negative_string = " ".join(negativelist)

# 将string变量传入w的generate()方法,给词云输入文字
w1.generate(positive_string)
w2.generate(negative_string)

# 将积极、消极的两个词云图片导出到当前文件夹
w1.to_file('./output/emotion-word-cloud-positive.png')
w2.to_file('./output/emotion-word-cloud-negative.png')
print('词云生成完成')

生成效果:

emotion-word-cloud

2.3.7 人物阵营分色词云

camp-word-cloud.py

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# -*- coding: utf-8 -*-

# 导入wordcloud库,并定义两个函数
from wordcloud import (WordCloud, get_single_color_func)
# 导入jieba分词
import jieba
# 导入imageio库中的imread函数,并用这个函数读取本地图片,作为词云形状图片
import imageio


class SimpleGroupedColorFunc(object):
"""Create a color function object which assigns EXACT colors
to certain words based on the color to words mapping

Parameters
----------
color_to_words : dict(str -> list(str))
A dictionary that maps a color to the list of words.

default_color : str
Color that will be assigned to a word that's not a member
of any value from color_to_words.
"""

def __init__(self, color_to_words, default_color):
self.word_to_color = {word: color
for (color, words) in color_to_words.items()
for word in words}

self.default_color = default_color

def __call__(self, word, **kwargs):
return self.word_to_color.get(word, self.default_color)


class GroupedColorFunc(object):
"""Create a color function object which assigns DIFFERENT SHADES of
specified colors to certain words based on the color to words mapping.

Uses wordcloud.get_single_color_func

Parameters
----------
color_to_words : dict(str -> list(str))
A dictionary that maps a color to the list of words.

default_color : str
Color that will be assigned to a word that's not a member
of any value from color_to_words.
"""

def __init__(self, color_to_words, default_color):
self.color_func_to_words = [
(get_single_color_func(color), set(words))
for (color, words) in color_to_words.items()]

self.default_color_func = get_single_color_func(default_color)

def get_color_func(self, word):
"""Returns a single_color_func associated with the word"""
try:
color_func = next(
color_func for (color_func, words) in self.color_func_to_words
if word in words)
except StopIteration:
color_func = self.default_color_func

return color_func

def __call__(self, word, **kwargs):
return self.get_color_func(word)(word, **kwargs)


mk = imageio.imread("./input/chinamap.png")
w = WordCloud(width=1000,
height=700,
background_color='white',
font_path='msyh.ttc',
mask=mk,
scale=15,
max_font_size=60,
max_words=20000,
font_step=1)


# 对来自外部文件的文本进行中文分词,得到string
f = open('./input/三国演义.txt', encoding='utf-8')
txt = f.read()
txtlist = jieba.lcut(txt)
string = " ".join(txtlist)

# 将string变量传入w的generate()方法,给词云输入文字
w.generate(string)

# 创建字典,按人物所在的不同阵营安排不同颜色,绿色是蜀国,橙色是魏国,紫色是东吴,粉色是诸侯群雄
color_to_words = {
'green': ['刘备', '刘玄德', '孔明', '诸葛孔明', '玄德', '关公', '玄德曰', '孔明曰',
'张飞', '赵云', '后主', '黄忠', '马超', '姜维', '魏延', '孟获',
'关兴', '诸葛亮', '云长', '孟达', '庞统', '廖化', '马岱'],
'red': ['曹操', '司马懿', '夏侯', '荀彧', '郭嘉', '邓艾', '许褚',
'徐晃', '许诸', '曹仁', '司马昭', '庞德', '于禁', '夏侯渊', '曹真', '钟会'],
'purple': ['孙权', '周瑜', '东吴', '孙策', '吕蒙', '陆逊', '鲁肃', '黄盖', '太史慈'],
'pink': ['董卓', '袁术', '袁绍', '吕布', '刘璋', '刘表', '貂蝉']
}

# 其它词语的颜色
default_color = 'gray'

# 构建新的颜色规则
grouped_color_func = GroupedColorFunc(color_to_words, default_color)

# 按照新的颜色规则重新绘制词云颜色
w.recolor(color_func=grouped_color_func)

# 将词云图片导出到当前文件夹
w.to_file('./output/camp-word-cloud.png')

生成效果:

camp-word-cloud

3. Echarts动态渲染词云

3.1 echarts-wordcloud介绍

项目简介:基于 wordcloud2.js 的 Apache ECharts 词云扩展

项目地址:https://github.com/ecomfe/echarts-wordcloud

注意事项:echarts-wordcloud现在有2.01.x两个版本,2.0对应echarts 5.x版本

1
2
$ npm install echarts
$ npm install echarts-wordcloud

3.2 动态渲染词云示例

项目结构:

1
2
3
4
5
6
echarts_version
├── lib
│ ├── echarts-wordcloud.min.js
│ └── echarts.min.js
├── logo.png
└── wordCloud.html

wordCloud.html代码:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<html>
<head>
<meta charset="utf-8">
<script src='./lib/echarts.min.js'></script>
<script src='./lib/echarts-wordcloud.min.js'></script>
</head>
<body>
<style>
html, body, #main {
width: 100%;
height: 100%;
margin: 0;
}
</style>
<div id='main'></div>
<script>
var chart = echarts.init(document.getElementById('main'));

var option = {
tooltip: {},
series: [ {
type: 'wordCloud',
gridSize: 2,
sizeRange: [12, 50],
rotationRange: [-90, 90],
shape: 'pentagon',
width: 600,
height: 400,
drawOutOfBound: true,
textStyle: {
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 160),
Math.round(Math.random() * 160),
Math.round(Math.random() * 160)
].join(',') + ')';
}
},
emphasis: {
textStyle: {
shadowBlur: 10,
shadowColor: '#333'
}
},
data: [
{
name: 'Sam S Club',
value: 10000,
textStyle: {
color: 'black'
},
emphasis: {
textStyle: {
color: 'red'
}
}
},
{
name: 'Macys',
value: 6181
},
{
name: 'Amy Schumer',
value: 4386
},
{
name: 'Jurassic World',
value: 4055
},
{
name: 'Charter Communications',
value: 2467
},
{
name: 'Chick Fil A',
value: 2244
},
{
name: 'Planet Fitness',
value: 1898
},
{
name: 'Pitch Perfect',
value: 1484
},
{
name: 'Express',
value: 1112
},
{
name: 'Home',
value: 965
},
{
name: 'Johnny Depp',
value: 847
},
{
name: 'Lena Dunham',
value: 582
},
{
name: 'Lewis Hamilton',
value: 555
},
{
name: 'KXAN',
value: 550
},
{
name: 'Mary Ellen Mark',
value: 462
},
{
name: 'Farrah Abraham',
value: 366
},
{
name: 'Rita Ora',
value: 360
},
{
name: 'Serena Williams',
value: 282
},
{
name: 'NCAA baseball tournament',
value: 273
},
{
name: 'Point Break',
value: 265
}
]
} ]
};

chart.setOption(option);

window.onresize = chart.resize;
</script>
</body>
</html>

渲染效果:

4. 参考资料

[1] Python词云可视化 from CNBLOG

[2] 词云可视化:四行Python代码轻松上手到精通 from Github

[3] 词云可视化:四行Python代码轻松上手到精通 from Bilibili

[4] 你不知道的词云 from python 123

[5] 原生js读取json文件 from CSDN

[6] 纯前端实现词云展示+附微博热搜词云Demo代码 from 稀土掘金

[7] 基于 wordcloud2.js 的 Apache ECharts 词云扩展 from Github