Pandas 将季度转换为每日,同时牢记不同的代码

Pandas convert quarterly to daily while keeping distinct tickers in mind

我正在处理财务数据,其中一些是按季度格式化的,另一些是按天格式化的。我的模型每天都需要所有这些,因此我需要每天重复相同的季度值。我一直在使用这个 并尝试使代码适应我的数据。

这是我的数据框头:

  date        ticker   value
0 31/03/1980  ECB/RA6  1.0
1 30/06/1980  ECB/RA6  4.0
2 30/09/1980  ECB/RA6  2.0
3 30/12/1980  ECB/RA6  3.0
4 31/03/1981  ECB/RA6  2.0

这是我想要的输出:

   date        ticker   value
0  01/01/1980  ECB/RA6  1.0
1  02/01/1980  ECB/RA6  1.0
2  03/01/1980  ECB/RA6  1.0
3  04/01/1980  ECB/RA6  1.0
4  05/01/1980  ECB/RA6  1.0
.  .           .        .
.  .           .        .
.  .           .        .
91 01/04/1980  ECB/RA6  4.0

还有我的代码:

df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y')
df = df.pivot(index='date', 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()

我现在明白问题出在哪里了,但我相信这应该不是问题。 我 运行 来自@Pierre D 的以下代码(删除后重复):

df = df.set_index('date')  # assuming 'date' is a proper Timestamp
df.index = df.index.to_period('Q')  # turn index into PeriodIndex('Q')
df = df.set_index('ticker', append=True).squeeze()
df2 = df[df.duplicated( keep = False)]

我得到 df2 的以下输出:

                value   value2
date    ticker      
1997Q2  AAPL    46850   NaN
1997Q3  AAPL    46850   NaN
2003Q1  MSFT    10137   19/12/2003
2003Q2  MSFT    10137   19/12/2003

如您所见,索引不同,但在这些实例中 value 和 value2 是相等的。我相信这应该不是问题,但是当我现在 运行:

df.unstack()

我收到以下错误:“ValueError:索引包含重复条目,无法重塑”

先谢谢大家了!

更新

问题已修改,表明 'value' 旁边还有其他列,从一些评论中我收集到“扩大范围”可能是一个问题(注意:我们通常会处理类似的时间序列,其中有数千个列没有任何问题)。

所以,这是另一种看法。它执行相同的初始步骤,将所谓的 'date' 转变为它真正的样子:每季度 Period。但随后它应用了一种在多索引 (time, key) 中按 key 组重新采样 time 的方法。这个问题有几个 Whosebug 答案,例如 .

全部加起来(举例):

# setup for example
txt = """  date        ticker   value   value2
0 31/03/1980  ECB/RA6  1.0  NA
1 30/06/1980  another  4.0  NA
2 30/09/1980  ECB/RA6  2.0  19/12/2003
3 30/12/1980  ECB/RA6  3.0  19/12/2003
4 31/03/1981  ECB/RA6  2.0  19/12/2003
"""
df = pd.read_csv(io.StringIO(re.sub(r' +', '\t', txt)),
                 sep='\t', index_col=[0],
                 parse_dates=['date', 'value2'])

# set date as index and convert to quarterly periods
df = df.set_index('date')
df.index = df.index.to_period('Q')

# and now the new resample method (here monthly,
# but change to 'D' for daily)
df = df.groupby('ticker').resample('M').ffill()

如果您愿意,您可以 .reset_index(),或者保持原样。这是没有重置索引的结果:

>>> df
                  ticker  value     value2
ticker  date                              
ECB/RA6 1980-03  ECB/RA6    1.0        NaT
        1980-04  ECB/RA6    1.0        NaT
        1980-05  ECB/RA6    1.0        NaT
        1980-06  ECB/RA6    1.0        NaT
        1980-07  ECB/RA6    1.0        NaT
        1980-08  ECB/RA6    1.0        NaT
        1980-09  ECB/RA6    2.0 2003-12-19
        1980-10  ECB/RA6    2.0 2003-12-19
        1980-11  ECB/RA6    2.0 2003-12-19
        1980-12  ECB/RA6    3.0 2003-12-19
        1981-01  ECB/RA6    3.0 2003-12-19
        1981-02  ECB/RA6    3.0 2003-12-19
        1981-03  ECB/RA6    2.0 2003-12-19
another 1980-06  another    4.0        NaT

原回答

我会这样做:首先,将您的 date 设置为索引并将其转换为 PeriodIndex,然后通过将每个股票行情成一列。然后重新取样:

df = df.set_index('date')  # assuming 'date' is a proper Timestamp
df.index = df.index.to_period('Q')  # turn index into PeriodIndex('Q')
df = df.set_index('ticker', append=True).squeeze().unstack()  # make wide: 1 col per ticker
df.resample('D').ffill()  # resample to daily, repeating the values

结果:

             value
ticker     ECB/RA6
date              
1980-01-01     1.0
1980-01-02     1.0
1980-01-03     1.0
1980-01-04     1.0
1980-01-05     1.0
...            ...
1981-03-27     2.0
1981-03-28     2.0
1981-03-29     2.0
1981-03-30     2.0
1981-03-31     2.0

如果改为按月重新采样,可能更容易检查结果:

df.resample('M').ffill()  # resample to daily, repeating the values

# out:
ticker   ECB/RA6
date            
1980-01      1.0
1980-02      1.0
1980-03      1.0
1980-04      4.0
1980-05      4.0
1980-06      4.0
1980-07      2.0
1980-08      2.0
1980-09      2.0
1980-10      3.0
1980-11      3.0
1980-12      3.0
1981-01      2.0
1981-02      2.0
1981-03      2.0

顺便说一句,观察缺失数据会发生什么是很有用的:

# with input df as:
        date   ticker  value
0 1980-03-31  ECB/RA6    1.0
1 1980-06-30  another    4.0
2 1980-09-30  ECB/RA6    2.0

# output:
ticker   ECB/RA6  another
date                     
1980-01      1.0      NaN
1980-02      1.0      NaN
1980-03      1.0      NaN
1980-04      NaN      4.0
1980-05      NaN      4.0
1980-06      NaN      4.0
1980-07      2.0      NaN
1980-08      2.0      NaN
1980-09      2.0      NaN

最后说明:当然,如果你想得到又高又瘦的结果,你可以再次叠加结果table(如果你愿意,甚至可以重置索引):

print(df.resample('M').ffill().stack().reset_index())

# out:
      date   ticker    0
0  1980-01  ECB/RA6  1.0
1  1980-02  ECB/RA6  1.0
2  1980-03  ECB/RA6  1.0
3  1980-04  another  4.0
4  1980-05  another  4.0
5  1980-06  another  4.0
6  1980-07  ECB/RA6  2.0
7  1980-08  ECB/RA6  2.0
8  1980-09  ECB/RA6  2.0