在pandas中将季度时间转换为日期的简洁方法

19

编辑:
如果你正在查看这个问题,你的字符串看起来像是1996-Q1,那么只需使用pd.to_datetime(df['Quarter'])将其转换为正确的pandas时间格式。这个问题是关于解决所有不符合此标准格式的季度日期。

原始问题:
我正在寻找一种好的、可读性强且易于理解的方式(可以记住下次使用),将Q3 1996转换为pandas datetime,例如在这种情况下是1996-07-01。 到目前为止,我找到了以下方法,但它非常丑陋:

df = pd.DataFrame({'Quarter':['Q3 1996', 'Q4 1996', 'Q1 1997']})
​
df['date'] = (
    pd.to_datetime(
        df['Quarter'].str.split(' ').apply(lambda x: ''.join(x[::-1]))
))
​
print(df)
   Quarter       date
0  Q3 1996 1996-07-01
1  Q4 1996 1996-10-01
2  Q1 1997 1997-01-01

我希望以下内容能够奏效, 因为它易于理解, 但不幸的是它无法实现:

df['date'] = pd.to_datetime(df['Quarter'], format='%q %Y')

问题还在于季度和年份的顺序显然是错误的,pandas无法进行简单的处理。

有人能帮我找到一种更清晰的方法将Q3 1996转换为pandas日期时间吗?

3个回答

29
您可以(而且应该)使用pd.PeriodIndex作为第一步,然后使用PeriodIndex.to_timestamp将其转换为时间戳:
qs = df['Quarter'].str.replace(r'(Q\d) (\d+)', r'\2-\1')
qs

0    1996-Q3
1    1996-Q4
2    1997-Q1
Name: Quarter, dtype: object

df['date'] = pd.PeriodIndex(qs, freq='Q').to_timestamp()
df

   Quarter       date
0  Q3 1996 1996-07-01
1  Q4 1996 1996-10-01
2  Q1 1997 1997-01-01

初始的替换步骤是必要的,因为 PeriodIndex 需要以 %Y-%q 格式输入你的周期。


另一个选择是在执行与之前相同的字符串替换后使用 pd.to_datetime

df['date'] = pd.to_datetime(
    df['Quarter'].str.replace(r'(Q\d) (\d+)', r'\2-\1'), errors='coerce')
df

   Quarter       date
0  Q3 1996 1996-07-01
1  Q4 1996 1996-10-01
2  Q1 1997 1997-01-01
如果性能很重要,你可以进行分割和合并操作,但必须保持代码清晰:
df['date'] = pd.to_datetime([
    '-'.join(x.split()[::-1]) for x in df['Quarter']])

df

   Quarter       date
0  Q3 1996 1996-07-01
1  Q4 1996 1996-10-01
2  Q1 1997 1997-01-01

@SandervandenOord 我认为这可能与使用的底层日期时间解析器有关(如果我没记错的话,是pytz)。但我不确定。我不知道有没有办法指定PeriodIndex的格式,但如果可以的话,那就太好了。 - cs95
我该如何获取对应于季度末的日期?例如,Q1 2018 变成 2018-03-31? - ifly6
2
@ifly6有同样的问题,只需输入to_timestamp(how='end')即可。 - User2321
1
@cs95 第一种解决方案似乎不再起作用了:df['date'] = pd.PeriodIndex(qs, freq='Q') 我得到了“错误的dtype”。使用 pandas 0.25.3 和 pytz 2019.2。你有任何想法为什么这不再起作用吗?或者是我犯了一个错误? - Sander van den Oord
@SandervandenOord,qs可能有问题,我需要更多信息。否则,我会在几个小时内回到我的电脑上,如果你能等一下,我可以帮你检查一下。 - cs95
显示剩余3条评论

12

给定一个类似2018-Q1的季度格式,可以使用内置的pd.to_datetime函数。由于一般性的答案必须处理各种存储季度观察值的方式(例如2018:12018:Q120181Q1:2018等),将数据强制转换为上述格式超出了我的答案范围。

但是,如果给定一个格式化的系列:

formatted_series = formatted_series_supplier() ...
df['date'] = pd.to_datetime(formatted_series)
例如:

For example:

>>> pd.to_datetime(pd.Series(['2018-Q1']))
0   2018-01-01
dtype: datetime64[ns]

如果你在处理监管数据,它几乎总是反映季度结束而不是开始(即你需要的不是2019-01-01,而是2019-03-31),那么你可以使用以下偏移量:

df['date'] = df['date'] + pd.offsets.QuarterEnd(0)

使用上面的示例,去掉中间结果,

>>> pd.to_datetime(pd.Series(['2018-Q1'])) + pd.offsets.QuarterEnd(0)
0   2018-03-31
dtype: datetime64[ns]
请注意,如果您希望在同一季度内获得正确的索引结尾日期,则必须向QuarterEnd提供0参数。否则,您将会得到类似于以下内容的结果:
>>> pd.to_datetime('2018-03-31') + pd.offsets.QuarterEnd()
Timestamp('2018-06-30 00:00:00')

7

使用切片取最后4个值,以第一个2为起点转换为日期时间:

df['date'] = pd.to_datetime(df['Quarter'].str[-4:] + df['Quarter'].str[:2])

在pandas中进行字符串操作较慢,因此如果不可能存在缺失值,请使用列表解析

#python 3.6+ 
df['date'] = pd.to_datetime([f'{x[-4:]}{x[:2]}' for x in df['Quarter']])
#python bellow
#df['date'] = pd.to_datetime(['{}{}'.format(x[-4:], x[:2]) for x in df['Quarter']])
print (df)
   Quarter       date
0  Q3 1996 1996-07-01
1  Q4 1996 1996-10-01
2  Q1 1997 1997-01-01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接