如何防止.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
。这导致 True
和 False
的布尔值被视为整数,结果如下:
~True = -2
~False = -1
这就是导致输出 DataFrame ds_b
显示 dtype
为 int64
的原因。将代码更改为以下内容应该可以解决该问题。
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
进行自动转换,最后它将 dtype
从 object
转换为原始类型。这里有一些时间供您比较,它不会引入大量开销。
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]])
是否可以在应用函数应用的对象的数据类型中工作? 据我了解,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
。这导致 True
和 False
的布尔值被视为整数,结果如下:
~True = -2
~False = -1
这就是导致输出 DataFrame ds_b
显示 dtype
为 int64
的原因。将代码更改为以下内容应该可以解决该问题。
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
进行自动转换,最后它将 dtype
从 object
转换为原始类型。这里有一些时间供您比较,它不会引入大量开销。
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]])