如何为DataFrame各ID统计与其他ID的1-6项共享物品数量?
问题描述
我有一个如下所示的DataFrame,其中每个id对应3至6个物品(item):
id item_1 item_2 item_3 item_4 item_5 item_6 1 13103802 13060661 13339404 12896842 13308823 NA 2 448361 497992 13103802* 13002842 NA NA 3 13031560 13103802* 13268709 2139908 1954965 12930979 4 13060661* 13339404* 446881 13406902 NA NA 5 12980231 12980231 12980231 NA NA NA 6 12896842* 13339404* 12717215 444032 13308823* NA 7 2098716 449342 13339070 12993196 2649922 NA 8 2678151 12700906 12903744 2623298 12736032 349511 9 2501765 2534504 2629353 NA NA NA 10 12955428 12766447 12944593 NA NA NA
需要为每个id统计其他id中与它共享至少1个、2个……最多6个物品的数量,最终在DataFrame中新增6列,分别对应1 item、2 items……6 items的其他id数量。例如id1的1 item列值为4(与id2、3、4、6共享至少1个物品),2 items列值为2(与id4、6共享至少2个物品),3 items列值为1(与id6共享至少3个物品)。
解决方案
方法1:基础循环实现(适合小数据集)
步骤1:预处理物品数据
先统一物品格式(去掉末尾的*),并将每个id的物品转换为集合,方便后续计算交集:
import pandas as pd # 假设数据已加载到df中,也可以直接构造数据 # df = pd.read_csv("your_data_file.csv") # 定义处理函数:提取每个id的有效物品,转为集合 def get_item_set(row): # 遍历item列,去掉*、过滤NA,转为字符串避免类型冲突 items = [str(x).rstrip('*') for x in row[1:] if pd.notna(x)] return set(items) # 新增item_set列存储每个id的物品集合 df['item_set'] = df.apply(get_item_set, axis=1)
步骤2:统计共享物品数量
遍历每个id,和其他id计算物品交集大小,再统计满足不同阈值的id数量:
# 定义新增列的名称 stat_cols = [f"{k} item{'s' if k>1 else ''}" for k in range(1, 7)] # 初始化统计结果 stats = {col: [] for col in stat_cols} for _, current_row in df.iterrows(): current_items = current_row['item_set'] # 初始化1-6个共享物品的计数 count_list = [0]*6 # 遍历其他所有id for _, other_row in df.iterrows(): if current_row['id'] == other_row['id']: continue # 跳过自身 other_items = other_row['item_set'] # 计算共享物品数量 common_count = len(current_items & other_items) # 对每个阈值k,只要共享数>=k就计数+1 for k_idx in range(6): if common_count >= (k_idx + 1): count_list[k_idx] += 1 # 将当前id的统计结果存入字典 for col, cnt in zip(stat_cols, count_list): stats[col].append(cnt) # 将统计结果合并到原DataFrame df = pd.concat([df, pd.DataFrame(stats)], axis=1)
方法2:矩阵化优化(适合大数据集)
如果数据量较大,循环效率偏低,可以用矩阵乘法实现更高效的计算:
# 提取所有唯一物品 all_unique_items = set() for item_set in df['item_set']: all_unique_items.update(item_set) all_unique_items = list(all_unique_items) # 构造物品存在矩阵:行是id,列是物品,值为1表示该id拥有该物品 item_matrix = pd.DataFrame(0, index=df['id'], columns=all_unique_items) for _, row in df.iterrows(): item_matrix.loc[row['id'], list(row['item_set'])] = 1 # 矩阵乘法得到两两id的共享物品数矩阵 common_count_matrix = item_matrix @ item_matrix.T # 统计每个id满足>=k的其他id数量(减去自身的1) for k in range(1, 7): col_name = f"{k} item{'s' if k>1 else ''}" df[col_name] = (common_count_matrix >= k).sum(axis=1) - 1
结果说明
执行完上述代码后,原DataFrame会新增6列,分别对应每个id与其他id共享至少1到6个物品的数量。以id1为例,结果会和示例一致:1 item=4,2 items=2,3 items=1,4 items及以后的列值为0。
内容的提问来源于stack exchange,提问作者Pawels
相关产品推荐
相关产品推荐

