栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

熊猫面具/哪里方法与NumPy np.where

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

熊猫面具/哪里方法与NumPy np.where

我使用的是pandas 0.23.3和Python 3.6,因此仅在您的第二个示例中,我才能看到运行时间的真正差异。

但是,让我们研究一下第二个示例的稍有不同的版本(这样我们就可以避免

2*df[0]
了)。这是我们计算机上的基准:

twice = df[0]*2mask = df[0] > 0.5%timeit np.where(mask, twice, df[0])  # 61.4 ms ± 1.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)%timeit df[0].mask(mask, twice)# 143 ms ± 5.27 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Numpy的版本比熊猫快2.3倍。

因此,让我们对这两个函数进行概要分析,以了解两者之间的区别-
当人们不太熟悉代码基础时,概要分析是一种了解全局的好方法:它比调试更快,并且比试图弄清楚发生什么情况更容易出错只需阅读代码即可。

我在Linux上使用

perf
。对于numpy的版本,我们得到(有关列表,请参阅附录A):

>>> perf record python np_where.py>>> perf reportOverhead  Command  Shared Object          Symbol          68,50%  python   multiarray.cpython-36m-x86_64-linux-gnu.so   [.] PyArray_Where   8,96%  python   [unknown]   [k] 0xffffffff8140290c   1,57%  python   mtrand.cpython-36m-x86_64-linux-gnu.so       [.] rk_random

我们可以看到,大部分时间都花在了

PyArray_Where
-大约69%上。未知符号是一个内核函数(事实上
clear_page
)-我在没有root特权的情况下运行,因此该符号无法解析。

对于大熊猫,我们得到了(代码参见附录B):

>>> perf record python pd_mask.py>>> perf reportOverhead  Command  Shared Object          Symbol         37,12%  python   interpreter.cpython-36m-x86_64-linux-gnu.so  [.] vm_engine_iter_task  23,36%  python   libc-2.23.so[.] __memmove_ssse3_back  19,78%  python   [unknown]   [k] 0xffffffff8140290c   3,32%  python   umath.cpython-36m-x86_64-linux-gnu.so        [.] DOUBLE_isnan   1,48%  python   umath.cpython-36m-x86_64-linux-gnu.so        [.] BOOL_logical_not

情况截然不同:

  • 熊猫并没有
    PyArray_Where
    在后台使用-最主要的时间消耗
    vm_engine_iter_task
    是numexpr-functionity。
  • 正在进行大量的内存复制-
    __memmove_ssse3_back
    大约
    25
    花费%的时间!内核的某些功能可能也与内存访问相关。

实际上,pandas-0.19

PyArray_Where
在引擎盖下使用,对于较旧的版本,perf-report报告看起来像:

Overhead  Command        Shared Object          Symbol    32,42%  python         multiarray.so          [.] PyArray_Where  30,25%  python         libc-2.23.so[.] __memmove_ssse3_back  21,31%  python         [kernel.kallsyms]      [k] clear_page   1,72%  python         [kernel.kallsyms]      [k] __schedule

因此,从根本上讲,它那时将

np.where
在幕后使用+一些开销(全部在数据复制之上,请参阅参考资料
__memmove_ssse3_back
)。

我看不到在熊猫的0.19版本中大熊猫会比numpy更快的情况-它只是增加了numpy功能的开销。熊猫的0.23.3版本是一个完全不同的故事-
这里使用numexpr-module,很可能在某些情况下熊猫的版本(至少稍微快一些)。

我不确定是否真的需要/必须进行这种内存复制-也许有人甚至可以称它为性能缺陷,但我只是不知道可以肯定什么。

我们可以通过剥离一些间接指示(通过

np.array
而不是
pd.Series
)来帮助熊猫不要复制。例如:

%timeit df[0].mask(mask.values > 0.5, twice.values)# 75.7 ms ± 1.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

现在,熊猫只慢25%。性能说明:

Overhead  Command  Shared Object          Symbol          50,81%  python   interpreter.cpython-36m-x86_64-linux-gnu.so  [.] vm_engine_iter_task  14,12%  python   [unknown]   [k] 0xffffffff8140290c   9,93%  python   libc-2.23.so[.] __memmove_ssse3_back   4,61%  python   umath.cpython-36m-x86_64-linux-gnu.so        [.] DOUBLE_isnan   2,01%  python   umath.cpython-36m-x86_64-linux-gnu.so        [.] BOOL_logical_not

数据复制要少得多,但是比numpy的版本要多,后者主要负责开销。

我的主要收获是:

  • 熊猫有可能至少比numpy快一点(因为有可能更快)。但是,大熊猫对数据复制的处理有些不透明,因此很难预测何时(由于不必要的数据复制)会掩盖这种潜力。

  • where
    /的性能
    mask
    成为瓶颈时,我将使用numba / cython来提高性能-请参阅下文,我比较幼稚的尝试使用numba和cython。


这个想法是要

np.where(df[0] > 0.5, df[0]*2, df[0])

版本,并消除了创建临时文件(即)的需要

df[0]*2

正如@ max9111所建议的那样,使用numba:

import numba as nb@nb.njitdef nb_where(df):    n = len(df)    output = np.empty(n, dtype=np.float64)    for i in range(n):        if df[i]>0.5: output[i] = 2.0*df[i]        else: output[i] = df[i]    return outputassert(np.where(df[0] > 0.5, twice, df[0])==nb_where(df[0].values)).all()%timeit np.where(df[0] > 0.5, df[0]*2, df[0])# 85.1 ms ± 1.61 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)%timeit nb_where(df[0].values)# 17.4 ms ± 673 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这比numpy的版本快5倍!

这是我到目前为止在Cython的帮助下提高性能的尝试,但效果较差:

%%cython -acimport numpy as npimport numpy as npcimport cython@cython.boundscheck(False)@cython.wraparound(False)def cy_where(double[::1] df):    cdef int i    cdef int n = len(df)    cdef np.ndarray[np.float64_t] output = np.empty(n, dtype=np.float64)    for i in range(n):        if df[i]>0.5: output[i] = 2.0*df[i]        else: output[i] = df[i]    return outputassert (df[0].mask(df[0] > 0.5, 2*df[0]).values == cy_where(df[0].values)).all()%timeit cy_where(df[0].values)# 66.7± 753 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

使速度提高25%。不确定,为什么cython比numba慢得多。


清单:

答: np_where.py:

import pandas as pdimport numpy as npnp.random.seed(0)n = 10000000df = pd.Dataframe(np.random.random(n))twice = df[0]*2for _ in range(50):      np.where(df[0] > 0.5, twice, df[0])

B: pd_mask.py:

import pandas as pdimport numpy as npnp.random.seed(0)n = 10000000df = pd.Dataframe(np.random.random(n))twice = df[0]*2mask = df[0] > 0.5for _ in range(50):      df[0].mask(mask, twice)


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/647028.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号