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 按季度转换为每日,同时牢记不同的代码的主要内容,如果未能解决你的问题,请参考以下文章
通过 Python 中的 pandas 将每日库存数据转换为每周