如何对比两个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)
关键说明
- 必须先将所有日期列转换为
datetime类型,否则字符串或其他类型的日期无法正确比较大小。 - 原代码的逻辑错误:内层循环第一次迭代就执行
break,导致仅能匹配df2的第一行数据,其余行的停机信息完全被忽略。 - 两种方法均避免了低效的双重循环,方法2在数据量较大时性能优势更明显。
内容的提问来源于stack exchange,提问作者Akash Makkar
相关产品推荐
相关产品推荐

