
pandas Nullable Integer 数据类型完整指南用 Int64 与 pandas.NA 告别整数变浮点的精度陷阱【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas本指南基于 pandas 官方用户手册integer_na.rst编写系统讲解 pandas 的可空整数nullable integer扩展类型为什么整数列一旦含缺失值就会被迫变成浮点数、IntegerArray如何在保持整数语义的同时使用pandas.NA表示缺失以及从构造、运算到归约的全套实战用法。读完本文你将掌握pd.array([...], dtypeInt64)的正确打开方式理解pandas.array与Series在 dtype 推断上的差异并能安全地在含缺失值的整数列上执行算术、比较、分组与求和操作而不丢失精度。为什么需要可空整数NaN 把整数变成了浮点在 pandas 的 缺失数据处理 章节中可以看到pandas 主要使用NaN表示缺失值。由于NaN在 NumPy 中是浮点数任何含缺失值的整数数组都会被强制提升为浮点类型。对大多数场景这无伤大雅但如果你的整数列是标识符identifier——例如用户 ID、订单号、外键——情况就变得棘手语义混乱将 ID 从整数转成浮点类型信息被破坏序列化、对接数据库或下游校验时容易出错精度丢失部分大整数无法被浮点数精确表示IEEE 754 双精度浮点只有 52 位尾数超过2^53的整数会丢失精度例如9007199254740993这样的 ID 在 float64 中会被舍入。pandas 给出的答案是可空整数扩展类型arrays.IntegerArray使用pandas.NA作为缺失值而不是NaN让缺失值不再污染整数 dtype。构造 IntegerArraypd.array 大写的 Int64IntegerArray是 pandas 内部实现的一种扩展类型可以通过pd.array配合整数 dtype 构造。注意官方强烈推荐显式指定 dtype避免依赖推断规则。import pandas as pd import numpy as np # 方式一显式 dtype 对象 arr pd.array([1, 2, None], dtypepd.Int64Dtype()) arr # IntegerArray # [1, 2, NA] # Length: 3, dtype: Int64字符串别名Int64大写 I与 NumPy 的 int64 不同Int64注意大写的I是字符串别名用于和 NumPy 的int64dtype 区分pd.array([1, 2, np.nan], dtypeInt64) # IntegerArray # [1, 2, NA] # Length: 3, dtype: Int64所有 NA 类值统一归一化为 pandas.NA构造时各种缺失值记号np.nan、None、pd.NA都会被统一替换为pandas.NApd.array([1, 2, np.nan, None, pd.NA], dtypeInt64) # IntegerArray # [1, 2, NA, NA, NA] # Length: 5, dtype: Int64存入 Series / DataFrame构造好的数组可以像普通 NumPy 数组一样存入Seriespd.Series(arr) # 0 1 # 1 2 # 2 NA # dtype: Int64也可以直接把列表对象连同 dtype 一起传给Series构造器pd.Series([1, 2, None], dtypeInt64)从源码看pd.array([1, None])会走_coerce_to_data_and_mask的推断路径numeric.py默认采用np.int64作为底层存储 dtype对应IntegerDtype._default_np_dtype见 integer.py。⚠️ 关键陷阱pandas.array 与 Series 的 dtype 推断规则不同官方文档明确警告当前pandas.array与pandas.Series使用不同的 dtype 推断规则# pandas.array 会推断出可空整数 dtype pd.array([1, None]) # IntegerArray # [1, NA] # Length: 2, dtype: Int64 pd.array([1, 2]) # IntegerArray # [1, 2] # Length: 2, dtype: Int64# 出于向后兼容Series 将其推断为普通整数或浮点 dtype pd.Series([1, None]) # 0 1.0 # 1 NaN # dtype: float64 pd.Series([1, 2]) # 0 1 # 1 2 # dtype: int64看到差异了吗pd.Series([1, None])得到的是float64含有NaN的浮点列而不是可空整数。为避免混淆官方建议始终显式提供 dtypepd.array([1, None], dtypeInt64) pd.Series([1, None], dtypeInt64)文档同时说明未来可能会为Series提供推断可空整数 dtype 的选项。预占位列的最佳实践用 pd.Series(pd.NA, dtypeInt64) 而非直接赋 pd.NA如果先创建一个全NA的新列、之后再用真实数据填充例如df[new_col] pd.NA该列的 dtype 会被设为object后续性能明显劣于合适的类型。更优做法是df pd.DataFrame() df[objects] pd.Series(pd.NA, dtypeInt64) df.dtypes # objects Int64 # dtype: object或其他支持NA的 dtype如Float64、string。注意直接df[new_col] pd.NA会得到objectdtypedf pd.DataFrame() df[objects] pd.NA df.dtypes # objects object # dtype: object底层实现两个 NumPy 数组data mask要理解可空整数的行为值得看一眼它的内部表示。IntegerArray继承自NumericArraynumeric.py后者又继承自BaseMaskedArraymasked.py。其 docstring 明确描述了内部结构integer.pydata一个 dtype 合适的 NumPy 整数数组存放实际数值mask一个布尔数组标记缺失位置True表示缺失。也就是说缺失信息与数值本身分离存储数值部分始终保持整数 dtype因此无需像传统方案那样把整数提升成浮点。这正好解释了为什么IntegerArray能鱼与熊掌兼得既有原生的整数表示又能表达缺失。IntegerArray的构造参数为参数类型说明valuesnumpy.ndarray1 维整数 dtype 数组masknumpy.ndarray1 维布尔 dtype 数组标记缺失位置copybool, default False是否复制values与maskIntegerDtype在底层存储上使用_internal_fill_value 1填充掩码位置避免向上转型并以np.int64作为默认存储 dtype。构造时若掩码存在values[mask]处会被填充该值见 numeric.py。可用的 dtype 全家桶从 integer.py 可以看到IntegerDtype派生出一系列注册好的具体 dtype覆盖有符号与无符号整数有符号Int8Dtype、Int16Dtype、Int32Dtype、Int64Dtype对应Int8~Int64无符号UInt8Dtype、UInt16Dtype、UInt32Dtype、UInt64Dtype对应UInt8~UInt64。它们之间的映射定义在模块末尾的NUMPY_INT_TO_DTYPE字典中integer.py例如np.dtype(np.int8)对应Int8Dtype()。构造示例pd.array([1, None, 3], dtypepd.Int32Dtype()) pd.array([1, None, 3], dtypeUInt16)运算行为向 NumPy 语义看齐缺失值自动传播涉及可空整数数组的运算与 NumPy 数组行为类似缺失值会传播需要时数据会强制转换为其他 dtype。s pd.Series([1, 2, None], dtypeInt64) # 算术缺失值传播 s 1 # 0 2 # 1 3 # 2 NA # dtype: Int64 # 比较缺失值传播结果中缺失仍为 NA s 1 # 0 True # 1 False # 2 NA # dtype: boolean # 切片操作 s.iloc[1:3] # 1 2 # 2 NA # dtype: Int64 # 与其他 dtype 运算自动对齐类型结果按需提升 s s.iloc[1:3].astype(Int8) # 0 NA # 1 4 # 2 NA # dtype: Int64 # 需要时强制转换加上浮点后结果变为可空浮点 s 0.01 # 0 1.01 # 1 2.01 # 2 NA # dtype: Float64注意s s.iloc[1:3]的结果在索引 0 处为NA这展示了可空整数在对齐运算类似 join 的索引对齐时缺失值的传播语义。在 DataFrame 中协同工作这些 dtype 可以作为DataFrame的列类型参与运算df pd.DataFrame({A: s, B: [1, 1, 3], C: list(aab)}) df # A B C # 0 1 1 a # 1 2 1 a # 2 NA 3 b df.dtypes # A Int64 # B int64 # C object # dtype: objectdf中混合了可空整数列A、普通整数列B和对象列C各列保持各自的 dtype。合并、重塑与类型转换# concat 合并后各列 dtype 保持 pd.concat([df[[A]], df[[B, C]]], axis1).dtypes # A Int64 # B int64 # C object # dtype: object # 可空整数可转回普通浮点 df[A].astype(float) # 0 1.0 # 1 2.0 # 2 NaN # dtype: float64astype(float)时NA会被转换为NaN这正是有缺失的整数被迫变浮点的经典场景——当你确实需要浮点结果时可以显式完成这一转换。归约与 groupby 操作sum等归约操作以及groupby聚合同样开箱即用df.sum(numeric_onlyTrue) # A 3.0 # B 5.0 # dtype: float64 df.sum() # A 3 # B 5 # C aab # dtype: object df.groupby(B).A.sum() # B # 1 3 # 3 0 # Name: A, dtype: Int64注意三处细节df.sum(numeric_onlyTrue)的结果是float64A列的和从Int64转为浮点全列df.sum()时C列按字符串拼接df.groupby(B).A.sum()的返回列保持Int64dtype——分组求和后的结果仍然是可空整数缺失值语义得以保留。标量缺失值pandas.NAIntegerArray使用pandas.NA作为标量缺失值。对单个缺失元素做切片会返回pandas.NAa pd.array([1, None], dtypeInt64) a[1] # NA这一点与np.nan有本质区别pd.NA是 pandas 统一的缺失值标记属于pandas自己的 NA 语义体系pandas.NA实例其参与运算时遵循缺失传播 结果类型提升的规则不会像np.nan那样把整数 dtype 直接拉成浮点。小结与推荐用法场景推荐写法创建可空整数数组pd.array([1, 2, None], dtypeInt64)创建含缺失的整数 Seriespd.Series([1, 2, None], dtypeInt64)给 DataFrame 预填 NA 列df[col] pd.Series(pd.NA, dtypeInt64)无符号变体UInt8~UInt64转回浮点s.astype(float)NA变NaN核心结论凡是对整数 ID 不得变浮点有硬性要求的场景标识符列、精确大整数、下游强类型约束都应使用可空整数 dtype对普通分析场景float64NaN仍是轻量默认选择。构造时始终显式传入 dtype是规避pandas.array与Series推断差异的最稳妥做法。想深入了解扩展类型的通用机制可继续阅读 扩展类型开发指南 与 缺失数据处理想看源码级实现可直接研读 pandas/core/arrays/integer.py 及其基类 pandas/core/arrays/numeric.py、pandas/core/arrays/masked.py。【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考