【问题标题】:How to have pandas perform a rolling average on a non-uniform x-grid如何让熊猫在非均匀 x 网格上执行滚动平均
【发布时间】:2021-03-01 23:50:22
【问题描述】:

我想执行滚动平均,但窗口在 x 中只有有限的“视觉”。我想要类似于下面的内容,但我想要一个基于 x 值而不是位置索引的窗口范围。

虽然在 pandas 中这样做是首选的 numpy/scipy 等价物也可以

import numpy as np 
import pandas as pd 

x_val = [1,2,4,8,16,32,64,128,256,512]
y_val = [x+np.random.random()*200 for x in x_val]

df = pd.DataFrame(data={'x':x_val,'y':y_val})
df.set_index('x', inplace=True)

df.plot()
df.rolling(1, win_type='gaussian').mean(std=2).plot()

所以我希望前 5 个值一起平均,因为它们彼此相差 10 个 xunit,但最后一个值保持不变。

【问题讨论】:

  • 一个选项可能是创建一个额外的(过滤的)列,只包含定义范围内的值(例如
  • x 值你能保证什么?它们是否保证严格增加?或者是否有可能有一个像 [1,2,4,3] 这样的 x 序列?
  • 如果有帮助,我们可以保证 x 严格递增(可以始终对 x 和 y 进行排序以确保这一点)。
  • 您能否添加一个输入和预期输出的虚拟示例?我对你提到的部分有点困惑but I want only values within a certain range (e.g. only values within a range of 10). 这里的 10 是什么?那是窗口大小吗?

标签: python pandas numpy scipy


【解决方案1】:

根据pandasdocumentation on rolling

移动窗口的大小。这是用于计算统计量的观察数。每个窗口的大小都是固定的。

因此,也许您需要像这样伪造具有各种窗口大小的滚动操作

test_df = pd.DataFrame({'x':np.linspace(1,10,10),'y':np.linspace(1,10,10)})
test_df['win_locs'] = np.linspace(1,10,10).astype('object')
for ind in range(10): test_df.at[ind,'win_locs'] = np.random.randint(0,10,np.random.randint(5)).tolist()

    
# rolling operation with various window sizes
def worker(idx_list):
    
    x_slice = test_df.loc[idx_list,'x']
    return np.sum(x_slice)

test_df['rolling'] = test_df['win_locs'].apply(worker)

如你所见,test_df

      x     y      win_locs  rolling
0   1.0   1.0        [5, 2]      9.0
1   2.0   2.0  [4, 8, 7, 1]     24.0
2   3.0   3.0            []      0.0
3   4.0   4.0           [9]     10.0
4   5.0   5.0     [6, 2, 9]     20.0
5   6.0   6.0            []      0.0
6   7.0   7.0     [5, 7, 9]     24.0
7   8.0   8.0            []      0.0
8   9.0   9.0            []      0.0
9  10.0  10.0  [9, 4, 7, 1]     25.0

滚动操作是通过apply 方法实现的。

但是,这种方式比原生的rolling慢很多,例如,

test_df = pd.DataFrame({'x':np.linspace(1,10,10),'y':np.linspace(1,10,10)})
test_df['win_locs'] = np.linspace(1,10,10).astype('object')
for ind in range(10): test_df.at[ind,'win_locs'] = np.arange(ind-1,ind+1).tolist() if ind >= 1 else []

使用上面的方法

%%timeit
# rolling operation with various window sizes
def worker(idx_list):
    
    x_slice = test_df.loc[idx_list,'x']
    return np.sum(x_slice)

test_df['rolling_apply'] = test_df['win_locs'].apply(worker)

结果是

41.4 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

在使用原生 rolling 时,速度要快约 50 倍

%%timeit
test_df['rolling_native'] = test_df['x'].rolling(window=2).sum()

863 µs ± 118 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

【讨论】:

    【解决方案2】:

    关键问题仍然存在:您希望通过滚动均值实现什么目标?

    数学上一个干净的方法是:

    1. 插值到 x 数据的最佳 dx
    2. 执行滚动平均
    3. 取出你想要的数据点(但要小心:这一步也是一种平均!)

    这是插值的代码:

    import numpy as np 
    import pandas as pd 
    import matplotlib.pyplot as plt
    from scipy.interpolate import interp1d
    
    x_val = [1,2,4,8,16,32,64,128,256,512]
    y_val = [x+np.random.random()*200 for x in x_val]
    
    df = pd.DataFrame(data={'x':x_val,'y':y_val})
    df.set_index('x', inplace=True)
    
    #df.plot()
    df.rolling(5, win_type='gaussian').mean(std=200).plot()
    
    
    #---- Interpolation -----------------------------------
    f1 = interp1d(x_val, y_val)
    f2 = interp1d(x_val, y_val, kind='cubic')
    
    dx = np.diff(x_val).min()  # get the smallest dx in the x-data set
    
    xnew = np.arange(x_val[0], x_val[-1]+dx, step=dx)
    ynew1 = f1(xnew)
    ynew2 = f2(xnew)
    
    #---- plot ---------------------------------------------
    fig = plt.figure(figsize=(15,5))
    plt.plot(x_val, y_val, '-o', label='data', alpha=0.5)
    plt.plot(xnew, ynew1, '|', ms = 15, c='r', label='linear', zorder=1)
    #plt.plot(xnew, ynew2, label='cubic')
    plt.savefig('curve.png')
    plt.legend(loc='best')
    plt.show()
    

    【讨论】:

      【解决方案3】:

      希望有人会提供更快的解决方案。
      同时,您可以使用DataFrame.iterrows() 来执行此操作:

      for idx,row in df.iterrows():
          df.loc[idx, 'avg'] = df.loc[idx-10:idx, 'y'].mean()
      

      输出:

                    y         avg
      x                          
      1     26.540168   26.540168
      2     28.255431   27.397799
      4    114.941475   56.579025
      8    156.347716   81.521197
      16   168.563203  162.455459
      32    36.054945   36.054945
      64   179.384703  179.384703
      128  225.098994  225.098994
      256  340.718363  340.718363
      512  551.927011  551.927011
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-04-26
        • 1970-01-01
        • 2013-06-21
        • 2016-09-10
        • 2019-07-27
        • 2019-02-13
        • 1970-01-01
        • 2017-09-12
        相关资源
        最近更新 更多