【问题标题】:Vectorized Custom function not working as expected In pandas矢量化自定义功能无法按预期在熊猫中工作
【发布时间】:2020-10-06 09:24:32
【问题描述】:

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的初学者

【问题讨论】:

    标签: python python-3.x pandas data-science vectorization


    【解决方案1】:

    在 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)
    

    事实证明,这是我发现的最快的方法。

    【讨论】:

      猜你喜欢
      • 2020-08-22
      • 1970-01-01
      • 2022-01-17
      • 2020-02-26
      • 1970-01-01
      • 2013-03-29
      • 1970-01-01
      • 2016-11-12
      相关资源
      最近更新 更多