datetime
和timestamp
与DataFrame
一起工作时,我遇到了一个大问题。我想确定日期的星期数,但是发现了一些矛盾之处。这种情况:
df = pd.DataFrame(['2018-12-28', '2018-12-29', '2018-12-30', '2018-12-31',
'2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04'],
columns=['datestr'])
df.date = pd.to_datetime(ddf.datestr, format='%Y-%m-%d')
df['week'] = ddf.date.apply(lambda x: x.week)
df['m'] = ddf.date.apply(lambda x: (x.year, x.week))
df['weekpy'] = ddf.m.apply(lambda d: datetime.strptime(str(d)+'-1', "(%Y, %W)-%w"))
给我
datestr week m weekpy
0 2018-12-28 52 (2018, 52) 2018-12-24
1 2018-12-29 52 (2018, 52) 2018-12-24
2 2018-12-30 52 (2018, 52) 2018-12-24
3 2018-12-31 1 (2018, 1) 2018-01-01
4 2019-01-01 1 (2019, 1) 2019-01-07
5 2019-01-02 1 (2019, 1) 2019-01-07
6 2019-01-03 1 (2019, 1) 2019-01-07
7 2019-01-04 1 (2019, 1) 2019-01-07
如您所见,2018-12-31
的日期timestamp
在2019年的第一周(星期一),但仍在2018年。此外,datetime
的日期2019年第一周的第一天是2019-01-07
。
我们如何处理呢?目的是按周分组
答案 0 :(得分:2)
熊猫Timestamp
和Python datetime.date
都报告ISO 8601 week numbers:
import datetime as DT
import pandas as pd
df = pd.DataFrame(['2018-12-28', '2018-12-29', '2018-12-30', '2018-12-31',
'2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04'],
columns=['date'])
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
df['datenum'] = df['date'].dt.week
df['py-ios-weeknum'] = [d.isocalendar()[1] for d in df['date'].dt.date]
df['py-iso-year'] = [d.isocalendar()[0] for d in df['date'].dt.date]
df['week'] = df.date.apply(lambda x: x.week)
df['m'] = df.date.apply(lambda x: (x.isocalendar()[0], x.isocalendar()[1]))
df['weekpy'] = df.m.apply(lambda d: DT.datetime.strptime(str(d)+'-1', "(%Y, %W)-%w"))
print(df)
收益
date datenum py-ios-weeknum py-iso-year week m weekpy
0 2018-12-28 52 52 2018 52 (2018, 52) 2018-12-24
1 2018-12-29 52 52 2018 52 (2018, 52) 2018-12-24
2 2018-12-30 52 52 2018 52 (2018, 52) 2018-12-24
3 2018-12-31 1 1 2019 1 (2019, 1) 2019-01-07
4 2019-01-01 1 1 2019 1 (2019, 1) 2019-01-07
5 2019-01-02 1 1 2019 1 (2019, 1) 2019-01-07
6 2019-01-03 1 1 2019 1 (2019, 1) 2019-01-07
7 2019-01-04 1 1 2019 1 (2019, 1) 2019-01-07
df['datenum'] = df['date'].dt.week
等效于df['week'] = df.date.apply(lambda x: x.week)
,
但是使用Series.dt
accessor(在编写和性能方面)要比df.date
所做的在apply
中的每个项目上调用lambda函数要快。
使用Python datetime.date
(或datetime.datetime
),这是获取
ISO周编号是呼叫其isocalendar
method:
In [76]: d = DT.datetime(2018,12,31)
In [87]: iso_year, iso_week_number, iso_weekday = d.isocalendar()
In [88]: print(iso_year, iso_week_number, iso_weekday)
2019 1 1
请注意,2018-12-31
处于ISO年度2019。要使df['weekpy']
返回ISO星期的第一天,您必须通过 ISO年度,{{1} },而不是实际的年份d.isocalendar()[0]
。
d.year
答案 1 :(得分:1)
鉴于跨两个不同日历年的那几周的.week
属性是如何设置的,我们需要一个对新年不了解的分组功能。最简单的解决方案是假设数据中没有跳过(每天都有一行)并将其划分为7个大块。
但这感觉真的很脆弱。取而代之的是,这里有一个实现询问“这个星期几是星期一?”的实现。并对此进行分组。
df.date.apply(lambda t: t - (t.dayofweek * pd.Timedelta('1 day')))
之所以有用,是因为一周中一周中给定一周的星期一的确切datetime
值是唯一的。这是demo。
这当然是假设您希望2018年12月31日与2019年1月1日位于同一周(可以说,这是因为它属于同一周一至周日的时间片,但是正如Idlehands指出的那样,由您决定的语义问题。
答案 2 :(得分:1)
使用isocalendar()
返回tuple
中的(year, week, day)
:
df['m'] = df['date'].apply(lambda x: x.isocalendar()[:2]) # only need the year and week
# 0 (2018, 52)
# 1 (2018, 52)
# 2 (2018, 52)
# 3 (2019, 1)
# 4 (2019, 1)
# 5 (2019, 1)
# 6 (2019, 1)
# 7 (2019, 1)
要计算一周的开始时间,请减去dayofweek
:
df['weekpy'] = df['date'].apply(lambda x: x - pd.Timedelta(days=x.dayofweek))
# 0 2018-12-24
# 1 2018-12-24
# 2 2018-12-24
# 3 2018-12-31
# 4 2018-12-31
# 5 2018-12-31
# 6 2018-12-31
# 7 2018-12-31
最终结果:
date week m weekpy
0 2018-12-28 52 (2018, 52) 2018-12-24
1 2018-12-29 52 (2018, 52) 2018-12-24
2 2018-12-30 52 (2018, 52) 2018-12-24
3 2018-12-31 1 (2019, 1) 2018-12-31
4 2019-01-01 1 (2019, 1) 2018-12-31
5 2019-01-02 1 (2019, 1) 2018-12-31
6 2019-01-03 1 (2019, 1) 2018-12-31
7 2019-01-04 1 (2019, 1) 2018-12-31