移动Pandas列,然后取下一个3值的平均值(post_shift)

时间:2017-03-24 20:24:19

标签: pandas iteration

我有一个数据框,df看起来像这样

           Open  High   Low  Close   Volume
Date                                        
2007-03-22  2.65  2.95  2.64   2.86   176389
2007-03-23  2.87  2.87  2.78   2.78    63316
2007-03-26  2.83  2.83  2.51   2.52    54051
2007-03-27  2.61  3.29  2.60   3.28   589443
2007-03-28  3.65  4.10  3.60   3.80  1114659
2007-03-29  3.91  3.91  3.33   3.57   360501
2007-03-30  3.70  3.88  3.66   3.71   185787

我尝试创建一个新列,首先将Open列3行(df.Open.shift(-3))移位,然后取自己的平均值和接下来的2个值。

例如,上面的数据框的Open列将被移位-3行,看起来像这样:

Date
2007-03-22    2.610
2007-03-23    3.650
2007-03-26    3.910
2007-03-27    3.700
2007-03-28    3.710
2007-03-29    3.710
2007-03-30    3.500

然后我想通过迭代获取接下来的3个值(包括它自己)的前向平均值。 所以第一次迭代将2.610(第一个值)+ 3.650 + 3.910(这是下一个值)除以3。 然后我们取下一个值3.650(第一个值)并做同样的事情。创建一列值。

起初我尝试过类似的事情:

df['Avg'] =df.Open.shift(-3).iloc[0:3].mean()   

但是这并没有遍历Open.shift的所有值

下一个循环似乎有效,但速度非常慢,我被告知在Pandas中使用for循环是不好的做法。

for i in range(0, len(df.Open)):
    df['Avg'][i] =df.Open.shift(-3).iloc[i:i+4].mean()

我试着想办法使用apply

df.Open.shift(-3).apply(loc[0:4].mean())
df.Open.shift(-3).apply(lambda x: x[0:4].mean())

但这些似乎会产生错误,例如

TypeError: 'float' object is not subscriptable

我无法想到这种优雅的做法。

谢谢。

2 个答案:

答案 0 :(得分:3)

您可以使用pandas rolling_mean。由于它使用了后向窗口,它将为前两行提供2.61(值本身)和3.13(行0和行1的平均值)。要处理这个问题,您可以使用shift(-2)将值移动2行。

pd.rolling_mean(df, window=3, min_periods=1).shift(-2)

输出:

            open
date    
2007-03-22  3.390000
2007-03-23  3.753333
2007-03-26  3.773333
2007-03-27  3.706667
2007-03-28  3.640000
2007-03-29  NaN
2007-03-30  NaN

答案 1 :(得分:1)

numpy解决方案

如所承诺的那样 注意:巨大的CAVEAT
这是一项先进的技术,不建议任何初学者使用!
使用它可能会意外地刮掉你的狮子狗秃头。小心!

as_strided

from numpy.lib.stride_tricks import as_strided
import numpy as np
import pandas as pd

# I didn't have your full data for all dates
# so I created my own array
# You should be able to just do
# o = df.Open.values
o = np.array([2.65, 2.87, 2.83, 2.61, 3.65, 3.91, 3.70, 3.71, 3.71, 3.50])

# because we shift 3 rows, I trim with 3:
# because it'll be rolling 3 period mean
# add two np.nan at the end
# this makes the strides cleaner.. sortof
# whatever, I wanted to do it
o = np.append(o[3:], [np.nan] * 2)

# strides are the size of the chunk of memory
# allocated to each array element.  there will
# be a stride for each numpy dimension.  for
# a one dimensional array, I only want the first
s = o.strides[0]

# it gets fun right here
as_strided(o, (len(o) - 2, 3), (s, s))
#          ^   \___________/    \__/
#          |               \       \______
# object to stride --- size of array ---  \
#                      to make           memory chunk
#                                        to step forward
#                                        per dimension

[[ 2.61  3.65  3.91]
 [ 3.65  3.91  3.7 ]
 [ 3.91  3.7   3.71]
 [ 3.7   3.71  3.71]
 [ 3.71  3.71  3.5 ]
 [ 3.71  3.5    nan]
 [ 3.5    nan   nan]]

现在我们采取平均值。一起

o = np.array([2.65, 2.87, 2.83, 2.61, 3.65, 3.91, 3.70, 3.71, 3.71, 3.50])
o = np.append(o[3:], [np.nan] * 2)
s = o.strides[0]
as_strided(o, (len(o) - 2, 3), (s, s)).mean(1)

array([ 3.39      ,  3.75333333,  3.77333333,  3.70666667,  3.64      ,
           nan,         nan])

您可以将其包装在熊猫系列中

pd.Series(
    as_strided(o, (len(o) - 2, 3), (s, s)).mean(1),
    df.index[3:],
)