如何防止.apply改变布尔熊猫系列的dtype

How to prevent .apply to change dtype of boolean panda Series

是否可以在应用函数应用的对象的数据类型中工作? 据我了解,dtype 已更改。

请看下面的MWE。这个结果不是我想要达到的。

import pandas as pd
ds_a = pd.Series([True,False,True])
ds_b = ds_a.apply(lambda x: ~x)
print(ds_a.dtype == ds_b.dtype)
print(ds_b.dtype)

结果:

False
int64

ds_b 应该与 ds_a 具有相同的数据类型(布尔值)。我对如何防止任何数据类型更改感兴趣。

编辑:对于我的用例,这是一个更好的 MWE。

请参阅以下(新)MWE。

import pandas as pd
ds_a = pd.Series([True,False,True,True,True,False])
ds_mask = pd.Series([True,False])
func = lambda x: pd.np.all(x==ds_mask)
ds_b = ds_a.rolling(len(ds_mask)).apply(func, raw=True)
print(a(ds_a[:2]).dtype)
print(ds_b.dtype)

结果:

dtype('bool')
float64

只需在您正在申请的 lambda 中添加对 boolean 的显式转换

import pandas as pd


ds_a = pd.Series([True,False,True])
ds_b = ds_a.apply(lambda x: bool(~x))
print(ds_a.dtype == ds_b.dtype)
print(ds_b.dtype)

所以问题不一定是 DataFrame 正在转换值。问题在于使用的是按位补码运算符 ~ 而不是逻辑运算符 not。这导致 TrueFalse 的布尔值被视为整数,结果如下:

~True = -2
~False = -1

这就是导致输出 DataFrame ds_b 显示 dtypeint64 的原因。将代码更改为以下内容应该可以解决该问题。

import pandas as pd


ds_a = pd.Series([True,False,True])
ds_b = ds_a.apply(lambda x: not x)
print(ds_a.dtype == ds_b.dtype)
print(ds_b.dtype)

但是,apply 方法将根据输入对系列的类型进行调整,这是正确的。例如,在您的例子中,它将 int 转换为 int64。如果您以后遇到这种行为并且不希望出现这种情况,请考虑以下代码。

ds_b = ds_a.apply(lambda x: ~x, convert_dtype=False).astype(ds_a.dtype)

这会阻止 apply 进行自动转换,最后它将 dtypeobject 转换为原始类型。这里有一些时间供您比较,它不会引入大量开销。

In [26]: %timeit ds_b = ds_a.apply(lambda x: ~x)                                
257 µs ± 5.67 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [27]: %timeit ds_b = ds_a.apply(lambda x: ~x).astype(ds_a.dtype)             
394 µs ± 23.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [28]: %timeit ds_b = ds_a.apply(lambda x: ~x, convert_dtype=False).astype(ds_
    ...: a.dtype)                                                               
359 µs ± 10.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

在您的最新示例中,Rolling 实例自动尝试将数据处理为 float64。使用 rolling 比使用 Series 或 DataFrame apply 更受限制。就目前而言,除了在最后转换结果之外,没有办法在 Pandas 中更改滚动操作的数据类型。为此,我会在最后看到上面用于转换 dtype 的代码,只需省略 Rolling 对象的 apply 方法的 convert_dtype 参数,因为它不适用。

如果您愿意使用 Pandas、a rolling function can be implemented using numpy 以外的软件包。见以下代码:

import numpy as np

def rolling_window(a, window):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

a = np.array([ True, False,  True,  True,  True, False])
mask = np.array([True, False])

b = (rolling_window(a, 2) == mask).all(axis=1, keepdims=True)

执行后,b 等于第二个 MVE 的预期输出,只是它是 numpy 数组的形式。

array([[ True],
       [False],
       [False],
       [False],
       [ True]])