如何加快对多个列执行总和的 pandas groupby?
How can I speed up a pandas groupby that is performing a sum on more than one column?
询问
我想加快 pandas groupby 的速度,它也在两列上应用求和并返回结果数据帧。
代码
df = df.groupby(['key','code','name','period','agg_metric'], sort=False, observed=True, dropna=False)[['metricA','metricB']]\
.sum().reset_index()
(该方法目前需要 2 分钟来处理我最大用例的数据。)
数据
总的来说,最大的数据框有大约 150 万行应用了 groupby。 Period 和 agg_metric 可以相互推断,其中只有 2 个 period 值(因此有 2 个 agg_metric 值)。 name值也可以从代码中推断出来。
groupby 之后,我剩下 700k 条记录。如果我理解正确,速度减慢是由于处理的结果组的数量。有没有一种可能的方法来向量化此方法并立即将求和应用于每个组,而不是我假设当前正在迭代的方法。
注释
我尝试过使用 groupby().agg({...})
和 groupby().apply(lambda)
,两者花费的时间大致相同。我也试过删除一些 groupby 列,然后稍后再将它们添加回来,但它并没有加快计算速度,因此不能保证将它们从 groupby 中移除。该代码段还具有 sort=False 和 observed=True,但两者都没有改善处理时间。
我已经尽可能多地浏览了所有资源(尤其是这篇很棒的参考资料:)。我是矢量化的新手,我正在这样做,因为我正在从我们的数据库中卸载几个查询。
你有什么类型的数据?
看起来 metricA
/ metricB
列的类型是 object
,而 pandas 对 Python 对象执行慢速求和而不是对 numpy 数组进行快速求和。尝试将指标列转换为 float64
或 integer
类型。
您可以使用 df.info()
方法检查数据类型。
证明:
from string import ascii_letters
from time import time
import numpy as np
import pandas as pd
from numpy.random import choice
N = 1_500_000
np.random.seed(123)
letters = list(ascii_letters)
words = ["".join(choice(letters, 5)) for i in range(30)]
df = pd.DataFrame(
{
"key": choice(words, N),
"code": choice(words, N),
"name": choice(words, N),
"period": np.random.randint(0, 10, N),
"agg_metric": choice(["mean", "sum", "count"], N),
"metricA": np.random.rand(N),
"metricB": np.random.rand(N),
}
)
def aggregate(df):
return (
df.groupby(
["key", "code", "name", "period", "agg_metric"],
sort=False,
observed=True,
dropna=False,
)[["metricA", "metricB"]]
.sum()
.reset_index()
)
start = time()
df2 = aggregate(df)
print(f"sum floats took {time() - start}")
start = time()
df3 = aggregate(df.astype({"metricA": object, "metricB": object}))
print(f"sum objects took {time() - start}")
assert df2.equals(df3)
输出:
sum floats took 0.2983248233795166
sum objects took 81.04267287254333
询问
我想加快 pandas groupby 的速度,它也在两列上应用求和并返回结果数据帧。
代码
df = df.groupby(['key','code','name','period','agg_metric'], sort=False, observed=True, dropna=False)[['metricA','metricB']]\
.sum().reset_index()
(该方法目前需要 2 分钟来处理我最大用例的数据。)
数据
总的来说,最大的数据框有大约 150 万行应用了 groupby。 Period 和 agg_metric 可以相互推断,其中只有 2 个 period 值(因此有 2 个 agg_metric 值)。 name值也可以从代码中推断出来。
groupby 之后,我剩下 700k 条记录。如果我理解正确,速度减慢是由于处理的结果组的数量。有没有一种可能的方法来向量化此方法并立即将求和应用于每个组,而不是我假设当前正在迭代的方法。
注释
我尝试过使用 groupby().agg({...})
和 groupby().apply(lambda)
,两者花费的时间大致相同。我也试过删除一些 groupby 列,然后稍后再将它们添加回来,但它并没有加快计算速度,因此不能保证将它们从 groupby 中移除。该代码段还具有 sort=False 和 observed=True,但两者都没有改善处理时间。
我已经尽可能多地浏览了所有资源(尤其是这篇很棒的参考资料:
你有什么类型的数据?
看起来 metricA
/ metricB
列的类型是 object
,而 pandas 对 Python 对象执行慢速求和而不是对 numpy 数组进行快速求和。尝试将指标列转换为 float64
或 integer
类型。
您可以使用 df.info()
方法检查数据类型。
证明:
from string import ascii_letters
from time import time
import numpy as np
import pandas as pd
from numpy.random import choice
N = 1_500_000
np.random.seed(123)
letters = list(ascii_letters)
words = ["".join(choice(letters, 5)) for i in range(30)]
df = pd.DataFrame(
{
"key": choice(words, N),
"code": choice(words, N),
"name": choice(words, N),
"period": np.random.randint(0, 10, N),
"agg_metric": choice(["mean", "sum", "count"], N),
"metricA": np.random.rand(N),
"metricB": np.random.rand(N),
}
)
def aggregate(df):
return (
df.groupby(
["key", "code", "name", "period", "agg_metric"],
sort=False,
observed=True,
dropna=False,
)[["metricA", "metricB"]]
.sum()
.reset_index()
)
start = time()
df2 = aggregate(df)
print(f"sum floats took {time() - start}")
start = time()
df3 = aggregate(df.astype({"metricA": object, "metricB": object}))
print(f"sum objects took {time() - start}")
assert df2.equals(df3)
输出:
sum floats took 0.2983248233795166
sum objects took 81.04267287254333