矢量化自定义函数未按预期工作 pandas

Vectorized Custom function not working as expected In pandas

this pycon talk为来源。

def clean_string(item):
    if type(item)==type(1):
        return item
    else:
        return np.nan

dataframe 对象有一列包含数字和字符串数据,我想将字符串更改为 np.nan 同时保留数值数据。

这种方法工作正常

df['Energy Supply'].apply(clean_string)

但是当我尝试使用矢量化时,所有列项的值都更改为 np.nan

df['Energy Supply'] = clean_string(df['Energy Supply'])  # vectorisation

但是上面的方法是将所有项目都转换为np.nan。我相信这是因为 clean_string 函数中的 type(item)pd.Series 类型。

有没有办法解决这个问题?

PS: 我是pandas

的初学者

向量化 pandas 中的操作并不总是可行的。我不知道 pandas 内置矢量化方法来获取系列中元素的类型,因此您的 .apply() 解决方案可能是最好的方法。

您的代码在第二种情况下不起作用的原因是您将整个系列传递给 clean_string() 函数。它将 Series 的类型与 type(1) 进行比较,即 False 然后 return 的一个值 np.nan。 Pandas在赋值回df的时候自动广播这个值,所以得到一列NaN。为了避免这种情况,您必须在 clean_string() 函数中遍历系列中的所有元素。

出于好奇,我尝试了其他几种方法,看看是否有任何方法比您的版本更快。为了测试,我创建了 10,000 和 100,000 个元素 pd.Series,交替使用整数和字符串值:

import numpy as np
import pandas as pd

s = pd.Series(i if i%2==0 else str(i) for i in range(10000))
s2 = pd.Series(i if i%2==0 else str(i) for i in range(100000))

这些测试是使用 pandas 1.0.3 和 python 3.8 完成的。

基线使用 clean_string()

In []: %timeit s.apply(clean_string)
3.75 ms ± 14.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In []: %timeit s2.apply(clean_string)
39.5 ms ± 301 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Series.str 方法

另一种测试字符串与非字符串的方法是使用系列的内置 .str 函数,例如,如果您应用 .str.len(),它将 return NaN 对于系列中的任何非字符串。这些甚至在 pandas 文档中被称为“Vectorized String Methods”,因此它们可能会更有效率。

In []: %timeit s.mask(s.str.len()>0)
6 ms ± 39.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In []: %timeit s2.mask(s2.str.len()>0)
56.8 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

不幸的是,这种方法比 .apply() 慢。尽管 "vectorized" 看起来这不是更好的方法。它也与 clean_string() 的逻辑不太相同,因为它测试的是字符串元素而不是整数元素。

直接将 type 应用于系列

基于,我决定测试使用.apply()type来获取每个元素的类型。一旦我们知道类型,与 int 进行比较并使用 .mask() 方法将任何非整数转换为 NaN.

In []: %timeit s.mask(s.apply(type)!=int)
1.88 ms ± 4.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In []: %timeit s2.mask(s2.apply(type)!=int)
15.2 ms ± 32.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这是我发现的最快的方法。