熊猫聚合中极慢的自定义功能

时间:2019-05-24 09:34:12

标签: python pandas

我必须在我拥有的数据集中的移动窗口中计算变异系数(std /均值)。代码很简单:

start_time = time.time()

channel_data = data[data['channel_id'] == ch].sort_values('datetime')
channel_data['datetime'] = pd.to_datetime(channel_data['datetime'],format='%Y-%m-%d %H:%M:%S').dt.floor('d')
channel_data = channel_data.set_index('datetime')

rolling_data = channel_data.rolling('{}D'.format(window)).agg({ 'val': coeff_of_var })

rolling_data = rolling_data.groupby(rolling_data.index.floor('d')).last()[window:]

print('Time needed: {} seconds'.format(time.time() - start_time))

我的问题是它非常慢。

coeff_of_var可以是scipy.stats.variation的自定义函数,也可以是lambda函数-我尝试了全部。我使用的自定义功能是

def coeff_of_var(series):
    return series.std() / series.mean()

data是一个尺寸为[3070584行x 5列]的DataFrame。 channel_data包含〜457710行。

这段代码在一个周期内,花了很长时间,我不得不在第一次执行时间的print之前将其停止...

我没有计算变异系数,而是尝试分别计算std和mean,即用[[std','mean']代替第5行大括号中的函数。新行是:

std_rolling_data = channel_data.rolling('{}D'.format(window)).agg({ 'val': ['std', 'mean'] })

执行时间降至:

Time needed: 0.9421329498291016 seconds
Time needed: 0.9423763751983643 seconds
Time needed: 0.9420042037963867 seconds
Time needed: 0.9560058116912842 seconds
Time needed: 0.9728689193725586 seconds

我还尝试将自定义函数简化为:

def coeff_of_var(series):
    return 1

在这种情况下,执行时间为:

Time needed: 32.465901136398315 seconds
Time needed: 34.14194059371948 seconds
Time needed: 38.883220195770264 seconds
Time needed: 44.430686950683594 seconds
Time needed: 42.99840021133423 seconds

您是否知道自定义功能缓慢的原因是什么?


编辑

您可以使用以下代码来复制问题:

import numpy as np
import pandas as pd

import time

def coeff_of_var(series):
    return 1 #series.std() / series.mean()

nrows = 3070584
ntrue = 450000
window = 5

rng = pd.date_range('2015-01-01 00:00:00', periods=nrows, freq='min')
data = pd.DataFrame({ 'A': rng, 'B' : np.random.randn(len(rng)), 'C' : np.random.randn(len(rng)), 'D' : np.random.randn(len(rng)), 'E' : np.zeros(len(rng), dtype=bool)})

data.loc[np.random.choice(np.arange(nrows), ntrue, replace=False), 'E'] = True

start_time = time.time()

channel_data = data[data['E'] == True].sort_values('A')
channel_data['A'] = pd.to_datetime(channel_data['A'],format='%Y-%m-%d %H:%M:%S').dt.floor('d')
channel_data = channel_data.set_index('A')

rolling_data = channel_data.rolling('{}D'.format(window)).agg({ 'B': coeff_of_var })

rolling_data = rolling_data.groupby(rolling_data.index.floor('d')).last()[window:]

print('Time needed: {} seconds'.format(time.time() - start_time))

2 个答案:

答案 0 :(得分:2)

我以相似的结果运行了您的代码,所以我在Google上搜索了一下。不幸的是,我发现的内容非常模糊,但我认为分享它可能仍会给您一些想法:

Optimize Custom Grouping Function

TL; DR:

  

熊猫无法优化自定义功能。它具有数量有限的内置分组方法。所有这些都经过优化,应该会产生更好的性能。

虽然我知道这不是一个令人满意的答案,但我希望它仍然会有所帮助。

答案 1 :(得分:1)

函数速度过慢的原因是您使用的是.agg而不是.apply。对于自定义函数,您想使用.apply。只需将代码更改为:

rolling_data = channel_data.rolling('{}D'.format(window))['B'].apply(coeff_of_var)

如果您只使coeff_of_var返回1,那么您的时间将是:

Time needed: 0.80185866355896 seconds

如果您使用自定义函数或lambda x更改代码以计算变异系数,那么对我而言所花费的时间为10秒。

最快的方法就是您所说的:

rolling_data = channel_data.rolling('{}D'.format(window)).agg({'B':['mean','std']})
rolling_data = rolling_data['B']['std']/rolling_data['B']['mean']

我选了哪个

Time needed: 0.7320513725280762 seconds