如何阻止 Pandas DataFrame 无缘无故地将 int 转换为 float?

Posted

技术标签:

【中文标题】如何阻止 Pandas DataFrame 无缘无故地将 int 转换为 float?【英文标题】:How to stop Pandas DataFrame from converting int to float for no reason? 【发布时间】:2019-08-22 14:42:39 【问题描述】:

我正在创建一个小的 Pandas DataFrame 并向其中添加一些应该是整数的数据。但即使我非常努力地将 dtype 显式设置为 int 并且只提供 int 值,它总是最终变成浮点数。这对我来说毫无意义,而且行为似乎也不完全一致。

考虑以下 Python 脚本:

import pandas as pd

df = pd.DataFrame(columns=["col1", "col2"])  # No dtype specified.
print(df.dtypes)  # dtypes are object, since there is no information yet.
df.loc["row1", :] = int(0)  # Add integer data.
print(df.dtypes)  # Both columns have now become int64, as expected.
df.loc["row2", :] = int(0)  # Add more integer data.
print(df.dtypes)  # Both columns are now float64???
print(df)  # Shows as 0.0.

# Let's try again, but be more specific.
del df  
df = pd.DataFrame(columns=["col1", "col2"], dtype=int)  # Explicit set dtype.
print(df.dtypes)  # For some reason both colums are already float64???
df.loc["row1", :] = int(0)
print(df.dtypes)  # Both colums still float64.

# Output:
"""
col1    object
col2    object
dtype: object
col1    int64
col2    int64
dtype: object
col1    float64
col2    float64
dtype: object
      col1  col2
row1   0.0   0.0
row2   0.0   0.0
col1    float64
col2    float64
dtype: object
col1    float64
col2    float64
dtype: object
"""

我可以通过在最后执行df = df.astype(int) 来修复它。还有其他方法可以修复它。但这不应该是必要的。我试图弄清楚我做错了什么,导致列首先变成浮动。

发生了什么事?

Python 版本 3.7.1 熊猫版本 0.23.4

编辑:

我想也许有些人误解了。此 DataFrame 中从来没有任何 NaN 值。它创建后立即如下所示:

Empty DataFrame
Columns: [col1, col2]
Index: []

这是一个数据框,df.shape=0,但其中没有 NaN,只是还没有行。

我还发现了更糟糕的事情。即使我在添加数据使其变为 int 后执行df = df.astype(int)只要我添加更多数据,它就会再次变为浮动

df = pd.DataFrame(columns=["col1", "col2"], dtype=int)
df.loc["row1", :] = int(0)
df.loc["row2", :] = int(0)
df = df.astype(int)  # Force it back to int.
print(df.dtypes)  # It is now ints again.
df.loc["row3", :] = int(0)  # Add another integer row.
print(df.dtypes)  # It is now float again???

# Output:
"""
col1    int32
col2    int32
dtype: object
col1    float64
col2    float64
dtype: object
"""

suggested fix in version 0.24 似乎与我的问题无关。该功能与 Nullable Integer 数据类型有关。我的数据中没有 NaN 或 None 值。

【问题讨论】:

如果存在 nan 或空行,则假定为浮点数。这是针对较新版本修复的,请参阅this 我对这个问题的答案很感兴趣。但是,如果您使用零填充数据框,并将框架设置为 int,则修改行不会更改为浮动。似乎只添加行。 .loc 试图访问一个不存在的索引,所以我猜浮动是在创建新的行索引时出现的。 @run-out 是的,这也是我观察到的。也许您不应该像我正在做的那样将行附加到 DataFrame 中的新索引。但显然可以这样做,因此您会认为它要么得到妥善处理,要么引发警告/异常。 每当您向 DataFrame 添加行(或连接两个 df 等)时,Pandas 都会重铸所有 dtype。据推测,它对某些事情感到困惑并默认为浮动。请注意,如果您首先从具有定义索引的空 DataFrame 开始,然后按照您的描述添加值,您将获得 dtype int64。 阅读 indexing.py 从文档字符串中可以清楚地看出 .loc 仅用于切片,而不是添加行或列。它为类 _LocIndexer 声明了以下 _valid_types = ("labels (MUST BE IN THE INDEX) 。在大写字母中不少于!可以以这种方式创建行的事实似乎超出了 .loc 的范围, ,所以像 int 这样的小故障会发生并且将会发生。 【参考方案1】:

df.loc["rowX"] = int(0) 将起作用并解决问题中提出的问题。 df.loc["rowX",:] = int(0) 不起作用。这是一个惊喜。

df.loc["rowX"] = int(0) 提供了在保留所需数据类型的同时填充空数据框的能力。但是一次可以为一整行这样做。

df.loc["rowX"] = [np.int64(0), np.int64(1)] 有效。

.loc[] 适用于每个https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html 的基于标签的分配。注意:0.24 文档没有描述 .loc[] 用于插入新行。

文档显示使用.loc[] 以列敏感的方式通过赋值添加行。但是在DataFrame 填充数据的情况下这样做。

但是在空帧上切片时会变得很奇怪。

import pandas as pd
import numpy as np
import sys

print(sys.version)
print(pd.__version__)

print("int dtypes preserved")
# append on populated DataFrame
df = pd.DataFrame([[0, 0], [1,1]], index=['a', 'b'], columns=["col1", "col2"])
df.loc["c"] = np.int64(0)
# slice existing rows
df.loc["a":"c"] = np.int64(1)
df.loc["a":"c", "col1":"col2":1] = np.int64(2)
print(df.dtypes)

# no selection AND no data, remains np.int64 if defined as such
df = pd.DataFrame(columns=["col1", "col2"], dtype=np.int64)
df.loc[:, "col1":"col2":1] = np.int64(0)
df.loc[:,:] = np.int64(0)
print(df.dtypes)

# and works if no index but data
df = pd.DataFrame([[0, 0], [1,1]], columns=["col1", "col2"])
df.loc[:,"col1":"col2":1] = np.int64(0)
print(df.dtypes)

# the surprise... label based insertion for the entire row does not convert to float
df = pd.DataFrame(columns=["col1", "col2"], dtype=np.int64)
df.loc["a"] = np.int64(0)
print(df.dtypes)

# a surprise because referring to all columns, as above, does convert to float
print("unexpectedly converted to float dtypes")
df = pd.DataFrame(columns=["col1", "col2"], dtype=np.int64)
df.loc["a", "col1":"col2"] = np.int64(0)
print(df.dtypes)

3.7.2 (default, Mar 19 2019, 10:33:22) 
[Clang 10.0.0 (clang-1000.11.45.5)]
0.24.2
int dtypes preserved
col1    int64
col2    int64
dtype: object
col1    int64
col2    int64
dtype: object
col1    int64
col2    int64
dtype: object
col1    int64
col2    int64
dtype: object
unexpectedly converted to float dtypes
col1    float64
col2    float64
dtype: object

【讨论】:

df.loc["rowX"] 实际上有效,而 df.loc["rowX", :] 无效。非常令人惊讶。我想这是我的具体问题的解决方案,但由于它是如此模糊,我宁愿以更安全的方式来做。在您的链接中,我没有看到 .loc[] 用于插入。你确定吗?我只看到它用于更改现有行。 我运行了您的代码,但得到的结果与您不同。我有 Pandas 0.23,你有 0.24。所以我猜他们已经解决了一些问题。你的第二个和第四个例子对我来说失败了。 """ 3.7.1(默认,2018 年 12 月 10 日,22:54:23)[MSC v.1915 64 位(AMD64)] 0.23.4 int dtypes 保留 col1 int64 col2 int64 dtype: object col1 float64 col2 float64 dtype: object col1 int64 col2 int64 dtype: object col1 float64 col2 float64 dtype: object 意外转换为 float dtypes col1 float64 col2 float64 dtype: object """ @PaulMag 你是对的,文档实际上并没有描述插入,只是对现有行的分配。所以我们有歧义,对此感到抱歉。将编辑答案。

以上是关于如何阻止 Pandas DataFrame 无缘无故地将 int 转换为 float?的主要内容,如果未能解决你的问题,请参考以下文章

根据名称阻止 pandas 列中的文本

阻止 Pandas 将 int 转换为 float

按行规范化 pandas DataFrame

pandas.DataFrame:如何使用外部参数 applymap()

如何在 Pandas 中遍历 DataFrame 中的行

如何在 Pandas 中遍历 DataFrame 中的行