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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 01:27:33