如果你擅长使用Pandas转换数据、创建特征、清理数据等,那么你可以轻松使用Dask和Numba并行加速你的工作.单论速度,Dask打败Python,Numba打败Dask,所以Numba+Dask基本无敌。将数值计算拆分为Numba子函数并使用Daskmap_partition+apply而不是使用Pandas。对于100万行数据,使用Pandas方法和混合数值计算创建新特征的速度比使用Numba+Dask方法慢很多倍。蟒蛇:60.9x|达斯克:8.4倍|Numba:5.8x|Numba+Dask:1x作为旧金山大学的数据科学硕士,我经常和数据打交道。使用Apply函数是我用来创建新功能或清理数据的众多技巧之一。现在,我只是一名数据科学家,不是计算机科学专家,但我是一名喜欢修补并让代码运行得更快的程序员。现在,我将分享我在并行应用程序方面的经验。大多数Python爱好者可能都知道Python实现的全局解释器锁(GIL),它占用了计算机中的所有CPU能力。更糟糕的是,我们的主要数据处理包,如Pandas,很少启用并行处理代码。应用函数vsMultiprocessing.map%timedf.some_col.apply(lambdax:clean_transform_kthx(x))Walltime:HAH!RIPBUDDY#WHYYOUNORUNINPARALLEL!?Tidyverse已经在处理数据方面做了一些不错的事情,Plyr是我最喜欢的数据包之一,它允许R用户轻松并行化他们的数据应用程序。HadleyWickham说:“plyr是一套处理一组问题的工具:需要将一个大的数据结构分解成一些统一的数据块,然后对每个数据块应用一个函数,最后将所有结果组合在一起。”对于Python,我希望有一个像plyr这样的包可用。然而,目前这样的包不存在,但我能够使用并行包组成一个简单的解决方案。Dask之前在Spark上花过一些时间,所以在开始使用Dask的时候,比较容易掌握它的主要内容。Dask旨在能够在多核CPU上并行处理任务,并且还借鉴了Pandas的许多语法规则。现在开始本文中的示例。对于最近的数据挑战,我试图获取外部数据源(包含许多地理编码点)并将其与大量社区进行匹配以进行分析。在计算欧氏距离时,使用最大启发式将最大值分配给邻域。初始应用:my_df.apply(lambdax:nearest_street(x.lat,x.lon),axis=1)Dask应用:dd.from_pandas(my_df,npartitions=nCores).\map_partitions(\lambdadf:df.apply(\lambdax:nearest_street(x.lat,x.lon),axis=1)).\compute(get=get)#importsattheend两者看起来很像,apply的核心语句是map_partitions,有一个compute()语句。此外,必须初始化npartitions。分区通过将Pandas数据帧分成块来工作。对于我的电脑,配置是6核-12线程。我只需要告诉它使用12个分区,Dask会完成剩下的工作。接下来,将map_partitions的lambda函数应用于每个分区。由于大部分数据处理代码都是独立运行的,因此您不必太担心这些操作的顺序。***,compute()函数告诉Dask处理剩下的,然后给我最终的计算结果。在这里,compute()调用Dask将apply应用于每个分区并使其并行处理。由于我是通过迭代行生成一个新的队列(特征),而Daskapply只作用于列,所以我没有使用Daskapply,下面是Dask程序:一个线性操作(基本上是勾股定理)对数据进行分类,所以认为像下面的Python代码这样的东西会运行得更快一些。foriinintersections:l3=np.sqrt((i[0]-[1])**2+(i[2]-i[3])**2)#...Somemoreofthesedist=l1+l2ifdist<(l3*1.2):matches.append(dist)#...Morestuff###yougettheidea,有一个for-loopcheckingtoseeif###mypointsareclosetomystreetsandthenreturningclosest###Ievenusednumpy,这意味着快对吧?Broadcasting是NumpyComputing处理机制中用来描述两个不同形状矩阵的数学运算。假设我有一个数组,我将通过逐个迭代和转换每个单元格来更改它#overonearrayforcellinarray:cell*CONSTANT-CONSTANT2#overtwoarraysforiinrange(len(array)):array[i]=array[i]+array2[i]相反,我可以完全跳过for循环,并对整个数组执行操作。Numpy与广播混合以执行元素乘积(按位乘法)。#overonearray(array*CONSTANT)-CONSTANT2#overtwoarraysofsamelength#differentlengthsfollowbroadcastingrulesarray=array-array2Broadcasting可以实现更多的功能,现在看骨架代码:fromnumbaimportjit@jit#numbamagicdefsome_func()l3_arr=np.sqrt((intersections[:,0]-intersections[:,1])**2+\(intersections[:,2]-intersections[:,3])**2)#nowl3isanarraycontainingallofmyblocklengths#likewise,l1andl2arenowequalsizedarrays#containingdistanceofpointtoallintersectionsdist=l1_arr+l2_arrmatch_arr=dist<(l3_arr*1.2)#soinsteadofterating,Ijustimmediatelycompareallofmy#point-to-streetdistancesatonceandhaveahandy#booleanindex本质上,代码的功能是改变数组。好处是运行速度非常快,在并行处理速度上什至可以和Dask相提并论。其次,如果您使用的是最低限度的Numpy和Python,则可以即时编译任何函数。不好的一面是它只适用于Numpy和简单的Python语法。我必须将所有数值计算从我的函数转换为子函数,但计算速度会提高得非常快。简单地使用map_partition()将Numba函数与Dask结合使用,如果并行性和广播紧密配合以加快速度,您将看到大型数据集的加速。大幅改善。上面第一张图表明,没有广播的线性计算表现不佳,并行处理和Dask对速度提升也有效果。此外,可以清楚地发现Dask和Numba的组合优于其他方法。上面第二张图稍微复杂一点,它的横坐标是行数的对数。从第二张图可以发现,对于1k到10k的小数据集,单独使用Numba的性能要优于结合使用Numba+Dask,虽然Numba+Dask在大数据集上的性能非常好好的。优化为了能够使用Numba进行JIT编译,我重写了函数以更好地利用广播。之后,重新运行这些函数发现,对于相同的代码,JIT的平均执行速度提高了24%。可以肯定地说,必须有进一步的优化方法才能使执行速度更快,但目前还没有找到。Dask是一个非常友好的工具。本文使用Dask+Numba取得的最大成果是运行速度提升了60倍。如果你知道其他提高执行速度的技巧,欢迎在留言区分享。作者信息ErnestKim是旧金山大学的硕士生,专注于机器学习和数据科学。
