使用bincounts快速转换为多索引的pandas数据帧

时间:2017-03-13 22:09:48

标签: python loops pandas numpy

我有来自不同类别项目的星级评分(1,2或3星)的用户的数据,其中每个项目可能属于多个类别。在我当前的数据框中,每行代表一个评级,类别是一个热门编码,如下所示:

import numpy as np
import pandas as pd

df_old = pd.DataFrame({
    'user': [1, 1, 2, 2, 2],
    'rate': [3, 2, 1, 1, 2],
    'cat1': [1, 0, 1, 1, 1],
    'cat2': [0, 1, 0, 0, 1]
})
#    user  rate  cat1  cat2
# 0     1     3     1     0
# 1     1     2     0     1
# 2     2     1     1     0
# 3     2     1     1     0
# 4     2     2     1     1

我希望将其转换为新的数据框架,由userrate进行多重索引,显示每个星级评分的每类别分数。我目前正在使用循环执行此操作:

multi_idx = pd.MultiIndex.from_product(
    [df_old.user.unique(), range(1,4)],
    names=['user', 'rate']
)

df_new = pd.DataFrame(  # preallocate in an attempt to speed up the code
    {'cat1': np.nan, 'cat2': np.nan},
    index=multi_idx
)

df_new.sort_index(inplace=True)
idx = pd.IndexSlice

for uid in df_old.user.unique():
    for cat in ['cat1', 'cat2']:
        df_new.loc[idx[uid, :], cat] = np.bincount(
            df_old.loc[(df_old.user == uid) & (df_old[cat] == 1),
                       'rate'].values, minlength=4)[1:]
#            cat1  cat2
# user rate            
# 1    1      0.0   0.0
#      2      0.0   1.0
#      3      1.0   0.0
# 2    1      2.0   0.0
#      2      1.0   1.0
#      3      0.0   0.0

不幸的是,上面的代码对我的真实数据帧来说是无可救药的慢,这个数据帧很长并且包含许多类别。我该如何消除循环?

1 个答案:

答案 0 :(得分:1)

使用多索引,您可以汇总旧数据框并重新编制索引:

df_old.groupby(['user', 'rate']).sum().reindex(multi_idx).fillna(0)

enter image description here

或者在@piRSquared评论时,执行reindex并在一步填充缺失值:

df_old.groupby(['user', 'rate']).sum().reindex(multi_idx, fill_value=0)