如何对熊猫的操作进行矢量化以提高速度?

问题描述 投票:1回答:3

这是SKU亲和力问题。我有这样的数据帧。每个ctn_id都有多个sku_codes。

enter image description here

dfr = pd.DataFrame(columns=['ctn_id','sku_code'])
dfr['ctn_id'] = np.random.randint(low=1,high=21,size=200)
dfr['sku_code'] = np.random.choice(['a','b','c','d'],size=200)
dfr.drop_duplicates(['ctn_id','sku_code'], inplace=True)

我想填写以下数据框。

enter image description here

dfx = pd.DataFrame(columns=['sku_code','a','b','c','d'])
dfx['sku_code'] = ['a','b','c','d']
dfx = dfx.fillna(0)
dfx.set_index('sku_code',inplace=True)

使用下面的逻辑

for idx in dfr['ctn_id'].unique():
    x = list(dfr[dfr['ctn_id'] == idx]['sku_code'].unique())
    for skui in dfx.index:
        if skui in x:
            for skuj in x:
                dfx.loc[skui, skuj] = dfx.loc[skui, skuj] + 1

我有2.5M ctn_ids和400 sk_codes,使其总计十亿次分配操作。有没有更好的方法来使用熊猫或任何其他包?

python pandas parallel-processing bigdata vectorization
3个回答
2
投票

对于具有ctn_idintegers,,我们可以使用基于数组赋值的方法来获取2D网格上的所有映射,然后使用矩阵乘法来获取分箱求和,类似于@scomes's post中所示 -

Ie = dfr.ctn_id.values
J = dfr.sku_code.values

I = pd.factorize(Ie,sort=False)[0]
col2IDs,col2L = pd.factorize(J,sort=True) #use sort=False if order is irrelevant
a = np.zeros((I.max()+1,col2IDs.max()+1),dtype=int)
a[I,col2IDs] = 1
df_out = pd.DataFrame(a.T.dot(a), columns=col2L, index=col2L)

备选方案#1

为了获得更好的性能,我们可以使用float值进行矩阵乘法。为此,使用float dtype获取a。因此,设置a,就像这样 -

a = np.zeros((I.max()+1,col2IDs.max()+1),dtype=float)

备选方案#2

或者使用布尔数组存储1s然后转换dtype:

a = np.zeros((I.max()+1,col2IDs.max()+1),dtype=bool)
a[I,col2IDs] = 1
a = a.astype(float)

2
投票

更新以处理来自随机输入的重复项

这个答案假定没有重复的行(具有相同ctn_id和sku_code的行)。您可以轻松扩展该用例的答案。

是的,您可以旋转数据框,以便ctn_ids是行,sku_codes是列。为此,您可以添加一个全1的虚拟列,然后使用

dfr['Dummy'] = 1
piv = dfr.drop_duplicates().pivot('ctn_id', 'sku_code', 'Dummy').fillna(0.0)

现在你基本上有一个稀疏矩阵,只要有ctn_id / sku_code关系,就有1,否则为0。从这里你可以使用矩阵代数。

mat = piv.values
counts = mat.T.dot(mat)

变量counts有你想要的东西(它将是对称的,值将是在ctn_id中一起看到sku_codes的次数,这是我相信你正在寻找的。


1
投票

好吧,我会试一试。

不确定这是否足够快但我会说它已经比你的链接循环更快。

它使用hacky方式执行“矢量化”集合差异。

s = df.groupby(['sku_code']).ctn_id.agg(set)
pd.DataFrame(map(lambda s: list(map(len,s)), np.array(s) & np.array(s).reshape([-1,1])))

    0   1   2   3
0   18  17  18  16
1   17  19  19  17
2   18  19  20  17
3   16  17  17  17

使用您提供的样本,性能提升约100倍。

# your method
79.4 ms ± 3.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# my try
668 µs ± 30.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
© www.soinside.com 2019 - 2024. All rights reserved.