【问题标题】:apply / vectorize / speed up column-wise cleanup function to pandas dataframe对熊猫数据框应用/矢量化/加速按列清理功能
【发布时间】:2019-06-04 12:08:02
【问题描述】:

我有一些数据管道代码,它们根据名称将转换/清理逻辑应用于 Pandas 数据框的列。

现在我正在使用 df.iteritems() 对列进行迭代,根据 this guide 关于优化 Pandas 应用函数的说法,它比粗循环要好,但它是“运行大多数标准函数的效率最低的方法”。

我想通过利用 Pandas 对这些操作进行矢量化的能力或其他一些并行方法来提高此代码的性能。

我看到的所有工作示例都说明了如何逐行(例如,在系列上计算而不是在单行上计算),但我无法找到如何做到这一点的一个很好的例子按列

这是一个使用来自 scikit learn 的波士顿数据集的可重现/玩具示例。期望的结果是以矢量化/并行方式实现清理逻辑(不使用.iteritems() 或循环)。谢谢!

from typing import Callable

# sample df from sklearn
from sklearn import datasets
boston = datasets.load_boston()
boston = pd.DataFrame(boston.data, columns=boston.feature_names)
boston.head()

def double_it(col: pd.Series) -> pd.Series:
    return col.multiply(2)

def make_string(col: pd.Series) -> pd.Series:
    return col.astype(str)

def do_nothing(col: pd.Series) -> pd.Series:
    return col

def match_cleaner(col_name: str) -> Callable:
    if col_name in ['ZN', 'NOX', 'INDUS', 'AGE']:
        return double_it
    elif col_name in ['TAX', 'DIS', 'CHAS', 'PTRATIO']:
        return make_string
    else:
        print(col_name)
        return do_nothing

for key, value in boston.iteritems():
    cleaning_func = match_cleaner(key)
    boston.loc[:, key] = cleaning_func(value)

# confirm changes
boston.head()
print(boston.dtypes)

【问题讨论】:

    标签: python python-3.x pandas parallel-processing vectorization


    【解决方案1】:

    您可以使用pandas.DataFrame.applyapply 方法默认将提供的函数应用于数据框中的所有列。但是您需要稍微修改一下您的 match_cleaner 函数。

    def match_cleaner2(col):
         col_name = col.name
         if col_name in ['ZN', 'NOX', 'INDUS', 'AGE']:
             return double_it(col)
         elif col_name in ['TAX', 'DIS', 'CHAS', 'PTRATIO']:
             return make_string(col)
         else:
             return do_nothing(col)
    
    b2 = boston.apply(match_cleaner2)
    b2.head()
          CRIM             ZN          INDUS  ...   PTRATIO       B  LSTAT
    0  0.00632  3.932955e+246  5.047292e+245  ...      15.3  396.90   4.98
    1  0.02731   0.000000e+00  1.544777e+246  ...      17.8  396.90   9.14
    2  0.02729   0.000000e+00  1.544777e+246  ...      17.8  392.83   4.03
    3  0.03237   0.000000e+00  4.763245e+245  ...      18.7  394.63   2.94
    4  0.06905   0.000000e+00  4.763245e+245  ...      18.7  396.90   5.33
    
    %timeit boston.apply(match_cleaner2)
    3.68 ms ± 68.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    def original():
         for k, v in boston.iteritems():
             clean_f = match_cleaner(k)
             boston.loc[:, k] = clean_f(v)
    
    original()
    boston.head()
          CRIM             ZN          INDUS  ...   PTRATIO       B  LSTAT
    0  0.00632  3.932955e+246  5.047292e+245  ...      15.3  396.90   4.98
    1  0.02731   0.000000e+00  1.544777e+246  ...      17.8  396.90   9.14
    2  0.02729   0.000000e+00  1.544777e+246  ...      17.8  392.83   4.03
    3  0.03237   0.000000e+00  4.763245e+245  ...      18.7  394.63   2.94
    4  0.06905   0.000000e+00  4.763245e+245  ...      18.7  396.90   5.33
    
    
    pd.testing.assert_frame_equal(b2, boston) # boston was modified in place
    
    # No AssertionError means frames are equal
    
    %timeit original()
    6.14 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    所以从一个非常粗略的实验来看,apply 函数看起来可以加快 40% 左右的速度。

    【讨论】:

      猜你喜欢
      • 2022-01-17
      • 1970-01-01
      • 2015-09-30
      • 1970-01-01
      • 2018-02-05
      • 1970-01-01
      • 2019-05-13
      • 2021-12-28
      • 1970-01-01
      相关资源
      最近更新 更多