【问题标题】:faster way to calculate a rolling sum in a dataframe在数据框中计算滚动和的更快方法
【发布时间】:2021-01-19 23:28:50
【问题描述】:

为了计算交易量加权移动平均线 (VWMA),我收集了一个总和(价格*交易量)并将其除以总和(交易量)。

我需要一种更快的方法来从前一行获取一个值并将其添加到当前行的一个值中。

我有以下数据框:

import pandas as pd
from itertools import repeat

df = pd.DataFrame({'dtime': ['16:00', '15:00', '14:00', '13:00', '12:00', '11:00', '10:00', '09:00', '08:00', '07:00', '06:00', '05:00', '04:00', '03:00', '02:00', '01:00'],
'time': [1800, 1740, 1680, 1620, 1560, 1500, 1440, 1380, 1320, 1260, 1200, 1140, 1080, 1020, 960, 900],
'price': [100.1, 102.7, 108.5, 105.3, 107.1, 103.4, 101.8, 102.7, 101.6, 99.8, 100.2, 97.7, 99.3, 100.1, 102.5, 103.9],
'volume': [6.0, 6.5, 5.4, 6.3, 6.4, 7.1, 6.7, 6.2, 5.7, 1.2, 2.4, 3.9, 5.2, 8.9, 7.2, 6.5]
}, columns = ['dtime', 'time', 'price', 'volume']).set_index('dtime')

df.insert(df.shape[1], "PV", df['price']*df['volume'])
df.insert(df.shape[1], "flag", list(repeat(0.0,len(df))))
df.insert(df.shape[1], "PVsum_2", list(repeat(0.0,len(df))))
df.insert(df.shape[1], "Vsum_2", list(repeat(0.0,len(df))))
df.insert(df.shape[1], "VWMA_2", list(repeat(0.0,len(df))))

这是

df = 
       time  price  volume      PV   flag  PVsum_2  Vsum_2  VWMA_2
dtime                                                             
16:00  1800  100.1     6.0  600.60    0.0      0.0     0.0     0.0
15:00  1740  102.7     6.5  667.55    0.0      0.0     0.0     0.0
14:00  1680  108.5     5.4  585.90    0.0      0.0     0.0     0.0
13:00  1620  105.3     6.3  663.39    0.0      0.0     0.0     0.0
12:00  1560  107.1     6.4  685.44    0.0      0.0     0.0     0.0
11:00  1500  103.4     7.1  734.14    0.0      0.0     0.0     0.0
10:00  1440  101.8     6.7  682.06    0.0      0.0     0.0     0.0
09:00  1380  102.7     6.2  636.74    0.0      0.0     0.0     0.0
08:00  1320  101.6     5.7  579.12    0.0      0.0     0.0     0.0
07:00  1260   99.8     1.2  119.76    0.0      0.0     0.0     0.0
06:00  1200  100.2     2.4  240.48    0.0      0.0     0.0     0.0
05:00  1140   97.7     3.9  381.03    0.0      0.0     0.0     0.0
04:00  1080   99.3     5.2  516.36    0.0      0.0     0.0     0.0
03:00  1020  100.1     8.9  890.89    0.0      0.0     0.0     0.0
02:00   960  102.5     7.2  738.00    0.0      0.0     0.0     0.0
01:00   900  103.9     6.5  675.35    0.0      0.0     0.0     0.0

现在我正在使用 for 循环来检查每一行是否设置了“标志”。

#----pseudo code----
#for each row in df (from bottom to top, excluding the very bottom row)
#   if flag[row] is not set:
#      PVsum_2[row] = PV[row] + PV[row + 1]
#      Vsum_2[row]  = volume[row] + volume[row + 1]
#      VWMA_2[row]  = PVsum_2[row] / Vsum_2[row]
#      flag[row] = 1.0
#----pseudo code----

my_dict = {'dtime'  :   0,
           'time'   :   1,
           'price'  :   2,
           "volume" :   3,
           'PV'     :   4,
           'check'  :   5,
           'PVsum_2':   6,
           'Vsum_2' :   7,
           'VWMA_2' :   8}

for row in reversed(range(len(df)-1)):
    # if flag value is not set (i.e. flag == 0)
    if not df['flag'][row]:
        # sum of current and previous PV (price*volume) values
        a = df['PV'][row] + df['PV'][row+1]
        df.iloc[row, my_dict['PVsum_2']-1] = a
        
        # sum of current and previous volumes
        b = df['volume'][row] + df['volume'][row+1]
        df.iloc[row, my_dict['Vsum_2']-1] = b
        
        # PVsum_2 / Vsum_2
        c = (a / b) if b != 0.0 else 0.0                                                 
        df.iloc[row, my_dict['VWMA_2']-1] = c
        
        # set check value to 1.0
        df.iloc[row, my_dict['flag']-1] = 1.0

但是对于大型数据集(超过 500 行)而言,这需要很长时间

我正在寻找更快、更优雅的东西。

完成后数据框应如下所示(注意底行未更改):

df = 

       time  price  volume      PV  flag  PVsum_2  Vsum_2      VWMA_2
dtime                                                                
16:00  1800  100.1     6.0  600.60   1.0  1268.15    12.5  101.452000
15:00  1740  102.7     6.5  667.55   1.0  1253.45    11.9  105.331933
14:00  1680  108.5     5.4  585.90   1.0  1249.29    11.7  106.776923
13:00  1620  105.3     6.3  663.39   1.0  1348.83    12.7  106.207087
12:00  1560  107.1     6.4  685.44   1.0  1419.58    13.5  105.154074
11:00  1500  103.4     7.1  734.14   1.0  1416.20    13.8  102.623188
10:00  1440  101.8     6.7  682.06   1.0  1318.80    12.9  102.232558
09:00  1380  102.7     6.2  636.74   1.0  1215.86    11.9  102.173109
08:00  1320  101.6     5.7  579.12   1.0   698.88     6.9  101.286957
07:00  1260   99.8     1.2  119.76   1.0   360.24     3.6  100.066667
06:00  1200  100.2     2.4  240.48   1.0   621.51     6.3   98.652381
05:00  1140   97.7     3.9  381.03   1.0   897.39     9.1   98.614286
04:00  1080   99.3     5.2  516.36   1.0  1407.25    14.1   99.804965
03:00  1020  100.1     8.9  890.89   1.0  1628.89    16.1  101.173292
02:00   960  102.5     7.2  738.00   1.0  1413.35    13.7  103.164234
01:00   900  103.9     6.5  675.35   0.0     0.00     0.0    0.000000

最终新数据将被添加到数据框的顶部,如下所示,并且需要再次更新。

df = 
           time  price  volume      PV  flag  PVsum_2  Vsum_2  VWMA_2
    dtime                                                             
    19:00  1980  100.1     6.0  600.60   0.0     0.0      0.0    0.0
    18:00  1920  102.7     6.5  667.55   0.0     0.0      0.0    0.0
    17:00  1860  108.5     5.4  585.90   0.0     0.0      0.0    0.0
    16:00  1800  100.1     6.0  600.60   1.0  1268.15    12.5  101.452000
    15:00  1740  102.7     6.5  667.55   1.0  1253.45    11.9  105.331933
    14:00  1680  108.5     5.4  585.90   1.0  1249.29    11.7  106.776923
    13:00  1620  105.3     6.3  663.39   1.0  1348.83    12.7  106.207087
    12:00  1560  107.1     6.4  685.44   1.0  1419.58    13.5  105.154074
    11:00  1500  103.4     7.1  734.14   1.0  1416.20    13.8  102.623188
    10:00  1440  101.8     6.7  682.06   1.0  1318.80    12.9  102.232558
    09:00  1380  102.7     6.2  636.74   1.0  1215.86    11.9  102.173109
    08:00  1320  101.6     5.7  579.12   1.0   698.88     6.9  101.286957
    07:00  1260   99.8     1.2  119.76   1.0   360.24     3.6  100.066667
    06:00  1200  100.2     2.4  240.48   1.0   621.51     6.3   98.652381
    05:00  1140   97.7     3.9  381.03   1.0   897.39     9.1   98.614286
    04:00  1080   99.3     5.2  516.36   1.0  1407.25    14.1   99.804965
    03:00  1020  100.1     8.9  890.89   1.0  1628.89    16.1  101.173292
    02:00   960  102.5     7.2  738.00   1.0  1413.35    13.7  103.164234
    01:00   900  103.9     6.5  675.35   0.0     0.00     0.0    0.000000

【问题讨论】:

  • 你试过用rolling()方法吗?

标签: python pandas dataframe matrix offset


【解决方案1】:

您似乎没有以正确的方式使用pandas。我建议您快速浏览一下教程。

首先,以下几行

df.insert(df.shape[1], "flag", list(repeat(0.0,len(df))))
df.insert(df.shape[1], "PVsum_2", list(repeat(0.0,len(df))))
df.insert(df.shape[1], "Vsum_2", list(repeat(0.0,len(df))))
df.insert(df.shape[1], "VWMA_2", list(repeat(0.0,len(df))))

可以更容易写成:

df['flag'] = 0
df['PVsum_2'] = 0
df['Vsum_2'] = 0
df['VWMA_2'] = 0

但您似乎甚至不需要真正初始化这些列。

您也不需要 for 循环,因为您可以对齐 2 个数据帧,一个是您的原始数据帧,另一个是您已移动所有行的数据帧。例如:

df_shift = df.shift(-1)

然后您可以使用法线向量化计算来实现您想要的,例如:

df['PVsum_2'] = df['PV'] + df_shift['PV']
df['Vsum_2'] = df['volume'] + df_shift['volume']
idx = df['Vsum_2'] != 0   # this is your check whether that value is different from 0 
df.loc[idx, 'VWMA_2'] = df.loc[idx, 'PVsum_2'] / df.loc[idx, 'VSum_2']  # and now use that index to only calculate VWMA_2 where the Vsum_2 was 0

希望你能明白这个想法,并可以做一些小的调整,让它完全按照你的意愿工作。

【讨论】:

  • 这太棒了!不记得为什么我花了这么多精力制作填充 0 的列。但是谢谢你的简化。另外,这正是我正在寻找的解决方案,谢谢!过去需要 30 分钟才能运行,现在只需 30 秒。非常感谢!如果你知道好的pandas教程,请推荐。
  • 我已经开始使用以下书籍学习pandashttps://jakevdp.github.io/PythonDataScienceHandbook/03.00-introduction-to-pandas.html。会有更短的教程或文档,比如https://pandas.pydata.org/pandas-docs/stable/user_guide/10min.html,但我认为 Jake 的书提供了很多额外的背景信息。
猜你喜欢
  • 1970-01-01
  • 2013-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
相关资源
最近更新 更多