NumPy或Pandas:在存在NaN值的情况下保持数组类型为整数

229

有没有一种首选的方法来保持一个numpy数组的数据类型固定为int(或者int64或其他),同时列表中仍然有一个元素是numpy.NaN呢?

特别是,我正在将一种内部数据结构转换为Pandas DataFrame。在我们的结构中,我们有整数类型的列,但仍然有NaN(但是列的dtype是int)。如果我们将其作为DataFrame,则似乎会将所有内容重新分配为float,但我们确实希望是int

想法是什么?

尝试过的事情:

我尝试使用pandas.DataFrame下的from_records()函数,其中coerce_float=False,但这并没有帮助。我还尝试使用NumPy掩码数组,填充值为NaN,但也没有成功。所有这些都导致列数据类型成为float。


1
你可以使用numpy的掩蔽数组吗? - mgilson
我会试试看。我也尝试使用pandas.DataFrame下的from_records函数,并设置参数coerce_float=False,但是没有成功……它仍然使新数据类型为float64 - ely
1
是的,没有运气。即使使用掩码数组,它仍然会转换为浮点数。看起来 Pandas 的处理方式是这样的:“有 NaN 吗?……那么一切都是浮点数。”希望有办法解决这个问题。 - ely
10个回答

127

NaN 无法存储在整数数组中。这是 pandas 目前的已知限制;我一直在等待 NumPy 中 NA 值的进展(类似于 R 中的 NAs),但是看起来至少需要 6 个月到一年的时间才能实现这些功能:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(此功能从 pandas 版本 0.24 开始添加,但请注意,它需要使用扩展数据类型 Int64(大写字母),而不是默认的数据类型 int64(小写字母): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support


7
嗨Wes,这方面有更新吗?我们遇到了问题,连接列会根据原始列表中NA值的存在转换为int或float。(在尝试合并这些数据框时会产生问题) - Carst
1
更新链接:http://pandas-docs.github.io/pandas-docs-travis/whatsnew/v0.24.0.html#optional-integer-na-support - techvslife

121

2
目前,您必须指定一个特殊的dtype,例如“'Int64'”,才能使其正常工作。当它默认启用时,它将变得更好。 - Jean Paul
太好了!不过有一个小问题,PyCharm无法在调试窗口中显示数据框(dataframe),如果以这种方式使用。您可以查看我的另一个问题的答案,了解如何强制显示它:https://dev59.com/1FkT5IYBdhLWcg3wL8n3#53288781(那里的原始问题不同,但是显示数据框的解决方案有效)。 - Alaa M.
我必须使用 'Int64' 吗?还是有类似于 'Int8' 的东西吗?与 np.float 相比,它使用了大量的内存。 - Superdooperhero
'Int8' 看起来可以工作,但 np.float 仍然加载得更快。问题似乎是它没有在中间释放内存。假设垃圾收集器最终会运行。 - Superdooperhero

11

如果您试图将一个带有NA值的浮点数 (1.143) 向量转换为整数 (1),并且将其转换为新的 '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

我的使用场景是有一个浮点数序列,我想将其四舍五入成整数。但是,当你使用.round()函数进行四舍五入时,仍会保留小数,因此需要转换为整数才能去除小数部分。


1
对于未来的寻求者,我使用这种方法时遇到了错误。然后我注意到整数大小写的区别。请注意,Int64 != int64。希望能帮助到某些人。 - Alexander Santos

9
如果性能不是主要问题,您可以存储字符串。
df.col = df.col.dropna().apply(lambda x: str(int(x)) )

你可以随意混合使用NaN。如果你真的想要整数,根据应用程序,你可以使用-1, 或者0, 或者1234567890或其他专门用于表示NaN的值。

你也可以暂时复制列:一个带有浮点数,另一个实验用的带有整数或字符串。然后在每个合理的地方插入asserts检查两者是否同步。经过足够的测试,你可以放弃使用浮点数。


8

这并非所有情况的解决方案,而是我的(基因组坐标)我已经采取了将0用作NaN的方法。

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

这至少允许使用适当的“本地”列类型,使减法、比较等操作按预期工作。

7

Pandas v0.24+

从v0.24版本开始,支持在整数序列中使用NaN。在v0.24“新特性”部分中有相关信息,更多细节请参阅可空整数数据类型

Pandas v0.23及更早版本

通常情况下,最好使用float系列,即使该系列因包含NaN值而从int向上转换为float。这可以启用基于NumPy的矢量化计算,否则将处理Python级别的循环。

文档建议:“一种可能的方法是使用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的能力。

这种权衡在很大程度上是基于内存和性能原因,并且还使得结果Series继续保持“数值”。

文档还提供规则以进行由于包含NaN而导致的上升转换:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object

3

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是浮点数,这迫使具有任何缺失值的整数数组变成浮点数。在某些情况下,这可能并不重要。但是,如果您的整数列是标识符,那么转换为浮点数可能会有问题。有些整数甚至无法表示为浮点数。


2
如果文本数据中有空格,则通常应为整数的列将被转换为float64 dtype作为浮点数,因为int64 dtype无法处理nulls。如果您正在加载一些带空白的文件(这些空白将变成float64),而另一些没有空白(这些将变成int64),则可能会导致不一致的模式。
此代码将尝试将任何数字类型列转换为Int64(而不是int64),因为Int64可以处理nulls。
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

1

自从 pandas v 0.24.0 版本以后,这是现在可以实现的。

pandas 0.24.x 发布说明 引用:“Pandas 现在具备了保留缺失值的整数数据类型的能力。


0

我知道OP只要求使用NumPy或Pandas,但我认为值得提到polars作为支持所需功能的替代品。

Polars中,整数列中的任何缺失值都是简单的null值,并且该列仍然是整数列。

有关更多信息,请参见Polars - 用户指南 > 从Pandas转移


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接