Matplotlib + wordcloud 词云图
重点:wordcloud是独立第三方库,不是matplotlib内置,matplotlib只负责展示词云图片 安装:
pip install wordcloud jieba核心逻辑:词频越高,文字字号越大;适合快速看文本高频关键词(影评、评论、摘要文本非常适配 CommentInfo.csv)
核心流程(中文必考)
中文句子是连在一起的,wordcloud默认只能按空格分词✅ 中文标准流程: 原始文本 → jieba分词 → 过滤停用词 → 用空格拼接词语 → WordCloud生成词云 → plt.imshow展示 + plt.axis("off")关闭坐标轴
WordCloud 常用参数表(考点)
| 参数 | 说明 |
|---|---|
| font_path | 中文必写!字体文件路径,Windows黑体:C:/Windows/Fonts/simhei.ttf,不设置中文方框乱码 |
| width / height | 词云画布像素宽高,默认400×200 |
| background_color | 背景颜色,'white'/'black' |
| max_words | 最多展示多少个词,默认200 |
| max_font_size / min_font_size | 最大、最小字号 |
| stopwords | set集合,停用词(的、了、是、很等无意义词,不显示) |
| mask | 蒙版ndarray,自定义形状词云(心形、logo轮廓) |
| collocations | 是否提取二元词组,中文一般设置collocations=False,避免重复统计 |
基础中文词云完整示例(适配影评文本)
import jieba from wordcloud import WordCloud import matplotlib.pyplot as plt # 1. 准备影评文本(这里可以替换成CommentInfo.csv里所有评论合并) text_raw = "这部电影剧情很好演员演技在线画面很美剧情紧凑,很好看,推荐推荐,剧情不错,演员很棒" # 2. jieba分词 words = jieba.lcut(text_raw) # 3. 自定义停用词 stop_words = {"的","了","是","很","也","就","都","看"} # 过滤停用词、单字 word_list = [w for w in words if w not in stop_words and len(w)>1] # 空格拼接(wordcloud要求!) text_cut = " ".join(word_list) # 4. 创建词云对象 wc = WordCloud( font_path = "C:/Windows/Fonts/simhei.ttf", width=800, height=400, background_color="white", max_words=100, collocations=False ) # 生成词云 wc.generate(text_cut) # 5. matplotlib展示词云 plt.figure(figsize=(10,5)) plt.imshow(wc, interpolation="bilinear") # bilinear平滑 plt.axis("off") # 关闭坐标轴,词云必须写 plt.title("影评关键词词云") plt.tight_layout() plt.show() # 保存图片 wc.to_file("wordcloud.png")进阶:基于CommentInfo.csv 影评词云(课程作业版本)
import pandas as pd import jieba from wordcloud import WordCloud import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = 'SimHei' plt.rcParams['axes.unicode_minus'] = False # 读取影评数据 df = pd.read_csv('datasource/CommentInfo.csv',encoding='gbk') # 合并全部评论内容,假设评论字段 comment all_comment = "".join(df["content"].astype(str)) # 分词+过滤 stop_words = {"的", "了", "是", "很", "也", "就", "都", "这个", "一个"} words = jieba.lcut(all_comment) word_filter = [w for w in words if w not in stop_words and len(w) > 1] text_ready = " ".join(word_filter) # 生成词云 wc = WordCloud( font_path="C:/Windows/Fonts/simhei.ttf", width=900, height=500, background_color="white", max_words=120, collocations=False ) wc.generate(text_ready) # 绘图展示 fig, ax = plt.subplots(figsize=(10, 6)) ax.imshow(wc, interpolation="bilinear") ax.axis("off") ax.set_title("影评评论关键词词云") plt.show()高频易错点【考试常考】
- ❌ 中文直接丢给generate() → 全部方框,必须指定font_path + jieba分词 + 空格拼接
- ❌ 忘记
plt.axis("off")→ 词云旁边多出坐标轴刻度,非常难看 - ❌ 没有停用词过滤:大量“的、了、是”霸占画面,没有分析价值
- ❌ collocations=True:容易出现连续两字重复词组,中文一般关掉
interpolation="bilinear":图片平滑渲染,不加边缘会锯齿
词云适合/不适合场景
✅ 适合:文本高频关键词快速概览、影评、弹幕、新闻摘要 ❌ 不适合:精确对比词频大小(字号肉眼很难精准对比,精确对比用柱状图)
补充:蒙版形状词云(拓展考点)
from PIL import Image import numpy as np mask_img = np.array(Image.open("heart.png")) wc = WordCloud(font_path="simhei.ttf", mask=mask_img, background_color="white")mask会让词云贴合图片白色以外轮廓。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>wordcloud词云选择题自测</title> <style> *{box-sizing:border-box;font-family:"Microsoft Yahei",sans-serif} body{max-width:900px;margin:20px auto;padding:0 16px;background:#f8fafc;color:#222} h1{text-align:center;color:#2c5282;font-size:24px} .q-item{background:#fff;border:1px solid #e2e8f0;border-radius:10px;padding:18px;margin-bottom:16px} .q-title{font-weight:bold;margin-bottom:12px;line-height:1.6} .opt{padding:10px 14px;margin:8px 0;border:1px solid #e2e8f0;border-radius:8px;cursor:pointer;transition:0.2s} .opt:hover{background:#edf2f7} .opt.right{background:#c6f6d5;border-color:#48bb78} .opt.wrong{background:#fed7d7;border-color:#f56565} .ans-box{margin-top:12px;padding:12px;background:#f0f7ff;border-radius:8px;color:#2b6cb0;display:none} .btn-group{text-align:center;margin:24px 0} button{padding:10px 20px;border:none;border-radius:8px;background:#3182ce;color:#fff;font-size:16px;cursor:pointer;margin:0 8px} #score{font-size:18px;text-align:center;margin:16px 0;font-weight:bold;color:#2c5282} </style> </head> <body> <h1>wordcloud词云 自测20题</h1> <div id="score"></div> <div id="container"></div> <div class="btn-group"> <button id="submitAll">提交全部,查看得分</button> <button id="resetAll">重置试卷</button> </div> <script> const questionList = [ { q:"1. 生成中文词云,wordcloud库是否属于matplotlib内置模块?", opts:["是,直接import即可","不是,需要单独pip安装wordcloud","是,属于pyplot子模块","不是,需要安装pandas"], answer:1, explain:"wordcloud是第三方独立库,不属于matplotlib,需要执行`pip install wordcloud`安装。" }, { q:"2. wordcloud库对中文文本处理的硬性要求是?", opts:["文本直接传入generate","分词后词语之间用空格隔开","分词后词语用逗号隔开","不需要分词"], answer:1, explain:"wordcloud默认以空格作为词语分隔标记;中文必须先分词,再用空格拼接词语字符串。" }, { q:"3. 实现中文词云,用来中文分词的常用库是?", opts:["numpy","jieba","pandas","PIL"], answer:1, explain:"jieba是Python主流中文分词库,lcut()进行精确分词。" }, { q:"4. WordCloud中font_path参数的作用是?", opts:["设置词云背景颜色","指定中文字体路径,解决中文方框乱码","设置最大词语数量","设置蒙版图片"], answer:1, explain:"font_path传入字体文件路径,是中文词云必不可少的参数,不设置中文会显示方框。" }, { q:"5. 绘制词云展示时,`plt.axis(\"off\")`作用是?", opts:["关闭图例","关闭坐标轴、刻度边框","关闭标题","关闭图片平滑"], answer:1, explain:"词云图不需要坐标轴,axis(\"off\")隐藏坐标轴和刻度。" }, { q:"6. 下面哪一个是停用词的作用?", opts:["放大高频词字号","过滤“的、了、是”这类无意义虚词","修改词云形状","修改字体大小"], answer:1, explain:"停用词一般是助词、副词,没有分析价值,过滤后关键词更清晰。" }, { q:"7. WordCloud参数 collocations=False,主要针对中文的目的是?", opts:["开启二元词组搭配统计","关闭二元词组,避免重复组合词语","设置背景透明","限制最大词数"], answer:1, explain:"collocations默认True,会自动拼接相邻词语生成二元短语,中文场景一般关闭,防止干扰分词结果。" }, { q:"8. 代码 `jieba.lcut(text)` 返回的数据类型是?", opts:["字符串","列表,每个元素是切分后的词语","字典","numpy数组"], answer:1, explain:"lcut精确分词,返回词语列表;cut返回生成器。" }, { q:"9. 想要自定义心形、圆形轮廓的词云,需要使用哪个参数?", opts:["stopwords","mask","max_font_size","background_color"], answer:1, explain:"mask接收图片转成的numpy数组,作为蒙版,词云按照图片轮廓填充文字。" }, { q:"10. WordCloud的generate()函数接收的数据类型是?", opts:["词语列表list","用空格分隔的字符串str","DataFrame","数组"], answer:1, explain:"generate只接收字符串,词语之间空格分隔,不能直接传入list。" }, { q:"11. 词云里面词语字号大小由什么决定?", opts:["词语长度","词频(出现次数),频次越高字号越大","词语拼音首字母","手动指定每个词大小"], answer:1, explain:"词云核心逻辑:词频越高,渲染字号越大。" }, { q:"12. 代码 `plt.imshow(wc, interpolation=\"bilinear\")` 中interpolation=\"bilinear\"作用?", opts:["关闭坐标轴","图片双线性插值,让文字边缘平滑,减少锯齿","修改字体","设置背景色"], answer:1, explain:"bilinear是平滑插值方式,优化图像显示效果。" }, { q:"13. WordCloud参数max_words含义是?", opts:["单个词语最大长度","词云最多展示多少个关键词","字体最大字号","分词最大数量"], answer:1, explain:"max_words控制词云展示词语上限,超过的低频词舍弃。" }, { q:"14. 下面哪一项不适合用词云图来展示?", opts:["影评文本高频关键词概览","弹幕热点词汇","精确对比两个词语出现次数差值","新闻摘要关键词"], answer:2, explain:"肉眼很难通过字号精准对比频次差异;精确词频对比优先柱状图。" }, { q:"15. stopwords传入的数据类型一般是?", opts:["集合set","数字","图片数组","DataFrame"], answer:0, explain:"stopwords接收集合,查找判断速度更快,格式示例:{\"的\",\"了\",\"很\"}。" }, { q:"16. WordCloud中background_color参数作用?", opts:["文字颜色","词云画布背景颜色","边框颜色","坐标轴颜色"], answer:1, explain:"background_color='white'代表白色背景。" }, { q:"17. 读取蒙版图片,并且转为numpy数组,一般需要用到哪两个库?", opts:["jieba + pandas","PIL.Image + numpy","matplotlib + pandas","wordcloud + jieba"], answer:1, explain:"Image.open打开图片,np.array()转为蒙版数组传给mask。" }, { q:"18. 生成词云后,`wc.to_file(\"ciyun.png\")`功能是?", opts:["在窗口展示图片","将词云保存为本地图片文件","关闭词云对象","设置标题"], answer:1, explain:"to_file是wordcloud自带保存方法;plt.show()才是窗口展示。" }, { q:"19. 下面哪一个属于典型停用词?", opts:["剧情","演员","的","画面"], answer:2, explain:"“的”是助词,无实际分析意义,属于停用词。" }, { q:"20. 中文词云常见报错:文字全部显示方框,最不可能的原因是?", opts:["没有设置font_path字体路径","没有用jieba分词","没有安装numpy","没有空格拼接分词结果"], answer:2, explain:"numpy缺失不会直接造成中文方框乱码;字体缺失、未分词、没有空格分隔才是主要原因。" } ]; const container = document.getElementById('container'); let userAnswers = Array(questionList.length).fill(null); function render(){ container.innerHTML = ''; questionList.forEach((item,idx)=>{ const div = document.createElement('div'); div.className = 'q-item'; let html = `<div class="q-title">${item.q}</div>`; item.opts.forEach((optStr, optIdx)=>{ let cls = 'opt'; if(userAnswers[idx]!==null){ if(optIdx === item.answer) cls += ' right'; else if(optIdx === userAnswers[idx] && optIdx!==item.answer) cls += ' wrong'; } html += `<div class="${cls}" data-q="${idx}" data-opt="${optIdx}">${String.fromCharCode(65+optIdx)}. ${optStr}</div>`; }) html += `<div class="ans-box" id="ans${idx}"><b>解析:</b>${item.explain}</div>`; div.innerHTML = html; container.appendChild(div); }) bindClick(); } function bindClick(){ document.querySelectorAll('.opt').forEach(el=>{ el.onclick = function(){ const qIdx = +this.dataset.q; if(userAnswers[qIdx]!==null) return; userAnswers[qIdx] = +this.dataset.opt; render(); } }) } document.getElementById('submitAll').onclick = function(){ let rightCnt = 0; for(let i=0;i<questionList.length;i++){ if(userAnswers[i] === questionList[i].answer) rightCnt++; document.getElementById('ans'+i).style.display = 'block'; } document.getElementById('score').innerText = `得分:${rightCnt} / ${questionList.length}`; } document.getElementById('resetAll').onclick = function(){ userAnswers = Array(questionList.length).fill(null); document.getElementById('score').innerText = ''; render(); } render(); </script> </body> </html>