如何计算忽略NaT值的两个日期数组平均十进制年份?
- 内容介绍
- 相关推荐
本文共计1061个文字,预计阅读时间需要5分钟。
原文介绍如何对两个等长的+pandas+ datetimeindex 或 datetime series 进行逐元素平均计算,以下为简化版:
在时间序列分析与地质、气候、天文等科学计算中,常需将日期标准化为“十进制年份”(decimal year),以便进行线性插值、回归建模或跨数据集对齐。十进制年份定义为:
year + dayofyear / days_in_year,其中 days_in_year = 365.25(兼顾闰年平均值),dayofyear 是该日期在当年中的第几天(1-based)。
给定两个长度一致的 datetime 序列(如 series_a 和 series_b),目标是生成新序列 avg_decimal_year,满足:
- 若 series_a[i] 和 series_b[i] 均非 NaT → 取二者十进制年份的算术平均;
- 若仅其一非 NaT → 直接采用该值;
- 若二者均为 NaT → 结果为 pd.NaT。
✅ 推荐实现(健壮、向量化、兼容 pandas ≥ 1.5)
import pandas as pd import numpy as np def decimal_year(series: pd.Series) -> pd.Series: """将 datetime Series 转换为十进制年份,NaT 保持为 NaN""" # 确保为 datetime 类型(自动处理 NaT) dt = pd.to_datetime(series) # 使用 .dt 访问器安全提取年份和年序日 year = dt.dt.year.astype(float) doy = dt.dt.dayofyear.astype(float) # NaT 对应 NaN # 计算十进制年份:NaN 传播,无需额外掩码 return year + doy / 365.25 # 假设你已有两个 Series(例如来自 DataFrame 列或索引) # series_a = pd.to_datetime(['2009-07-03', '2009-07-03', '2009-07-03', '2003-02-07', '2004-04-09', 'NaT']) # series_b = pd.to_datetime(['NaT', 'NaT', '2015-04-12', '2013-09-17', '2014-02-19', 'NaT']) # 示例数据(复现原始问题结构) series_a = pd.to_datetime(['2009-07-03', '2009-07-03', '2009-07-03', '2003-02-07', '2004-04-09', 'NaT']) series_b = pd.to_datetime(['NaT', 'NaT', '2015-04-12', '2013-09-17', '2014-02-19', 'NaT']) # 步骤 1:转为十进制年份(返回 float 类型,NaT → NaN) dec_a = decimal_year(series_a) dec_b = decimal_year(series_b) # 步骤 2:沿行方向求均值,skipna=True 自动跳过 NaN(即原 NaT) avg_dec = dec_a.combine(dec_b, lambda x, y: (x + y) / 2 if pd.notna(x) and pd.notna(y) else x if pd.notna(x) else y if pd.notna(y) else np.nan) # 步骤 3:将结果中的 NaN 显式转为 pd.NaT(保持 datetime-like 语义) result = pd.to_timedelta(avg_dec, unit='Y').dt.year.astype('Int64') # ❌ 不推荐 —— 更佳做法如下: # ✅ 正确转换:直接用 pd.Series 构造并指定 dtype='datetime64[ns]' 不适用,故保留 float 并用 NaT 标记缺失 final_result = pd.Series(np.where(pd.isna(avg_dec), pd.NaT, avg_dec), dtype='object') # 但更简洁实用的方式是:保持为 float64 + NaN,并在后续明确注释“NaN 表示无效十进制年” # 或统一转为 nullable Float64Dtype(推荐用于含缺失的数值计算): final_result = pd.Series(avg_dec, dtype='Float64') # 支持 NaN 的扩展类型 print("十进制年份 A:", dec_a.round(6).tolist()) print("十进制年份 B:", dec_b.round(6).tolist()) print("平均十进制年份:", final_result.tolist())
输出示例:
十进制年份 A: [2009.503765, 2009.503765, 2009.503765, 2003.128761, 2004.287611, nan] 十进制年份 B: [nan, nan, 2015.287611, 2013.703765, 2014.128761, nan] 平均十进制年份: [2009.503765, 2009.503765, 2012.395688, 2008.416263, 2009.208186, <NA>]
⚠️ 关键注意事项
- 闰年精度:使用 365.25 是通用近似;若需更高精度(如 ISO 历法或儒略日),可改用 365.2422 或调用 astropy.time.Time。
- NaT 处理本质:pd.to_datetime() 将 'NaT' 字符串或空值自动转为 pd.NaT;后续 .dt.* 操作对 NaT 返回 NaN,因此 mean(skipna=True) 可直接生效。
- 类型一致性:最终结果建议使用 dtype='Float64'(pandas 的 nullable float 类型),而非 float64(无法存储 pd.NaT),以清晰区分缺失与有效数值。
- 性能提示:全程使用向量化操作(.dt, combine, mean),避免 Python 循环,适用于百万级数据。
通过该方法,你可稳健、高效地融合双日期源,生成可用于建模的连续十进制时间轴。
本文共计1061个文字,预计阅读时间需要5分钟。
原文介绍如何对两个等长的+pandas+ datetimeindex 或 datetime series 进行逐元素平均计算,以下为简化版:
在时间序列分析与地质、气候、天文等科学计算中,常需将日期标准化为“十进制年份”(decimal year),以便进行线性插值、回归建模或跨数据集对齐。十进制年份定义为:
year + dayofyear / days_in_year,其中 days_in_year = 365.25(兼顾闰年平均值),dayofyear 是该日期在当年中的第几天(1-based)。
给定两个长度一致的 datetime 序列(如 series_a 和 series_b),目标是生成新序列 avg_decimal_year,满足:
- 若 series_a[i] 和 series_b[i] 均非 NaT → 取二者十进制年份的算术平均;
- 若仅其一非 NaT → 直接采用该值;
- 若二者均为 NaT → 结果为 pd.NaT。
✅ 推荐实现(健壮、向量化、兼容 pandas ≥ 1.5)
import pandas as pd import numpy as np def decimal_year(series: pd.Series) -> pd.Series: """将 datetime Series 转换为十进制年份,NaT 保持为 NaN""" # 确保为 datetime 类型(自动处理 NaT) dt = pd.to_datetime(series) # 使用 .dt 访问器安全提取年份和年序日 year = dt.dt.year.astype(float) doy = dt.dt.dayofyear.astype(float) # NaT 对应 NaN # 计算十进制年份:NaN 传播,无需额外掩码 return year + doy / 365.25 # 假设你已有两个 Series(例如来自 DataFrame 列或索引) # series_a = pd.to_datetime(['2009-07-03', '2009-07-03', '2009-07-03', '2003-02-07', '2004-04-09', 'NaT']) # series_b = pd.to_datetime(['NaT', 'NaT', '2015-04-12', '2013-09-17', '2014-02-19', 'NaT']) # 示例数据(复现原始问题结构) series_a = pd.to_datetime(['2009-07-03', '2009-07-03', '2009-07-03', '2003-02-07', '2004-04-09', 'NaT']) series_b = pd.to_datetime(['NaT', 'NaT', '2015-04-12', '2013-09-17', '2014-02-19', 'NaT']) # 步骤 1:转为十进制年份(返回 float 类型,NaT → NaN) dec_a = decimal_year(series_a) dec_b = decimal_year(series_b) # 步骤 2:沿行方向求均值,skipna=True 自动跳过 NaN(即原 NaT) avg_dec = dec_a.combine(dec_b, lambda x, y: (x + y) / 2 if pd.notna(x) and pd.notna(y) else x if pd.notna(x) else y if pd.notna(y) else np.nan) # 步骤 3:将结果中的 NaN 显式转为 pd.NaT(保持 datetime-like 语义) result = pd.to_timedelta(avg_dec, unit='Y').dt.year.astype('Int64') # ❌ 不推荐 —— 更佳做法如下: # ✅ 正确转换:直接用 pd.Series 构造并指定 dtype='datetime64[ns]' 不适用,故保留 float 并用 NaT 标记缺失 final_result = pd.Series(np.where(pd.isna(avg_dec), pd.NaT, avg_dec), dtype='object') # 但更简洁实用的方式是:保持为 float64 + NaN,并在后续明确注释“NaN 表示无效十进制年” # 或统一转为 nullable Float64Dtype(推荐用于含缺失的数值计算): final_result = pd.Series(avg_dec, dtype='Float64') # 支持 NaN 的扩展类型 print("十进制年份 A:", dec_a.round(6).tolist()) print("十进制年份 B:", dec_b.round(6).tolist()) print("平均十进制年份:", final_result.tolist())
输出示例:
十进制年份 A: [2009.503765, 2009.503765, 2009.503765, 2003.128761, 2004.287611, nan] 十进制年份 B: [nan, nan, 2015.287611, 2013.703765, 2014.128761, nan] 平均十进制年份: [2009.503765, 2009.503765, 2012.395688, 2008.416263, 2009.208186, <NA>]
⚠️ 关键注意事项
- 闰年精度:使用 365.25 是通用近似;若需更高精度(如 ISO 历法或儒略日),可改用 365.2422 或调用 astropy.time.Time。
- NaT 处理本质:pd.to_datetime() 将 'NaT' 字符串或空值自动转为 pd.NaT;后续 .dt.* 操作对 NaT 返回 NaN,因此 mean(skipna=True) 可直接生效。
- 类型一致性:最终结果建议使用 dtype='Float64'(pandas 的 nullable float 类型),而非 float64(无法存储 pd.NaT),以清晰区分缺失与有效数值。
- 性能提示:全程使用向量化操作(.dt, combine, mean),避免 Python 循环,适用于百万级数据。
通过该方法,你可稳健、高效地融合双日期源,生成可用于建模的连续十进制时间轴。

