python - 在索引和插入行时防止强制熊猫数据帧




pandas coercion (4)

在第一种情况下,您可以使用 空的整数数据类型 。 系列选择不会强制 float 并且值将放置在 object 容器中。 然后正确创建字典,并将基础值存储为 np.int64

df = pd.DataFrame({'a': [1], 'b': [2.2]})
df['a'] = df['a'].astype('Int64')

d = dict(df.loc[0])
#{'a': 1, 'b': 2.2}

type(d['a'])
#numpy.int64

使用您的语法,这 几乎也 适用于第二种情况,但是此方法对 object 不利,因此效果不佳:

df.loc[1] = {'a': 5, 'b': 4.4}
#   a    b
#0  1  2.2
#1  5  4.4

df.dtypes
#a     object
#b    float64
#dtype: object

但是,我们可以对在末尾添加一行(带有RangeIndex)的语法进行一些小的更改,现在可以正确处理类型了。

df = pd.DataFrame({'a': [1], 'b': [2.2]})
df['a'] = df['a'].astype('Int64')

df.loc[df.shape[0], :] = [5, 4.4]
#   a    b
#0  1  2.2
#1  5  4.4

df.dtypes
#a      Int64
#b    float64
#dtype: object

我正在处理熊猫数据帧的各个行,但在索引和插入行时遇到了强制问题。 熊猫似乎总是想将int / float混合类型强制转换为全浮点类型,我看不出对此行为有任何明显的控制。

例如,这是一个简单的数据帧,其中 aintbfloat

import pandas as pd
pd.__version__  # '0.25.2'

df = pd.DataFrame({'a': [1], 'b': [2.2]})
print(df)
#    a    b
# 0  1  2.2
print(df.dtypes)
# a      int64
# b    float64
# dtype: object

在索引一行时,这是一个强制问题:

print(df.loc[0])
# a    1.0
# b    2.2
# Name: 0, dtype: float64
print(dict(df.loc[0]))
# {'a': 1.0, 'b': 2.2}

这是插入一行时的强制问题:

df.loc[1] = {'a': 5, 'b': 4.4}
print(df)
#      a    b
# 0  1.0  2.2
# 1  5.0  4.4
print(df.dtypes)
# a    float64
# b    float64
# dtype: object

在这两种情况下,我都希望 a 列保留为整数类型,而不是被强制转换为浮点类型。


每当从数据框获取数据或将数据追加到数据框并需要保持数据类型相同时,都应避免转换为其他内部结构,这些内部结构不了解所需的数据类型。

当您执行 df.loc[0] 它将转换为 pd.Series

>>> type(df.loc[0])
<class 'pandas.core.series.Series'>

现在, Series 将只有一个 dtype 。 因此,强制 int float

而是将结构保留为 pandas.pydata.org/pandas-docs/stable/reference/api/…

>>> type(df.loc[[0]])
<class 'pandas.core.frame.DataFrame'>

选择所需的行作为框架,然后转换为 dict

>>> df.loc[[0]].to_dict(orient='records')
[{'a': 1, 'b': 2.2}]

同样,要添加新行,请使用pandas pd.DataFrame.append 函数,

>>> df = df.append([{'a': 5, 'b': 4.4}]) # NOTE: To append as a row, use []
   a    b
0  1  2.2
0  5  4.4

以上不会引起类型转换,

>>> df.dtypes
a      int64
b    float64
dtype: object

经过一番挖掘后,这里有一些非常丑陋的解决方法。 (一个更好的答案将被接受。)

此处发现的 一个怪癖是非数字列会停止强制转换,因此这是如何将一行索引到 dict

dict(df.assign(_='').loc[0].drop('_', axis=0))
# {'a': 1, 'b': 2.2}

插入一行可以通过创建一行新的数据框来完成:

df = df.append(pd.DataFrame({'a': 5, 'b': 4.4}, index=[1]))
print(df)
#    a    b
# 0  1  2.2
# 1  5  4.4

这两个技巧都没有针对大型数据帧进行优化,因此,我将不胜感激!


问题的根源在于

  1. 熊猫数据框的索引返回熊猫系列

我们可以看到:

type(df.loc[0])
# pandas.core.series.Series

在您的情况下,一系列只能具有一个dtype,即int64或float64。

我有两种解决方法:

print(df.loc[[0]])
# this will return a dataframe instead of series
# so the result will be
#    a    b
# 0  1  2.2

# but the dictionary is hard to read
print(dict(df.loc[[0]]))
# {'a': 0    1
# Name: a, dtype: int64, 'b': 0    2.2
# Name: b, dtype: float64}

要么

print(df.astype(object).loc[0])
# this will change the type of value to object first and then print
# so the result will be
# a      1
# b    2.2
# Name: 0, dtype: object

print(dict(df.astype(object).loc[0]))
# in this way the dictionary is as expected
# {'a': 1, 'b': 2.2}
  1. 将字典追加到数据框时,它将首先将字典转换为 Series ,然后追加。 (因此,同样的问题再次发生)

https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py#L6973

if isinstance(other, dict):
    other = Series(other)

因此,您的解决方案实际上是一个可靠的解决方案,否则我们可以:

df.append(pd.Series({'a': 5, 'b': 4.4}, dtype=object, name=1))
#    a    b
# 0  1  2.2
# 1  5  4.4






coercion