首页 > 解决方案 > pandas groupby + apply 的快速替代方案?

问题描述

我有一个 pandas 数据框,如下所示(大约 100 万行):

Column_1    Column_2    Column_3    Column_4    Column_5    Column_6    Column_7    Column_8    Column_9    Column_10
…           …           …           …           …           …           …           …           …           …
…           …           …           …           …           …           …           …           …           …
…           …           …           …           …           …           …           …           …           …
…           …           …           …           …           …           …           …           …           …

我想要做:

grouping = ["Column_1", "Column_2", "Column_3", "Column_4"]
df.groupby(grouping).apply(lambda x: pd.Series({
              'new_column_1':func_1(x),
              'new_column_2':func_2(x),
              'new_column_3':func_3(x)}
            )).reset_index()

这有效,但速度非常慢。函数 [func_1, func_2, func_3] 是我想应用于每个组的自定义函数。

我阅读了其他关于为什么这么慢的堆栈溢出讨论。我发现的原因是 pandas groupby + apply 使用 python 循环而不是矢量化。但那我怎么能加快速度呢?

例如,假设:

def func_1(x) {
     return sum(x["Column_5"] >= x["Column_6"]) / sum(x["Column_5"] <= x["Column_6"])
}

def func_2(x) {
         return max(x["Column_8"]) + min(x["Column_9"])
    }

def func_3(x) {
         return len(x)
    }

如果没有 pandas groupby + numpy,我们怎么能做同样的操作?

标签: pythonpandasnumpyvectorization

解决方案


看起来您想比较每行中 2 个不同列的值,然后逐行计算比较的结果,然后对计数进行数学运算。如果是这样,请创建 2 个具有比较结果的新列,然后对这些新列求和并比较数字。矢量化而不是迭代。看这个玩具例子:

row1list = [1, 2]
row2list = [5, 3]
row3list = [5, 4]
row4list = [5, 5]
df = pd.DataFrame([row1list, row2list, row3list, row4list],
                  columns=['Column_5', 'Column_6'])

df[['col5 >= col6', 'col6 <= col5']] = 0, 0  
# start with 0, else you get nan or 1 in the next comparison

df.loc[df['Column_5'] >= df['Column_6'], 'col5 >= col6'] = 1
df.loc[df['Column_5'] <= df['Column_6'], 'col6 <= col5'] = 1
print(df)
#    Column_5  Column_6  col5 >= col6  col6 <= col5
# 0         1         2             0             1
# 1         5         3             1             0
# 2         5         4             1             0
# 3         5         5             1             1

answer_of_func1 = sum(df['col5 >= col6']) / sum(df['col6 <= col5'])
print(answer_of_func1)
# 1.5

推荐阅读