首页 > 解决方案 > 如何将此时间序列转换为 UTC?

问题描述

我第一次来这里:)

我有关注 Pandas DataFrame

    date            time
0   2018-03-24      23
1   2018-03-24      24
2   2018-03-25      1
3   2018-03-25      2
4   2018-03-25      3
5   2018-03-25      4
6   2018-03-25      5
7   2018-03-25      6
8   2018-03-25      7
9   2018-03-25      8
10  2018-03-25      9
11  2018-03-25      10
12  2018-03-25      11
13  2018-03-25      12
14  2018-03-25      13
15  2018-03-25      14
16  2018-03-25      15
17  2018-03-25      16
18  2018-03-25      17
19  2018-03-25      18
20  2018-03-25      19
21  2018-03-25      20
22  2018-03-25      21
23  2018-03-25      22
24  2018-03-25      23
25  2018-03-26      1
26  2018-03-26      2
27  2018-03-26      3
28  2018-03-26      4
29  2018-03-26      5
30  2018-03-26      6
31  2018-10-27      23
32  2018-10-27      24
33  2018-10-28      1
34  2018-10-28      2
35  2018-10-28      3
36  2018-10-28      4
37  2018-10-28      5
38  2018-10-28      6
39  2018-10-28      7
40  2018-10-28      8
41  2018-10-28      9
42  2018-10-28      10
43  2018-10-28      11
44  2018-10-28      12
45  2018-10-28      13
46  2018-10-28      14
47  2018-10-28      15
48  2018-10-28      16
49  2018-10-28      17
50  2018-10-28      18
51  2018-10-28      19
52  2018-10-28      20
53  2018-10-28      21
54  2018-10-28      22
55  2018-10-28      23
56  2018-10-28      24
57  2018-10-28      25
58  2018-10-29      1
59  2018-10-29      2

time应该代表一天中的小时减一,例如

    date            time
2   2018-03-25      1 

等于2018-03-25 00:00欧洲/伦敦时间。

生成此时间序列数据的设备设置为根据'Europe/London'时区工作,这就是为什么 2018 年 3 月 25 日结束于第 23 小时(记录[2:25]),而 2018 年 10 月 28 日结束于第 25 小时(记录[33:58])(DST 开关)。

这是我已经尝试过的东西:

from pytz import timezone
tz = timezone("Europe/London")
dst_switch = [dt for dt in tz._utc_transition_times if dt.year == 2018]
dst_switch_date_range = pd.date_range(dst_switch[0],dst_switch[1], freq='h', tz='utc')

df['datetime'] = pd.to_datetime(df.date) + pd.to_timedelta(df.time - 1, unit='h') 

df['dt_utc']     = df['datetime'].dt.tz_localize('UTC')
df['dst_switch'] = df['datetime'].map(lambda dt: 1 if dt in dst_switch_date_range else -1)
df['dt_p']     = df['datetime'] + pd.to_timedelta(df['dst_switch'], unit='h')
df['dt_utc_p'] = df['dt_p'].dt.tz_localize('Europe/London', ambiguous='NaT', nonexistent='NaT').dt.tz_convert('UTC')

df[['date', 'time', 'dt', 'dt_utc', 'map','dt_p', 'dt_utc_p']]

这导致:

       date           time   dt                      dt_utc                       dst_switch     dt_p                    dt_utc_p
0      2018-03-24     23     2018-03-24 23:00:00     2018-03-24 22:00:00+00:00     -1            2018-03-24 21:00:00     2018-03-24 21:00:00+00:00
1      2018-03-24     24     2018-03-25 00:00:00     2018-03-24 23:00:00+00:00     -1            2018-03-24 22:00:00     2018-03-24 22:00:00+00:00
2      2018-03-25     1      2018-03-25 01:00:00     2018-03-25 00:00:00+00:00     -1            2018-03-24 23:00:00     2018-03-24 23:00:00+00:00
3      2018-03-25     2      2018-03-25 02:00:00     2018-03-25 01:00:00+00:00     1             2018-03-25 02:00:00     2018-03-25 01:00:00+00:00
4      2018-03-25     3      2018-03-25 03:00:00     2018-03-25 02:00:00+00:00     1             2018-03-25 03:00:00     2018-03-25 02:00:00+00:00
5      2018-03-25     4      2018-03-25 04:00:00     2018-03-25 03:00:00+00:00     1             2018-03-25 04:00:00     2018-03-25 03:00:00+00:00
6      2018-03-25     5      2018-03-25 05:00:00     2018-03-25 04:00:00+00:00     1             2018-03-25 05:00:00     2018-03-25 04:00:00+00:00
7      2018-03-25     6      2018-03-25 06:00:00     2018-03-25 05:00:00+00:00     1             2018-03-25 06:00:00     2018-03-25 05:00:00+00:00
8      2018-03-25     7      2018-03-25 07:00:00     2018-03-25 06:00:00+00:00     1             2018-03-25 07:00:00     2018-03-25 06:00:00+00:00
9      2018-03-25     8      2018-03-25 08:00:00     2018-03-25 07:00:00+00:00     1             2018-03-25 08:00:00     2018-03-25 07:00:00+00:00
10     2018-03-25     9      2018-03-25 09:00:00     2018-03-25 08:00:00+00:00     1             2018-03-25 09:00:00     2018-03-25 08:00:00+00:00
11     2018-03-25     10     2018-03-25 10:00:00     2018-03-25 09:00:00+00:00     1             2018-03-25 10:00:00     2018-03-25 09:00:00+00:00
12     2018-03-25     11     2018-03-25 11:00:00     2018-03-25 10:00:00+00:00     1             2018-03-25 11:00:00     2018-03-25 10:00:00+00:00
13     2018-03-25     12     2018-03-25 12:00:00     2018-03-25 11:00:00+00:00     1             2018-03-25 12:00:00     2018-03-25 11:00:00+00:00
14     2018-03-25     13     2018-03-25 13:00:00     2018-03-25 12:00:00+00:00     1             2018-03-25 13:00:00     2018-03-25 12:00:00+00:00
15     2018-03-25     14     2018-03-25 14:00:00     2018-03-25 13:00:00+00:00     1             2018-03-25 14:00:00     2018-03-25 13:00:00+00:00
16     2018-03-25     15     2018-03-25 15:00:00     2018-03-25 14:00:00+00:00     1             2018-03-25 15:00:00     2018-03-25 14:00:00+00:00
17     2018-03-25     16     2018-03-25 16:00:00     2018-03-25 15:00:00+00:00     1             2018-03-25 16:00:00     2018-03-25 15:00:00+00:00
18     2018-03-25     17     2018-03-25 17:00:00     2018-03-25 16:00:00+00:00     1             2018-03-25 17:00:00     2018-03-25 16:00:00+00:00
19     2018-03-25     18     2018-03-25 18:00:00     2018-03-25 17:00:00+00:00     1             2018-03-25 18:00:00     2018-03-25 17:00:00+00:00
20     2018-03-25     19     2018-03-25 19:00:00     2018-03-25 18:00:00+00:00     1             2018-03-25 19:00:00     2018-03-25 18:00:00+00:00
21     2018-03-25     20     2018-03-25 20:00:00     2018-03-25 19:00:00+00:00     1             2018-03-25 20:00:00     2018-03-25 19:00:00+00:00
22     2018-03-25     21     2018-03-25 21:00:00     2018-03-25 20:00:00+00:00     1             2018-03-25 21:00:00     2018-03-25 20:00:00+00:00
23     2018-03-25     22     2018-03-25 22:00:00     2018-03-25 21:00:00+00:00     1             2018-03-25 22:00:00     2018-03-25 21:00:00+00:00
24     2018-03-25     23     2018-03-25 23:00:00     2018-03-25 22:00:00+00:00     1             2018-03-25 23:00:00     2018-03-25 22:00:00+00:00
25     2018-03-26     1      2018-03-26 01:00:00     2018-03-26 00:00:00+00:00     1             2018-03-26 01:00:00     2018-03-26 00:00:00+00:00
26     2018-03-26     2      2018-03-26 02:00:00     2018-03-26 01:00:00+00:00     1             2018-03-26 02:00:00     2018-03-26 01:00:00+00:00
27     2018-03-26     3      2018-03-26 03:00:00     2018-03-26 02:00:00+00:00     1             2018-03-26 03:00:00     2018-03-26 02:00:00+00:00
28     2018-03-26     4      2018-03-26 04:00:00     2018-03-26 03:00:00+00:00     1             2018-03-26 04:00:00     2018-03-26 03:00:00+00:00
29     2018-03-26     5      2018-03-26 05:00:00     2018-03-26 04:00:00+00:00     1             2018-03-26 05:00:00     2018-03-26 04:00:00+00:00
30     2018-03-26     6      2018-03-26 06:00:00     2018-03-26 05:00:00+00:00     1             2018-03-26 06:00:00     2018-03-26 05:00:00+00:00
31     2018-10-27     23     2018-10-27 23:00:00     2018-10-27 22:00:00+00:00     1             2018-10-27 23:00:00     2018-10-27 22:00:00+00:00
32     2018-10-27     24     2018-10-28 00:00:00     2018-10-27 23:00:00+00:00     1             2018-10-28 00:00:00     2018-10-27 23:00:00+00:00
33     2018-10-28     1      2018-10-28 01:00:00     2018-10-28 00:00:00+00:00     1             2018-10-28 01:00:00     NaT
34     2018-10-28     2      2018-10-28 02:00:00     2018-10-28 01:00:00+00:00     1             2018-10-28 02:00:00     2018-10-28 02:00:00+00:00
35     2018-10-28     3      2018-10-28 03:00:00     2018-10-28 02:00:00+00:00     -1            2018-10-28 01:00:00     NaT
36     2018-10-28     4      2018-10-28 04:00:00     2018-10-28 03:00:00+00:00     -1            2018-10-28 02:00:00     2018-10-28 02:00:00+00:00
37     2018-10-28     5      2018-10-28 05:00:00     2018-10-28 04:00:00+00:00     -1            2018-10-28 03:00:00     2018-10-28 03:00:00+00:00
38     2018-10-28     6      2018-10-28 06:00:00     2018-10-28 05:00:00+00:00     -1            2018-10-28 04:00:00     2018-10-28 04:00:00+00:00
39     2018-10-28     7      2018-10-28 07:00:00     2018-10-28 06:00:00+00:00     -1            2018-10-28 05:00:00     2018-10-28 05:00:00+00:00
40     2018-10-28     8      2018-10-28 08:00:00     2018-10-28 07:00:00+00:00     -1            2018-10-28 06:00:00     2018-10-28 06:00:00+00:00
41     2018-10-28     9      2018-10-28 09:00:00     2018-10-28 08:00:00+00:00     -1            2018-10-28 07:00:00     2018-10-28 07:00:00+00:00
42     2018-10-28     10     2018-10-28 10:00:00     2018-10-28 09:00:00+00:00     -1            2018-10-28 08:00:00     2018-10-28 08:00:00+00:00
43     2018-10-28     11     2018-10-28 11:00:00     2018-10-28 10:00:00+00:00     -1            2018-10-28 09:00:00     2018-10-28 09:00:00+00:00
44     2018-10-28     12     2018-10-28 12:00:00     2018-10-28 11:00:00+00:00     -1            2018-10-28 10:00:00     2018-10-28 10:00:00+00:00
45     2018-10-28     13     2018-10-28 13:00:00     2018-10-28 12:00:00+00:00     -1            2018-10-28 11:00:00     2018-10-28 11:00:00+00:00
46     2018-10-28     14     2018-10-28 14:00:00     2018-10-28 13:00:00+00:00     -1            2018-10-28 12:00:00     2018-10-28 12:00:00+00:00
47     2018-10-28     15     2018-10-28 15:00:00     2018-10-28 14:00:00+00:00     -1            2018-10-28 13:00:00     2018-10-28 13:00:00+00:00
48     2018-10-28     16     2018-10-28 16:00:00     2018-10-28 15:00:00+00:00     -1            2018-10-28 14:00:00     2018-10-28 14:00:00+00:00
49     2018-10-28     17     2018-10-28 17:00:00     2018-10-28 16:00:00+00:00     -1            2018-10-28 15:00:00     2018-10-28 15:00:00+00:00
50     2018-10-28     18     2018-10-28 18:00:00     2018-10-28 17:00:00+00:00     -1            2018-10-28 16:00:00     2018-10-28 16:00:00+00:00
51     2018-10-28     19     2018-10-28 19:00:00     2018-10-28 18:00:00+00:00     -1            2018-10-28 17:00:00     2018-10-28 17:00:00+00:00
52     2018-10-28     20     2018-10-28 20:00:00     2018-10-28 19:00:00+00:00     -1            2018-10-28 18:00:00     2018-10-28 18:00:00+00:00
53     2018-10-28     21     2018-10-28 21:00:00     2018-10-28 20:00:00+00:00     -1            2018-10-28 19:00:00     2018-10-28 19:00:00+00:00
54     2018-10-28     22     2018-10-28 22:00:00     2018-10-28 21:00:00+00:00     -1            2018-10-28 20:00:00     2018-10-28 20:00:00+00:00
55     2018-10-28     23     2018-10-28 23:00:00     2018-10-28 22:00:00+00:00     -1            2018-10-28 21:00:00     2018-10-28 21:00:00+00:00
56     2018-10-28     24     2018-10-29 00:00:00     2018-10-28 23:00:00+00:00     -1            2018-10-28 22:00:00     2018-10-28 22:00:00+00:00
57     2018-10-28     25     2018-10-29 01:00:00     2018-10-29 00:00:00+00:00     -1            2018-10-28 23:00:00     2018-10-28 23:00:00+00:00
58     2018-10-29     1      2018-10-29 01:00:00     2018-10-29 00:00:00+00:00     -1            2018-10-28 23:00:00     2018-10-28 23:00:00+00:00
59     2018-10-29     2      2018-10-29 02:00:00     2018-10-29 01:00:00+00:00     -1            2018-10-29 00:00:00     2018-10-29 00:00:00+00:00

我需要什么:我需要将这两列合并到日期时间列中,然后将时区从转换'Europe/London''UTC'(或其他方式:调整列中的值time以适应'UTC'然后与date列结合)

我的逻辑线:1 年有 24 小时 * 365 天 = 8760 小时。如果我取全年的时间序列数据,我应该得到 363 天,每条记录 24 条记录,1 天有 23 条记录,1 天有 25 条记录,总共 8712 + 23 + 25 = 8760 条记录,所以,每个记录一条一年中的一个小时。

我失败的地方:将我的逻辑转换为代码:P。我不知道如何抵消这些日期,所以一切都会有意义,这意味着 DST 切换日期之间的平滑过渡,没有重复的时间序列值,也没有时间序列中的缺失值(漏洞)。

预期结果

    date           time   dt_utc                   
0   2018-03-24     23     2018-03-24 22:00:00+00:00
1   2018-03-24     24     2018-03-24 23:00:00+00:00
2   2018-03-25     1      2018-03-25 00:00:00+00:00
3   2018-03-25     2      2018-03-25 01:00:00+00:00
4   2018-03-25     3      2018-03-25 02:00:00+00:00
5   2018-03-25     4      2018-03-25 03:00:00+00:00
6   2018-03-25     5      2018-03-25 04:00:00+00:00
7   2018-03-25     6      2018-03-25 05:00:00+00:00
8   2018-03-25     7      2018-03-25 06:00:00+00:00
9   2018-03-25     8      2018-03-25 07:00:00+00:00
10  2018-03-25     9      2018-03-25 08:00:00+00:00
11  2018-03-25     10     2018-03-25 09:00:00+00:00
12  2018-03-25     11     2018-03-25 10:00:00+00:00
13  2018-03-25     12     2018-03-25 11:00:00+00:00
14  2018-03-25     13     2018-03-25 12:00:00+00:00
15  2018-03-25     14     2018-03-25 13:00:00+00:00
16  2018-03-25     15     2018-03-25 14:00:00+00:00
17  2018-03-25     16     2018-03-25 15:00:00+00:00
18  2018-03-25     17     2018-03-25 16:00:00+00:00
19  2018-03-25     18     2018-03-25 17:00:00+00:00
20  2018-03-25     19     2018-03-25 18:00:00+00:00
21  2018-03-25     20     2018-03-25 19:00:00+00:00
22  2018-03-25     21     2018-03-25 20:00:00+00:00
23  2018-03-25     22     2018-03-25 21:00:00+00:00
24  2018-03-25     23     2018-03-25 22:00:00+00:00
25  2018-03-26     1      2018-03-25 23:00:00+00:00
26  2018-03-26     2      2018-03-26 00:00:00+00:00
27  2018-03-26     3      2018-03-26 01:00:00+00:00
28  2018-03-26     4      2018-03-26 02:00:00+00:00
29  2018-03-26     5      2018-03-26 03:00:00+00:00
30  2018-03-26     6      2018-03-26 04:00:00+00:00
31  2018-10-27     23     2018-10-27 21:00:00+00:00
32  2018-10-27     24     2018-10-27 22:00:00+00:00
33  2018-10-28     1      2018-10-27 23:00:00+00:00
34  2018-10-28     2      2018-10-28 00:00:00+00:00
35  2018-10-28     3      2018-10-28 01:00:00+00:00
36  2018-10-28     4      2018-10-28 02:00:00+00:00
37  2018-10-28     5      2018-10-28 03:00:00+00:00
38  2018-10-28     6      2018-10-28 04:00:00+00:00
39  2018-10-28     7      2018-10-28 05:00:00+00:00
40  2018-10-28     8      2018-10-28 06:00:00+00:00
41  2018-10-28     9      2018-10-28 07:00:00+00:00
42  2018-10-28     10     2018-10-28 08:00:00+00:00
43  2018-10-28     11     2018-10-28 09:00:00+00:00
44  2018-10-28     12     2018-10-28 10:00:00+00:00
45  2018-10-28     13     2018-10-28 11:00:00+00:00
46  2018-10-28     14     2018-10-28 12:00:00+00:00
47  2018-10-28     15     2018-10-28 13:00:00+00:00
48  2018-10-28     16     2018-10-28 14:00:00+00:00
49  2018-10-28     17     2018-10-28 15:00:00+00:00
50  2018-10-28     18     2018-10-28 16:00:00+00:00
51  2018-10-28     19     2018-10-28 17:00:00+00:00
52  2018-10-28     20     2018-10-28 18:00:00+00:00
53  2018-10-28     21     2018-10-28 19:00:00+00:00
54  2018-10-28     22     2018-10-28 20:00:00+00:00
55  2018-10-28     23     2018-10-28 21:00:00+00:00
56  2018-10-28     24     2018-10-28 22:00:00+00:00
57  2018-10-28     25     2018-10-28 23:00:00+00:00
58  2018-10-29     1      2018-10-29 00:00:00+00:00
59  2018-10-29     2      2018-10-29 01:00:00+00:00

请帮忙 :)

标签: pythonpandastimezone

解决方案


问题中的优秀 MCVE。让日期时间、时区和时间增量来处理 DST。我没有检查结果,但它应该是好的。

import pandas as pd
from pandas.compat import StringIO
print(pd.__version__)

data = """index   date            hour
0   2018-03-24      23
1   2018-03-24      24
2   2018-03-25      1
3   2018-03-25      2
4   2018-03-25      3
5   2018-03-25      4
6   2018-03-25      5
7   2018-03-25      6
8   2018-03-25      7
9   2018-03-25      8
10  2018-03-25      9
11  2018-03-25      10
12  2018-03-25      11
13  2018-03-25      12
14  2018-03-25      13
15  2018-03-25      14
16  2018-03-25      15
17  2018-03-25      16
18  2018-03-25      17
19  2018-03-25      18
20  2018-03-25      19
21  2018-03-25      20
22  2018-03-25      21
23  2018-03-25      22
24  2018-03-25      23
25  2018-03-26      1
26  2018-03-26      2
27  2018-03-26      3
28  2018-03-26      4
29  2018-03-26      5
30  2018-03-26      6
31  2018-10-27      23
32  2018-10-27      24
33  2018-10-28      1
34  2018-10-28      2
35  2018-10-28      3
36  2018-10-28      4
37  2018-10-28      5
38  2018-10-28      6
39  2018-10-28      7
40  2018-10-28      8
41  2018-10-28      9
42  2018-10-28      10
43  2018-10-28      11
44  2018-10-28      12
45  2018-10-28      13
46  2018-10-28      14
47  2018-10-28      15
48  2018-10-28      16
49  2018-10-28      17
50  2018-10-28      18
51  2018-10-28      19
52  2018-10-28      20
53  2018-10-28      21
54  2018-10-28      22
55  2018-10-28      23
56  2018-10-28      24
57  2018-10-28      25
58  2018-10-29      1
59  2018-10-29      2"""

df = pd.read_csv(StringIO(data), sep='\s+', index_col=0)
df['hour'] = pd.to_timedelta(df['hour'] - 1, 'h')
df['date'] = pd.to_datetime(df['date'])
df['naive_datetime'] = df['date'] + df['hour']
df.set_index(df['naive_datetime'], inplace=True)
df.index = df.index.tz_localize('Europe/London').tz_convert('UTC')

print(df)

生产

pytz.exceptions.NonExistentTimeError: 2018-03-25 01:00:00

因此,必须处理不存在的日期时间。有许多选项可以做到这一点,一个是忽略本地 DST 时区中不存在的日期时间,该时区每年跳转两次。

# receives non-existent time exception because of naive datetime that does not exist in Europe/London
#df.index = df.index.tz_localize('Europe/London').tz_convert('UTC')

# receives AmbiguousTimeError: Cannot infer dst time from 2018-10-28 01:00:00 as there are no repeated times
#df.index = df.index.tz_localize('Europe/London', ambiguous='infer').tz_convert('UTC')

df.index = df.index.tz_localize('Europe/London', ambiguous='NaT', nonexistent='NaT').tz_convert('UTC')
df.index.name = "datetime Europe/London"

# if there is timestamped data in the dataframe, something has to be done with it.
# The data for the missing time is probably best dropped
#df = df[df.index.notnull()]

# interpolate doesn't work: https://github.com/pandas-dev/pandas/issues/11701, but then again the time does not exist in local tz...
#df['datetime Europe/London interpolated'] = df.index.to_series().interpolate(method='linear')


推荐阅读