-
CountVectorizer
→ Count Vectorizer
计数向量化器
-
词频
→ TF = Term Frequency
词条频率
-
TF-IDF
→ Term Frequency -- Inverse Document Frequency
词条频率 - 逆文档频率
ountVectorizer、TF、TF-IDF本来是给 NLP(自然语言)用的,但常被借用来处理「多值离散字段」,即把「多值离散字段」当成文本来用!
一、三者层层递进关系
先看一业务场景
我们有一个离散字段 merchant_type(用户交易活动的商户类型),每个用户的交易记录如下:
| card_id | merchant_type(商户类型) |
|---|---|
| user1 | 餐饮, 餐饮, 超市, 餐饮 |
| user2 | 超市, 超市, 便利店 |
| user3 | 餐饮, 便利店, 餐饮, 超市 |
我们把每个用户的交易记录,看成一段"文本"(伪文本):
- user1 的"文本":
餐饮 餐饮 超市 餐饮 - user2 的"文本":
超市 超市 便利店 - user3 的"文本":
餐饮 便利店 餐饮 超市
第一层:CountVectorizer(计数向量化)
CountVectorizer(计数向量化)是一个工具,作用是:把文本/类别数据,转换成一个由「词频」组成的矩阵。
它的工作分两步:
- 构建词表:把所有出现过的"词"(这里就是所有商户类型)收集起来,形成一个固定的词汇表。
- 计数:对每一段文本,统计词表中每个词出现的次数。
举例:对我们的交易数据
- 构建词表 :
["餐饮", "超市", "便利店"] - 计数:对每个用户的"文本"统计词频。
结果矩阵:
| card_id | 餐饮 | 超市 | 便利店 |
|---|---|---|---|
| user1 | 3 | 1 | 0 |
| user2 | 0 | 2 | 1 |
| user3 | 2 | 1 | 1 |
第二层:词频(Term Frequency, TF)
词频,就是某个词在单条文本/单个用户 中出现的次数。
它是 CountVectorizer 的直接计算的结果,也是构建更复杂特征(如 TF-IDF)的基础。
在我们的例子里,表格里的每一个数字,都是一个"词频":
user1的餐饮词频是 3user2的便利店词频是 1
词频的优缺点
- ✅ 优点 :直接反映用户的行为频次,和数值情况用
groupby做的count统计逻辑完全一致,非常直观。 - ❌ 缺点:它不考虑词的"重要性"。比如"超市"这个词,所有用户都用,词频很高,但它无法区分用户的独特偏好。
第三层:TF-IDF(Term Frequency -- Inverse Document Frequency,词频-逆文档频率)
它是在"词频(TF)"的基础上,为克服其缺点,引入了"逆文档频率(IDF)"来衡量词的稀有度,从而给不同的词赋予不同的权重。
公式:
TF-IDF=词频(TF)×逆文档频率(IDF) \text{TF-IDF} = \text{词频(TF)} \times \text{逆文档频率(IDF)} TF-IDF=词频(TF)×逆文档频率(IDF)
- TF(词频):就是第二层我们讲的,词在当前文本中出现的次数。
- IDF(逆文档频率) :衡量这个词在所有文本/所有用户中的稀有程度。词越稀有,IDF值越高。
举例:对我们的交易数据
第一步:计算 IDF(稀有度)
总共有 3 个用户(文本)。
餐饮:出现在 2 个用户中 → 不算太普遍超市:出现在 3 个用户中 → 非常普遍便利店:出现在 2 个用户中 → 不算太普遍
IDF 的简化计算:
IDF(词)=log(总用户数出现该词的用户数) \text{IDF}(词) = \log(\frac{\text{总用户数}}{\text{出现该词的用户数}}) IDF(词)=log(出现该词的用户数总用户数)
- IDF(餐饮)=log(3/2)≈0.405\text{IDF(餐饮)} = \log(3/2) ≈ 0.405IDF(餐饮)=log(3/2)≈0.405
- IDF(超市)=log(3/3)=0\text{IDF(超市)} = \log(3/3) = 0IDF(超市)=log(3/3)=0
- IDF(便利店)=log(3/2)≈0.405\text{IDF(便利店)} = \log(3/2) ≈ 0.405IDF(便利店)=log(3/2)≈0.405
第二步:计算 TF-IDF
以 user1 为例:
餐饮:TF=3×IDF=0.405≈1.215TF=3 \times IDF=0.405 ≈ 1.215TF=3×IDF=0.405≈1.215超市:TF=1×IDF=0=0TF=1 \times IDF=0 = 0TF=1×IDF=0=0便利店:TF=0×IDF=0.405=0TF=0 \times IDF=0.405 = 0TF=0×IDF=0.405=0
最终 TF-IDF 矩阵:
| card_id | 餐饮_tfidf | 超市_tfidf | 便利店_tfidf |
|---|---|---|---|
| user1 | 1.215 | 0 | 0 |
| user2 | 0 | 0 | 0.405 |
| user3 | 0.81 | 0 | 0.405 |
TF-IDF 完美解决了"词频"的缺点:
- 高频普遍词被削弱:所有用户都爱去的"超市",IDF为0,权重被压低,无法区分用户。
- 低频独特词被强化:用户的独特偏好(如 user1 高频的"餐饮"、user2 的"便利店")被放大,权重很高,能有效区分用户行为。
三者的递进关系总结
| 层级 | 核心概念 | 本质 | 作用 |
|---|---|---|---|
| 1️⃣ CountVectorizer | 工具 | 把文本/类别列表转换成词频矩阵 | 从非数值数据中提取基础频次特征 |
| 2️⃣ 词频 (TF) | 结果/基础特征 | 词在单条文本中的出现次数 | 反映用户的行为频次,是构建更复杂特征的基础 |
| 3️⃣ TF-IDF | 进阶特征 | 词频 × 稀有度(IDF) | 反映用户的独特行为偏好,是对词频特征的补充和优化 |
一句话串起来:
CountVectorizer 工具,产出了词频(TF);在词频的基础上,乘以逆文档频率(IDF),就得到了 TF-IDF。
二、TF-IDF 极简代码示例
用上述交易商户类型场景举例。
python
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
# 1. 构造数据(每个用户的商户类型行为)
data = {
'card_id': ['user1', 'user2', 'user3'],
'merchant_text': [
'餐饮 餐饮 超市 餐饮', # user1
'超市 超市 便利店', # user2
'餐饮 便利店 餐饮 超市' # user3
]
}
df = pd.DataFrame(data)
# 2. 初始化 TF-IDF 工具
tfidf = TfidfVectorizer()
# 3. 把文本转成 TF-IDF 特征矩阵
tfidf_matrix = tfidf.fit_transform(df['merchant_text'])
# 4. 转成 DataFrame 方便查看
tfidf_df = pd.DataFrame(
tfidf_matrix.toarray(),
columns=tfidf.get_feature_names_out()
)
# 5. 合并回原数据
result = pd.concat([df[['card_id']], tfidf_df], axis=1)
print(result)
运行结果
card_id 便利店 餐饮 超市
0 user1 0.000000 0.927743 0.372302
1 user2 0.720338 0.000000 0.693642
2 user3 0.542701 0.542701 0.643187
这个结果代表什么?
- 数字越大 = 这个商户类型对这个用户越独特、越重要
- 数字越小 = 越普通、越没有区分度
重点解读:
-
user1 的 餐饮 分数最高(0.93)
因为他经常去餐饮,且餐饮不是所有人都去 → 独特行为
-
超市 分数都不高
因为大家都去超市 → 太普遍,不重要
-
便利店 只对 user2、user3 重要
因为不是所有人都去 → 有区分度
1. TF-IDF 用于离散字段做特征
例如:
- merchant_type(商户类型)
- city(城市)
- category(品类)
- brand(品牌)
把每个用户的历史行为拼成一段文本,直接用 TF-IDF 生成强特征。
2. 作用:
- 自动识别用户独特偏好
- 自动降低大家都有的普遍行为权重
- 给模型提供区分度极强的特征