Pandas:计算指定时间段内设备开机时长的时间差求和
设备开机天数统计解决方案
原始数据
给定记录设备开关机日期的DataFrame:
import pandas as pd data = { 'Date': ['2019-09-25', '2019-10-10', '2019-12-01', '2020-01-11', '2020-01-23', '2020-03-28', '2020-08-27', '2021-01-10', '2021-02-17', '2021-06-30'], 'Action': ['Turned On', 'Turned Off', 'Turned On', 'Turned Off', 'Turned On', 'Turned Off', 'Turned On', 'Turned Off', 'Turned On', 'Turned Off'] } df = pd.DataFrame(data) df['Date'] = pd.to_datetime(df['Date'])
需求说明
需要计算设备在任意指定时间段(如季度、首次激活后每12个月区间)内的开机天数,核心难点是处理跨统计时段的开关机记录:
- 开机时间早于统计时段起始,关机时间在时段内
- 开机时间在时段内,关机时间晚于统计时段结束
- 开机、关机时间完全覆盖统计时段
例如2019年第四季度(2019-10-01至2019-12-31)的开机天数计算:
- 2019-10-01至2019-10-10(原开机记录为2019-09-25,需排除9月部分)
- 2019-12-01至2019-12-31(原关机记录为2020-01-11,需排除2020年1月部分)
- 合计40天(注:天数计算是否包含首尾可根据需求调整)
现有方法的不足
直接计算连续开关机日期的差值求和,无法处理跨时段的区间:
- 会遗漏开机在时段前但关机在时段内的有效天数
- 会错误统计开机在时段内但关机在时段外的多余天数
解决方案
核心思路是计算每个开关机区间与统计时段的重叠部分,求和所有有效重叠天数。
步骤1:生成开关机区间对
将原始数据中的Turned On和Turned Off记录配对,形成完整的开机-关机区间:
# 提取所有开机、关机日期 on_dates = df[df['Action'] == 'Turned On']['Date'].reset_index(drop=True) off_dates = df[df['Action'] == 'Turned Off']['Date'].reset_index(drop=True) # 生成区间DataFrame interval_df = pd.DataFrame({ 'start': on_dates, 'end': off_dates })
步骤2:定义统计天数计算函数
编写函数处理任意时段的重叠天数计算,支持自定义是否包含首尾日期:
def calculate_on_days(intervals, period_start, period_end, include_end=True): # 转换为datetime类型 period_start = pd.to_datetime(period_start) period_end = pd.to_datetime(period_end) # 计算每个区间与统计时段的重叠起止点 intervals['overlap_start'] = intervals['start'].apply(lambda x: max(x, period_start)) intervals['overlap_end'] = intervals['end'].apply(lambda x: min(x, period_end)) # 计算有效重叠天数 def get_days(row): if row['overlap_start'] > row['overlap_end']: return 0 days_diff = (row['overlap_end'] - row['overlap_start']).days return days_diff + 1 if include_end else days_diff intervals['overlap_days'] = intervals.apply(get_days, axis=1) # 返回总开机天数 return intervals['overlap_days'].sum()
步骤3:测试示例
2019年第四季度统计
q4_2019_start = '2019-10-01' q4_2019_end = '2019-12-31' total_q4 = calculate_on_days(interval_df, q4_2019_start, q4_2019_end) print(f"2019年第四季度开机天数:{total_q4}") # 输出:40(与示例一致,包含首尾日期)
2020年第一季度统计
q1_2020_start = '2020-01-01' q1_2020_end = '2020-03-31' total_q1 = calculate_on_days(interval_df, q1_2020_start, q1_2020_end) print(f"2020年第一季度开机天数:{total_q1}")
自定义12月周期统计(首次激活后)
first_activation = df['Date'].min() # 生成3个连续12月周期 periods = [ (first_activation, first_activation + pd.DateOffset(years=1)), (first_activation + pd.DateOffset(years=1), first_activation + pd.DateOffset(years=2)), (first_activation + pd.DateOffset(years=2), first_activation + pd.DateOffset(years=3)) ] for idx, (start, end) in enumerate(periods, 1): days = calculate_on_days(interval_df, start, end) print(f"第{idx}个12月周期({start.date()} ~ {end.date()})开机天数:{days}")
方法优势
- 支持任意时段的统计,包括季度、年度、自定义区间
- 自动处理跨时段的开关机记录,精准计算重叠天数
- 可通过
include_end参数灵活调整天数计算规则(是否包含关机当日)
内容的提问来源于stack exchange,提问作者steliosbl
相关产品推荐
相关产品推荐

