必须先用pd.to_datetime()将时间列转为datetime64类型才能相减,否则报TypeError;结果为timedelta64[ns],需用.dt.days等 accessor 提取数值,注意空值和时区处理。

直接用 pd.to_datetime() 转再相减,别用字符串硬算
时间列是字符串时,df['end'] - df['start'] 会报 TypeError: unsupported operand type(s) for -: 'str' and 'str'。Pandas 不会自动识别时间格式,必须显式转为 datetime64 类型才能做算术运算。
- 先确保两列都用
pd.to_datetime()转换,加errors='coerce'把非法值变NaT,避免中断 - 相减结果是
timedelta64[ns],不是数字,不能直接参与数值计算(比如求均值、画图) - 如果原始数据含时区(如
'2023-01-01 10:00:00+08:00'),转换后保留时区信息,跨时区相减可能触发警告,建议统一用dt.tz_localize(None)剥离(除非业务真需要时区感知)
timedelta64 提取天数:优先用 .dt.days,别用 .days
得到 timedelta64 列后,想取整数天数,常见错误是写成 diff.days —— 这会报 AttributeError,因为 Series 没有 days 属性,只有其内部每个 Timedelta 对象才有。
- 正确写法是
diff.dt.days,.dt是 Pandas 的 datetime accessor,专用于向量化提取属性 -
.dt.days返回的是“完整天数”,即忽略小时/分钟部分的整数(pd.Timedelta('36h').days == 1) - 如果要总小时数,用
.dt.total_seconds() / 3600;要精确到秒的总秒数,直接用.dt.total_seconds()
空值和异常值会让 .dt.days 变成 NaN,得提前处理
NaT(Not a Time)在相减后仍存在,调用 .dt.days 会返回 NaN,后续做 groupby().mean() 或填入数据库时容易出错。
- 检查空值:用
df['diff'].isna().sum()看有多少行没算出来 - 填充默认值:比如
df['diff_days'] = df['diff'].dt.days.fillna(-1),-1 表示无效差值(比用 0 更不易误判) - 过滤掉异常大的值:
df = df[df['diff_days'].between(0, 3650)](排除动辄上千年的时间差,通常是脏数据)
性能敏感场景下,避免重复调用 .dt accessor
如果既要天数、又要总秒数、还要是否超 7 天的布尔标记,别写三遍 diff.dt.xxx —— .dt 每次调用都会重新解析底层 timedelta64 数据,有开销。
立即学习“Python免费学习笔记(深入)”;
- 一次性提取多个字段:用
pd.to_timedelta(df['diff']).view('int64') // (24*3600*10**9)手动算天数(仅限纳秒精度且无时区场景) - 更稳妥的做法是先赋值给中间变量:
td = df['diff'].dt,再写td.days、td.total_seconds() - 对百万行以上数据,这种写法能省 10%–20% 时间,但可读性略降,按需权衡
实际用的时候,最常漏掉的是第一行的类型转换——很多人以为列名叫 'start_time' 就一定是时间类型。真正跑起来报错才发现是字符串,再回头补 to_datetime,浪费调试时间。


















