求助:无法在给定DataFrame上手动实现TF-IDF
实现自定义TF-IDF的高效方案(针对含单词列表的DataFrame)
看起来你已经搞定了最繁琐的数据清洗环节,接下来实现TF-IDF完全可以用更高效的方式,不用拆分每行成单独DataFrame——那种做法确实会因为频繁的DataFrame操作拖慢速度。下面我一步步给你拆解实现思路和代码:
一、计算TF(词频)
你的需求是对每个文档统计词频,再应用log(1+count)转换。这里用pandas.Series.apply结合collections.Counter就能高效完成,完全不需要拆分数据:
首先导入需要的库:
import pandas as pd import numpy as np from collections import Counter
然后定义TF计算函数,对每个文档的单词列表生成词频字典,再做对数转换:
def calculate_tf(word_list): # 统计当前文档的词频 word_counts = Counter(word_list) # 应用log(1+count)公式转换 tf_dict = {word: np.log1p(count) for word, count in word_counts.items()} return tf_dict
把这个函数应用到text列,生成新的tf列:
df['tf'] = df['text'].apply(calculate_tf)
这样每行的tf列就是一个字典,键是单词,值是对应的TF值。如果想把它展开成列(每个单词对应一列),可以用pd.json_normalize,不过如果文档词汇量很大,可能会生成非常多列,按需选择即可:
tf_df = pd.json_normalize(df['tf']) # 和原DataFrame合并 df = pd.concat([df, tf_df], axis=1)
二、计算IDF(逆文档频率)
IDF的核心是统计每个单词出现在多少个文档中,然后计算log(N / DF)(N是总文档数,DF是单词出现的文档数)。这里用explode把每个单词拆成单独行,再结合groupby统计文档数,效率会很高:
- 先给原DataFrame加一个唯一的文档标识(直接用索引就行):
df['doc_id'] = df.index
- 把text列拆分成每行一个单词,同时保留对应的文档ID:
exploded_df = df.explode('text')
- 统计每个单词对应的唯一文档数(DF):
df_counts = exploded_df.groupby('text')['doc_id'].nunique().reset_index() df_counts.columns = ['word', 'df']
- 计算IDF值:
total_docs = len(df) df_counts['idf'] = np.log(total_docs / df_counts['df'])
现在df_counts里就存储了每个单词的IDF值。
三、计算TF-IDF
现在需要把每个文档的TF和对应的IDF相乘,得到最终的TF-IDF值:
首先把IDF转成字典,方便快速查找:
idf_dict = df_counts.set_index('word')['idf'].to_dict()
然后定义TF-IDF计算函数:
def calculate_tfidf(tf_dict): # 遍历当前文档的TF字典,乘以对应单词的IDF值 tfidf_dict = {word: tf * idf_dict.get(word, 0) for word, tf in tf_dict.items()} return tfidf_dict
应用到tf列生成tfidf列:
df['tfidf'] = df['tf'].apply(calculate_tfidf)
同样,如果需要展开成列,用pd.json_normalize即可:
tfidf_df = pd.json_normalize(df['tfidf']) df = pd.concat([df, tfidf_df], axis=1)
为什么这个方法更高效?
- 避免了拆分每行成单独DataFrame的操作,减少了大量内存开销和重复的DataFrame初始化
- 用
Counter做词频统计是Python中最高效的方式之一 explode+groupby是pandas处理列表型数据的标准高效范式,比手动遍历快得多
内容的提问来源于stack exchange,提问作者Avihay Shahar
相关产品推荐
相关产品推荐

