在Pandas中将Int64作为默认整数数据类型而不是标准的int64

30
我希望所有的数据框,无论它们是从构造器中的哪一个重载建立的,还是派生自 .read_csv().read_xlsx().read_sql() 或任何其他方法,都使用新的可为空的 Int64 数据类型 作为所有整数的默认 dtype,而不是 int64
如果没有“好”的方法,我愿意采取任何疯狂的方式来实现这一点,包括子类化 DataFrame 或 Series 类,并重新实现任意数量的方法和构造函数属性等。
我的问题是,能否做到这一点? 如果可以,我该如何实现?

10
“我愿意达到任何精神失常的程度来完成这件事。” 在那一刻,任何事情都有可能发生。 - Paritosh Singh
2
这并不是那么简单(显然),但你不能只写几行代码将你可能拥有的任何 int 列转换为可空吗? - cs95
2个回答

4
您可以使用类似这样的函数:
def nan_ints(df, convert_strings=False, subset=None):
    types = ["int64", "float64"]
    if subset is None:
        subset = list(df)
    if convert_strings:
        types.append("object")
    for col in subset:
        if df[col].dtype in types:
            df[col] = (
                df[col].astype(float, errors="ignore").astype("Int64", errors="ignore")
            )
    return df

该代码会遍历每一列并将其转换为Int64(整数),如果是int,则将其转换为Int64。 如果是float,则仅当该列中的所有值都可以转换为int(除了NaN)时,才将其转换为Int64。 我们还提供了一个选项,可以使用convert_strings参数将字符串转换为Int64。

df1 = pd.DataFrame({'a':[1.1,2,3,1],
                  'b':[1,2,3,np.nan],
                  'c':['1','2','3',np.nan],
                  'd':[3,2,1,np.nan]})


nan_ints(df1,convert_strings=True,subset=['b','c'])
df1.info()

将返回以下内容:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4 entries, 0 to 3
Data columns (total 4 columns):
a    4 non-null float64
b    3 non-null Int64
c    3 non-null Int64
d    3 non-null float64
dtypes: Int64(2), float64(2)
memory usage: 216.0 bytes

如果你打算在每个DataFrame中使用此功能,可以将函数添加到一个模块中,并在每次想要使用pandas时导入它。

from my_module import nan_ints

然后只需像这样使用:

nan_ints(pd.read_csv(path))

注意: 可空整数数据类型是从0.24.0版本开始的新特性。这里是文档


1
这会截断浮点数,可能是不希望的(我认为通常是不希望的)... - Andy Hayden
1
如果该列中的所有浮点数都是整数,则它只会截断浮点列。在我看来,这似乎风险很低...我想我们可以添加一个可选参数来接受要转换的列的列表。 @Any Hayden我应该做这个编辑吗? - braintho
1
为什么在 .astype('Int64') 之前要调用 .astype(float) - Alasdair
你可以使用astype的errors='ignore'选项来摆脱try块。如果无法转换,它将只返回原始对象,这与你现在的行为相同。 - Jon
@Jon 我不确定这是否使代码更易读,但我已经编辑了代码,使用了你的建议。 - braintho
显示剩余3条评论

2
我会选择猴子补丁技术。最简单的方法是对 DataFrame 构造函数进行猴子补丁。具体操作如下:
import pandas
pandas.DataFrame.__old__init__ = pandas.DataFrame.__init__
def new_init(self, data=None, index=None, columns=None, dtype=pd.Int64Dtype(), copy=False):
    self.__old__init__(data=data, index=index, columns=None, dtype=dtype, copy=copy)

pandas.DataFrame.__init__ = new_init

当然,你会冒着破坏世界的风险。祝你好运!

最初的回答


2
pd.DataFrame 构造函数会为每一列推断 dtype,如果没有指定的话。这个解决方案会强制所有调用者显式地传递 dtype=None 来模拟这种行为,这可能会破坏库中许多内置函数的功能。 - GZ0
没错,这就是我所说的“打破世界”的意思。这是一种试错的解决方案。它将取决于他们如何使用库。如果他们处理的所有数据都是整数数据,那么它可能有效。 - Joel
1
我希望有一种方法来monkey patch pandas,但是我的列是不同类型的混合,所以我需要比这更复杂的东西。 - Alasdair
好的,我明白了。我不知道有没有一种方法可以在不涉及Pandas机制的情况下进行修补。我敢打赌你可以在更深层次上进行修补,但这需要相当多的侦查工作,并且可能会依赖于内部API。 - Joel

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