如何用Pandas高效获取车辆时间线中的缺失年份?
需求:提取车辆的缺失年份用于填充
我有一个记录车辆年度时间线观测数据的Pandas DataFrame,数据结构如下:
单车辆示例数据
import pandas as pd import numpy as np df1 = pd.DataFrame({ "Vehicle Type": ["truck", "truck", "truck", "truck", "truck"], "Vehicle ID": ["XYZ", "XYZ", "XYZ", "XYZ", "XYZ"], "Year": [1, 2, 3, 4, 5], "Earliest Fact": [pd.NaT, pd.NaT, "2018-04-18", "2019-01-02", "2020-01-02"], "Latest Fact": [pd.NaT, pd.NaT, "2019-01-01", "2020-01-01", "2020-12-31"], "Fact History": [np.nan, np.nan, 11.5, 11.7, 5], "Days Worked": [np.nan, np.nan, 234, 256, 43], "Days Available": [np.nan, np.nan, 260, 272, 57] }) # 修正原代码笔误,应为df1而非df df1[["Earliest Fact", "Latest Fact"]] = df1[["Earliest Fact", "Latest Fact"]].apply(pd.to_datetime, errors="coerce")
该车辆的记录始于第3年至第5年(Fact History小于12表示不满12个月记录),第5年为仍在记录的当前年,第1、2年无记录。其他车辆也存在类似的年份记录缺失情况。
另一车辆示例数据
df2 = pd.DataFrame({ "Vehicle Type": ["van", "van", "van", "van", "van", "van", "van"], "Vehicle ID": ["ABC", "ABC", "ABC", "ABC", "ABC", "ABC", "ABC"], "Year": [1, 2, 3, 4, 5, 6, 7], "Earliest Fact": [pd.NaT, pd.NaT, "2018-04-18", "2019-01-02", "2020-01-02", "2021-01-01", "2022-01-01"], "Latest Fact": [pd.NaT, pd.NaT, "2019-01-01", "2020-01-01", "2020-12-31", "2021-12-31", "2023-01-01"], "Fact History": [np.nan, np.nan, 5, 11.7, 12, 12, 5.7], "Days Worked": [np.nan, np.nan, 100, 256, 273, 300, 94], "Days Available": [np.nan, np.nan, 130, 272, 290, 320, 141] })
该车辆首次记录年(第3年)仅为部分记录(5个月历史),第7年为当前年。
缺失年份规则
需要系统地获取每辆车的缺失年份,以便用自定义函数填充。缺失年份需满足:
- 首次记录年之前的无记录年份;
- 首次记录年为部分记录的情况;
但不能包含车辆的当前年(当前年必然为部分记录)。
原冗长解决方案
# concatenate dfs from above into one data-frame df = pd.concat([df1, df2], axis = "index") full = df[["Vehicle ID", "Year", "Fact History"]].loc[df["Fact History"].notnull()] reference = pd.merge( full.groupby("Vehicle ID")["Year"].min().reset_index().rename(columns={"Year": "Begins At"}), full.groupby("Vehicle ID")["Year"].max().reset_index().rename(columns={"Year": "Current Year"}), on="Vehicle ID" ) reference = pd.merge( reference, full.rename(columns={"Year": "Begins At", "Fact History": "History Begins"}), on=["Vehicle ID", "Begins At"], how="left" ) reference["Missing Years"] = reference.apply( lambda row: ", ".join(map(str, range(1, row["Begins At"] + 1))) if row["History Begins"] < 10 else np.nan, axis=1 )
简洁实现方法
通过分组聚合+向量化操作简化流程,避免多次merge和低效的apply:
import pandas as pd import numpy as np # 合并示例数据 df = pd.concat([df1, df2], axis="index") # 统一转换日期列 df[["Earliest Fact", "Latest Fact"]] = df[["Earliest Fact", "Latest Fact"]].apply(pd.to_datetime, errors="coerce") # 按车辆分组,一次性计算关键指标 vehicle_stats = df.dropna(subset=["Fact History"]).groupby("Vehicle ID").agg( begins_at=("Year", "min"), # 首次记录年份 current_year=("Year", "max"), # 当前记录年份 first_history=("Fact History", "first") # 首次记录的时长 ).reset_index() # 生成缺失年份:仅当首次记录为部分记录(时长<12)时,生成1到begins_at的年份 vehicle_stats["Missing Years"] = np.where( vehicle_stats["first_history"] < 12, vehicle_stats["begins_at"].apply(lambda x: ", ".join(map(str, range(1, x+1)))), np.nan )
代码说明
- 用
dropna(subset=["Fact History"])直接筛选有效记录行,无需额外创建中间变量; - 一次
groupby.agg完成所有关键指标计算,替代原方案的多次merge操作; - 用
np.where实现向量化条件判断,比逐行apply更高效,代码结构更清晰; - 严格匹配需求逻辑:仅当首次记录的
Fact History小于12(部分记录)时,生成1到首次记录年的所有年份作为缺失年份,自动排除当前年。
内容的提问来源于stack exchange,提问作者jmcgowan
相关产品推荐
相关产品推荐

