【问题标题】:Pandas DataFrame convert data from BST/Clock to GMT/UTC and vice versaPandas DataFrame 将数据从 BST/时钟转换为 GMT/UTC,反之亦然
【发布时间】:2019-11-29 09:54:17
【问题描述】:

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

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

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

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

对于以 BST/Clock 时间记录数据的探针,当时钟前进时,只有 23 个小时周期的数据,而不是正常的 24 个。当时钟回退时,有 25 个小时周期而不是 24 个。

问题分为三个部分。第一个是我想以 GMT/UTC 格式标准化一个数据帧中的数据,所以在时钟前进的那一天,我有 23 个读数,我需要将第二天/行的第一列中的读数移动到列前一天24日。然后我需要继续移动所有列数据,向左移动一个位置。我需要重复这个过程,将一行的第一列移动到前一行的最后一列,然后将该行中的所有其他列移动到 1 个位置,直到 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)

第二部分是我想为数据框中的数据计算 3 月和 10 月的最后一个星期日。我正在研究类似的东西:

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 中,最好的方法是什么?

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

【问题讨论】:

    标签: python pandas dataframe datetime dst


    【解决方案1】:

    这是我试图回答所有三个问题的尝试。也许您只需要一些逻辑并使用时区包,例如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
    

    注意,我没有使用日期作为索引,我假设您拥有所有日期的信息,因此 index + 1 始终是下一个日期。如果不是这种情况,则使用日期循环索引,而不是添加 1,而是将 1 天添加到日期时间。

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

    【讨论】:

    • 谢谢 calestini,我会看看你的建议,有很多东西要采纳!最终的数据框应该是统一的,每天有 24 列数据
    • 嗨@Ron,上面的代码应该给你24列。我保留第 24 列用于演示/比较,但您实际上可以删除它,因为它仅在计算期间使用。
    猜你喜欢
    • 2019-03-20
    • 2016-09-20
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    • 2011-06-27
    • 2011-07-24
    • 2021-10-03
    • 1970-01-01
    相关资源
    最近更新 更多