矢量化自定义函数未按预期工作 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)
这是我发现的最快的方法。
以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)
这是我发现的最快的方法。