在 pandas 中向量化操作并不总是可行的。我不知道使用 pandas 内置矢量化方法来获取系列中元素的类型,因此您的 .apply() 解决方案可能是最好的方法。
您的代码在第二种情况下不起作用的原因是您将整个系列传递给您的 clean_string() 函数。它将Series的类型与type(1)比较,即False,然后返回一个值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(),它将返回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() 慢。尽管被“矢量化”,但它看起来并不是一个更好的方法。它与clean_string() 的逻辑也不完全相同,因为它测试的是字符串元素而不是整数元素。
将type 直接应用于系列
基于this answer,我决定使用.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)
事实证明,这是我发现的最快的方法。