将包含 NaN 的 Pandas 列转换为 dtype `int`

Posted

技术标签:

【中文标题】将包含 NaN 的 Pandas 列转换为 dtype `int`【英文标题】:Convert Pandas column containing NaNs to dtype `int` 【发布时间】:2014-02-12 18:54:50 【问题描述】:

我从 .csv 文件读取数据到 Pandas 数据框,如下所示。对于其中一列,即id,我想将列类型指定为int。问题是id 系列有缺失/空值。

当我在读取 .csv 时尝试将 id 列转换为整数时,我得到:

df= pd.read_csv("data.csv", dtype='id': int) 
error: Integer column has NA values

或者,我在阅读后尝试转换列类型,但这次我得到:

df= pd.read_csv("data.csv") 
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer

我该如何解决这个问题?

【问题讨论】:

我认为如果缺少/NaN 值,整数值无法转换或存储在系列/数据框中。我认为这与 numpy 兼容性有关(我在这里猜),如果您想要缺少值兼容性,那么我会将值存储为浮点数 见这里:pandas.pydata.org/pandas-docs/dev/…;当您缺少值时,您必须有一个 float dtype(或者技术上是 object dtype,但效率低下);您使用 int 类型的目标是什么? 我相信这是一个 NumPy 问题,并非特定于 Pandas。很遗憾,因为在很多情况下,使用允许 null 值的可能性的 int 类型比一大列浮点数更有效。 我也有这个问题。我有多个数据框,我想根据几个“整数”列的字符串表示来合并它们。但是,当其中一个整数列具有 np.nan 时,字符串转换会产生一个“.0”,这会引发合并。只是让事情稍微复杂一些,如果有简单的解决方法会很好。 @Rhubarb,可选的可空整数支持现已正式添加到 pandas 0.24.0 - 终于 :) - 请在下面找到更新的答案。 pandas 0.24.x release notes 【参考方案1】:

整数列中缺少 NaN 代表是 pandas "gotcha"。

通常的解决方法是简单地使用浮点数。

【讨论】:

除了把它们当作花车对待之外,还有其他解决方法吗? @jsc123 你可以使用 object dtype。这带有一个小的健康警告,但在大多数情况下效果很好。 你能提供一个如何使用object dtype的例子吗?我一直在浏览 pandas 文档和谷歌搜索,我读过这是推荐的方法。但是,我还没有找到如何使用 object dtype 的示例。 在 v0.24 中,您现在可以执行 df = df.astype(pd.Int32Dtype())(转换整个数据帧,或)df['col'] = df['col'].astype(pd.Int32Dtype())。其他可接受的可为空整数类型是 pd.Int16Dtypepd.Int64Dtype。选择你的毒药。 它是 NaN 值,但 isnan 检查根本不起作用 :(【参考方案2】:

在 0.24.+ 版中,pandas 已经获得了保存具有缺失值的整数 dtype 的能力。

Nullable Integer Data Type.

Pandas 可以使用arrays.IntegerArray 表示可能缺少值的整数数据。这是在 pandas 中实现的扩展类型。它不是整数的默认 dtype,也不会被推断;您必须将 dtype 显式传递给 array()Series

arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)

0      1
1      2
2    NaN
dtype: Int64

要将列转换为可为空的整数,请使用:

df['myCol'] = df['myCol'].astype('Int64')

【讨论】:

注意dtype必须是"Int64"而不是"int64"(第一个'i'必须大写) df.myCol = df.myCol.astype('Int64')df['myCol'] = df['myCol'].astype('Int64') 这对某些人来说可能很明显,但我认为仍然值得注意的是,您可以使用任何 Int(例如 Int16Int32),如果数据框非常大节省内存。 @jezrael,在这种情况下这不起作用......?它对我不起作用,我找不到通用的解决方案。 我收到TypeError: cannot safely cast non-equivalent float64 to int64【参考方案3】:

我的用例是在加载到数据库表之前整理数据:

df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)

删除 NaN,转换为 int,转换为 str,然后重新插入 NAN。

它不漂亮,但它完成了工作!

【讨论】:

我一直在努力加载序列号,其中一些为空,其余为浮点数,这救了我。 OP 想要一列整数。转成字符串不满足条件。 只有当 col 还没有 -1 时才有效。否则会弄乱数据 那么如何回到int..?? 这会产生一列字符串!!对于当前版本的pandas 的解决方案,请参阅***.com/questions/58029359/…【参考方案4】:

现在可以创建一个包含 NaN 作为 dtype int 的 pandas 列,因为它现在已正式添加到 pandas 0.24.0

pandas 0.24.x release notes 引用:“Pandas 已经获得了保存具有缺失值的整数 dtype 的能力

【讨论】:

【参考方案5】:

如果你绝对想在一列中组合整数和 NaN,你可以使用 'object' 数据类型:

df['col'] = (
    df['col'].fillna(0)
    .astype(int)
    .astype(object)
    .where(df['col'].notnull())
)

这会将 NaN 替换为整数(不管是哪个),转换为 int,转换为 object,最后重新插入 NaN。

【讨论】:

【参考方案6】:

几周前我遇到了一些被格式化为“对象”的离散特征的问题。这个解决方案似乎有效。

for col in discrete:
    df[col] = pd.to_numeric(df[col],errors='coerce').astype(pd.Int64Dtype())

【讨论】:

【参考方案7】:

如果可以删除具有 NaN 值的行,您可以使用 .dropna()

df = df.dropna(subset=['id'])

或者, 使用 .fillna().astype() 将 NaN 替换为值并将它们转换为 int。

我在处理包含大整数的 CSV 文件时遇到了这个问题,而其中一些整数丢失了 (NaN)。使用 float 作为类型不是一种选择,因为我可能会失去精度。

我的解决方案是使用 str 作为中间类型。 然后,您可以在后面的代码中随意将字符串转换为 int。我将 NaN 替换为 0,但您可以选择任何值。

df = pd.read_csv(filename, dtype='id':str)
df["id"] = df["id"].fillna("0").astype(int)

为了说明,这里是一个浮点数如何降低精度的示例:

s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)

输出是:

1.2345678901234567e+19 12345678901234567168 12345678901234567890

【讨论】:

【参考方案8】:

从 Pandas 1.0.0 开始,您现在可以使用 pandas.NA 值。这不会强制缺失值的整数列为浮点数。

读取数据时,您只需:

df= pd.read_csv("data.csv", dtype='id': 'Int64')  

请注意,'Int64' 被引号包围,而 I 大写。这将 Panda 的“Int64”与 numpy 的 int64 区分开来。

附带说明,这也适用于 .astype()

df['id'] = df['id'].astype('Int64')

文档在这里 https://pandas.pydata.org/pandas-docs/stable/user_guide/integer_na.html

【讨论】:

【参考方案9】:

如果您可以修改存储的数据,请使用标记值作为缺失的id。一个常见的用例,由列名推断,id 是一个整数,严格大于零,您可以使用0 作为标记值,​​以便您可以编写

if row['id']:
   regular_process(row)
else:
   special_process(row)

【讨论】:

【参考方案10】:

这里的大多数解决方案都会告诉您如何使用占位符整数来表示空值。如果您不确定整数不会出现在源数据中,那么这种方法就没有帮助。我的方法将格式化没有十进制值的浮点数并将空值转换为无。结果是一个对象数据类型,当加载到 CSV 中时,它看起来像一个具有空值的整数字段。

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '0:.0f'.format(pandas.to_numeric(x)))

【讨论】:

这种方法会增加很多内存开销,尤其是在较大的数据帧上【参考方案11】:

无论您的 pandas 系列是 object 数据类型还是简单的 float 数据类型,以下方法都可以使用

df = pd.read_csv("data.csv") 
df['id'] = df['id'].astype(float).astype('Int64')

【讨论】:

谢谢@Abhishek Bhatia 这对我有用。【参考方案12】:
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])

【讨论】:

您是否有理由更喜欢这个公式而不是接受的答案中提出的公式?如果是这样,编辑您的答案以提供该解释会很有用 - 特别是因为有十个其他答案正在争夺注意力。 虽然此代码可以解决 OP 的问题,但最好包含关于您的代码如何/为何解决它的解释。通过这种方式,未来的访问者可以从您的帖子中学习,并将其应用到他们自己的代码中。 SO 不是编码服务,而是知识资源。此外,高质量、完整的答案更有可能得到支持。这些功能,以及所有帖子都是独立的要求,是 SO 的一些优势,因为它是一个区别于论坛的平台。您可以edit 添加其他信息和/或使用源文档补充您的解释。【参考方案13】:

如果你想在链接方法时使用它,你可以使用assign:

df = (
     df.assign(col = lambda x: x['col'].astype('Int64'))
)

【讨论】:

【参考方案14】:

使用.fillna() 将所有NaN 值替换为0,然后使用astype(int) 将其转换为int

df['id'] = df['id'].fillna(0).astype(int)

【讨论】:

有效,但我认为用 0 替换 NaN 会改变数据的含义。【参考方案15】:

对于任何需要在包含 NULL/NaN 的列中具有 int 值,但在无法使用其他答案中提到的 pandas 版本 0.24.0 可空整数特性的约束下工作的人,我建议使用将列转换为对象类型pd.哪里:

df = df.where(pd.notnull(df), None)

这会将数据框中的所有 NaN 转换为 None,将混合类型的列视为对象,但将 int 值保留为 int,而不是 float。

【讨论】:

【参考方案16】:

首先您需要指定更新的整数类型,Int8 (...Int64) 可以处理空整数数据(pandas 版本 >= 0.24.0)

df = df.astype('Int8')

但您可能只想定位特定列,其中包含整数数据与 NaN/null 混合:

df = df.astype('col1':'Int8','col2':'Int8','col3':'Int8')

此时,NaN 被转换为<NA>,如果你想用 df.fillna() 改变默认的 null 值,你需要在你想改变的列上强制转换对象数据类型,否则你会看 TypeError: <U1 cannot be converted to an IntegerDtype

你可以这样做 df = df.astype(object) 如果您不介意将每个列数据类型更改为对象(单独地,每个值的类型仍然保留)...或者 df = df.astype("col1": object,"col2": object) 如果您更喜欢定位单个列。

这应该有助于强制与空值混合的整数列保持格式化为整数并将空值更改为您喜欢的任何值。我无法评价这种方法的效率,但它适用于我的格式化和打印目的。

【讨论】:

【参考方案17】:

我在使用 pyspark 时遇到了这个问题。由于这是在 jvm 上运行的代码的 python 前端,因此它需要类型安全,并且不能选择使用 float 而不是 int。我通过将 pandas pd.read_csv 包装在一个函数中解决了这个问题,该函数将在将用户定义的列转换为所需的类型之前用用户定义的填充值填充用户定义的列。这是我最终使用的:

def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
    if custom_dtype is None:
        return pd.read_csv(file_path, **kwargs)
    else:
        assert 'dtype' not in kwargs.keys()
        df = pd.read_csv(file_path, dtype = , **kwargs)
        for col, typ in custom_dtype.items():
            if fill_values is None or col not in fill_values.keys():
                fill_val = -1
            else:
                fill_val = fill_values[col]
            df[col] = df[col].fillna(fill_val).astype(typ)
    return df

【讨论】:

【参考方案18】:

试试这个:

df[['id']] = df[['id']].astype(pd.Int64Dtype())

如果你打印的是dtypes,你会得到id Int64而不是普通的one int64

【讨论】:

【参考方案19】:

以下解决方案是唯一符合我目的的解决方案,我认为这是使用最新版 Pandas 时的最佳解决方案。

df['A'] = np.floor(pd.to_numeric(df['A'],
                   errors='coerce'))
                   .astype('Int64')

我在 *** 上找到了解决方案,请参阅下面的链接了解更多信息。 https://***.com/a/67021201/9294498

【讨论】:

请解释您的解决方案。【参考方案20】:

首先删除包含 NaN 的行。然后对剩余的行进行整数转换。 最后再次插入删除的行。 希望它会工作

【讨论】:

【参考方案21】:

使用 pd.to_numeric()

df["DateColumn"] = pd.to_numeric(df["DateColumn"])

简单干净

【讨论】:

如果列中有 NaN 值,pd.to_numeric 会将 dtype 转换为 float 而不是 int,因为 NaN 被视为浮点数。【参考方案22】:

Int64 的问题与许多其他解决方案一样,如果您有 null 值,它们将被替换为 <NA> 值,这不适用于 pandas 默认的“NaN”函数,例如 isnull()fillna()。或者,如果您将值转换为 -1,您最终可能会删除您的信息。我的解决方案有点蹩脚,但将提供 int 值和 np.nan,允许 nan 函数在不影响您的值的情况下工作。

            def to_int(x):
                try:
                    return int(x)
                except:
                    return np.nan

            df[column] = df[column].apply(to_int)

【讨论】:

【参考方案23】:

遇到了类似的问题。那是我的解决方案:

def toint(zahl = 1.1):
    try:
        zahl = int(zahl)
    except:
        zahl = np.nan
    return zahl

print(toint(4.776655), toint(np.nan), toint('test'))

4楠楠

df = pd.read_csv("data.csv") 
df['id'] = df['id'].astype(float)
df['id'] = toint(df['id'])

【讨论】:

【参考方案24】:

由于这里没有看到答案,我也不妨补充一下:

如果您由于某种原因仍然无法像我一样在依赖具有旧版 pandas 的库时处理 np.na 或 pd.NA,则可以将 NAN 转换为空字符串:

df.select_dtypes('number').fillna(-1).astype(str).replace('-1', '')

【讨论】:

【参考方案25】:

使用pandas >.24 版本,输入Int64 支持nan。

如果您的花车没有经过圆形、地板、天花板或圆形,您可能会遇到错误。

df['A'] = np.floor(pd.to_numeric(df['A'], errors='coerce')).astype('Int64')

来源: https://***.com/a/67021201/1363742

【讨论】:

【参考方案26】:

我认为@Digestible1010101 的方法更适合 Pandas 1.2.+ 版本,这样的工作应该可以完成:

df = df.astype(
            'col_1': 'Int64',
            'col_2': 'Int64',
            'col_3': 'Int64',
            'col_4': 'Int64', )

【讨论】:

【参考方案27】:

假设您的 DateColumn 格式为 3312018.0 应转换为 03/31/2018 作为字符串。并且,有些记录丢失或为 0。

df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))

【讨论】:

以上是关于将包含 NaN 的 Pandas 列转换为 dtype `int`的主要内容,如果未能解决你的问题,请参考以下文章

Python Pandas 将字符串转换为 NaN

Pandas使用列标题作为值将多个列转换/合并为单个列

阻止 Pandas 将 int 转换为 float

从 pandas 转换为 numpy 后,如果数组包含 nan,则删除“nan”或减少 numpy 数组的长度 [重复]

在存在 NaN 的情况下将 pandas 列拆分为新列

Pandas 中日期列的最大值/最小值,列包含 nan 值