简介:这是一份围绕 Python 词云生成的完整教程文档,面向对文本挖掘与数据可视化感兴趣的初学者和中级用户,可服务于项目报告、数据分析、社交媒体监控与课堂演示等场景。压缩包内只有 1 个 PDF 文件,体积 2.07MB,单篇文档覆盖从概念到实际应用的全部内容,便于随时查阅。教程先说明词云的定义、作用与生成原理,再介绍 wordcloud 与 matplotlib 两个核心库的用法,并给出 Python 3.7、wordcloud 1.9.3、matplotlib 3.5.3 的环境版本参考。7 个案例由浅入深:最简调用、中文不分词、中文分词,到蒙版形状、自定义配色、关键词与权重提取等进阶用法,每个案例均附完整代码、分步解析与运行结果,读者可据此复现效果,替换文本和蒙版,制作个性化词云。目前已有 109 人学习下载。
1. 从一份 PDF 到可跑的词云:7 个案例先解决什么问题
这份《python实现词云完整教程7个案例.pdf》是一份偏实战的 python 课程资源,核心不是讲词云概念,而是把 WordCloud 从最小调用一路推到中文分词、蒙版形状、颜色函数和关键词权重。很多人第一次做词云会卡在中文方框、蒙版发黑、颜色不生效这三个地方,教程里的 7 个案例正好按这个顺序拆开。适合已经会一点 Python、想把文本分析结果放进报告或演示的人;如果你只想先复制一张图,也能从案例 01 直接跑通。
它解决的是“文本能读、图能出、参数能调”这三件事。案例 01 到 03 把英文文本、中文不分词、中文分词三条路线摆在一起,让你先看到差异;案例 04 到 06 进入蒙版形状、蒙版取色和自定义颜色函数;案例 07 用 jieba.analyse.extract_tags 提取关键词和权重,再交给 generate_from_frequencies。路线不绕,适合拿一份自己的文本直接替换进去试。
2. 环境与最小调用:把 WordCloud 跑通的第一条命令
2.1 版本锁与依赖安装:Python 3.7 / wordcloud 1.9.3 / matplotlib 3.5.3
教程给的环境是 Python 3.7、wordcloud 1.9.3、matplotlib 3.5.3。这个组合不算新,但做词云够用,尤其是老项目里 numpy、Pillow 版本已经被其他依赖牵住时,按教程版本对齐能少掉很多“装上了但 import 报错”的麻烦。我一般会先建独立虚拟环境,不把词云依赖装进系统 Python,后面换案例也不用反复卸载。
# 建一个独立环境,避免和系统里的 numpy/Pillow 打架 python -m venv venv # Linux/macOS 激活 source venv/bin/activate # Windows 激活 # venv\Scripts\activate # 按教程版本装核心库,jieba/Pillow/numpy 按当前环境补齐 pip install wordcloud==1.9.3 matplotlib==3.5.3 jieba pillow numpy这段命令的逻辑很直接:先隔离环境,再锁 wordcloud 和 matplotlib 的主版本,最后补 jieba、Pillow、numpy。参数上只有两个版本号是硬约束,来自教程;jieba、Pillow、numpy 没有在教程里给出版本,不要硬编一个不存在的版本号。安装完先跑python -c "import wordcloud, matplotlib; print(wordcloud.__version__, matplotlib.__version__)",能打印出 1.9.3 和 3.5.3 再往下走。如果 pip 解析冲突,优先看 numpy 和 Pillow 是不是被其他包锁死,不要一上来就升级 Python。
2.2 最小案例拆解:generate 与 to_file 的边界
案例 01 是最简调用,代码短,但它把 WordCloud 的两个核心动作暴露得很清楚:generate(text)负责根据文本生成布局,to_file()负责把内存里的图落成文件。很多人把plt.show()当成保存,关掉窗口就什么都没有;也有人把to_file()写在generate()之前,拿到的还是空对象。先把这个顺序跑顺,后面加蒙版和颜色才不会乱。
# -*- coding: utf-8 -*- from wordcloud import WordCloud import matplotlib.pyplot as plt # 英文示例文本:按空格分词,WordCloud 默认按空格切词 with open('english_text.txt', 'r', encoding='utf-8') as f: text = f.read() # 固定 random_state,方便复现布局 wc = WordCloud( width=800, height=600, background_color='white', random_state=42 ).generate(text) # 显示:interpolation 只影响显示平滑,不影响保存像素 plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() # 保存:必须在 generate 之后 wc.to_file('wordcloud.png')代码里width=800, height=600是画布尺寸,background_color='white'是背景色,random_state=42是布局随机种子。generate(text)的输入是“已经分好词、用空格连接”的字符串,英文文本天然有空格,所以直接读进来就能用;中文文本如果没有空格,就会被当成一个长串,这也是案例 02 要单独看“中文不分词”的原因。interpolation='bilinear'只控制 matplotlib 显示时的插值方式,保存出来的 PNG 还是 WordCloud 自己渲染的像素,不要指望它提升清晰度。plt.axis('off')只是关掉坐标轴,和保存无关。
2.3 英文文本读取与编码:open 不写 encoding 的代价
案例 01 用open('english_text.txt').read()也能跑,但在 Windows 上很容易翻车:系统默认编码可能是 GBK,文本里只要有非 ASCII 字符就抛UnicodeDecodeError。我习惯在所有文本读取里显式写encoding='utf-8',这不是洁癖,是让错误尽早暴露。如果文本来源复杂,先做一次编码探测,再决定用 utf-8 还是 gbk,不要用errors='ignore'把半截文本吞掉。
from pathlib import Path path = Path('english_text.txt') raw = path.read_bytes() # 先看前几十个字节,判断有没有 BOM 或明显不是 utf-8 的字节 print(raw[:40]) try: text = raw.decode('utf-8') except UnicodeDecodeError: # 常见备选:gbk;不要长期用 ignore 掩盖问题 text = raw.decode('gbk', errors='replace') print(len(text), text[:80])这段代码先读字节,再尝试 utf-8,失败后退到 gbk,并且用errors='replace'把无法解码的字符替换掉,至少能看出问题位置。参数上raw[:40]是抽样,不是完整校验;errors='replace'会留下替换符,后续词云里可能出现乱码方块,所以它只适合排查,不适合最终产出。文本读取这一步稳了,后面generate()才不会把编码问题误判成字体问题。
2.4 常用参数表:先知道哪些旋钮值得拧
WordCloud 的参数不少,但做词云真正高频的就是下面这些。先按默认值跑一张,再逐项改,不要一次把十几个参数全写上去,否则出问题很难定位。
| 参数 | 常用值/写法 | 作用与注意点 |
|---|---|---|
width/height | 800 / 600 | 画布像素,蒙版存在时以蒙版形状为准 |
font_path | 中文字体文件路径 | 中文必须指定,否则方框 |
mask | np.array(Image.open(...)) | 控制词云形状,白色区域不画词 |
background_color | 'white'/None | 配合mode='RGBA'出透明背景 |
mode | 'RGB'/'RGBA' | 透明背景用 RGBA |
max_words | 200 左右 | 词太多会挤,太少会空 |
stopwords | set([...]) | 中文停用词要自己补 |
random_state | 42 | 固定布局,方便复现 |
prefer_horizontal | 0.9 | 控制横排比例,中文可适当调低 |
collocations | False | 关闭双词组合,中文分词后更干净 |
contour_width | 1~3 | 蒙版轮廓线宽 |
contour_color | 'black' | 蒙版轮廓颜色 |
color_func | 自定义函数 | 控制每个词的颜色 |
这张表里最容易被忽略的是collocations。WordCloud 默认会考虑双词组合,英文里可能生成 “data science” 这种短语,中文分词后也可能把两个相邻词拼在一起,看起来像多出来的词。做中文词云时我一般先设collocations=False,让词频统计口径更接近 jieba 的分词结果。prefer_horizontal默认 0.9,中文词较长时可以降到 0.7 左右,减少竖排造成的阅读别扭,但不要降得太狠,否则布局会变稀。
3. 中文词云的分水岭:jieba 分词与字体路径
3.1 中文不分词案例:先看失败样本再决定是否分词
案例 02 故意不做分词,直接读中文文本生成词云。它的价值不是让你长期这么用,而是让你亲眼看到“不分词会怎样”:WordCloud 默认按空格切词,中文整段没有空格,结果往往只有几块巨大的词,或者干脆空白。教程里用mode='RGBA'和background_color=None做透明背景,这个组合本身没问题,但如果文本没分词,透明背景也救不了布局。
# -*- coding: utf-8 -*- from wordcloud import WordCloud import matplotlib.pyplot as plt # 中文文本:这里故意不分词,观察 WordCloud 的默认切词行为 with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() wc = WordCloud( font_path='chinese_font.ttf', # 换成你环境里可用的中文字体 width=800, height=600, mode='RGBA', background_color=None ).generate(text) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() wc.to_file('main_2.png')font_path='chinese_font.ttf'是占位路径,实际要换成你机器上存在的中文字体文件。mode='RGBA'表示输出带 alpha 通道,background_color=None让背景透明,保存 PNG 时透明才有效;如果保存成 JPG,透明会被填成白色或黑色。案例 02 的运行结果通常不会太好看,这不是代码错,而是输入文本没有词边界。先接受这个失败样本,再切到案例 03,你会更清楚 jieba 到底解决了什么。
3.2 jieba 分词案例:cut 的三种模式与词频口径
案例 03 在读取文本后加了一行text = ' '.join(jieba.cut(text)),把分词结果用空格连接,WordCloud 才能按空格切词。jieba 常见三种模式:精确模式jieba.cut(text)尽量切准;全模式jieba.cut(text, cut_all=True)把所有可能成词的片段都扫出来,召回高但歧义多;搜索引擎模式jieba.cut_for_search(text)在精确模式基础上对长词再切,适合做索引。词云一般用精确模式,词频口径更稳。
# -*- coding: utf-8 -*- from wordcloud import WordCloud import matplotlib.pyplot as plt import jieba with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() # 精确模式:词云最常用,避免全模式带来的碎片词 words = jieba.cut(text) text = ' '.join(words) # 打印前 100 个字符,确认分词结果里有没有大量单字 print(text[:100]) wc = WordCloud( font_path='chinese_font.ttf', width=800, height=600, mode='RGBA', background_color=None, collocations=False, random_state=42 ).generate(text) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() wc.to_file('wordcloud.png')这段代码的关键在' '.join(jieba.cut(text)):jieba.cut返回生成器,直接传给 WordCloud 不行,必须先拼成字符串。collocations=False是我额外加的,教程原案例没写,但中文分词后关闭双词组合更干净。print(text[:100])是排查手段,看看有没有“的 的 了 了”这种高频虚词;如果有,下一节用停用词处理。参数上random_state=42固定布局,方便你对比不同分词模式的结果,不然每次图都不一样,很难判断是分词变了还是随机布局变了。
3.3 字体路径与中文方框:排查顺序不要反
中文词云最常见的翻车现场是“图出来了,但全是方框”。原因通常不是 WordCloud 不支持中文,而是font_path指向的字体文件不存在,或者字体本身不包含中文字形。我的排查顺序是:先确认文件存在,再确认字体能被 PIL 打开,最后确认 WordCloud 真的用了这个路径。不要一上来就改matplotlib的全局字体,WordCloud 的font_path和 matplotlib 的显示字体是两条线。
from pathlib import Path from PIL import ImageFont font_file = Path('chinese_font.ttf') print('exists:', font_file.exists()) if font_file.exists(): # 能打开只说明文件格式可读,不代表包含中文字形 font = ImageFont.truetype(str(font_file), 20) print('font loaded:', font.getname())Path.exists()解决“路径写错”的问题,ImageFont.truetype解决“文件不是有效字体”的问题。如果这两步都过了,词云还是方框,检查文本编码和分词结果,确认不是乱码先进去了。还有一个容易忽略的点:WordCloud 保存的 PNG 和 matplotlib 窗口显示可能不一致,窗口里方框但保存文件正常,或者反过来。以to_file()的产物为准,不要只盯plt.show()。
3.4 停用词与标点清洗:把噪音挡在 generate 之前
中文文本里“的、了、在、是、我、你、他”这类词频率极高,不处理就会占据词云最大字号,最后图看起来很热闹,实际没有信息量。WordCloud 自带的stopwords默认偏英文,中文要自己补。我一般先跑一遍不做停用词的图,把最大的十几个词抄下来,再决定哪些进停用词表。标点符号可以在分词前用正则清一遍,也可以在停用词里加,看文本干净程度。
import re from wordcloud import WordCloud with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() # 去掉常见标点和空白,保留中文、英文、数字 text = re.sub(r'[^\u4e00-\u9fa5A-Za-z0-9]+', ' ', text) stopwords = set([ '的', '了', '在', '是', '我', '你', '他', '她', '它', '和', '与', '就', '都', '而', '及', '或', '一个', '没有', '不是' ]) wc = WordCloud( font_path='chinese_font.ttf', stopwords=stopwords, max_words=200, collocations=False, background_color='white', random_state=42 ).generate(' '.join(__import__('jieba').cut(text))) wc.to_file('wordcloud_clean.png')正则[^\u4e00-\u9fa5A-Za-z0-9]+把非中文、非英文、非数字的字符替换成空格,减少标点粘连。stopwords是集合,查找快;max_words=200控制显示词数,太多会挤成一团。这里用__import__('jieba')是为了代码块短一点,实际项目里正常import jieba更清楚。停用词不是越多越好,把有业务含义的词误杀,词云会失去解释力;每次加停用词都留一个注释,后面换文本时知道哪些是针对当前语料加的。
4. 蒙版形状与颜色:把词云从方块变成可控图形
4.1 mask 的数组语义:白色区域不画词
案例 04 用mask=np.array(Image.open('black_mask.png'))控制词云形状。WordCloud 对 mask 的规则是:白色区域(#FFFFFF)被忽略,非白区域参与绘制。也就是说,你想要词填满哪个形状,就把那个形状做成非白,背景做成白色。很多人拿黑底白图的蒙版直接跑,结果词全跑到背景上,形状反了,还以为是代码问题。做蒙版时用白底黑图最稳,形状边缘不要有灰阶过渡,否则会出现毛边。
# -*- coding: utf-8 -*- from wordcloud import WordCloud from PIL import Image import numpy as np import matplotlib.pyplot as plt import jieba with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() text = ' '.join(jieba.cut(text)) # 白底黑图:黑色形状区域参与绘制,白色背景被忽略 mask = np.array(Image.open('black_mask.png')) wc = WordCloud( mask=mask, font_path='chinese_font.ttf', mode='RGBA', background_color=None, contour_width=1, contour_color='black', random_state=42 ).generate(text) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() wc.to_file('main_4.png')mask必须是 numpy 数组,不是文件路径;Image.open()只负责读图,np.array()才转成 WordCloud 要的矩阵。contour_width和contour_color给形状加轮廓,适合报告里让边界更清楚;不需要就删掉。background_color=None配合mode='RGBA'出透明背景,但如果蒙版本身有灰阶,透明边缘可能带脏点。我一般先把蒙版缩到 800 像素宽左右再跑,太大不仅慢,还会让词被挤到看不清。
4.2 ImageColorGenerator 取色:颜色跟随原图但会翻车
案例 05 在蒙版基础上加了ImageColorGenerator,让词的颜色从蒙版图片里取。它的逻辑是:根据词在蒙版上的位置,取对应区域的颜色,所以蒙版最好是彩色的,而且颜色分布要和你想表达的重点一致。如果蒙版是黑白的,取出来的颜色会非常单调,甚至接近黑色,图看起来像没上色。用法上,generate()之后调用recolor(color_func=image_colors),顺序不能反。
from wordcloud import WordCloud, ImageColorGenerator from PIL import Image import numpy as np import matplotlib.pyplot as plt import jieba with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() text = ' '.join(jieba.cut(text)) mask = np.array(Image.open('color_mask.png')) wc = WordCloud( mask=mask, font_path='chinese_font.ttf', mode='RGBA', background_color=None, random_state=42 ).generate(text) # 从彩色蒙版中取色,必须在 generate 之后 image_colors = ImageColorGenerator(mask) wc.recolor(color_func=image_colors) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() wc.to_file('main_5.png')ImageColorGenerator(mask)接收的是同一个蒙版数组,wc.recolor()会重新给已经布局好的词上色,不会重新布局。参数上可以给ImageColorGenerator传default_color,当某个词取不到颜色时用它兜底;但更常见的翻车是蒙版颜色太暗,词全变成深色,和背景糊在一起。遇到这种情况,先把蒙版调亮,或者换自定义颜色函数,不要硬调background_color掩盖问题。
4.3 自定义颜色函数:random_color 的参数与可复现性
案例 06 用color_func自定义颜色,函数签名是word, font_size, position, orientation, font_path, random_state,返回一个颜色字符串。教程里的写法是拼hsl(...),色相固定、饱和度和亮度随机,出来是偏暖的随机色。这个写法能跑,但有两个边界:不同 PIL/matplotlib 版本对 hsl 字符串的解析可能不一致;随机色不可复现,报告里想复现同一张图会很难。我一般改成返回#RRGGBB,并用random_state控制随机。
import random from wordcloud import WordCloud from PIL import Image import numpy as np import matplotlib.pyplot as plt import jieba with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() text = ' '.join(jieba.cut(text)) mask = np.array(Image.open('color_mask.png')) def random_color(word, font_size, position, orientation, font_path, random_state): # 用传入的 random_state 生成稳定随机数,避免每次颜色都变 rng = random.Random(random_state) r = rng.randint(60, 200) g = rng.randint(60, 200) b = rng.randint(60, 200) return '#%02x%02x%02x' % (r, g, b) wc = WordCloud( color_func=random_color, mask=mask, font_path='chinese_font.ttf', mode='RGBA', background_color=None ).generate(text) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() wc.to_file('main_06.png')color_func的六个参数里,word是当前词,font_size是字号,position是词的位置,orientation是横竖排,font_path是字体路径,random_state是 WordCloud 给的随机状态。用random.Random(random_state)而不是全局random.randint,同一个词在同一张图里颜色稳定,换random_state才会变。返回#RRGGBB比 hsl 更稳,兼容性好。颜色范围不要全取 0~255,太亮或太暗都会让词和图底混在一起,60~200 是保守区间。
4.4 蒙版制作与阈值检查:别让灰阶边缘毁掉形状
蒙版不是随便找张图就行。常见做法是白底、黑形,形状内部纯黑,背景纯白,边缘不要抗锯齿灰边。灰边在 WordCloud 里会被当成非白区域,导致形状外面多出一圈毛刺。可以用 PIL 做一次二值化,把接近白的像素强制成白,接近黑的强制成黑。检查时打印mask.shape、mask.min()、mask.max(),确认不是全白或全黑。
from PIL import Image import numpy as np mask_img = Image.open('black_mask.png').convert('L') # 二值化:白底黑形,阈值 200 是经验值,按你的图调整 mask_img = mask_img.point(lambda p: 255 if p > 200 else 0) mask = np.array(mask_img) print(mask.shape, mask.min(), mask.max()) mask_img.save('black_mask_clean.png')convert('L')转灰度,point()做阈值,255 if p > 200 else 0让背景更干净。阈值 200 不是固定值,蒙版边缘本来就灰,可以调到 230 或 240。打印min/max是为了确认图里既有 0 又有 255;如果全是 255,词云会空白;全是 0,形状会铺满整个画布,失去蒙版意义。这一步做完再喂给 WordCloud,比在词云参数里反复试更省时间。
5. 常见问题排查:关键词权重、中文方框与蒙版发黑
5.1 案例 07 的权重路径:extract_tags 与 generate_from_frequencies
案例 07 不再把整段分词文本丢给generate(),而是先用jieba.analyse.extract_tags(text, topK=200, withWeight=True)提取关键词和权重,再把结果转成字典,交给generate_from_frequencies(freq)。这条路径的好处是你可以控制“哪些词进入词云”和“每个词多大”,比单纯词频更接近 TF-IDF 的重要性。坑在于extract_tags返回的是列表,元素是(词, 权重),不能直接传给 WordCloud,必须转成{词: 权重}。
# -*- coding: utf-8 -*- from wordcloud import WordCloud, ImageColorGenerator from PIL import Image import numpy as np import matplotlib.pyplot as plt import jieba.analyse with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() # topK 控制关键词数量,withWeight=True 才会返回权重 freq = jieba.analyse.extract_tags(text, topK=200, withWeight=True) print(freq[:20]) # 转成 WordCloud 需要的字典格式 freq_dict = {word: weight for word, weight in freq} mask = np.array(Image.open('color_mask.png')) wc = WordCloud( mask=mask, font_path='chinese_font.ttf', mode='RGBA', background_color=None, random_state=42 ).generate_from_frequencies(freq_dict) image_colors = ImageColorGenerator(mask) wc.recolor(color_func=image_colors) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.show() wc.to_file('main_07.png')topK=200表示取前 200 个关键词,太少会空,太多会挤;withWeight=True一定要开,否则返回的是纯词列表,转字典时没有权重。generate_from_frequencies()接收的字典值是权重,不是词频,权重越大字号越大。recolor()仍然放在生成之后,和案例 05 的顺序一致。如果图里出现很多单字,说明extract_tags的切词结果偏碎,可以先用停用词或自定义词典干预,再重新提取。
5.2 现象:中文全是方框;原因:字体路径或字形缺失
现象很直观:词云布局出来了,但每个词都是空心方框,或者只有英文正常、中文全是方块。原因通常有三层:font_path指向的文件不存在;文件存在但不是中文字体;文本编码错了,进 WordCloud 的本来就是乱码。解决顺序是先Path.exists(),再ImageFont.truetype()加载,最后打印text[:100]看文本。不要先改 matplotlib 的rcParams,那影响的是显示,不是 WordCloud 渲染。
from pathlib import Path from PIL import ImageFont font_file = Path('chinese_font.ttf') assert font_file.exists(), '字体文件不存在,先改 font_path' font = ImageFont.truetype(str(font_file), 20) print(font.getname()) with open('novel.txt', 'r', encoding='utf-8') as f: sample = f.read()[:100] print(sample)assert让路径问题立刻暴露,getname()确认字体能加载,打印样本文本确认不是乱码。如果字体加载正常、文本也正常,但保存的图还是方框,换一个确定包含中文的字体文件再试。字体路径不要写相对路径里的../太多层,脚本一换目录就找不到;我习惯把字体和脚本放同一层,或者用绝对路径常量。
5.3 现象:蒙版发黑、词云空白;原因:黑白区域反了或背景色冲突
蒙版发黑通常有两种:一种是词全挤在背景上,形状区域反而空的,说明黑白区域反了;另一种是整张图几乎全黑,词的颜色也是深色,和背景糊在一起。WordCloud 的规则是白色区域忽略、非白区域绘制,所以蒙版应该是白底黑形。如果你拿黑底白形,形状会反过来。空白则可能是蒙版全白,或者max_words太小、文本太短。
from PIL import Image import numpy as np mask = np.array(Image.open('black_mask.png').convert('L')) print('shape:', mask.shape) print('min/max:', mask.min(), mask.max()) print('white ratio:', (mask > 200).mean()) # 如果白色比例过高,说明形状区域太小,词没地方放white ratio是白色像素占比,超过 0.95 基本等于没形状,词云会空。min/max能看出是不是全白或全黑。解决上,先把蒙版二值化,再确认background_color和mode:透明背景用mode='RGBA'、background_color=None;要白底就用background_color='white'、mode='RGB'。不要一边设透明背景一边保存 JPG,透明会丢。
5.4 现象:颜色函数不生效;原因:recolor 顺序或返回值不对
颜色函数不生效的典型表现是:明明写了color_func,图里还是默认蓝色系;或者ImageColorGenerator调了没反应。原因多半是recolor()写在了generate()之前,或者color_func返回值不是 WordCloud 能解析的颜色。自定义函数必须返回颜色字符串,#RRGGBB最稳;返回None、元组、或者拼错的hsl都可能被忽略。
def safe_color(word, font_size, position, orientation, font_path, random_state): # 返回十六进制颜色,避免 hsl 解析差异 return '#3b7dd8' wc = WordCloud( color_func=safe_color, font_path='chinese_font.ttf', background_color='white' ).generate(text) # 如果用了 ImageColorGenerator,必须在这之后 recolor # wc.recolor(color_func=ImageColorGenerator(mask)) wc.to_file('color_check.png')color_func在WordCloud()构造时传入,recolor()在generate()之后调用,这两条顺序别混。safe_color返回固定色,用来验证函数有没有被调用;如果固定色生效,再换成随机色。ImageColorGenerator依赖蒙版有颜色,黑白蒙版取色会很单调,这不是 bug,是输入不适合。
5.5 现象:词太少或全是虚词;原因:停用词、max_words 与文本长度
词太少常见于三种情况:文本本身短,max_words设得太小,停用词把有意义的词也杀了。全是虚词则是停用词没补全,中文的“的、了、是”会占据最大字号。解决顺序是先打印词频前 30,确认哪些该杀;再调max_words到 100~300;最后看文本长度,几百字的文本不要指望生成很密的词云。
from collections import Counter import jieba with open('novel.txt', 'r', encoding='utf-8') as f: text = f.read() words = [w for w in jieba.cut(text) if len(w.strip()) > 0] counter = Counter(words) print(counter.most_common(30))Counter打前 30 个高频词,比盲猜停用词靠谱。len(w.strip()) > 0只是去空白,不能替代停用词。看到高频虚词就加进stopwords,看到有意义的词被误杀就移出。max_words不要一上来设 1000,词云不是词表,200 左右通常够报告用;文本只有几千字时,设太大只会让布局变稀。
6. 验收与进阶:把词云从“能看”推到“能讲”
词云做完不要只看“好不好看”,要先验收“词对不对”。我通常做三件事:把extract_tags的前 30 个词打印出来,和词云里最大的词对照;固定random_state,同一份文本连续跑两次,确认布局和颜色可复现;保存文件按文本名_分词方式_蒙版_日期.png命名,后面回看知道参数。下面这个小函数把“读文本、分词、停用词、生成、保存”串在一起,适合当项目里的最小封装。
from pathlib import Path from wordcloud import WordCloud import jieba def build_wordcloud(text_path, font_path, out_path, stopwords=None): text = Path(text_path).read_text(encoding='utf-8') words = ' '.join(jieba.cut(text)) wc = WordCloud( font_path=font_path, stopwords=stopwords or set(), max_words=200, collocations=False, background_color='white', random_state=42 ).generate(words) wc.to_file(out_path) return out_path build_wordcloud('novel.txt', 'chinese_font.ttf', 'novel_wordcloud.png')这个函数只保留最稳的参数:max_words=200、collocations=False、random_state=42。蒙版和颜色函数不建议一开始就塞进去,先让文本路径和字体路径跑通,再逐层加。验收时看输出文件是否存在、尺寸是否和预期一致、最大词是不是你预期的关键词。如果最大词是“的”,不要调颜色,回去补停用词。
进阶用法上,我习惯把generate_from_frequencies和普通generate各跑一张,对比词频口径和 TF-IDF 口径的差异;再把color_func换成按词性上色,比如名词暖色、动词冷色,报告里更容易讲。需要对照原始 PDF 的,按文末说明获取即可。从那以后我每次做词云都先跑一遍最小案例,再把font_path、mask、color_func三个参数单独验证一遍;如果你也在做文本可视化,希望帮到你。
本文还有配套的精品资源,点击获取