
如果你已经跟着上一篇的流程把分词、去停用词、词频统计跑通了那你多半遇到了一个让人憋屈的现象图表画了一堆词数了一箩筐但业务方问“所以用户到底在抱怨啥”的时候你只能指着“客服”“商品”“快递”这几个高频词绕圈子。词频回答的是“什么词被反复提到”回答不了“什么词真正有辨识度”。这次“文本数据分析二”就专门处理这个卡点。这一篇会从“词频”进阶到“关键词”把TF-IDF和TextRank的原理、代码、参数、坑全部过一遍再顺手解决文本相似度计算和结果可视化的问题。内容偏实操适合已经跑通分词流程、想进一步做真实业务分析的读者哪怕你对算法细节不熟跟着代码走也能把一套完整的文本分析流水线搭起来。1. 为什么第二篇要解决“关键词提取”1.1 词频统计的局限在哪儿先用一个我实际处理过的场景说事。某电商售后团队给我一批工单数据大概几千条内容比较乱有投诉、有咨询、有退换货记录。我按常规流程走完分词、去停用词、统计词频之后排名前五的词是“客服”“问题”“客户”“商品”“解决”。这五个词能说明什么最多说明“客服在解决客户商品问题”这是一句不用跑数据也能猜到的废话。问题不在流程而在词频本身的天性。词频衡量的是“出现次数”但一个词在文本里反复出现未必代表它重要。反而是那些在某几条文本里集中出现、在整体语料里很少露面的词才更像这批数据的“身份标记”。比如“屏幕划痕”这个词在几千条工单里可能只出现在屏幕问题相关的几十条记录中但它几乎能直接定位到一类具体的售后场景。这种词词频统计很难给你找出来。所以从“文本数据分析一”到“二”核心转变就是把“出现得多”升级为“更能区分文本”。有了这个视角后面所有方法都是围绕它展开的。1.2 关键词提取的两条主流路线关键词提取不一定要上深度学习模型。对大部分业务分析场景两条经典路线足够用。第一条是统计加权路线代表算法是TF-IDF。它的逻辑是一个词如果在某篇文本中出现次数多又在整个语料库中很少出现那它对这篇文本就很重要。这像是在说“稀缺即价值”和“物以稀为贵”一个意思。第二条是图算法路线代表算法是TextRank。它的逻辑更偏“关系”把文本里的词看成节点词和词之间有共现关系就连接起来然后迭代计算每个词的权重。权重高的词就是文本里的“中心词”相当于在一个社交网络里找到了关键人物。这两条路线各有适用场景后面会单开章节细讲。先记住一个结论TF-IDF更偏向“词和文档的区分度”TextRank更偏向“词在文本结构里的枢纽地位”。理解到这个层面你就知道什么时候该用谁了。2. TF-IDF用“稀缺性”给词做加权2.1 公式拆成两半来理解TF-IDF是两个部分的乘积分别是TFTerm Frequency词频和IDFInverse Document Frequency逆文档频率。TF部分很好理解就是词在文档里出现的次数除以文档总词数。它的作用是反映“词在这篇文档里有多活跃”。如果一篇手机评论里反复出现“屏幕”和“划痕”这两个词的TF值就会比较高。IDF部分稍微绕一点公式是IDF log( 总文档数 / (包含该词的文档数 1) )分母加1是为了防止某个词在所有文档里都没出现过导致除零。IDF的含义是包含这个词的文档越少IDF越大说明这个词越“稀有”。比如“划痕”可能只有20条评论包含而“手机”几乎每条都有那“划痕”的IDF远大于“手机”。把两部分乘起来TF-IDF既保留了“在这篇文档里活跃”的语义又惩罚了“到处都能见到”的背景词。你再看高频词“商品”“客服”它们TF可能不低但IDF非常低综合权重也就被压下去了。这也是为什么TF-IDF在实际分析里比纯词频好用太多。2.2 代码实现与关键参数用Python做TF-IDF不需要自己实现公式Scikit-learn的TfidfVectorizer直接搞定。配合Jieba做中文分词代码可以控制在二十行以内。import jieba from sklearn.feature_extraction.text import TfidfVectorizer # 原始文本每一条代表一个文档 corpus [ 手机收到了但屏幕有一道划痕售后处理很慢等了三天才回复物流倒是挺快, 这款笔记本散热效果一般打游戏风扇声音很大退货流程倒是很顺利, 客服态度很好但换货的快递一直没发出系统显示已经三天了 ] def cut_text(text): # 用jieba精确模式分词再用空格拼接 return .join(jieba.cut(text)) corpus_cut [cut_text(text) for text in corpus] # 构造TF-IDF向量 vectorizer TfidfVectorizer(max_features500, ngram_range(1, 2)) X vectorizer.fit_transform(corpus_cut) # 输出每个词的权重方便检查结果 for word, index in vectorizer.vocabulary_.items(): print(f{word}: {X[0, index]:.4f})先说几个我反复用到的参数。max_features500限制特征词典大小取词频靠前的500个词。语料量大时一定要设这个值不然特征维度会爆炸矩阵稀疏到后面根本跑不动。ngram_range(1, 2)意思是把单个词和相邻两个词的组合都算作特征。“屏幕划痕”作为一个二元词组比单独的“屏幕”和“划痕”携带更多上下文信息。对中文短文本来说开启bigram通常能提升关键词的语义完整度。min_df和max_df也值得调。min_df5表示词至少在5篇文档中出现才算特征过滤只出现一两次的噪声词max_df0.8表示在超过80%的文档中都出现的词直接丢掉这些基本是通用背景词。2.3 我踩过的几个坑第一个坑是停用词表不够全。TF-IDF虽然能降低背景词权重但对“我们”“这个”“因为”这类纯语法词效果有限。建议自己维护一份业务停用词表把“亲”“亲亲”“麻烦”这类客服语料里的高频客套词也加进去否则它们会占据大量特征空间。第二个坑是没有过滤标点和数字。原始文本里常混着“187****1234”这种手机号分词后可能变成单个数字或符号直接进入特征词典。我在预处理阶段就会用正则把手机号、纯数字、网址、邮箱全部替换或移除再进分词。第三个坑是语料量太少时结果不稳定。TF-IDF里的IDF依赖“包含该词的文档数”如果总共只有几十条文本IDF的估计会非常随机。我的经验是少于200条文本时TF-IDF结果只能用来找感觉不建议直接作为业务结论依据最好先用TextRank和人工抽检交叉验证。3. TextRank用“投票关系”找关键节点3.1 核心思想一句话讲透TextRank最早受PageRank启发。PageRank的思路是一个网页的重要程度由其他重要网页链接过来决定链接越多且来源越重要这个网页就越重要。TextRank把这个逻辑搬到了文本里。它把每一个词当作一个网页词与词之间的“链接”靠共现关系建立在一个滑动窗口内同时出现的词就有边连接。然后算法反复迭代把权重在词与词之间传播最后每个词得到一个稳定分数排序靠前就是关键词。举个例子。“屏幕有划痕但物流很快”设置窗口大小为5那么“屏幕”“划痕”“物流”“很快”这四个词可能都在一个窗口内它们之间两两建边。迭代之后“划痕”如果和很多重要词都发生共现它的分数就会被抬起来。这就解释了为什么TextRank能抓住一些TF-IDF抓不住的东西它不只看词出现的多少更看词“和谁一起出现”。在长文本里这种共现关系能天然保留句子结构带来的语义上下文。3.2 调用方式与常用参数Jieba库内建了TextRank实现不用自己搭图调用起来比较省事。import jieba.analyse text 客服态度很好但换货的快递一直没发出系统显示已经三天了用户要求尽快处理 # 提取top5关键词显示权重 result jieba.analyse.textrank( text, topK5, withWeightTrue, allowPOS(ns, n, vn, v, a) ) for word, weight in result: print(f{word}: {weight:.4f})allowPOS是这里最值得说的参数。它限定了参与排序的词性范围等于告诉算法“你只看名词、动词、形容词、地名别把副词和助词拉进来”。不加的话类似“很”“了”“就”这种词偶尔会混进结果特别影响观感。topK控制返回关键词数量。短文本取3到5个就够长文本可以取10个以上。窗口大小是通过代码内部参数控制的Jieba默认窗口是5如果发现提取出的词比较散、不成句可以把窗口调小到3让词和词之间的联系更紧如果希望看到更多关联组合可以调大到8。3.3 TF-IDF和TextRank怎么选直接给结论后面实战会再印证。维度TF-IDFTextRank核心逻辑用文档间分布差异加权用词共现关系迭代投票数据要求需要成规模的语料库单篇文本也能计算典型场景多篇短文本横向对比筛选单篇长文档摘要挖掘结果特点偏“稀有词”区分度强偏“上下文枢纽”平滑规整主要问题依赖语料质量短语料不稳对长文效果好短文本略泛实操选择很直接如果你手里是几千条客服工单、评论、问卷反馈要做“这批数据整体在聊什么”用TF-IDF更合适如果是对某一条长微博或一篇新闻做独立的关键信息抽取TextRank会更好。当然两个一起用做交叉验证是成本最低的稳健方案。4. 文本相似度给文本之间的“距离”量个尺4.1 从关键词到向量文本相似度是个绕不开的话题。做文本去重、评论聚类、相似问题归并都需要回答同一个问题“这两条文本像不像”答案的起点是把文本变成向量。没有向量文本就只是一串字符无法做数学运算。TfidfVectorizer把每篇文档映射成一个固定维度的向量维度就是词典里的词项每个位置的数值就是该词在这篇文档里的TF-IDF权重。于是几千条文本就成了一个矩阵矩阵的每一行代表一篇文档。这里有一个很多人刚接触时想不通的问题为什么不让计算机直接比较字符是否相等因为文本表达太灵活了“屏幕有问题”和“屏幕坏了有瑕疵”字符不一样但语义高度接近。向量化的意义就是把这种语义层面的接近转换成几何层面的接近。4.2 余弦相似度的计算实操文本向量常用的相似度是余弦相似度。它的公式是两向量夹角的余弦值取值范围在-1到1之间越接近1越相似。为什么不用欧氏距离因为欧氏距离会受向量长度影响。两篇同样内容的文本一个写500字一个写2000字TF-IDF向量的模长可能差很多欧氏距离会认为它们不相似余弦相似度只看“方向”天然克服了文档长度差异。代码实现非常简单from sklearn.metrics.pairwise import cosine_similarity # X是上一步得到的TF-IDF矩阵 similarity_matrix cosine_similarity(X) # 看第0条和第1条的相似度 print(f第0条和第1条相似度: {similarity_matrix[0][1]:.4f}) print(f第0条和第2条相似度: {similarity_matrix[0][2]:.4f})拿到相似度矩阵之后你可以做两件事。第一找相似度超过阈值的文本对把它们归为一类。比如客服系统里用户反复提交同一问题通过相似度阈值0.8直接识别重复工单减少人工重复处理。第二把相似度矩阵喂给聚类算法先降维再用KMeans等无监督方法做主题分组。这样你不必靠人工读几千条记录也能快速得到几个粗略的话题簇。4.3 相似度分析里最容易被忽视的细节短文本的相似度非常容易变形。两句都很短的话比如“质量差”和“客服差”分词后可能只有一个重叠词相似度接近0但实际都指向负面评价。这个问题在传统TF-IDF路线里几乎无解只能通过扩充同义词表或者后续换用词向量来处理。另一个细节是阈值怎么定。不要上来就拍板“相似度大于0.8就去重”。不同语料分布下的相似度分布差异很大我的习惯是先统计一下相似度矩阵的分布比如看看0.6、0.7、0.8分别有多少对文本再结合业务抽检定阈值。盲目定阈值的结果不是误杀就是漏网。5. 可视化让分析结果通俗可讲5.1 词云快速看重点文本分析做完如果只给业务方一张Excel词频表对方大概率不会认真看。可视化是文本分析的关键一步不是为了好看是为了降低沟通成本。词云是最直观的手段。把每个词的权重映射成词云中的字号权重越大字号越大。用WordCloud库实现很顺手但中文场景必须指定中文字体否则所有字都会变成方框。from wordcloud import WordCloud import matplotlib.pyplot as plt import jieba text .join(jieba.cut(open(data.txt, encodingutf-8).read())) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, # 换成你自己的中文字体路径 max_words100, width800, height600, background_colorwhite ) wc.generate(text) plt.imshow(wc) plt.axis(off) plt.show()做词云有一个经验不建议直接把原始文本丢进去而是先跑一遍停用词过滤和关键词提取只把得分靠前的词做成词云。否则你会得到一张被“客服”“问题”这种大词包围的图信息量又回到词频统计的老路上。5.2 关键词共现网络看词与词的关系词云只能看“哪些词重要”看不出“它们之间什么关系”。如果需要分析话题结构可以做一张关键词共现网络。构建方法不复杂在分词后的文本上设定一个滑动窗口窗口内任意两个词算一次共现统计共现次数。过滤掉低频词和背景词之后用NetworkX建图节点是词节点大小代表权重边代表共现关系边的粗细代表共现强度。看共现网络有个很直觉的作用你能一眼看出哪些词经常抱团。比如“物流”“迟迟”“不发货”“退款”形成一个密集子图那基本就能判断这批文本里存在一个“物流延迟导致退款”的话题簇。这种结构特征词云很难直观呈现。我通常会把共现网络图和词云放在同一份分析报告里词云回答“主要有什么”网络回答“哪些词总一起出现”。两份图一对照给业务方讲结论的时候底气足很多。6. 完整实操从原始文本到分析结论6.1 先把分析目标拆清楚前面讲了一堆方法但更重要的是一套流程怎么串起来。我用一个模拟场景演示你拿去换自己的数据就能用。场景某平台客服收到一批关于“发货和退换货”的用户反馈总量约800条。目标是快速定位最集中的问题类型给运营一个优先级排序。这一步的关键是先定义输出。我的输出不是“一些关键词”而是一句话“在发货环节物流信息更新滞后是最集中的问题在退换货环节退款到账慢是最集中的问题。”有了这个目标后面的技术选型就很清晰先用TF-IDF做整体关键词再按文本相似度分组细看最后人工抽检验证。6.2 预处理和特征提取原始文本很脏不能直接分词。我先统一做几件事把所有换行符处理成空格删除纯数字、网址、手机号、邮箱把“客服”“用户”“您好”这类对话客套词加入停用词表。然后才进入jieba分词。import re import jieba from sklearn.feature_extraction.text import TfidfVectorizer stopwords set() for line in open(stopwords.txt, encodingutf-8): stopwords.add(line.strip()) def clean_text(text): text re.sub(rhttps?://\S, , text) text re.sub(r\d, , text) text re.sub(r\s, , text) return text def cut_and_filter(text): words jieba.cut(clean_text(text)) return .join(w for w in words if w.strip() and w not in stopwords) corpus_cut [cut_and_filter(t) for t in feedback_list] vectorizer TfidfVectorizer(max_features800, ngram_range(1, 2), min_df3, max_df0.7) X vectorizer.fit_transform(corpus_cut)min_df3在这里很有用至少3条文本出现的词才保留把只出现过一次的偶然噪声挡掉。max_df0.7则把“客服”“问题”这类超过70%文本都有的背景词排除掉比后面再手动剔除省事。6.3 组合应用与结果解读TF-IDF结果出来后我拿Top20关键词给运营同事看对方反馈“物流信息不更新”“退款慢”“发错货”这些核心词确实在Top里说明方向对了。但Top20里也有几个比较模糊的复合词比如“一直”“还是”这些是停用词表没覆盖掉的口语化高频词直接追加进停用词表重新跑。接着把TF-IDF矩阵做余弦相似度阈值先定0.6找出彼此相似的文本簇。这时候TextRank登场对每个聚类簇内部做二次提取相当于“簇内关键词”。簇A的TextRank结果是“物流、不更新、三天、联系客服”簇B是“退款、不到账、银行卡、已关闭”。两簇的差异一下就清楚了再拉几条例句人工确认结论基本能定稿。整套流程下来800条文本从清洗到输出结论用时大概半小时不需要训练模型不依赖标注数据。这种“低成本快出活”的路线是业务分析场景最需要的能力。7. 常见问题与排查技巧7.1 提取结果全是噪声词怎么办先查停用词表再把数字和标点过滤前置。如果都做了噪声仍然多大概率是语料太杂、话题太多。我的办法是先对文本做一次粗分类比如按“发货”“售后”“质量”分桶再在桶内各自提取关键词。小范围语料的关键词质量通常远好过把所有文本混在一起硬提。7.2 程序跑不动、内存溢出特征维度是最常见的肇事者。TfidfVectorizer如果不设max_features特征数可能膨胀到几十万维矩阵稀疏度巨大但是内存占用仍然失控。另一个办法是分批读取不要一口气把几百万条文本全部读进内存。数据量大时直接用spark或者分批循环生成TF-IDF矩阵结果差异不大。7.3 分析结果不知道靠不靠谱关键词提取没有标准答案所以验证很重要。我习惯抽10到20条原始文本人工判断提取出的关键词是否落在核心话题上再算一个粗糙的准确率。如果准确率低于七成就检查停用词表、min_df、max_df和分词效果通常调一轮就能改善。还有一个技巧把TF-IDF和TextRank两份结果都跑出来取交集或合并比单用其中一个稳定得多。7.4 中文词云乱码这是WordCloud最常见的坑。中文字体文件必须显式传入font_path默认字体不支持中文。另外要注意操作系统路径差异别直接抄网上的绝对路径先确认本机字体文件真实存在。7.5 相似度计算结果不符合直觉如果一对明显是在说同一件事的文本相似度却很低先分词看结果。Jieba对某些专业词汇会切得过碎比如“自动退款”被切成“自动”和“退款”导致语义被拆散。解决方式是把这类业务词加进自定义词典再分词重跑一遍。我个人在实际操作中的体会是文本数据分析做到“关键词提取相似度分组可视化”这一层已经能覆盖绝大多数业务分析需求。很多人一上来就想上深度学习、微调大模型实际连数据预处理都没做好结果事倍功半。把TF-IDF和TextRank这两个基础方法用扎实配合一套好用的停用词表你在文本分析上的输出质量已经超过多数临时抱佛脚的团队。最后再分享一个小技巧把每次调优后的参数和结果记录下来形成“语料类型参数组合效果评价”的对照表。下次再遇到类似文本直接套用历史经验不用重新踩一遍分词和阈值的坑。文本分析这东西方法多是一回事能不能稳定复现才是真正拉开差距的地方。