You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对比两个Pandas DataFrame日期列并生成标记列

问题描述

我有两个DataFrame:

  • 第一个DataFrame(df1)包含2021年1月1日至12月31日的逐日数据,每条数据对应一个设备(unit列)和日期(Date列)。
  • 第二个DataFrame(df2)记录了各设备的停机时段,每条数据包含设备(UNIT列)、停机起始日期(STARTDate列)和停机结束日期(EndDate列)。

需求:基于df2中的停机时段,在df1中新增一列标记,标记该设备当日是否处于停机状态(1表示停机,0表示正常运行)。

我尝试了以下代码但未成功:

dum = []
for i in range(0,df1.shape[0]) :
    tagname = df1['unit'][i]
    day     = df1['Date'][i] 
    
    for j in range(0, df2.shape[0]) :
        if ((tagname == df2['UNIT'][j]) and (df2['STARTDate'][j] <= Date <= df2['EndDate'][j])) :
            dum.append(1)
            break
        else :
            dum.append(0)
            break
解决方案

你的代码核心问题是内层循环仅检查df2的第一行就直接break,不管是否匹配到对应设备的停机时段,导致结果完全错误;同时双重循环的效率极低,数据量大时会严重拖慢运行速度。推荐以下两种高效解决方案:

方法1:合并DataFrame后做条件判断

先按设备名合并两个DataFrame,再判断日期是否落在停机区间内,最后还原原df1的结构:

import pandas as pd

# 先确保所有日期列是datetime类型,否则无法正确比较
df1['Date'] = pd.to_datetime(df1['Date'])
df2['STARTDate'] = pd.to_datetime(df2['STARTDate'])
df2['EndDate'] = pd.to_datetime(df2['EndDate'])

# 按设备名左合并,保留df1的所有行
merged_df = df1.merge(df2, left_on='unit', right_on='UNIT', how='left')

# 生成停机标记:日期在停机区间内则标记1,否则0
merged_df['停机标记'] = merged_df.apply(
    lambda row: 1 if pd.notna(row['STARTDate']) and (row['STARTDate'] <= row['Date'] <= row['EndDate']) else 0,
    axis=1
)

# 去重并删除合并带来的冗余列,还原df1结构
df1 = merged_df.drop_duplicates(subset=['unit', 'Date']).drop(columns=['UNIT', 'STARTDate', 'EndDate'])

方法2:使用区间索引做快速匹配(大数据量首选)

利用pd.IntervalIndex为每个设备的停机时段创建索引,再快速匹配日期是否在区间内:

import pandas as pd

# 转换日期列类型
df1['Date'] = pd.to_datetime(df1['Date'])
df2['STARTDate'] = pd.to_datetime(df2['STARTDate'])
df2['EndDate'] = pd.to_datetime(df2['EndDate'])

# 为每个设备创建对应的停机区间索引字典
unit_intervals = {}
for unit in df2['UNIT'].unique():
    unit_shutdown = df2[df2['UNIT'] == unit]
    intervals = pd.IntervalIndex.from_arrays(unit_shutdown['STARTDate'], unit_shutdown['EndDate'], closed='both')
    unit_intervals[unit] = intervals

# 定义函数判断单条数据是否处于停机状态
def is_shutdown(row):
    intervals = unit_intervals.get(row['unit'], pd.IntervalIndex([]))
    return 1 if any(row['Date'] in interval for interval in intervals) else 0

# 生成标记列
df1['停机标记'] = df1.apply(is_shutdown, axis=1)

关键说明

  1. 必须先将所有日期列转换为datetime类型,否则字符串或其他类型的日期无法正确比较大小。
  2. 原代码的逻辑错误:内层循环第一次迭代就执行break,导致仅能匹配df2的第一行数据,其余行的停机信息完全被忽略。
  3. 两种方法均避免了低效的双重循环,方法2在数据量较大时性能优势更明显。

内容的提问来源于stack exchange,提问作者Akash Makkar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 22:09:23