【问题标题】:Converting PANDAS dataframe from monthly to daily将 PANDAS 数据帧从每月转换为每日
【发布时间】:2015-06-19 04:46:43
【问题描述】:

我有一个包含 2014 年月度数据的数据框,其中包含一系列 317 个股票代码(317 个股票代码 x 12 个月 = DF 中的 3,804 行)。我想将其转换为每日数据框(317 个代码 x 365 天 = 115,705 行)。因此,我认为我需要在将每月值分布到每个月的每一天时进行上采样或重新索引,但我无法让它正常工作。

数据框目前是这种格式:

>>> df
month    ticker   b    c
2014-1   AAU      10   .04     #different values every month for each ticker
2014-2   AAU      20   .03
2014-3   AAU      13   .06
.
2014-12  AAU      11   .03
.
.
.
2014-1   ZZY      11   .11
2014-2   ZZY      6    .03
.
2014-12  ZZY      17   .09

这就是我想要的:

>>> df
day          ticker   b    c
2014-01-01   AAU      10   .04  #same values every day in month for each ticker
2014-01-02   AAU      10   .04
2014-01-03   AAU      10   .04
.
2014-01-31   AAU      10   .04
2014-02-01   AAU      20   .03
2014-02-02   AAU      20   .03
.
2014-02-28   AAU      20   .03
.
.
.
2014-12-30   ZZY      17   .09 
2014-12-31   ZZY      17   .09 

我已尝试将 groupby 与按天重新采样相结合,但更新后的数据框将从日期“2014-01-13”而不是 1 月 1 日开始,并以“2014-12-01”而不是 12 月 31 日结束.我还尝试将月份值从“2014-1”更改为“2014-01-01”等,但重新采样的数据帧仍以“2014-01-01”结束。必须有一个更简单的方法来解决这个问题,所以我会很感激任何帮助。我整天都在兜圈子。

【问题讨论】:

  • 看看resample
  • 谢谢,埃德。我想我不够明确——我一直在尝试使用重采样,但更新后的 DF 从 1 月 13 日而不是 1 月 1 日开始。

标签: python pandas


【解决方案1】:

首先,将月份-日期字符串解析为 Pandas 时间戳:

df['month'] = pd.to_datetime(df['month'], format='%Y-%m')
#        month ticker   b     c
# 0 2014-01-01    AAU  10  0.04
# 1 2014-02-01    AAU  20  0.03
# 2 2014-03-01    AAU  13  0.06
# 3 2014-12-01    AAU  11  0.03
# 4 2014-01-01    ZZY  11  0.11
# 5 2014-02-01    ZZY   6  0.03
# 6 2014-12-01    ZZY  17  0.09

接下来,旋转 DataFrame,使用月份作为索引,将代码作为列级别:

df = df.pivot(index='month', columns='ticker')
#              b         c      
# ticker     AAU ZZY   AAU   ZZY
# month                         
# 2014-01-01  10  11  0.04  0.11
# 2014-02-01  20   6  0.03  0.03
# 2014-03-01  13 NaN  0.06   NaN
# 2014-12-01  11  17  0.03  0.09

现在通过旋转,我们以后可以更轻松地向前填充每一列。

现在找到开始和结束日期:

start_date = df.index.min() - pd.DateOffset(day=1)
end_date = df.index.max() + pd.DateOffset(day=31)

有趣的是,请注意,添加 pd.DateOffset(day=31) 并不总是会导致日期在第 31 天结束。如果月份是 2 月,添加 pd.DateOffset(day=31) 会返回 2 月的最后一天:

In [130]: pd.Timestamp('2014-2-28') + pd.DateOffset(day=31)
Out[130]: Timestamp('2014-02-28 00:00:00')

这很好,因为这意味着添加 pd.DateOffset(day=31) 将始终为我们提供该月的最后一个有效日期。

现在我们可以重新索引并前向填充 DataFrame:

dates = pd.date_range(start_date, end_date, freq='D')
dates.name = 'date'
df = df.reindex(dates, method='ffill')

产生

In [160]: df.head()
Out[160]: 
             b         c      
ticker     AAU ZZY   AAU   ZZY
date                          
2014-01-01  10  11  0.04  0.11
2014-01-02  10  11  0.04  0.11
2014-01-03  10  11  0.04  0.11
2014-01-04  10  11  0.04  0.11
2014-01-05  10  11  0.04  0.11

In [161]: df.tail()
Out[161]: 
             b         c      
ticker     AAU ZZY   AAU   ZZY
date                          
2014-12-27  11  17  0.03  0.09
2014-12-28  11  17  0.03  0.09
2014-12-29  11  17  0.03  0.09
2014-12-30  11  17  0.03  0.09
2014-12-31  11  17  0.03  0.09

要将代码从列索引中移出并移回列中:

df = df.stack('ticker')
df = df.sortlevel(level=1)
df = df.reset_index()

所以把它们放在一起:

import pandas as pd
df = pd.read_table('data', sep='\s+')
df['month'] = pd.to_datetime(df['month'], format='%Y-%m')
df = df.pivot(index='month', columns='ticker')

start_date = df.index.min() - pd.DateOffset(day=1)
end_date = df.index.max() + pd.DateOffset(day=31)
dates = pd.date_range(start_date, end_date, freq='D')
dates.name = 'date'
df = df.reindex(dates, method='ffill')

df = df.stack('ticker')
df = df.sortlevel(level=1)
df = df.reset_index()

产量

In [163]: df.head()
Out[163]: 
        date ticker   b     c
0 2014-01-01    AAU  10  0.04
1 2014-01-02    AAU  10  0.04
2 2014-01-03    AAU  10  0.04
3 2014-01-04    AAU  10  0.04
4 2014-01-05    AAU  10  0.04

In [164]: df.tail()
Out[164]: 
          date ticker   b     c
450 2014-12-27    ZZY  17  0.09
451 2014-12-28    ZZY  17  0.09
452 2014-12-29    ZZY  17  0.09
453 2014-12-30    ZZY  17  0.09
454 2014-12-31    ZZY  17  0.09

【讨论】:

  • 非常感谢!这是一个非常优雅的答案。我很感激你花时间写下来。我什至没有想过使用数据透视表,而且 DateOffset 技巧很不错。很好的工具包。
  • 太棒了!很好的解释
  • 您如何使用包含重复项的月份索引来旋转数据框?不太明白。想要实现类似但返回:ValueError: Index contains duplicate entries, cannot reshape
【解决方案2】:

让我们做一个综合实验。假设我们有一个每日时间序列数据:

dates = pd.date_range(start, end, freq='D')
ts = pd.Series(data, index=dates)

通过平均一个月内的所有数据来生成每月时间序列:

ts_mon = ts.resample('MS', how='mean')

现在尝试将此每月时间序列上采样回每日时间序列,并在一个月内使用统一的值。第一个使用 reindex 从@unutbu 借一步的方法效果很好:

ts_daily = ts_mon.reindex(dates, method='ffill')
Out:
  2000-01-01 100.21
  2000-01-02 100.21
  ...
  2000-12-30 80.75
  2000-12-31 80.75

使用重采样的第二种方法不起作用,因为它返回上个月的第一天:

ts_daily = ts_mon.resample('D').ffill()
Out:
  2000-01-01 100.21
  2000-01-02 100.21
  ...
  2000-11-30 99.33
  2000-12-01 80.75

【讨论】:

  • 这很好用,但是有没有办法在日期之间将值除以月中的天数?
猜你喜欢
  • 2018-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 2016-01-29
  • 2017-11-02
  • 2015-10-31
  • 1970-01-01
相关资源
最近更新 更多