Pandas DataFrame 将数据从 BST/Clock 转换为 GMT/UTC,反之亦然

Pandas DataFrame convert data from BST/Clock to GMT/UTC and vice versa

虽然这看起来很复杂,但我希望有人已经解决了类似的问题,因为它本质上是在处理夏令时。

我有一些记录温度的设备。一些探测器在 GMT/UTC 中记录时间,而其他探测器在 BST/Clock 中记录时间。

探测器每小时记录一次温度数据,因此对于记录数据 GMT/UTC 的探测器,数据有 24 列,第 0 列对应 00:00,第 1 列对应 01:00 等一年中的第几天。

在英国,时钟在 3 月的最后一个星期日凌晨 1 点拨快 1 小时,在 10 月的最后一个星期日凌晨 2 点拨快 1 小时。

对于在BST/Clock时间记录数据的探测器,当时钟向前时,只有 23 小时的数据周期,而不是正常的 24。当时钟倒退时,有 25 小时的周期。 24.

问题分为 3 个部分。首先是我想以 GMT/UTC 格式标准化一个数据帧中的数据,所以在时钟前进的那一天,我有 23 个读数,我需要将读数移动到下一个 [=40] 的第一列=] 到前一天的第 24 列。然后我需要继续将所有列数据向左移动一个位置。我需要重复这个过程,将一行的第一列移动到前一行的最后一列,然后将行中的所有其他列移动到一个位置,直到并包括 10 月的最后一个星期日,那里有 25 个读数。这是我要实现的目标的示例:

# BST/Clock Data Format 
bst = [{0:9, 1:6, 2:7, 3:4, 22:2, 23:1},
        {0:10, 1:12, 2:9, 3:8, 22:16},
        {0:11, 1:9, 2:8, 3:12, 22:15, 23:16}, 
        {0:1, 1:6, 2:5, 3:7, 22:6, 23:8, 24:9},
        {0:2, 1:2, 2:4, 3:4, 22:3, 23:2}] 


# Create BST/Clock df
df_bst = pd.DataFrame(bst, index=['24/03/2018', '25/03/2018', '26/03/2018', '28/10/2018', '29/10/2018'])
df_bst.index = pd.to_datetime(df_bst.index, dayfirst=True)


# Format of what GMT/UTC data should look like
gmt = [{0:9, 1:6, 2:7, 3:4, 22:2, 23:1},
        {0:10, 1:12, 2:9, 3:8, 22:16, 23:11},
        {0:9, 1:8, 2:12, 3:15, 22:16, 23:5}, 
        {0:6, 1:5, 2:7, 3:6, 22:8, 23:9},
        {0:2, 1:2, 2:4, 3:4, 22:3, 23:2}] 

df_gmt = pd.DataFrame(gmt, index=['24/03/2018', '25/03/2018', '26/03/2018', '28/10/2018', '29/10/2018'])
df_gmt.index = pd.to_datetime(df_gmt.index, dayfirst=True)

print('Initial format')
print(df_bst)
print()
print('What data should look like after translation with the last Sunday in Mar 2018 and Oct 2018 being 25/03/2018 and 28/10/2018 respectively')
print(df_gmt)

第二部分是我想为数据框中的数据计算三月和十月的最后一个星期日。我正在做类似的事情:

for month in (3, 10):
    last_sunday = max([week[-1] for week in calendar.monthcalendar(2018, month)])
    x = datetime.datetime(2018, month, last_sunday)
    print(x.strftime('%d/%m/%Y'))

并使用 DatetimeIndex.year 提取年份,但有时它们的数据集跨越一年以上,因此我需要在 31/03/2018 - 28/10/2018 和然后又在 31/03/2019 到今天之间。

问题的最后一部分是偶尔我想报告 14:00 BST/Clock - 18:00 BST/Clock 之间的平均温度。鉴于我的数据存储在 GMT/UTC 中,最好的方法是什么?

我尝试了不同的选项,包括本地化,但无济于事。

这是我尝试回答所有三个问题的尝试。也许您只需要一些逻辑并使用时区包,例如 ptyz,这有助于您了解时区是否到位,而不是尝试自己计算。具体来说,您可能对两个时区感兴趣:

import pytz

utc = pytz.utc #utc all year round
btc = pytz.timezone('Europe/London') #utc with the time difference already there.

取决于一年中的时间(btc 与否),日期是否相同:

dt1 = datetime.datetime.strptime('2018-03-24','%Y-%m-%d')
dt2 = datetime.datetime.strptime('2018-03-26','%Y-%m-%d')

print ('Different:', utc.localize(dt1), btc.localize(dt1))
print ('Identical:', utc.localize(dt2), btc.localize(dt2))

[out]:
Different: 2018-03-24 00:00:00+00:00 2018-03-24 00:00:00+00:00
Identical: 2018-03-26 00:00:00+00:00 2018-03-26 00:00:00+01:00

使用这两个时区,您可以比较两个时区下的日期是否相同,以确定 BTC 的开始和周期。例如:

import pytz
import pandas as pd
import datetime

utc = pytz.utc
btc = pytz.timezone('Europe/London')

bst = [{0:9, 1:6, 2:7, 3:4, 22:2, 23:1},
        {0:10, 1:12, 2:9, 3:8, 22:16},
        {0:11, 1:9, 2:8, 3:12, 22:15, 23:16}, 
        {0:1, 1:6, 2:5, 3:7, 22:6, 23:8, 24:9},
        {0:2, 1:2, 2:4, 3:4, 22:3, 23:2}]

df = pd.DataFrame(bst)

df['dates'] =  ['24/03/2018', '25/03/2018', '26/03/2018', '28/10/2018', '29/10/2018']

# date on utc
df['dates_utc'] = df['dates'].apply(lambda x: utc.localize(datetime.datetime.strptime(x,'%d/%m/%Y')))

# date on Europe/London
df['dates_wdtz'] = df['dates'].apply(lambda x: btc.localize(datetime.datetime.strptime(x,'%d/%m/%Y')))

# check if is a btc day
df['is_btc'] = df['dates_utc'] > df['dates_wdtz']

然后我们可以为第一个 btc 日创建一个临时标志,因为这是唯一一个只修改最后一个小时的日子:

df['btc_starts'] = df['is_btc'].shift(-1)
df['btc_first_day'] = (df['is_btc']==False) & (df['btc_starts'] == True)

并修改特定日期:

ix_first_day = df[df['btc_first_day']==True].index
df.loc[ix_first_day, 23] = int(df.loc[ix_first_day+1, 0])

对于所有其他日期,我们可以简单地将 -1 逻辑应用于所有小时列:

btc_days = df[df['is_btc']==True].index

for hour in range(0,25,1):
    if hour == 24:
        df.loc[btc_days, hour] = df.loc[btc_days + 1, hour]
    else:
        df.loc[btc_days, hour] = df.loc[btc_days, hour+1]

## drop temporary columns
df.drop(['dates_utc','dates_wdtz','is_btc','btc_starts','btc_first_day'], axis=1, inplace=True)

这将为我们提供以下结果:

Out[15]: 
    0   1   2   3    22    23   24       dates
0   9   6   7   4   2.0   1.0  NaN  24/03/2018
1  10  12   9   8  16.0  11.0  NaN  25/03/2018
2  12  12  12  12  16.0   NaN  NaN  26/03/2018
3   7   7   7   7   8.0   9.0  9.0  28/10/2018
4   2   2   4   4   3.0   2.0  NaN  29/10/2018

注意我没有使用日期作为索引,我假设你有所有日期的信息,因此索引 + 1 总是下一个日期。如果不是这种情况,那么您将使用日期遍历索引,而不是添加 1,而是将 1 天添加到日期时间。

加上以上: - 您可以自动了解日期是否在 btc 上 - 如果您取日期并更改日期时间,则可以重新格式化日期,例如mydate.astimezone(btc)。在 pytz docs 查看更多 - 您无需计算时区何时出现。