简介:面向NLP初学者与数据可视化爱好者的一份词云生成实践资源,覆盖中文分词、英文分词及自定义形状词云等常见需求,适用于新闻热点分析、社交媒体舆情监控等场景。压缩包内共32个文件,以3个Python脚本(分别用于中文词云、英文词云和背景更换)、4个txt语料(含哈工大停用词表)、16个png效果图、2个jpeg示意图、4个xml项目配置及说明文档为主,整体大小仅3.46MB,便于快速下载与本地运行。目前已有234人学习下载,适合在课堂演示、课程设计或项目实践中参考。其中不仅包含jieba中文分词、英文文本预处理以及基于wordcloud的词云生成代码,还提供了哈工大停用词表、示例文本以及不同模板形状的生成效果图,可帮助读者直观理解文本预处理、词频统计和可视化参数调优流程,进一步掌握如何将NLP技术落地为美观的信息展示工具。通过动手修改背景模板与停用词列表,还能快速生成个性化词云,提升数据表达力。
1. 词云图不是画图,是NLP预处理的可视化出口
词云图最容易被误解成一个“画图函数”,实际上它是NLP自然语言处理中最直观的频次可视化终端。一张干净的词云图背后一定要经历文本清洗、分词、停用词过滤、词频统计和画布布局,任何一步偷懒都会直接在图上暴露出来。中文和英文在这条流水线上的差异集中在分词阶段:英文可以靠空格和正则粗切,中文必须让分词库来判断词边界,否则屏幕上就是一堆单字。这个题目的三个关键词“中文词云、英文词云、不同形状的词云”,正好对应了NLP中文本切分、权重统计和结果呈现三件事。对做舆情分析、nlp新闻处理的人来说,这是比模型调参更常遇到的基础活;对准备课程设计的新手来说,这也是一个能完整讲清楚NLP流程的作业载体。
2. 词云生成的完整链路:分词、停用词、词频与画布渲染
很多人的做法是拿到一段文本直接丢给WordCloud,然后发现英文词云还能看,中文词云全是单字。原因在于wordcloud库本身只是一个布局引擎,它接收的其实是一个“词 - 权重”映射,而不是未加工的文本。真正的NLP流程发生在调用WordCloud之前:清洗原文、切分词语、统计词频、过滤噪声词,最后再把统计结果交给词云。
2.1 词频统计:词云的数据源不是文本而是频次
从软件设计上看,WordCloud的generate(text)方法内部仍然会做一次split和counter统计,所以直接把原始文本传进去并不是“绕过分词”,而是等于把分词主动权交给了wordcloud自己的正则逻辑。对英文文本这可能没问题,对中文文本则会导致相邻汉字被组合成毫无语义的字符块。
我一般在项目里会把词云抽象成三步:清洗原文、切分词语、统计计数。这样做的最大好处是任何一步出问题都能单独验证。比如打印Counter(freq).most_common(20),就能知道是分词没生效,还是停用词过滤太稀疏。数据源问题不解决,后面调任何颜色、形状都是白费功夫。
2.2 英文分词:空格切分之外还要做归一化
英文分词看起来最简单,直接text.split()就能拿到单词列表,但在真实文本里,大小写和标点会干扰统计结果。“China”和“china”如果被当成两个词,高频词排序就会失真;“world!”带着感叹号,也会与“world”分开计数。更稳妥的做法是先用正则抽取完整单词,同时统一小写:
import re words = re.findall(r"\b[a-zA-Z]+\b", text.lower())这段正则把连续字母作为词边界,忽略数字和标点。这样做对词云场景已经足够。词形还原在NLP中很有用,但在词云这种只看字号的场景里并不是必须的,因为reader和read如果被合并反而会丢掉形态信息。你要是更关心词干语义,可以接入WordNet的lemmatizer;如果只是做新闻词频展示,我一般不建议过度处理。
2.3 中文分词:jieba在词云场景的选型理由
中文没有天然空格,分词器必须切在“词”的边界上。可用工具不少,jieba、pkuseg、HanLP都是常见选项。但在词云任务中,我通常固定用jieba的精确模式,不用pkuseg也不用HanLP。原因很简单:词云只需要基础切分,不需要词性标注和句法分析,jieba的延迟低、词典可扩展,而且能往分词结果里轻松注入自定义词表。
| 工具 | 分词速度 | 词云场景适配度 | 额外资源 |
|---|---|---|---|
| jieba | 快 | 高,可加载自定义词典 | 不需要 |
| pkuseg | 中 | 中,准确率更高但模型加载慢 | 需要下载模型 |
| HanLP | 中慢 | 低,功能过剩 | 依赖较重 |
这里要区分jieba的三种模式:精确模式、全模式、搜索引擎模式。词云场景只推荐精确模式,它把句子切成分词结果中最合理的组合;全模式会把所有可能词都输出,导致“中华”“华人”“人民”同时出现,词频失真。搜索引擎模式用于搜索分词,同样不适合词云。
2.4 停用词过滤:高频噪声词的取舍
处理中文舆情词云时,我经常看到用户把“我们”“可以”“这样”这类词变成大号字,这就是停用词表没建好。停用词不是越长越好,而是要根据语料调整。比如新闻文本里必须加“记者”“报道”“近日”等新闻生产端高频词;诗词可视化里则要考虑“之”“乎”“者”“也”等文言虚词。
过滤逻辑用集合判断即可:
from collections import Counter freq = Counter(word for word in words if word not in stopwords)需要特别提醒的是,停用词表会直接影响NLP情感分析的结果。像“不好”如果只按字切,可能会被拆成“不”和“好”,“不”一旦被停用,情感极性就反了。不过词云场景不像情感分析那么敏感,因为它只展示整体词频,不做极性还原。
3. 用 Python 实现中文词云与英文词云的最小可运行代码
前面的链路拆完后,这一章直接给可运行代码。下面所有示例都以本地文本文件和常用库为基础,不依赖任何在线数据源。代码顺序按英到中、由简到繁排列。
3.1 环境安装与版本提示
pip install wordcloud matplotlib jieba这三个库是词云场景的标准组合。wordcloud依赖numpy和Pillow,如果Python版本较新,pip会自动拉取兼容版。若在3.11以上遇到扩展包安装失败,常见做法是用conda先重建一个Python 3.9环境,再执行上面的pip命令。我一般不去追wordcloud的最新小版本,稳定版的行为差异足够大时再升级。
3.2 英文词云:从文本文件到PNG
下面用一段英文新闻文本生成基础词云。保存文件名为english_wordcloud.py:
# english_wordcloud.py import re import matplotlib.pyplot as plt from wordcloud import WordCloud text = open("news_en.txt", encoding="utf-8").read() words = re.findall(r"\b[a-zA-Z]+\b", text.lower()) clean_text = " ".join(words) wc = WordCloud(width=800, height=500, background_color="white", max_words=200, collocations=False) wc.generate(clean_text) plt.figure(figsize=(8, 5)) plt.imshow(wc, interpolation="bilinear") plt.axis("off") plt.savefig("wordcloud_en.png", dpi=150)代码逻辑分三段:先正则抽取所有单词,再用空格拼接,最后交给WordCloud布局。这里必须关注collocations=False参数,它的默认值是True,wordcloud会主动检测“machine learning”这类二元词组,并把它们合并为一个词条展示。如果你做新闻处理时更关注单个实体词,就应该关掉;如果你明确想做短语展示,那就保留True。两种操作没有绝对优劣,但默认行为容易让你得到一张与自己预期不符的词云。
| 参数 | 含义 | 示例值 |
|---|---|---|
| width/height | 画布像素大小 | 800/500 |
| background_color | 背景色 | white |
| max_words | 最多绘制词数 | 200 |
| collocations | 是否合并英文二元词组 | False |
| min_font_size | 最小字号,限制过小词的出现 | 默认4 |
3.3 中文词云:jieba 分词与字体路径
中文词云相比英文多两个步骤:分词和指定字体。wordcloud的默认字体是DejaVu Sans,不含中文字符,不指定font_path时中文输出一定是方框。常见做法是把系统字体复制到项目目录下,比如Windows下的msyh.ttc,macOS下的PingFang.ttc,Linux下常见simhei.ttf。下面代码用generate_from_frequencies直接传入词频字典:
# chinese_wordcloud.py import jieba from collections import Counter from wordcloud import WordCloud import matplotlib.pyplot as plt text = open("news_zh.txt", encoding="utf-8").read() stopwords = set(open("stopwords.txt", encoding="utf-8").read().split()) seg_list = [ w for w in jieba.cut(text) if w.strip() and w not in stopwords and len(w) > 1 ] freq = Counter(seg_list) wc = WordCloud(font_path="fonts/simhei.ttf", width=800, height=500, background_color="white", max_words=200) wc.generate_from_frequencies(freq) plt.figure(figsize=(8, 5)) plt.imshow(wc, interpolation="bilinear") plt.axis("off") plt.savefig("wordcloud_zh.png", dpi=150)这里的顺序值得说明:jieba.cut(text)返回生成器,列表推导式中先去掉空白词,再去掉停用词,最后用len(w) > 1过滤掉单字。这个单字过滤在中文词云里非常重要,因为很多高频字如“的”“是”“在”即使不在停用词表里,也会因为单字形态占据视觉焦点。Counter统计后,直接由generate_from_frequencies消费,避免wordcloud再次分词。
很多教程会先" ".join(seg_list)再交给wc.generate(text),这也能跑通,但多了一次内部切分。对长文本来说,用generate_from_frequencies更直接,也能让你在统计阶段就发现问题。
3.4 词频字典与文本输入的选择时机
喂给词云的两种方式在不同场景各有优势:
- 如果你只有一段原始文本,且不在意分词细节,直接用
wc.generate(text)最省事。 - 如果你已经用jieba做过分词、停用词过滤,一定要用
generate_from_frequencies(freq),因为它接受一个Counter对象,按现成的词频布局。
另一个反直觉点是:传给词频字典的值不需要归一化。wordcloud内部会把频次线性映射到字号范围,你只要保证相对大小是对的。如果某个词频高到几万,其他词只有几十,反而要通过max_words限制词数和min_font_size调小下限,让高频词不撑爆画面。我曾经见过有人专门写个min-max归一化再传入,结果和wordcloud自带缩放逻辑叠加,字号分布反而不可控。
4. 深入不同形状的词云:mask 图片、颜色函数与诗词可视化
不同形状的词云并不是wordcloud的隐藏功能,而是它公开的mask参数配合Pillow和NumPy一起实现的。这一章我们会把形状词云的原理讲清楚,再结合“唐宋诗词词云可视化”和NLP情感分析这类实际场景展开。
4.1 mask 参数:为什么形状不生效的根源是黑白值
WordCloud的mask参数接收一个NumPy二维数组,它的值只有两类,0代表可以绘制文字的区域,255代表透明区域。也就是说,形状实际上是靠“挖洞”来定义的。
下面是一段可运行代码。假设你已经有一张白底黑心形图片heart_shape.png:
# mask_wordcloud.py import numpy as np from PIL import Image import jieba from collections import Counter from wordcloud import WordCloud import matplotlib.pyplot as plt text = open("news_zh.txt", encoding="utf-8").read() stopwords = set(open("stopwords.txt", encoding="utf-8").read().split()) seg_list = [w for w in jieba.cut(text) if w.strip() and w not in stopwords and len(w) > 1] freq = Counter(seg_list) mask_pic = np.array(Image.open("heart_shape.png")) wc = WordCloud(mask=mask_pic, font_path="fonts/simhei.ttf", background_color="white", contour_width=1, contour_color="steelblue", max_words=200) wc.generate_from_frequencies(freq) wc.to_file("wordcloud_heart.png")参数说明:contour_width=1会在mask边缘加一条描边,颜色由contour_color控制,这能掩盖图片锯齿。background_color='white'填充形状以外的区域;如果你想要透明底PNG,把它改成None。
形状不生效时,先打印np.unique(mask_pic),看mask数组里到底是0和1还是0和255。wordcloud对0/1也能处理,但边缘平滑度和显示效果不如0/255。很多在线图片是JPEG,自带灰度阴影,导致mask值一堆中间值,最后词云布局错乱。
4.2 按原图着色:ImageColorGenerator 的用法
生成形状词云后,默认颜色是蓝紫色系。如果你希望词云颜色跟随原图,例如心形是红色渐变,那么需要ImageColorGenerator:
from wordcloud import ImageColorGenerator image_colors = ImageColorGenerator(mask_pic) wc.recolor(color_func=image_colors) wc.to_file("wordcloud_heart_color.png")recolor必须在generate或generate_from_frequencies之后调用,因为它要读取已有的词块位置。recolor不会改变文字布局,只替换颜色。这一步适合做品牌视觉,比如把公司logo轮廓作为mask,再按logo配色填充文字。
| 颜色方案 | 调用方式 | 适用场景 |
|---|---|---|
| 默认蓝紫 | 不修改color_func | 快速验证词频 |
| 单色 | color_func=lambda *args, **kwargs: "#e74c3c" | 品牌色输出 |
| 原图渐变 | recolor(color_func=ImageColorGenerator(mask)) | 形状视觉展示 |
4.3 结合 NLP 情感分析与唐宋诗词词云可视化
在CSDN上经常能看到“唐宋诗词词云可视化”这类项目,它们通常不只是把整本诗集塞进一张图,而是先做情感分析再分形状展示。比如把诗句中带有“愁”“孤”“寒”的句子归为负向情绪,把“春”“欢”“归”归为正向情绪,然后用两个不同mask分别生成词云,让整个输出有叙事层次。
下面是一个用情感词集合做分桶的最小示例:
# emotion_bucket.py positive_words = {"春", "日", "花开", "乐", "归"} negative_words = {"愁", "孤", "独", "寒", "泪", "悲"} def split_by_emotion(seg_list): pos = [w for w in seg_list if w in positive_words] neg = [w for w in seg_list if w in negative_words] return pos, neg pos_list, neg_list = split_by_emotion(seg_list) # 对 pos_list 和 neg_list 分别做Counter,再传给不同mask的WordCloud这个示例不依赖复杂模型,但演示了词云如何与NLP情感分析协同:先把评论或诗词按情感倾向分成两个语料桶,再分别出图。对新闻数据处理也一样,如果把所有评论混作一张词云,正向词和负向词会互相淹没,视觉上很热闹却看不出情绪倾向。分开出图虽然工作量翻倍,反而信息量更大。
5. 验证与排错:让词云可复现而不是靠运气
词云最大的坑是你无法从最终图片判断问题是出在数据还是出在渲染。因此我把验证步骤放在最前面,先看词频再改样子。
5.1 生成前先打印Top20词
在把任何东西交给WordCloud之前,先打印词频表:
from collections import Counter print(freq.most_common(20))检查标准有三个:如果高频词全是“我们”“可以”这类词,是停用词表没生效;如果全是单字,是分词没生效;如果数量集中在两三个词,则是文本语料本身过于单调。这一步能直接定位问题,省去反复调mask的时间。
5.2 三个常见陷阱与修复方式
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 中文显示为方框 | font_path未指定或字体不含中文字符 | 传项目中文字体路径 |
| mask不生效,词云仍为矩形 | mask图不是黑白二值,或值域不符合要求 | np.array(Image.open(...).convert("L"))后检查np.unique |
| 词云里全是单字 | 分词没做,text内部按空格切分中文 | 先jieba.cut再传词频字典 |
需要注意,mask图片必须处理后转为灰度模式再转数组。更保险的操作是:
from PIL import Image mask_pic = np.array(Image.open("shape.png").convert("L"))convert("L")会去掉颜色通道,把图片变成0到255的灰色矩阵。这比直接读彩色图更稳定,因为PIL对PNG透明通道的处理偶尔会引入第四个通道,导致mask维度不匹配。
5.3 字号分布的关键参数:relative_scaling
最后一个实用性技巧是字号控制。relative_scaling控制字号与词频的关系,默认值近似按排序决定字号,设为1时严格按频次比例缩放。对长文本,我一般会设置relative_scaling=0.5并配合max_font_size=80,这样第一名和第十名的字号差相对明显,但又不至于让最高频词独占画面。
调整这两个参数时,不需要改动任何前面的分词逻辑,直接重新调用WordCloud构造函数并保存即可。这种配置方式在舆情词云里非常常用:先用max_font_size限制上限,再用relative_scaling控制梯度,可以让词云在尺寸缩略图中依然保持可读性。
本文还有配套的精品资源,点击获取