【问题标题】:How to prevent .apply to change dtype of boolean panda Series如何防止.apply更改布尔熊猫系列的dtype
【发布时间】:2020-03-10 12:40:02
【问题描述】:

是否有可能在应用应用函数的对象的数据类型中工作? 据我了解,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

【问题讨论】:

  • stackoverflow.com/questions/52078594/…这个问题不回答我的问题
  • 您是否有意进行按位补码而不是逻辑not?按位补码将布尔值视为整数 0 和 1。然后,它采用这些值的按位补码。所以~False = -1 和~True = -2。这导致它们被转换为int64。
  • 在上述情况下,将值转换回bool(如下所示)将导致ds_b 的所有值成为True,因为True 的补码~ 和False 或 1 和 0 将始终为非零值。
  • @JamesMchugh 你是对的。我必须检查这是否适用于我的真正问题。
  • @JamesMchugh 我做了一个更好的 MWE

标签: python pandas boolean series


【解决方案1】:

只需在您正在申请的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)

【讨论】:

  • 这会导致两种数据类型转换吗?布尔-> int64? --> 布尔 |我知道热得到我想要的 dtype 的结果,但我想知道如何防止转换发生。我不明白 apply 必须更改数据类型的原因
  • 我相信@James Mchugh 描述了转换的原因:您使用了按位补码运算符~ 而不是逻辑not。
【解决方案2】:

所以问题不一定是 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]])

【讨论】:

  • 另外我在doc 中找到了他们的关键字,我在使用滚动和应用时得到了TypeError,因为意外关键字 convert_dtype
  • 感谢您的时间安排。这似乎并不重要,但它以某种方式困扰我,我做错了什么
  • @TimK 那是因为使用您添加的新代码,您不再对Series 执行apply,而是在window 对象上调用它。请参阅these docs。 convert_dtype 参数不适用于该方法。
  • @TimK 答案对你有用吗?如果不是,如何调整它以更好地回答您的问题?如果是这样,你介意接受吗?这不仅可以让您回馈回答者花时间回答您的问题,而且还可以通过向用户展示您所面临问题的正确解决方案来改善 SO 社区。对于面临相同问题的其他用户来说,这可能是一笔巨大的财富。
  • 非常感谢您提供的信息丰富的回答。如果想接受您的回答。但据我所知,答案是:Pandas 尚不支持其他 dtype 的滚动窗口然后浮动,因此我的方法是不可能的。如果你再澄清一点,我会接受的。对我来说一个更好的解决方案是在这种情况下只使用 numpy,在那里我可以做我想做的事而不会丢弃我的 boolean dtype(尤其是在大型数据集中,...)
猜你喜欢
  • 1970-01-01
  • 2022-06-27
  • 1970-01-01
  • 2019-01-14
  • 2016-01-15
  • 1970-01-01
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多