NumPy 或 Pandas:将数组类型保持为整数,同时具有 NaN 值
- 2024-12-12 08:41:00
- admin 原创
- 148
问题描述:
是否有一种首选方法可以使数组的数据类型numpy
固定为int
(或int64
或其他),同时仍将其中的元素列为numpy.NaN
?
具体来说,我正在将内部数据结构转换为 Pandas DataFrame。在我们的结构中,我们有整数类型的列,这些列仍然有 NaN(但列的 dtype 是 int)。如果我们将其设为 DataFrame,它似乎会将所有内容重新转换为浮点数,但我们确实希望如此int
。
有什么想法吗?
尝试过的事情:
我尝试使用from_records()
pandas.DataFrame 下的函数,coerce_float=False
但这没有帮助。我还尝试使用 NumPy 掩码数组,使用 NaN fill_value,这也不起作用。所有这些都导致列数据类型变成浮点数。
解决方案 1:
NaN
无法存储在整数数组中。这是目前 pandas 的一个已知限制;我一直在等待 NumPy 中 NA 值的进展(类似于 R 中的 NA),但看起来 NumPy 至少需要 6 个月到一年的时间才能获得这些功能:
http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na
(此功能从 pandas 0.24 版开始添加,但请注意,它需要使用扩展 dtype Int64(大写),而不是默认 dtype int64(小写):
https: //pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support
)
解决方案 2:
从0.24 版开始,pandas 中已经添加了此功能。
此时,需要使用扩展 dtype 'Int64'
(大写),而不是默认 dtype 'int64'
(小写)。
解决方案 3:
如果您尝试将浮点 (1.143) 向量转换为整数 (1),并且该向量有 NA,则将其转换为新的“Int64”dtype 会出错。为了解决这个问题,您必须对数字进行四舍五入,然后执行“.astype('Int64')”
s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0 1
1 2
2 NaN
dtype: Int64
我的用例是,我有一个浮点数系列,我想将其四舍五入为 int,但是当执行 .round() 时仍然有小数,因此需要转换为 int 以删除小数。
解决方案 4:
如果性能不是主要问题,您可以存储字符串。
df.col = df.col.dropna().apply(lambda x: str(int(x)) )
然后,您可以将 和 混合NaN
,只要您愿意。如果您确实想要整数,则根据您的应用程序,您可以使用-1
、 或0
、 或1234567890
或其他专用值来表示NaN
。
您还可以临时复制列:一个与您已有的一样,使用浮点数;另一个是实验性的,使用整数或字符串。然后asserts
在每个合理的位置插入,检查两者是否同步。经过足够的测试后,您可以放弃浮点数。
解决方案 5:
这不是所有情况的解决方案,但我的(基因组坐标)我已使用 0 作为 NaN
a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)
这至少允许使用正确的“本机”列类型,减法、比较等操作可以按预期工作
解决方案 6:
Pandas v0.24+
支持NaN
整数系列的功能将在 v0.24 及以上版本中提供。v0.24 “新功能”部分提供了相关信息,可空整数数据类型下提供了更多详细信息。
Pandas v0.23 及更早版本
一般而言,最好尽可能使用float
系列,即使系列由于包含值而从int
向上转换到也是如此。这样可以实现基于 NumPy 的矢量化计算,否则将处理 Python 级循环。float
`NaN`
文档确实建议:“一种可能性是使用dtype=object
数组来代替。”例如:
s = pd.Series([1, 2, 3, np.nan])
print(s.astype(object))
0 1
1 2
2 3
3 NaN
dtype: object
出于美观的原因,例如输出到文件,这可能是更好的选择。
Pandas v0.23 及更早版本:背景
NaN
被视为float
。文档目前(截至 v0.23)指定了整数系列被上溯到 的原因float
:
由于 NumPy 缺乏从头开始内置的高性能 NA 支持,因此主要损害就是无法用整数数组表示 NA。
这种权衡主要是出于内存和性能的原因,同时也为了让结果系列继续保持“数字”性质。
该文档还提供了因包含而进行的向上转型规则NaN
:
Typeclass Promotion dtype for storing NAs
floating no change
object no change
integer cast to float64
boolean cast to object
解决方案 7:
Pandas v1.00 的新功能 +
您不再(也不能)使用numpy.nan
。现在您有pandas.NA
。
请阅读:https ://pandas.pydata.org/pandas-docs/stable/user_guide/integer_na.html
IntegerArray 目前处于实验阶段。其 API 或实现可能会在未经警告的情况下发生变化。
在 1.0.0 版更改: 现在使用 pandas.NA 作为缺失值,而不是 numpy.nan。
在处理缺失数据中,我们看到 Pandas 主要使用 NaN 来表示缺失数据。由于 NaN 是浮点数,因此这会强制将包含任何缺失值的整数数组转换为浮点数。在某些情况下,这可能并不重要。但如果您的整数列是标识符,则转换为浮点数可能会有问题。有些整数甚至不能表示为浮点数。
解决方案 8:
如果文本数据中有空格,则通常为整数的列将转换为浮点数作为 float64 dtype,因为 int64 dtype 无法处理空值。如果您加载多个文件,其中一些文件带有空格(最终将为 float64,而其他没有空格的文件将为 int64),这可能会导致架构不一致
此代码将尝试将任何数字类型列转换为 Int64(而不是 int64),因为 Int64 可以处理空值
import pandas as pd
import numpy as np
#show datatypes before transformation
mydf.dtypes
for c in mydf.select_dtypes(np.number).columns:
try:
mydf[c] = mydf[c].astype('Int64')
print('casted {} as Int64'.format(c))
except:
print('could not cast {} to Int64'.format(c))
#show datatypes after transformation
mydf.dtypes
解决方案 9:
我知道 OP 只要求 NumPy 或 Pandas,但我认为值得一提的是,polars是支持所请求功能的替代方案。
Polars
整数列中的任何缺失值都只是值null
,并且该列仍然是整数列。
请参阅Polars - 用户指南 > 来自 Pandas以了解更多信息。
解决方案 10:
从 pandas v 0.24.0 开始,这已经成为可能
pandas 0.24.x 发行说明
引用:“ Pandas 已经获得了保存具有缺失值的整数数据类型的能力。