Python pandas如何分组匹配

Python pandas how to group and match

假设我们有两个 table,transproduct。假设 trans table 包含用户购买的超过十亿行。

我正在尝试查找同一用户经常一起购买(同一天购买)的配对产品,例如葡萄酒和开瓶器、薯条和啤酒等。

我正在尝试查找前五个配对产品及其名称。

trans 和 prod 数据帧:-

trans = {'ID':[1,1,2,2,3,3,1,5,5,6,6,6],
        'productID':[11,22,11,22,33,77,11,77,88,11,22,77],
        'Year':['2022-01-01','2022-01-01','2020-01-05','2020-01-05','2019-01-01','2019-01-01','2020-01-07','2020-01-08',
                '2020-01-08','2021-06-01','2021-06-01','2021-06-01']}
trans = pd.DataFrame(trans)
trans['Year'] = pd.to_datetime(trans['Year'])
trans


product = {'productID':[11,22,33,44,55,77,88],
        'prodname':['phone','Charger','eaphones','headset','scratchgaurd','pin','cover']}
product = pd.DataFrame(product)
product

到目前为止,我的代码尝试对具有相同 ID 和年份的项目进行排名,然后尝试获取产品名称。

transprod = pd.merge(trans,product,on='productID' , how='inner')
transprod


transprod['Rank'] = transprod.groupby('ID')['Year'].rank(method = 'dense').astype(int)
transprod = transprod.sort_values(['ID','productID','Rank'])
transprod

期望的输出:

Product 1 | Product 2 | Count
phone       charger      3
Charger      pin         1
eaphones     pin         1
pin         cover        1

非常感谢您的帮助。提前致谢

您可以按 ID(和日期)对交易 table 进行分组,并列出每个订单的所有产品对。 itertools.combinations 在这里很有用。通过先把集合放在一个订单上,你可以忽略多个相同的项目。

由于一对出现的顺序无关紧要,因此您可以构建所有对的平面列表并使用 collections.Counter 实例对它们进行计数。首先对每对进行排序确保您可以忽略一对中项目的顺序。

产品table可以转化为字典,方便查找。这将提供一种将产品名称添加到 table 结果的方法。

from itertools import combinations
from collections import Counter

pairs_by_trans = trans.groupby(['ID', 'Year'])['productID'].agg(
                       lambda x: list(combinations(set(x), 2)))
pairs_flat = [tuple(sorted(pair)) for row in pairs_by_trans for pair in row]

counts = Counter(pairs_flat)
top_counts = pd.DataFrame(counts.most_common(5),
                          columns=['pair', 'count'])

prodname = {k: v for k, v in product.values}
top_counts['names'] = top_counts['pair'].apply(lambda x: (prodname[x[0]], 
                                                          prodname[x[1]]))

top_counts
    pair    count   names
0   (11, 22)    3   (phone, Charger)
1   (33, 77)    1   (eaphones, pin)
2   (77, 88)    1   (pin, cover)
3   (11, 77)    1   (phone, pin)
4   (22, 77)    1   (Charger, pin)

下面的解决方案非常适合我


transprod = pd.merge(trans,product,on='productID' , how='inner')



transprod['Rank'] = transprod.groupby('ID')['Year'].rank(method = 'dense').astype(int)
transprod = transprod.sort_values(['ID','productID','Rank'])


def checkprod(x):
    v1  = (x['Rank']==x['Rank'].shift(-1))
    
    return (x[v1 | v1.shift(1)])

out = transprod.groupby('ID').apply(checkprod).reset_index(drop=True)

pairs = out.groupby(['ID','Rank'])['prodname'].agg(
                       lambda x: list(combinations(set(x), 2)))


Counter(list(itertools.chain(*pairs)))