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

Posted

技术标签:

【中文标题】Pandas 按季度转换为每日,同时牢记不同的代码【英文标题】:Pandas convert quarterly to daily while keeping distinct tickers in mind 【发布时间】:2021-03-28 16:17:25 【问题描述】:

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

这是我的数据框头:

  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:索引包含重复条目,无法重塑”

提前谢谢大家!

【问题讨论】:

您必须在同一个数据框中有多个代码? 您可以为每个代码创建一个单独的列吗?这样你就可以按日期索引。 @JakobL 是的,我的代码列中有超过 600 个代码。此外,为了更好地阅读这个问题,我省略了其他专栏。因此,除了“值”列之外,还有其他列。我不确定从这个角度处理它是否明智/可行。 【参考方案1】:

更新

问题已被修改以表明除了'value' 之外还有其他列,并且从我收集的一些 cmets 中我收集到“扩大”可能是一个问题(注意:我们通常处理具有数千列的类似时间序列而没有任何问题)。

所以,这是另一种看法。它执行相同的初始步骤,将声称的'date' 转换为它的真正含义:每季度一次的Period。但随后它应用了一种通过key 组在多索引(time, key) 中重新采样time 的方法。该问题有多个 *** 答案,例如 this one。

一起来(举例):

# 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 = 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

最后说明:当然,如果您想将结果作为一个又高又瘦的桌子,您可以再次堆叠(如果您愿意,甚至可以重置索引):

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

【讨论】:

当我运行您的代码时,我收到与运行自己的代码时完全相同的错误消息:“ValueError:索引包含重复条目,无法重塑”。此错误发生在代码的第三行:df = df.set_index('ticker', append=True).squeeze().unstack()。我认为这是由于日期列中存在重复的事实造成的。 你的意思是df.groupby(['date', 'ticker']).size().max() > 1'?重复日期(对于不同的代码)是可以预期的,并且处理得很好,但(date, ticker) 元组的值不会重复。如果有,您需要决定如何处理这些值(如果不同)。一个快速的第一个检查是df.drop_duplicates(),以防某些整行重复。 你是对的,(date,ticker) 元组的数据中有重复项。但是,当我使用 df = df.drop_duplicates(['date','ticker']) 并获得 df.groupby(['date', 'ticker']).size().max() = 1 时,我仍然在代码的 unstack() 部分遇到相同的错误。 我编辑了问题并指定了导致此错误的具体原因。这似乎是一个问题,日期和股票代码(索引元组)不是重复的,而是数据框的剩余值。我不明白为什么unstack() 要求这些值不同。 好的,因为有多个列会改变问题,所以我调整了答案。请看看这是否适合你。

以上是关于Pandas 按季度转换为每日,同时牢记不同的代码的主要内容,如果未能解决你的问题,请参考以下文章

将每日股票价格变成每周/每月/每季度/学期/每年?

将字符串转换为日期 [带年份和季度]

下采样到季度级别并在 Pandas 中获取季度结束日期值

通过 Python 中的 pandas 将每日库存数据转换为每周

在 Python Pandas 中将带有元素日期标签的年度财政数据元组转换为时间序列

比较季度数据:Python(Pandas) 中的迭代以比较来自作为数据框导入的四个不同 excel 文件的多列