1000+倍!超强Python『向量化』数据处理提速攻略


标星★置顶公众号     爱你们   

作者:Cheever

编译:1+1=6 

今天公众号给大家好好讲讲基于Pandas和NumPy,如何高速进行数据处理!

1

向量化

1000倍的速度听起来很夸张。Python并不以速度著称。这是真的吗?当然有可能 ,关键在于你如何操作!

如果在数据上使用for循环,则完成所需的时间将与数据的大小成比例。但是还有另一种方法可以在很短的时间内得到相同的结果,那就是向量化。

这意味着要花费15秒的时间来编写代码,并且在15毫秒的时间内跑出结果。

当然,根据数据集的不同,库文件、硬件版本的不同,所以实际结果可能会有所不同。

公众号也多次发过相关文章:

1、30倍!使用Cython加速Python代码

2、71803倍!超强Pandas循环提速攻略

3、CuPy:将Numpy提速700倍!

4、10个提高工作效率的Pandas小技巧

5、高逼格使用Pandas加速代码,向for循环说拜拜!

那么什么是向量化?

简而言之,向量化是一种同时操作整个数组而不是一次操作一个元素的方法,这也得益于Numpy数组。

我们先导入测试数据:

第一次向量化测试:

以这个函数为例。这是一个非常基本的条件逻辑,我们需要为lead status创建一个新列。

我们使用Pandas的优化循环函数apply(),但它对我们来说太慢了。

或者使用如下方法:

接下来,我们尝试一下使用向量化。将整个Series作为参数传递到函数中,而不是对每一行。

但没有成功。if语句试图确定Series作为一个整体的真实性,而不是比较Series中的每个元素,所以这是错误的

2

numpy.where()

语法很简单,就像Excel的IF()。

第一个参数是逻辑条件Numpy,它将为数组中的每个元素计算一个布尔数组。当条件满足且为True时,将返回第二个参数,否则返回第三个参数。

看下面的例子:

numpy.where()它从我们的条件中创建一个布尔数组,并在条件为真或假时返回两个参数,它对每个元素都这样做。这对于在Dataframe中创建新列非常有用。

比apply函数快344倍!

如果我们在Series添加了.values ,它的作用是返回一个NumPy数组,里面是我的级数中的数据。

现在的numpy.where(),只查看数组中的原始数据,而不必负责Pandas Series带来的内容,如index或其他属性。这个小的变化通常会在时间上产生巨大的差异。

各位!一开始,我们应用的if/else函数的时间超过了8秒,现在我们已经将其缩短到不到9毫秒,这几乎是一个1000倍的转换!

3

numpy.vectorize()

这个函数将把Python函数转换成NumPy ufunc,这样它就可以处理向量化的方法。它向量化了你的函数,而不一定是这个函数如何应用于你的数据,这有很大的不同!

例子如下:

vectorize()将常规的Python函数转换成Numpy ufunc(通用函数),这样它就可以接收Numpy数组并生成Numpy数组。vectorize()主要是为了方便,而不是为了性能。实质上是一个for loop。

我们可以使用它的一种方式,包装我们之前的函数,在我们传递列时不起作用的函数,并向量化它。它比.apply()快得多,但也比.where()慢了17倍。所以在这种情况下,将坚持使用np.where()!

一些人认为这更快:使用index设置,但事实证明它实际上不是向量化!

代码如下:

4

Multiple conditions

类似这样的多个if/elif/elifs,如何向量化呢?

你可以调用np.where在任何情况下,代码长了就变得有点难读了

实际上有一个函数专门可以做多重条件的向量化,是什么呢?

5

numpy.select()

向量化if...elif...else。更简洁(甚至更快)和做多重嵌套np.where

np.select()的一个优点是它的layout。

你可以用你想要检查的顺序来表达你想要检查的条件。np.select将按从前到后的顺序对每个数组求值,当数据集中的某个给定元素的第一个数组为True时,将返回相应的选择。所以操作的顺序很重要!像np.where。其中,你的选择可以是标量,也可以是数组。只要它符合你的条件。

这是我们第一次尝试将多个条件从.apply()方法转换为向量化的解决方案。向量化选项将在0.1秒多一点的时间内返回列,.apply()将花费12.5秒。嵌套的np.where()解决方案工具179ms。

那么嵌套的多个条件,我们可以向量化吗?可以!

代码:

基本上,当使用np.select()时。根据经验,你需要为每个return语句设置n个条件,这样就可以将所有布尔数组打包到一个条件中,以返回一个选项。

代码如下

如果添加了.values:

4

更复杂的

有时必须使用字符串,有条件地从字典中查找内容,比较日期,有时甚至需要比较其他行的值。我们来看看!

1、字符串

假设你需要在一系列文本中搜索特定的模式,如果匹配,则创建一个新的series这是一种.apply方法。

np.vectorize()时:

同时,当使用向量化方法处理字符串时,Pandas为我们提供了向量化字符串操作的.str()。contains基本上和re.search做的是一样的,它会给我们相同的结果。

为什么.str向量化这么慢?

字符串操作很难并行化,所以.str方法是向量化的,这样就不必为它们编写for循环。使用.apply执行基本的Python是更快的选择。

一般来说,我们还建议你使用str方法来避免循环,但是如果你的速度变慢了,这会让你很痛苦,试试循环是否能帮你节省一些时间。

2、字典lookups

对于进行字典查找,我们可能会遇到这样的情况,如果为真,我们希望从字典中获取该series键的值并返回它,就像下面代码中的下划线一样。

你可以使用.map()在向量化方法中执行相同的操作。

3、日期

有时你可能需要做一些日期计算(确保你的列已经转换为datetime对象)。这是一个计算周数的函数。以天为单位的两个日期之差除以7得到过去的周数。下面是使用.apply()的方法。

有两种向量化方法。第一种方法是使用pandas .dt series datetime访问器。除了改变语法以适应np.where。我们要做的就是在.dt之前加上.days ,效果很好。

完成此计算的另一种更加Numpy向量化的方法是将Numpy数组转换为timedeltas,获得day值,然后除以7。这和最终结果是一样的,只是下面的那个代码更长。

4、使用来自其他行的值

在这个例子中,我们从Excel中重新创建了一个公式:

其中A列表示id,L列表示日期。

向量化所需要的所有函数都是在同一行上比较的值,这可以使用pandas.shift()实现!

确保你的数据正确排序,否则你的结果就没有意义!

很慢!

为了解决这个问题,我们对Pandas中的一个series使用.shift()将前一行移到相同的级别。一旦它们被转移到相同的级别,我就可以使用np.select()执行相同的条件向量化方法了!

5

其他

一种选择是使用apply跨CPU核并行化操作。因此,如果你有一个4核的i7,你可以将你的数据集分成4块,将你的函数应用到每一块,然后将结果合并在一起。注意:这不是一个很好的选择!

Dask是在Pandas API中工作的一个不错的选择。能够跨集群扩展到TB级的数据,或者甚至能够更有效地在一台机器上处理多核数据。

6

总结

向量化可以极大地加快速度!

  • np.where →一个逻辑条件

  • np.select →2+逻辑条件

如果你正在处理字符串/正则表达式函数,那么最好还是使用Python。或者如果你的逻辑重写起来很麻烦或者你不想重写,你可以考虑并行化应用函数或者像Dask这样的东西可以帮你实现。

最后,在优化之前一定要确保逻辑是合理的。

不成熟的优化是万恶之源!

2020年第9篇文章

量化投资与机器学习微信公众号,是业内垂直于Quant、MFE、Fintech、AI、ML等领域的量化类主流自媒体。公众号拥有来自公募、私募、券商、期货、银行、保险资管、海外等众多圈内18W+关注者。每日发布行业前沿研究成果和最新量化资讯。

你点的每个“在看”,都是对我们最大的鼓励