如何阻止 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?的主要内容,如果未能解决你的问题,请参考以下文章