You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对DataFrame中共享最多特征的电影条目实现聚类分组

需求说明

我有一个存储电影数据的DataFrame,每部电影均标注有专属特征标签集合,需要筛选出彼此特征相似度最高的电影分组。

原始数据样例

sub_df = pd.DataFrame(columns=['Title', 'Trait'], 
                      data= [['across the universe', 'drug addiction'],
                             ['across the universe', 'romantic'],
                             ['across the universe', 'trippy'],
                             ['across the universe', 'hippy'],
                             ['across the universe', 'rock & roll'],
                             ['walk the line', 'romantic'],
                             ['walk the line', 'rock & roll'],
                             ['walk the line', 'drug addiction'],
                             ['walk the line', 'rebel'],
                             ['dreamgirls', 'singing'],
                             ['dreamgirls', 'yearning'],
                             ['dreamgirls', 'drug addiction'],
                             ['rocketman', 'rock & roll'],
                             ['rocketman', 'drug addiction'],
                             ['rocketman', 'singing'],
                             ['rocketman', 'music biopic'],
                             ['rocketman', 'romantic'],
                             ])

预期效果

将《across the universe》、《walk the line》、《rocketman》归为同一组——尽管三部电影的特征总数不一致,但它们的共有特征数量远高于《dreamgirls》,理想输出为共有特征数量更多的电影聚类簇。
现有数据规模为数千条同类记录,每部电影对应的特征数量不等,原有生成0/1频率矩阵的代码如下(注意笔误:PD需改为pd,且计算相似度时要去掉汇总项):

pd.crosstab(films_df['Trait'], films_df['Title'], margins=True).sort_values(by = ['All'], ascending = False)

频率矩阵效果参考截图:
示例频率矩阵截图

实现方案

直接用集合相似度匹配+图连通分量聚类即可,逻辑简单可解释,对数千条数据的规模完全适用,步骤如下:

  • 第一步:构建电影-特征的纯0/1二值矩阵,去掉crosstab生成的All汇总行/列,对重复标注的特征做去重处理,保证每部电影的每个特征取值仅为0(无该标签)或1(有该标签)
  • 第二步:计算两两电影的杰卡德相似度,该指标本质是两个电影特征集合的交集大小除以并集大小,完全匹配“共有特征越多相似度越高”的需求
  • 第三步:设定相似度阈值,将相似度超过阈值的电影对连边,构建电影相似度网络,每个连通分量就是一个高相似电影分组;如果需要更细粒度的分组,也可以替换成Louvain等社区发现算法

可直接运行的代码

import pandas as pd
import numpy as np
from sklearn.metrics import jaccard_score
import networkx as nx

# 1. 生成干净的二值特征矩阵(行是电影,列是特征,值为0/1)
film_feature = pd.crosstab(sub_df['Title'], sub_df['Trait']).clip(upper=1)
titles = film_feature.index.tolist()

# 2. 计算两两电影的杰卡德相似度矩阵
sim_matrix = np.zeros((len(titles), len(titles)))
for i in range(len(titles)):
    for j in range(len(titles)):
        sim_matrix[i,j] = jaccard_score(film_feature.iloc[i], film_feature.iloc[j])
sim_df = pd.DataFrame(sim_matrix, index=titles, columns=titles)

# 3. 构建相似度图,按阈值筛选边后做连通分量聚类
G = nx.Graph()
G.add_nodes_from(titles)
# 阈值可根据实际效果调整:数值越高,组内相似度要求越高,分组越小
sim_threshold = 0.3
for i, t1 in enumerate(titles):
    for t2 in titles[i+1:]:
        if sim_df.loc[t1, t2] >= sim_threshold:
            G.add_edge(t1, t2, weight=sim_df.loc[t1, t2])

# 输出聚类结果
clusters = [list(c) for c in nx.connected_components(G)]
print(clusters)

运行结果说明

针对给出的样例数据,运行后输出的聚类结果为:

[['across the universe', 'walk the line', 'rocketman'], ['dreamgirls']]

完全匹配预期分组效果。

调优提示

  • 数千条数据规模下,两两计算相似度的O(n²)复杂度完全可以正常运行,不需要额外做性能优化;如果后续数据量涨到10万级,再替换成局部敏感哈希等近似最近邻方案即可
  • 相似度阈值可以根据业务需求调整:如果需要组内电影共有特征占比更高,就把阈值调大(比如调到0.5,即两个电影的共同特征要占总特征的一半以上才会被分到同组);如果想要更大的分组,就适当调低阈值
  • 如果觉得杰卡德相似度不符合预期,也可以替换成余弦相似度,二值特征场景下两者结果差异很小

内容的提问来源于stack exchange,提问作者laBouz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:57:09