首页 > 解决方案 > 在 Pandas DataFrame 中选择日期来计算夏令时

问题描述

我正在尝试在 Pandas DataFrame(包含半小时数据)中选择一系列日期来确定那些日子的夏令时。DST 开始于 9 月的最后一个星期日,结束于 4 月的第一个星期日。

import numpy as np
import pandas as pd
from datetime import datetime, date, timedelta

...

df0 = df0.set_index('datetime')

df0['mnth'] = pd.DatetimeIndex(df0.index).month
df0['dow'] = pd.DatetimeIndex(df0.index).dayofweek # Mon=0, ..., Sun=6

start_dst = df0.iloc[(df0.mnth==9) & (df0.dow==6).idxmax()]
end_dst = df0.iloc[(df0.mnth==4) & (df0.dow==6).idxmin()]
df0.index[start_dst:end_dst] = df0.index + pd.Timedelta('1h')

我的数据在 9 月至 4 月期间基本上向后移动了 1 小时,因此我需要在此期间的时间戳中添加 1 小时。但是当我定义时start_dst,我得到一个错误

TypeError: Cannot perform 'and_' with a dtyped [bool] array and scalar of type [bool]

我不知道如何改变start_dst

编辑:这是一个示例数据框:

# End DST: first Sunday of April, 1h backward (5 Apr 2020)
# Start DST: last Sunday of September, 1h forward (27 Sep 2020)
# 4,5,6 April 2020, 26,27,28 Sep 2020
d1 = '2020-04-04'
d2 = '2020-04-05'
d3 = '2020-04-06'
d4 = '2020-09-26'
d5 = '2020-09-27'
d6 = '2020-09-28'

df1 = pd.DataFrame()
df1['date'] = pd.to_datetime([d1]*24, format='%Y-%m-%d')
df1['time'] = (pd.date_range(d1, periods=24, freq='H') - pd.Timedelta(hours=1)).time
df1 = df1.set_index('date')

df2 = pd.DataFrame()
df2['date'] = pd.to_datetime([d2]*25, format='%Y-%m-%d')
df2['time'] = (pd.date_range(d2, periods=25, freq='H') - pd.Timedelta(hours=1)).time
df2 = df2.set_index('date')

df3 = pd.DataFrame()
df3['date'] = pd.to_datetime([d3]*24, format='%Y-%m-%d')
df3['time'] = (pd.date_range(d3, periods=24, freq='H')).time
df3 = df3.set_index('date')

df4 = pd.DataFrame()
df4['date'] = pd.to_datetime([d4]*24, format='%Y-%m-%d')
df4['time'] = (pd.date_range(d4, periods=24, freq='H')).time
df4 = df4.set_index('date')

df5 = pd.DataFrame()
df5['date'] = pd.to_datetime([d5]*23, format='%Y-%m-%d')
df5a = pd.DataFrame(pd.date_range('00:00', '01:59', freq='H').time)
df5b = pd.DataFrame(pd.date_range('01:00', '01:59', freq='H').time)
df5c = pd.DataFrame(pd.date_range('03:00', '22:00', freq='H').time)
df5['time'] = pd.concat([df5a,df5b,df5c],axis=0).values
df5 = df5.set_index('date')

df6 = pd.DataFrame()
df6['date'] = pd.to_datetime([d6]*24, format='%Y-%m-%d')
df6['time'] = (pd.date_range(d6, periods=24, freq='H') - pd.Timedelta(hours=1)).time
df6 = df6.set_index('date')

df0 = pd.DataFrame()
z = df1.append(df2).append(df3).append(df4).append(df5).append(df6)
df0['datetime'] = pd.to_datetime(z.index.astype(str)+' '+z.time.astype(str),
                            format='%Y-%m-%d %H:%M:%S')
df0 = df0.set_index('datetime')

df0['mnth'] = pd.DatetimeIndex(df0.index).month
df0['dow'] = pd.DatetimeIndex(df0.index).dayofweek # Mon=0, ..., Sun=6
df0['hour'] = pd.DatetimeIndex(df0.index).hour

标签: pythonpandasdataframedatetime

解决方案


您可以创建/定义一个函数,通过计算条件为您提供索引:

def get_indexex():
    try:
        idxmx=df0.index==((df0['dow']==6).idxmax())
        idxmn=df0.index==((df0['dow']==6).idxmin())
        start_dst = df0.loc[(df0['mnth']==9) & idxmx]
        end_dst = df0.loc[(df0['mnth']==4) & idxmn]
        if not start_dst.index.tolist():
            return df0.loc[:end_dst.index[-1]].index
        elif not end_dst.index.tolist():
            return  df0.loc[start_dst.index[0]:].index
        else:
            return  df0.loc[start_dst.index[0]:end_dst.index[-1]].index
    except IndexError:
        start_dst=df0.loc[(df0['dow'].eq(6) & df0['mnth'].eq(9)) & df0['hour'].eq(2)]
        end_dst=df0.loc[df0['mnth'].eq(4) & df0['hour'].eq(3)]
        if not start_dst.index.tolist():
            return df0.loc[:end_dst.index[-1]].index
        elif not end_dst.index.tolist():
            return  df0.loc[start_dst.index[0]:].index
        else:
            return  df0.loc[start_dst.index[0]:end_dst.index[-1]].index

最后:

df0['dt']=df0.index
m=df0.index.isin(get_indexex())
df0.loc[m,'dt']=df0.loc[m,'dt']+pd.Timedelta('1H')
df0.index=df0.pop('dt')

一些事情的原因:

  • 您无法更改子集的索引,因此为此我们创建了'dt'列并将该值设置为等于index我们数据框的值

  • 我们制作了 idxmx 变量idxmax()和 idxmn 变量,idxmin()它们正在比较数据帧的值idxmax()idxmin()与数据帧的值并index为您提供一个布尔数组,您会收到错误,因为(df0.dow==6).idxmax() or (df0.dow==6).idxmin()给您一个单一的值而不是一系列布尔值

  • 我们正在定义一个名为的函数get_indexex(),它将为您提供满足条件的索引的索引,以在start_dst为空数据帧时处理这种情况

  • 在函数内部还有一件事要注意,如果 start_dst 和 end_dst 包含多个条目,我们将索引到 start_dst 的第 0 个索引和 end_dst 的最后一个索引

更新:

2020-04-05 23:00:00从函数中获取信息是因为您的条件满足因此 end_dst 和 start_dst 中的任何一个都会为您提供结果,所以如果您不想要,那么您可以从函数中删除这种情况,所以现在它变为:

def get_indexex():
    start_dst=df0.loc[(df0['dow'].eq(6) & df0['mnth'].eq(9)) & df0['hour'].eq(2)]
    end_dst=df0.loc[df0['mnth'].eq(4) & df0['hour'].eq(3)]
    if not start_dst.index.tolist():
        return df0.loc[:end_dst.index[-1]].index
    elif not end_dst.index.tolist():
        return  df0.loc[start_dst.index[0]:].index
    else:
        return  df0.loc[start_dst.index[0]:end_dst.index[-1]].index

最后:

df0['dt']=df0.index
m=df0.index.isin(get_indexex())
df0.loc[m,'dt']=df0.loc[m,'dt']+pd.Timedelta('1H')
df0.index=df0.pop('dt')

推荐阅读