Чтобы предоставить самую свежую информацию об этом, если вы обновите pandas, производительность групповой прокрутки будет значительно улучшена. Это примерно в 4-5 раз быстрее в 1.1.0 и в 12 раз быстрее в ›1.2.0 по сравнению с 0.24 или 1.0.0.
Я считаю, что наибольшее улучшение производительности связано с этим PR, что означает, что он может больше в cython (до того, как он был реализован как groupby.apply(lambda x: x.rolling())
).
Я использовал приведенный ниже код для тестирования:
import pandas
import numpy
print(pandas.__version__)
print(numpy.__version__)
def stack_overflow_df():
obs_per_g = 20
g = 10000
obs = g * obs_per_g
k = 2
df = pandas.DataFrame(
data=numpy.random.normal(size=obs * k).reshape(obs, k),
index=pandas.MultiIndex.from_product(iterables=[range(g), range(obs_per_g)]),
)
return df
df = stack_overflow_df()
# N.B. droplevel important to make indices match
rolling_result = (
df.groupby(level=0)[[0, 1]].rolling(10, min_periods=1).sum().droplevel(level=0)
)
df[["value_0_rolling_sum", "value_1_rolling_sum"]] = rolling_result
%%timeit
# results:
# numpy version always 1.19.4
# pandas 0.24 = 12.3 seconds
# pandas 1.0.5 = 12.9 seconds
# pandas 1.1.0 = broken with groupby rolling bug
# pandas 1.1.1 = 2.9 seconds
# pandas 1.1.5 = 2.5 seconds
# pandas 1.2.0 = 1.06 seconds
# pandas 1.2.2 = 1.06 seconds
Я думаю, что нужно проявлять осторожность, пытаясь использовать numpy.cumsum для повышения производительности (независимо от версии pandas). Например, используя что-то вроде следующего:
# Gives different output
df.groupby(level=0)[[0, 1]].cumsum() - df.groupby(level=0)[[0, 1]].cumsum().shift(10)
Хотя это намного быстрее, вывод неверен. Этот сдвиг выполняется по всем рядам и смешивает совокупность разных групп. т.е. первый результат следующей группы сдвигается обратно в предыдущую группу.
Чтобы иметь такое же поведение, как указано выше, вам необходимо использовать apply:
df.groupby(level=0)[[0, 1]].cumsum() - df.groupby(level=0)[[0, 1]].apply(
lambda x: x.cumsum().shift(10).fillna(0)
)
что в самой последней версии (1.2.2) работает медленнее, чем при использовании прямой прокрутки. Следовательно, для групповых скользящих сумм я не думаю, что numpy.cumsum - лучшее решение для панд ›= 1.1.1
Для полноты, если ваши группы представляют собой столбцы, а не индекс, вы должны использовать такой синтаксис:
# N.B. reset_index important to make indices match
rolling_result = (
df.groupby(["category_0", "category_1"])[["value_0", "value_1"]]
.rolling(10, min_periods=1)
.sum()
.reset_index(drop=True)
)
df[["value_0_rolling_sum", "value_1_rolling_sum"]] = rolling_result
person
user2175850
schedule
21.02.2021