如何在python中创建产品订单的共现矩阵?
How to create a co-occurence matrix of product orders in python?
假设我们有以下数据框,其中包括客户订单 (order_id) 和单个订单包含的产品 (product_id):
import pandas as pd
df = pd.DataFrame({'order_id' : [1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3],
'product_id' : [365, 48750, 3333, 9877, 48750, 32001, 3333, 3333, 365, 11202, 365]})
print(df)
order_id product_id
0 1 365
1 1 48750
2 1 3333
3 1 9877
4 2 48750
5 2 32001
6 2 3333
7 3 3333
8 3 365
9 3 11202
10 3 365
了解产品对出现在同一个购物篮中的频率会很有趣。
如何在 python 中创建如下所示的共现矩阵:
365 48750 3333 9877 32001 11202
365 1 1 2 1 0 1
48750 1 0 2 1 1 0
3333 2 2 0 1 1 1
9877 1 1 1 0 0 0
32001 0 1 1 0 0 0
11202 1 0 1 0 0 0
非常感谢您的帮助!
我们首先将 df 按 order_id 分组,然后在每个组内计算所有可能的对。请注意,我们首先按 product_id 排序,因此不同组中的相同对始终处于相同的顺序
import itertools
all_pairs = []
for _, group in df.sort_values('product_id').groupby('order_id'):
all_pairs += list(itertools.combinations(group['product_id'],2))
all_pairs
我们从所有订单中获取所有对的列表
[('3333', '365'),
('3333', '48750'),
('3333', '9877'),
('365', '48750'),
('365', '9877'),
('48750', '9877'),
('32001', '3333'),
('32001', '48750'),
('3333', '48750'),
('11202', '3333'),
('11202', '365'),
('11202', '365'),
('3333', '365'),
('3333', '365'),
('365', '365')]
现在我们计算重复项
from collections import Counter
count_dict = dict(Counter(all_pairs))
count_dict
所以我们得到每对的计数,基本上就是你想要的
{('3333', '365'): 3,
('3333', '48750'): 2,
('3333', '9877'): 1,
('365', '48750'): 1,
('365', '9877'): 1,
('48750', '9877'): 1,
('32001', '3333'): 1,
('32001', '48750'): 1,
('11202', '3333'): 1,
('11202', '365'): 2,
('365', '365'): 1}
将其放回叉积 table 有点工作,关键是通过调用 .apply(pd.Series)
将元组拆分为列,并最终将其中一列移动到列名称来自 unstack
:
(pd.DataFrame.from_dict(count_dict, orient='index')
.reset_index(0)
.set_index(0)['index']
.apply(pd.Series)
.rename(columns = {0:'pid1',1:'pid2'})
.reset_index()
.rename(columns = {0:'count'})
.set_index(['pid1', 'pid2'] )
.unstack()
.fillna(0))
这会生成 'compact' 形式的 table 你之后只包含至少一对出现的产品
count
pid2 3333 365 48750 9877
pid1
11202 1.0 2.0 0.0 0.0
32001 1.0 0.0 1.0 0.0
3333 0.0 3.0 2.0 1.0
365 0.0 1.0 1.0 1.0
48750 0.0 0.0 0.0 1.0
更新
在评论
中进行了各种讨论之后,这是上述内容的一个相当简化的版本
import numpy as np
import pandas as pd
from collections import Counter
# we start as in the original solution but use permutations not combinations
all_pairs = []
for _, group in df.sort_values('product_id').groupby('order_id'):
all_pairs += list(itertools.permutations(group['product_id'],2))
count_dict = dict(Counter(all_pairs))
# We create permutations for _all_ product_ids ... note we use unique() but also product(..) to allow for (365,265) combinations
total_pairs = list(itertools.product(df['product_id'].unique(),repeat = 2))
# pull out first and second elements separately
pid1 = [p[0] for p in total_pairs]
pid2 = [p[1] for p in total_pairs]
# and get the count for those permutations that exist from count_dict. Use 0
# for those that do not
count = [count_dict.get(p,0) for p in total_pairs]
# Now a bit of dataFrame magic
df_cross = pd.DataFrame({'pid1':pid1, 'pid2':pid2, 'count':count})
df_cross.set_index(['pid1','pid2']).unstack()
我们完成了。 df_cross
下面
count
pid2 11202 32001 3333 365 48750 9877
pid1
11202 0 0 1 2 0 0
32001 0 0 1 0 1 0
3333 1 1 0 3 2 1
365 2 0 3 2 1 1
48750 0 1 2 1 0 1
9877 0 0 1 1 1 0
这应该是一个很好的起点,也许会有用
pd.crosstab(df['order_id '], df['product_id'])
product_id 365 3333 9877 11202 32001 48750
order_id
1 1 1 1 0 0 1
2 0 1 0 0 1 1
3 2 1 0 1 0 0
旋转使每一行对应一个产品,然后将每一行映射到 (df * row > 0).sum(1)
,这表示该产品与其他每个产品同时出现的订单数。
>>> df = df.pivot_table(index='product_id', columns='order_id', aggfunc='size')
>>> co_occ = df.apply(lambda row: (df * row > 0).sum(1), axis=1)
>>> co_occ
product_id 365 3333 9877 11202 32001 48750
product_id
365 2 2 1 1 0 1
3333 2 3 1 1 1 2
9877 1 1 1 0 0 1
11202 1 1 0 1 0 0
32001 0 1 0 0 1 1
48750 1 2 1 0 1 2
可以使用 np.fill_diagonal(co_occ.values, (df - 1).sum(1))
.[=13 将对角线修改为示例输出所隐含的约定(如果至少有两个产品以相同的顺序出现,则产品与自身同时出现) =]
假设我们有以下数据框,其中包括客户订单 (order_id) 和单个订单包含的产品 (product_id):
import pandas as pd
df = pd.DataFrame({'order_id' : [1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3],
'product_id' : [365, 48750, 3333, 9877, 48750, 32001, 3333, 3333, 365, 11202, 365]})
print(df)
order_id product_id
0 1 365
1 1 48750
2 1 3333
3 1 9877
4 2 48750
5 2 32001
6 2 3333
7 3 3333
8 3 365
9 3 11202
10 3 365
了解产品对出现在同一个购物篮中的频率会很有趣。
如何在 python 中创建如下所示的共现矩阵:
365 48750 3333 9877 32001 11202
365 1 1 2 1 0 1
48750 1 0 2 1 1 0
3333 2 2 0 1 1 1
9877 1 1 1 0 0 0
32001 0 1 1 0 0 0
11202 1 0 1 0 0 0
非常感谢您的帮助!
我们首先将 df 按 order_id 分组,然后在每个组内计算所有可能的对。请注意,我们首先按 product_id 排序,因此不同组中的相同对始终处于相同的顺序
import itertools
all_pairs = []
for _, group in df.sort_values('product_id').groupby('order_id'):
all_pairs += list(itertools.combinations(group['product_id'],2))
all_pairs
我们从所有订单中获取所有对的列表
[('3333', '365'),
('3333', '48750'),
('3333', '9877'),
('365', '48750'),
('365', '9877'),
('48750', '9877'),
('32001', '3333'),
('32001', '48750'),
('3333', '48750'),
('11202', '3333'),
('11202', '365'),
('11202', '365'),
('3333', '365'),
('3333', '365'),
('365', '365')]
现在我们计算重复项
from collections import Counter
count_dict = dict(Counter(all_pairs))
count_dict
所以我们得到每对的计数,基本上就是你想要的
{('3333', '365'): 3,
('3333', '48750'): 2,
('3333', '9877'): 1,
('365', '48750'): 1,
('365', '9877'): 1,
('48750', '9877'): 1,
('32001', '3333'): 1,
('32001', '48750'): 1,
('11202', '3333'): 1,
('11202', '365'): 2,
('365', '365'): 1}
将其放回叉积 table 有点工作,关键是通过调用 .apply(pd.Series)
将元组拆分为列,并最终将其中一列移动到列名称来自 unstack
:
(pd.DataFrame.from_dict(count_dict, orient='index')
.reset_index(0)
.set_index(0)['index']
.apply(pd.Series)
.rename(columns = {0:'pid1',1:'pid2'})
.reset_index()
.rename(columns = {0:'count'})
.set_index(['pid1', 'pid2'] )
.unstack()
.fillna(0))
这会生成 'compact' 形式的 table 你之后只包含至少一对出现的产品
count
pid2 3333 365 48750 9877
pid1
11202 1.0 2.0 0.0 0.0
32001 1.0 0.0 1.0 0.0
3333 0.0 3.0 2.0 1.0
365 0.0 1.0 1.0 1.0
48750 0.0 0.0 0.0 1.0
更新 在评论
中进行了各种讨论之后,这是上述内容的一个相当简化的版本import numpy as np
import pandas as pd
from collections import Counter
# we start as in the original solution but use permutations not combinations
all_pairs = []
for _, group in df.sort_values('product_id').groupby('order_id'):
all_pairs += list(itertools.permutations(group['product_id'],2))
count_dict = dict(Counter(all_pairs))
# We create permutations for _all_ product_ids ... note we use unique() but also product(..) to allow for (365,265) combinations
total_pairs = list(itertools.product(df['product_id'].unique(),repeat = 2))
# pull out first and second elements separately
pid1 = [p[0] for p in total_pairs]
pid2 = [p[1] for p in total_pairs]
# and get the count for those permutations that exist from count_dict. Use 0
# for those that do not
count = [count_dict.get(p,0) for p in total_pairs]
# Now a bit of dataFrame magic
df_cross = pd.DataFrame({'pid1':pid1, 'pid2':pid2, 'count':count})
df_cross.set_index(['pid1','pid2']).unstack()
我们完成了。 df_cross
下面
count
pid2 11202 32001 3333 365 48750 9877
pid1
11202 0 0 1 2 0 0
32001 0 0 1 0 1 0
3333 1 1 0 3 2 1
365 2 0 3 2 1 1
48750 0 1 2 1 0 1
9877 0 0 1 1 1 0
这应该是一个很好的起点,也许会有用
pd.crosstab(df['order_id '], df['product_id'])
product_id 365 3333 9877 11202 32001 48750
order_id
1 1 1 1 0 0 1
2 0 1 0 0 1 1
3 2 1 0 1 0 0
旋转使每一行对应一个产品,然后将每一行映射到 (df * row > 0).sum(1)
,这表示该产品与其他每个产品同时出现的订单数。
>>> df = df.pivot_table(index='product_id', columns='order_id', aggfunc='size')
>>> co_occ = df.apply(lambda row: (df * row > 0).sum(1), axis=1)
>>> co_occ
product_id 365 3333 9877 11202 32001 48750
product_id
365 2 2 1 1 0 1
3333 2 3 1 1 1 2
9877 1 1 1 0 0 1
11202 1 1 0 1 0 0
32001 0 1 0 0 1 1
48750 1 2 1 0 1 2
可以使用 np.fill_diagonal(co_occ.values, (df - 1).sum(1))
.[=13 将对角线修改为示例输出所隐含的约定(如果至少有两个产品以相同的顺序出现,则产品与自身同时出现) =]