【问题标题】:pandas MultiIndex rolling meanpandas MultiIndex 滚动平均值
【发布时间】:2017-05-09 00:14:25
【问题描述】:

前言:我是新手,但在这里和pandas documentation 搜索了几个小时,但没有成功。我也读过 Wes 的book

我正在为一家对冲基金建模股票市场数据,并且有一个简单的 MultiIndexed-DataFrame,其中包含代码、日期(每日)和字段。这里的样本来自彭博社。 3 个月 - 2016 年 12 月至 2017 年 2 月,3 个股票代码(AAPL、IBM、MSFT)。

import numpy as np
import pandas as pd
import os

# get data from Excel
curr_directory = os.getcwd()
filename = 'Sample Data File.xlsx'
filepath = os.path.join(curr_directory, filename)
df = pd.read_excel(filepath, sheetname = 'Sheet1', index_col = [0,1], parse_cols = 'A:D')

# sort
df.sort_index(inplace=True)

# sample of the data
df.head(15)
Out[4]: 
                           PX_LAST  PX_VOLUME
Security Name  date                          
AAPL US Equity 2016-12-01   109.49   37086862
               2016-12-02   109.90   26527997
               2016-12-05   109.11   34324540
               2016-12-06   109.95   26195462
               2016-12-07   111.03   29998719
               2016-12-08   112.12   27068316
               2016-12-09   113.95   34402627
               2016-12-12   113.30   26374377
               2016-12-13   115.19   43733811
               2016-12-14   115.19   34031834
               2016-12-15   115.82   46524544
               2016-12-16   115.97   44351134
               2016-12-19   116.64   27779423
               2016-12-20   116.95   21424965
               2016-12-21   117.06   23783165

df.tail(15)
Out[5]: 
                           PX_LAST  PX_VOLUME
Security Name  date                          
MSFT US Equity 2017-02-07    63.43   20277226
               2017-02-08    63.34   18096358
               2017-02-09    64.06   22644443
               2017-02-10    64.00   18170729
               2017-02-13    64.72   22920101
               2017-02-14    64.57   23108426
               2017-02-15    64.53   17005157
               2017-02-16    64.52   20546345
               2017-02-17    64.62   21248818
               2017-02-21    64.49   20655869
               2017-02-22    64.36   19292651
               2017-02-23    64.62   20273128
               2017-02-24    64.62   21796800
               2017-02-27    64.23   15871507
               2017-02-28    63.98   23239825

当我像这样计算每日价格变化时,它似乎有效,只有第一天是 NaN,应该是:

df.head(5)
Out[7]: 
                           PX_LAST  PX_VOLUME  px_change_%
Security Name  date                                       
AAPL US Equity 2016-12-01   109.49   37086862          NaN
               2016-12-02   109.90   26527997     0.003745
               2016-12-05   109.11   34324540    -0.007188
               2016-12-06   109.95   26195462     0.007699
               2016-12-07   111.03   29998719     0.009823

但每日 30 天交易量不会。它应该只是前 29 天的 NaN,但所有的都是 NaN:

# daily change from 30 day volume - doesn't work
df['30_day_volume'] = df.groupby(level=0,group_keys=True)['PX_VOLUME'].rolling(window=30).mean()
df['volume_change_%'] = (df['PX_VOLUME'] - df['30_day_volume']) / df['30_day_volume']

df.iloc[:,3:].tail(40)
Out[12]: 
                           30_day_volume  volume_change_%
Security Name  date                                      
MSFT US Equity 2016-12-30            NaN              NaN
               2017-01-03            NaN              NaN
               2017-01-04            NaN              NaN
               2017-01-05            NaN              NaN
               2017-01-06            NaN              NaN
               2017-01-09            NaN              NaN
               2017-01-10            NaN              NaN
               2017-01-11            NaN              NaN
               2017-01-12            NaN              NaN
               2017-01-13            NaN              NaN
               2017-01-17            NaN              NaN
               2017-01-18            NaN              NaN
               2017-01-19            NaN              NaN
               2017-01-20            NaN              NaN
               2017-01-23            NaN              NaN
               2017-01-24            NaN              NaN
               2017-01-25            NaN              NaN
               2017-01-26            NaN              NaN
               2017-01-27            NaN              NaN
               2017-01-30            NaN              NaN
               2017-01-31            NaN              NaN
               2017-02-01            NaN              NaN
               2017-02-02            NaN              NaN
               2017-02-03            NaN              NaN
               2017-02-06            NaN              NaN
               2017-02-07            NaN              NaN
               2017-02-08            NaN              NaN
               2017-02-09            NaN              NaN
               2017-02-10            NaN              NaN
               2017-02-13            NaN              NaN
               2017-02-14            NaN              NaN
               2017-02-15            NaN              NaN
               2017-02-16            NaN              NaN
               2017-02-17            NaN              NaN
               2017-02-21            NaN              NaN
               2017-02-22            NaN              NaN
               2017-02-23            NaN              NaN
               2017-02-24            NaN              NaN
               2017-02-27            NaN              NaN
               2017-02-28            NaN              NaN

由于 pandas 似乎是专为金融而设计的,我很惊讶这并不简单。

编辑:我也尝试了其他一些方法。

  • 尝试将其转换为 Panel (3D),但除了转换为 DataFrame 并返回之外,没有找到任何适用于 Windows 的内置函数,因此没有任何优势。
  • 尝试创建数据透视表,但找不到仅引用第一级 MultiIndex 的方法。 df.index.levels[0]...levels[1] 不起作用。

谢谢!

【问题讨论】:

    标签: python pandas multi-index


    【解决方案1】:

    你可以试试下面的方法看看是否有效?

    df['30_day_volume'] = df.groupby(level=0)['PX_VOLUME'].rolling(window=30).mean().values
    
    df['volume_change_%'] = (df['PX_VOLUME'] - df['30_day_volume']) / df['30_day_volume']
    

    【讨论】:

    • 工作,谢谢!我很好奇它背后的解释 - 为什么添加 .values 会有所帮助?
    • here看来values是DataFrame的一个属性,它返回DataFrame的NumPy表示,而Wes本人says是一个GroupBy对象本身就是一个 DataFrame。
    • groupby 和 rolling 函数创建了一个具有重复索引键的多索引系列,这在分配给 DF 列的过程中导致了问题。 .values 属性仅从 Series 中取出可以毫无问题地分配给 DF 列的值。
    【解决方案2】:

    我可以在使用 pandas_datareader 时验证 Allen 的答案是否有效,为 datareader 多索引修改 groupby 操作的索引级别。

    import pandas_datareader.data as web
    import datetime
    
    start = datetime.datetime(2016, 12, 1)
    end = datetime.datetime(2017, 2, 28)
    data = web.DataReader(['AAPL', 'IBM', 'MSFT'], 'yahoo', start, end).to_frame()
    
    data['30_day_volume'] = data.groupby(level=1).rolling(window=30)['Volume'].mean().values
    
    data['volume_change_%'] = (data['Volume'] - data['30_day_volume']) / data['30_day_volume']
    
    # double-check that it computed starting at 30 trading days. 
    data.loc['2017-1-17':'2017-1-30']
    

    原发帖者可能会尝试编辑这一行:

    df['30_day_volume'] = df.groupby(level=0,group_keys=True)['PX_VOLUME'].rolling(window=30).mean()
    

    到以下,使用 mean().values:

    df['30_day_volume'] = df.groupby(level=0,group_keys=True)['PX_VOLUME'].rolling(window=30).mean().values
    

    没有这个,数据就不能正确对齐,导致 NaN。

    【讨论】:

    • 太好了,感谢您的澄清。
    猜你喜欢
    • 2019-06-16
    • 2016-04-09
    • 1970-01-01
    • 2018-05-08
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    相关资源
    最近更新 更多