在 Pandas 日期时间列中标记夏令时 (DST) 小时

问题描述 投票:0回答:7

我创建了一个每小时日期数据框,现在我想创建一个列来标记每行(小时)是否处于夏令时。例如,在夏季,该标志应 == 1,而在冬季,该标志应 == 0。

# Localized dates dataframe
dates = pd.DataFrame(data=pd.date_range('2018-1-1', '2019-1-1', freq='h', tz='America/Denver'), columns=['date_time'])

# My failed attempt to create the flag column
dates['dst_flag'] = np.where(dates['date_time'].dt.daylight_saving_time == True, 1, 0)
python python-3.x pandas pytz
7个回答
4
投票

评论中有一个很好的链接,至少可以让您手动执行此操作。 AFAIK,没有矢量化的方法可以做到这一点。

import pandas as pd
import numpy as np
from pytz import timezone

# Generate data (as opposed to index)                                                                                                                                                                                  
date_range = pd.to_datetime(pd.date_range('1/1/2018', '1/1/2019', freq='h', tz='America/Denver'))
date_range = [date for date in date_range]

# Localized dates dataframe                                                                                                                                                           
df = pd.DataFrame(data=date_range, columns=['date_time'])

# Map transition times to year for some efficiency gain                                                                                                                                                     
tz = timezone('America/Denver')
transition_times = tz._utc_transition_times[1:]
transition_times = [t.astimezone(tz) for t in transition_times]
transition_times_by_year = {}
for start_time, stop_time in zip(transition_times[::2], transition_times[1::2]):
    year = start_time.year
    transition_times_by_year[year] = [start_time, stop_time]

# If the date is in DST, mark true, else false                                                                                                                                                              
def mark_dst(dates):
    for date in dates:
        start_dst, stop_dst = transition_times_by_year[date.year]
        yield start_dst <= date <= stop_dst
df['dst_flag'] = [dst_flag for dst_flag in mark_dst(df['date_time'])]

# Do a quick sanity check to make sure we did this correctly for year 2018                                                                                                                                  
dst_start = df[df['dst_flag'] == True]['date_time'][0] # First dst time 2018
dst_end = df[df['dst_flag'] == True]['date_time'][-1] # Last dst time 2018
print(dst_start)
print(dst_end)

此输出:

2018-03-11 07:00:00-06:00
2018-11-04 06:00:00-07:00

这“可能”是正确的。我没有手动进行 UTC 转换或任何检查时间是否与给定时区完全正确的操作。您至少可以通过快速谷歌搜索来验证日期是否正确。 一些陷阱:

  1. pd.date_range

    生成一个

    index
    ,而不是数据。我稍微更改了您的原始代码,使其成为数据而不是索引。我假设您已经有了数据。

  2. tz._utc_transition_times

    的结构有些愚蠢。这是开始/停止 utc DST 转换时间,但早期日期中有一些愚蠢的东西。不过从1965年开始应该就不错了。如果您的日期早于该日期,请将

    tz._utc_transition_times[1:]
    更改为
    tz._utc_transition_times
    。请注意,并非 1965 年之前的所有年份都存在。
    
    

  3. tz._utc_transition_times

    是“Python 私有”。它可能会在没有警告或通知的情况下进行更改,并且可能适用于未来或过去的

    pytz
    版本,也可能不适用于。我正在使用
    pytz
    版本 2017.3。我建议您运行此代码以确保输出匹配,如果不匹配,请确保使用版本 2017.3。
    
    

  4. HTH,祝你的研究/回归问题好运!


3
投票

其背后的基本思想是找出您所在时区的当前时间与 UTC 时间之间的差异。在冬季,时差将比 UTC 晚一小时。无论差异是什么,请添加获得标志 1 或 0 所需的内容。

在丹佛,夏季月份为 UTC-6,冬季月份为 UTC-7。因此,如果您采用丹佛的 tz 感知时间与 UTC 时间之间的差值,然后加 7,您将获得夏季月份的值为 1,冬季月份的值为 0。

import pandas as pd start = pd.to_datetime('2020-10-30') end = pd.to_datetime('2020-11-02') dates = pd.date_range(start=start, end=end, freq='h', tz='America/Denver') df1 = pd.DataFrame({'dst_flag': 1, 'date1': dates.tz_localize(None)}, index=dates) # add extra day on each end so that there are no nan's after the join dates = pd.to_datetime(pd.date_range(start=start - pd.to_timedelta(1, 'd'), end=end + pd.to_timedelta(1, 'd'), freq='h'), utc=True) df2 = pd.DataFrame({'date2': dates.tz_localize(None)}, index=dates) out = df1.join(df2) out['dst_flag'] = (out['date1'] - out['date2']) / pd.to_timedelta(1, unit='h') + 7 out.drop(columns=['date1', 'date2'], inplace=True)



1
投票

import pandas as pd import pytz # Create dates table and flag Daylight Saving Time dates dates = pd.DataFrame(data=pd.date_range('2018-1-1', '2018-12-31-23', freq='h'), columns=['date_time']) # Create a list of start and end dates for DST in each year, in UTC time dst_changes_utc = pytz.timezone('America/Denver')._utc_transition_times[1:] # Convert to local times from UTC times and then remove timezone information dst_changes = [pd.Timestamp(i).tz_localize('UTC').tz_convert('America/Denver').tz_localize(None) for i in dst_changes_utc] flag_list = [] for index, row in dates['date_time'].iteritems(): # Isolate the start and end dates for DST in each year dst_dates_in_year = [date for date in dst_changes if date.year == row.year] spring = dst_dates_in_year[0] fall = dst_dates_in_year[1] if (row >= spring) & (row < fall): flag = 1 else: flag = 0 flag_list.append(flag) print(flag_list) dates['dst_flag'] = flag_list del(flag_list)



0
投票

# Localized dates dataframe df = pd.DataFrame(data=pd.date_range('2018-1-1', '2019-1-1', freq='h', tz='America/Denver'), columns=['date_time']) df['utc_offset'] = df['date_time'].dt.strftime('%z').str[0:3].astype(float) df['utc_offset_shifted'] = df['utc_offset'].shift(-1) df['dst'] = df['utc_offset'] - df['utc_offset_shifted'] df_dst = df[(df['dst'] != 0) & (df['dst'])] df_dst = df_dst.drop(['utc_offset', 'utc_offset_shifted'], axis=1).reset_index(drop=True) print(df_dst)

此输出:

date_time dst 0 2018-03-11 01:00:00-07:00 -1.0 1 2018-11-04 01:00:00-06:00 1.0



0
投票

dates['dst_flag'] = dates['date_time'].apply(lambda x: x.tzname() == 'CEST')

这会将 
CET

中的所有时间标记为 False,将 CEST 中的所有时间标记为 True。我不确定我是否想在一个巨大的柱子上这样做。


0
投票
datetime

转换为 python

to_pydatetime()
,然后
datetime.dst()
给出 dst 偏移量的
timedelta
#time_delta should normally be 0.0 or 3600.0 seconds.
time_delta = [x.dst().total_seconds() for x in dates['date_time'].dt.to_pydatetime()]
dates['dst_flag'] = np.where(np.array(time_delta) < 0.5, 0, 1)



0
投票

这一行应该可以让您一次性完成所有操作,而无需进行不方便的转换等:

dates = pd.DataFrame(data=pd.date_range('2018-1-1', '2019-1-1', freq='h', tz='America/Denver'), columns=['date_time']) dates["dst_flag"]=dates.date_time.map(lambda date: int(date.dst() > pd.Timedelta(0)))

	
© www.soinside.com 2019 - 2024. All rights reserved.