【问题标题】:How to pass a large number of dataframe columns to numpy vectorize as argument如何将大量数据框列传递给 numpy 向量化作为参数
【发布时间】:2020-07-08 04:57:58
【问题描述】:

我有一个正好有 31 列,例如 100 行的数据框。

我需要创建一个包含 100 个字典的列表,这些字典的值来自不同的 31 列。

我目前正在使用apply() 函数来执行此操作:

my_df.apply(lambda row: _build_data(row, param1, param2, param3), axis=1)

但现在我想探索 numpy vectorize() 的可能性。问题是,从我正在阅读的内容来看,我应该将每一列作为单独的参数传递给它:

np.vectorize(_build_data)(my_df[col1], my_df[col2], ..., my_df[col31], param1, param2, param3)

这看起来不像 Python,我也不想定义一个有 34 个参数的函数。

您知道是否有其他方法可以做到这一点?

非常感谢您的帮助!

【问题讨论】:

  • np.vectorize 不是速度工具。尽管有这个名字,但它并不是“矢量化”可以加速你的代码。

标签: python-3.x pandas numpy vectorization apply


【解决方案1】:

正如您从np.vectorize 中看到的那样:提供矢量化功能主要是为了方便,而不是为了提高性能。该实现本质上是一个 for 循环。

因此正如 hpaulj 所说,它不会加快您的代码速度

但是,如果您仍然想使用它,您不必键入所有列,只需使用列表推导:

np.vectorize(_build_data)([my_df[c] for c in list(my_df)], param1, param2, param3)

【讨论】:

    【解决方案2】:

    我怀疑你在尝试使用 np.vectorize,因为你读到 numpy 的“向量化”是加速 pandas 代码的一种方式。

    In [29]: df = pd.DataFrame(np.arange(12).reshape(4,3), columns=['A','B','C'])                  
    In [30]: df                                                                                    
    Out[30]: 
       A   B   C
    0  0   1   2
    1  3   4   5
    2  6   7   8
    3  9  10  11
    

    逐行缓慢的取行均值的方法:

    In [31]: df.apply(lambda row: np.mean(row), axis=1)                                            
    Out[31]: 
    0     1.0
    1     4.0
    2     7.0
    3    10.0
    dtype: float64
    

    快速的numpy方法:

    In [32]: df.to_numpy()                                                                         
    Out[32]: 
    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 6,  7,  8],
           [ 9, 10, 11]])
    In [33]: df.to_numpy().mean(axis=1)                                                            
    Out[33]: array([ 1.,  4.,  7., 10.])
    

    也就是说,我们得到一个数据帧值的数组,并使用快速编译的方法来计算行均值。

    但是要为每一行制作类似于字典的东西:

    In [35]: df.apply(lambda row: {str(k):k for k in row}, axis=1)                                 
    Out[35]: 
    0        {'0': 0, '1': 1, '2': 2}
    1        {'3': 3, '4': 4, '5': 5}
    2        {'6': 6, '7': 7, '8': 8}
    3    {'9': 9, '10': 10, '11': 11}
    dtype: object
    

    我们必须迭代数组行,就像我们对数据框 apply 所做的那样:

    In [36]: [{str(k):k for k in row} for row in df.to_numpy()]                                    
    Out[36]: 
    [{'0': 0, '1': 1, '2': 2},
     {'3': 3, '4': 4, '5': 5},
     {'6': 6, '7': 7, '8': 8},
     {'9': 9, '10': 10, '11': 11}]
    

    数组方法更快:

    In [37]: timeit df.apply(lambda row: {str(k):k for k in row}, axis=1)                          
    1.13 ms ± 702 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    In [38]: timeit [{str(k):k for k in row} for row in df.to_numpy()]                             
    40.8 µs ± 157 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    但是apply 方法返回一个数据帧,而不是一个列表。我怀疑大部分额外的时间都在这一步。

    np.vectorize(和np.frompyfunc)也可用于迭代数组,但默认是迭代元素,而不是行或列。一般来说,它们比更显式的迭代要慢(就像我在 [36] 中所做的那样)。

    一种从列表中创建数据框的笨方法:

    In [53]: %%timeit 
        ...: df1 = pd.DataFrame(['one','two','three','four'],columns=['d'])   
        ...: df1['d'] =[{str(k):k for k in row} for row in df.to_numpy()]                                                                                       
    572 µs ± 18.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    【讨论】:

    • @hjpaulj 感谢您的详细回复。由于 df.to_numpy 不保留列名,我用 df.to_dict('records') 探索了时间,它和 apply 都非常相似。我将继续使用 apply,因为我还没有找到任何其他方法可以显着提高我的代码速度。
    • dt,to_records() 制作结构化数组
    猜你喜欢
    • 2014-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 2014-11-04
    • 2012-03-03
    相关资源
    最近更新 更多