当需要多列窗口中的所有变量时,如何应用滚动功能

时间:2019-04-23 23:21:25

标签: python pandas

我正在尝试计算一个滚动统计量,该统计量需要一个窗口中来自两个输入列的所有变量。

我唯一的解决方案涉及for循环。有没有更有效的方法,也许使用了Pandas的滚动和应用功能?

import pandas as pd
from statsmodels.tsa.stattools import coint


def f(x):
    return coint(x['a'], x['b'])[1]


df = pd.DataFrame(data={'a': [1, 2, 3, 4], 'b': [5, 6, 7, 8]})
df2 = df.rolling(2).apply(lambda x: f(x), raw=False)  # KeyError: 'a'

我得到KeyError:'a',因为df一次传递给一个系列(列)的f()。指定axis = 1会将一行和所有列发送到f(),但是两种方法都不能提供所需的一组观察值。

2 个答案:

答案 0 :(得分:2)

您可以尝试滚动,平均和求和:

df['result'] = df.rolling(2).mean().sum(axis=1)

   a  b  result
0  1  5     0.0
1  2  6     7.0
2  3  7     9.0
3  4  8    11.0

编辑

根据OP中问题中的新信息添加不同的答案。

设置功能。

import pandas as pd
from statsmodels.tsa.stattools import coint

def f(x):
    return coint(x['a'], x['b'])

创建数据和数据框:

a_data = [1,2,3,4]
b_data = [5,6,7,8]

df = pd.DataFrame(data={'a': a_data, 'b': b_data})

   a  b
0  1  5
1  2  6
2  3  7
3  4  8

在研究了硬币之后,我收集到您试图将两个滚动数组传递给f ['a']和f ['b']。下面将创建数组和数据框。

n=2
arr_a = [df['a'].shift(x).values[::-1][:n] for x in range(len(df['a']))[::-1]]
arr_b = [df['b'].shift(x).values[::-1][:n] for x in range(len(df['b']))[::-1]]

df1 = pd.DataFrame(data={'a': arr_a, 'b': arr_b})

n是滚动窗口的大小。

df1
            a           b
0  [1.0, nan]  [5.0, nan]
1  [2.0, 1.0]  [6.0, 5.0]
2  [3.0, 2.0]  [7.0, 6.0]
3      [4, 3]      [8, 7]

然后您可以使用apply。(f)发送数组中的行。

df1.iloc[(n-1):,].apply(f, axis=1)

您的输出如下:

1    (-inf, 0.0, [-48.37534, -16.26923, -10.00565])
2    (-inf, 0.0, [-48.37534, -16.26923, -10.00565])
3    (-inf, 0.0, [-48.37534, -16.26923, -10.00565])
dtype: object

运行此命令时,对于完全共线性的数据确实会出现错误,但我怀疑该错误会随着实际数据消失。

此外,我知道纯粹的矢量化解决方案可能会更快。我想知道如果您要寻找的是什么表现?

向真正为this problem here解决方案的@Zero致意。

答案 1 :(得分:0)

我尝试将总和放在滚动前:

import pandas as pd
import time
df = pd.DataFrame(data={'a': [1, 2, 3, 4], 'b': [5, 6, 7, 8]})
df2 = df.copy()
s = time.time()
df2.loc[:, 'mean1'] = df.sum(axis = 1).rolling(2).mean()
print(time.time() - s)
s = time.time()
df2.loc[:, 'mean2'] = df.rolling(2).mean().sum(axis=1)
print(time.time() - s)
df2
0.003737926483154297
0.005460023880004883
a   b   mean1   mean2
0   1   5   NaN 0.0
1   2   6   7.0 7.0
2   3   7   9.0 9.0
3   4   8   11.0    11.0

它比以前的答案要快一些,但效果相同,并且在大型数据集中差异可能很大。

您可以修改它以仅选择感兴趣的列:

s = time.time()
print(df[['a', 'b']].sum(axis = 1).rolling(2).mean())
print(time.time() - s)

0     NaN
1     7.0
2     9.0
3    11.0
dtype: float64
0.0033559799194335938