日期时间与时间戳之间的周数差异

时间:2019-02-08 15:52:16

标签: python datetime timestamp

datetimetimestampDataFrame一起工作时,我遇到了一个大问题。我想确定日期的星期数,但是发现了一些矛盾之处。这种情况:

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

我们如何处理呢?目的是按周分组

3 个答案:

答案 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