如何在Pandas中计算排除周末/节假日的时间小时差?
解决Pandas中计算德国工作日小时差的问题
嘿,我来帮你搞定这个在Pandas里计算德国工作日小时差的需求!你之前遇到的TypeError,主要是因为np.busday_count的使用方式不对——它默认只处理纯日期类型(不带时分秒),而且参数顺序也搞反了,得是起始日期在前,结束日期在后才行。
咱们一步步来实现你的需求,先从你的示例开始,再扩展到更通用的场景,甚至包含德国法定节假日的情况。
第一步:准备数据并确保类型正确
首先得把你的start和end列转换成Pandas的datetime类型,不然没法处理时间相关的计算:
import pandas as pd import numpy as np # 你的示例数据 data = {'start': ['2019-01-01 08:00:00'], 'end': ['2019-01-08 08:00:00']} df = pd.DataFrame(data) # 转换为datetime类型 df[['start', 'end']] = df[['start', 'end']].apply(pd.to_datetime)
第二步:计算基础工作日小时差(不含德国法定假日)
德国的工作日是周一到周五,和np.busday_count默认的weekmask='1111100'(周一到周五为工作日)一致,所以咱们直接用这个规则:
- 先提取日期部分,计算两个日期之间的工作日天数:
# 提取纯日期,传入busday_count,注意顺序是start在前,end在后 bus_days = np.busday_count(df['start'].dt.date, df['end'].dt.date)
你的示例中,2019-01-01到2019-01-08之间有5个工作日(1号是周二,8号是周二,中间跳过了两个周末),所以bus_days的值是5。
- 再处理时间部分的差值:
如果你的start和end时间点相同(比如都是08:00),直接用bus_days * 24就能得到小时数。如果时间点不同,就得提取时分秒转换成小时数,再做调整:
# 把时间部分转换成小时数(包含分钟、秒的小数) start_hour = df['start'].dt.hour + df['start'].dt.minute/60 + df['start'].dt.second/3600 end_hour = df['end'].dt.hour + df['end'].dt.minute/60 + df['end'].dt.second/3600 # 计算总工作日小时数 df['Diff'] = bus_days * 24 + (end_hour - start_hour)
运行后你的示例会得到100.0,完全符合预期!
第三步:加入德国法定节假日的排除
如果需要排除德国的法定假日,咱们可以把这些假日整理成数组,传给bus_days_count的holidays参数。比如2019年的德国全国性法定假日:
# 2019年德国全国性法定假日(部分州可能有额外假日,可自行补充) german_holidays_2019 = [ '2019-01-01', '2019-04-19', '2019-04-22', '2019-05-01', '2019-05-30', '2019-06-09', '2019-10-03', '2019-12-25', '2019-12-26' ] # 转换为datetime64[D]类型,符合busday_count的要求 holidays = np.array(german_holidays_2019, dtype='datetime64[D]') # 计算包含假日排除的工作日天数 bus_days_with_holidays = np.busday_count( df['start'].dt.date, df['end'].dt.date, holidays=holidays ) # 重新计算小时差 df['Diff_with_holidays'] = bus_days_with_holidays * 24 + (end_hour - start_hour)
这样就能精准排除德国的法定假日啦!
为啥你之前的代码报错?
你之前写的np.busday_count(df['end'], df['start'])有两个问题:
- 参数顺序反了:
busday_count的第一个参数是起始日期,第二个是结束日期,反过来会得到负数或者错误结果; - 数据类型不对:
df['start']是带时分秒的datetime64[ns]类型,而busday_count需要的是纯日期类型(datetime64[D]),所以得用dt.date或者dt.floor('D')转换后再传入。
内容的提问来源于stack exchange,提问作者PV8




