数据科学家的排序技巧

原题 | Surprising Sorting Tips for Data Scientistshtml

做者 | Jeff Halepython

原文 | towardsdatascience.com/surprising-…git

译者 | kbsc13("算法猿的成长"公众号做者)github

声明 | 翻译是出于交流学习的目的,欢迎转载,但请保留本文出于,请勿用做商业或者非法用途算法

导读

这篇文章介绍了 Python 中几个经常使用库的排序技巧,包括原生 Python的、Numpy、Pandas、PyTorch、TensorFlow 以及 SQL。sql

前言

如今其实有很大基础的排序算法,其中有的算法速度很快并且只须要不多的内存,有的算法更适合用于数据量很大的数据,有的算法适合特定排序的数据,下面的表格给出了大部分经常使用的排序算法的时间复杂度和空间复杂度:shell

对于大部分数据科学问题,并不须要精通全部排序算法的基础实现。事实上,过早进行优化有时候会被认为是全部错误的根源。不过,了解哪一个库以及须要使用哪些参数进行排序是很是有帮助的,下面是我作的一份小抄:数据库

接下来将分别介绍上述这几个库的排序方法,不过首先是介绍本文用到的这几个库的版本,由于不一样版本的排序方法可能会有些不一样:编程

python 3.6.8
numpy 1.16.4
pandas 0.24.2
tensorflow==2.0.0-beta1  #tensorflow-gpu==2.0.0-beta1 slows sorting
pytorch 1.1
复制代码

Python

Python 包含两个内置的排序方法:api

  • my_list.sort() 会修改列表自己的排序顺序,应该它返回值是 None
  • sorted(my_list) 是复制一份列表并进行排序,它不会修改原始列表的数值,返回排序好的列表。

sort 方法是二者中速度更快的,由于是修改列表自己的关系。但这种操做是很是危险的,由于会修改原始数据。

两种排序方法的默认排序方式都是升序--由小到大。大部分排序方法均可以接受一个参数来改变排序方式为降序,不过,不幸的是,每一个库的这个参数名字都不相同。

在 python 中,这个参数名字是 reverse,若是设置 reverse=True 表示排序方式是降序--从大到小。

key 也是一个参数名字,能够用于建立本身的排序标准,好比sort(key=len) 表示根据元素的长度进行排序。

在 python 中的惟一排序算法是TimsortTimsort是源自归并排序和插入排序,它会根据须要排序的数据的特征选择排序方法。好比,须要排序的是一个短列表,就选择插入排序方法。更详细的Timsort实现能够查看 Brandon Skerritt 的文章:

skerritt.blog/timsort-the…

Timsort是一个稳定的排序算法,这表示对于相同数值的元素,排序先后会保持原始的顺序。

对于 sort()sorted() 两个方法的记忆,这里提供一个小技巧,由于sorted() 是一个更长的词语,因此它的运行速度更长,由于须要作一个复制的操做。

Numpy

Numpy 是 Python 用于科学计算的基础库,它一样也有两个排序方法,一个改变数组自己,另外一个进行复制操做:

  • my_array.sort() 修改数组自己,但会返回排序好的数组;
  • np.sort(my_array) 复制数组并返回排序好的数组,不会改变原始数组

下面是两个方法可选的参数:

  • axis 整数类型,表示选择哪一个维度进行排序,默认是 -1,表示对最后一个维度进行排序;
  • kind 排序算法的类型,可选为 {quicksort’, ‘mergesort’, ‘heapsort’, ‘stable’},排序算法,默认是快速排序--quicksort
  • order 当数组 a 是定义了字段的,这个参数能够决定根据哪一个字段进行比较。

不过须要注意的是这个排序算法的使用和对这些参数名字的期待会有所不一样,好比传递kind=quicksort实际上采用的是一个 introsort 算法,这里给出 numpy 的文档解释:

当没有足够的进展的时候,会转成堆排序算法,它可让快速排序在最糟糕的状况的时间复杂度是 O(n*log(n))

stable会根据待排序数据类型自动选择最佳的稳定排序算法。而若是选择 mergesort 参数,则会根据数据类型采用 timsort 或者 radix sort 。由于 API 的匹配性限制了选择实现方法而且也固定了对不一样数据类型的排序方法。

Timsort是用于排序好的或者接近排序好的数据,对于随机排列的数据,它的效果几乎和 mergesort 同样。目前它是做为排序算法,而若是没有设置 kind 参数,默认选择仍是快速排序quicksort ,而对于整数数据类型,'mergesort' 和 'stable' 被映射为采用 radix sort 方法

上述来自 numpy 的文档解释,以及做者的部分修改:

github.com/numpy/numpy…

在上述介绍的几个库中,只有 numpy 是没有能够控制排序方式的参数,不过它能够经过切片的方式快速反转一个数组--my_arr[::-1]

numpy 的算法参数在更加友好的 pandas 中能够继续使用,而且我发现函数能够很容易就保持。

Pandas

Pandas 中对 DataFrame 的排序方法是 df.sort_values(by=my_column) ,参数有:

  • bystr 或者是 list of str ,必须指定。根据哪一个或者哪些列进行排序。若是参数axis 是 0 或者 index ,那么包含的就是索引级别或者是列标签。若是 axis 是 1 或者 columns ,那么包含的就是列级别或者索引标签。
  • axis{0 or index, 1 or columns},默认是 0。排序的轴
  • ascending: bool 或者list of bool 。默认是 True 。排序方式,升序或者降序,能够指定多个值,但数量必须匹配 by 参数的数量。
  • inplacebool ,默认是 False 。若是是真,那就是修改自己数值,不然就是复制一份;
  • kind{quicksort, mergesort, heapsort, stable},默认是 quicksort。排序算法的选择。详情能够看看numpyndarray.np.sort 。在 pandas 中这个参数只会在对单个标签或者列中使用
  • na_position{'first', 'last'} 。默认是 'last' 。这是指定 NaN 放置的位置,first 是将其放在开头,last 就是放在末尾。

对于 Series 相似也是一样的排序方法。但Series 并不须要指定 by 参数,由于不会有多列。

因为底层实现是采用 numpy ,因此一样能够获得很好的优化排序选项,但 pandas 由于其便利性会额外耗时一点。

默认对单列的排序算法是采用 Numpy 的 quicksort ,固然实际上调用的排序算法是 introsort ,由于堆排序会比较慢。而对于多列的排序算法,Pandas 确保采用的是 Numpy 的 mergesort ,但实际上会采用 Timsort 或者 Radix sort 算法。这两个都是稳定的排序算法,而且对多列进行排序的时候也是必须采用稳定的排序算法。

对于 Pandas,必须记住的是这些关键知识点是:

  • 排序方面的名字:sort_values()
  • 须要指定参数 by=column_name 或者是一个列名字的列表
  • 倒序的关键参数是 ascending
  • 稳定排序是采用 mergesort 参数值

在作数据探索分析的时候,通常在对 DataFrame 作求和和排序数值的时候都采用方法 Series.value_counts()。这里介绍一个代码片断用于对每列出现次数最多的数值进行求和和排序:

for c in df.columns:
  print(f"---- {c} ----")
  print(df[c].value_counts().head())
复制代码

Dask ,是一个基于 Pandas 的用于处理大数据的库,尽管已经开始进行讨论,直到2019年秋天的时候,尚未实现并行排序的功能。关于这个库,其 github 地址:

github.com/dask/dask

若是是小数据集,采用 Pandas 进行排序是一个不错的选择,可是数据量很大的时候,想要在 GPU 上并行搜索,就须要采用 TensorFlow 或者 PyTorch 了。

TensorFlow

TensorFlow 是目前最流行的深度学习框架,这里能够看下我写的这篇对比不一样深度学习框架的流行性和使用方法的文章:

towardsdatascience.com/which-deep-…

下面的介绍都是 TensorFlow 2.0 版本的 GPU 版本。

在 TensorFlow 中,排序方法是 tf.sort(my_tensor) ,返回的是一个排序好的 tensor 的拷贝。可选的参数有:

  • axis{int, optional},选择在哪一个维度进行排序操做。默认是 -1,表示最后一个维度。
  • direction{ascending or discending}。升序仍是降序。
  • name{str, optional}。给这个操做的命名。

tf.sort 采用的是 top_k 方法,而 top_k 是采用 CUB 库来使得 CUDA GPUs 更容易实现并行化操做。正如官方文档说的:

CUB 提供给 CUDA 编程模型的每一层提供了最好的可复用的软件组件。

TensorFlow 的排序算法经过 CUB 库采用在 GPU 上的 radix sort ,详细介绍能够查看:

github.com/tensorflow/…

TensorFlow 的 GPU 信息能够查看:

www.tensorflow.org/install/gpu

若是须要让 GPU 兼容 2.0 版本,须要采用下列安装命令:

!pip3 install tensorflow-gpu==2.0.0-beta1
复制代码

下面这个代码能够查看是否每行代码都在 GPU 或者 CPU 上运行:

tf.debugging.set_log_device_placement(True)
复制代码

若是须要指定使用一个 GPU, 代码以下所示:

with tf.device('/GPU:0'):
  %time tf.sort(my_tf_tensor)
复制代码

若是是想用CPU,只须要将上述代码第一行修为: with tf.device('/CPU:0'),也就是替换 GPU 为 CPU 便可。

tf.sort() 是很是容易记住的方法,另外就是记住须要改变排序顺序,是修改参数 direction

PyTorch

PyTorch 的排序方法是:torch.sort(my_tensor),返回的也是排序好的 tensor 的拷贝,可选参数有:

  • dim{dim, optional}。排序的维度。
  • descending{bool, optional}。控制排序的顺序(升序仍是降序)
  • out{tuple, optional}Tensor, LongTensor 的输出元祖,可用于做为输出的缓存。

经过下列代码来指定采用 GPU:

gpu_tensor=my_pytorch_tensor.cuda()
%time torch.sort(gpu_tensor)
复制代码

PyTorch 在面对一个数据量大于一百万行乘10万列的数据集的时候,是经过 Thrust 实现分割的并行排序。

但不幸的是,我尝试在谷歌的 Cola 上经过 Numpy 构建一个 1.1M * 100 K 的随机数据集的时候出现内存不足的错误,而后尝试用 GCP 的 416 MB,出现一样的内存不足的错误。

Thrust 是一个并行算法库,可使得性能在 GPUs 和多核 GPUs 之间移植。它能够自动选择最有效率的排序算法实现。而刚刚介绍的 TensorFlow 使用的 CUB 库是对 Thrust 的封装。因此 PyTorch 和 TensorFlow 都采用类似的排序算法实现方式。

和 TensorFlow 同样,PyTorch 的排序方法也是很是直接,很容易记住:torch.sort()。二者稍微不一样的就是排序顺序的参数名字:TensorFlow 是 direction,而 PyTorch 是 descending 。另外,不要忘记经过 .cuda() 方法指定采用 GPU 来提升对大数据集的计算速度。

在大数据集经过 GPU 进行排序是很好的选择,但直接在 SQL 上排序也是有意义的。

SQL

在 SQL 中进行排序一般都是很是快速,特别是数据加载到内存中的时候。

SQL 只是一个说明书,并无指定排序算法的具体实现方式。好比 Postgres 根据环境选择采用 disk merge sort ,或者 quick sort 。若是内存足够,可让数据加载在内存中,提升排序的速度。经过设置 work_mem 来增长可用的内存,具体查看:

wiki.postgresql.org/wiki/Tuning…

其余的 SQL 数据库采用不一样的排序算法,好比根据下面这个回答,谷歌的 BigQuery 经过一些技巧实现 introsort

stackoverflow.com/a/53026600/…

在 SQL 中进行排序是经过命令 ORDER_BY ,这个用法和 python 的实现仍是有区别的。但它这个命令名字很独特,因此很容易记住。

若是是实现降序,采用关键词 DESC。因此查询顾客的名字,并根据字母表的倒序来返回的语句是以下所示:

SELECT Names FROM Customers
ORDER BY Names DESC;
复制代码

比较

对上述介绍的方法,我都作了一个分析,采用一样的 100万数据,单列,数组或者列表的数据格式。使用的是谷歌的 Colab Jupyter Notebook,而后硬件方面是 K80 GPU, Intel(R) 的 Xeon(R) CPU @2.30GHZ。

源码地址:colab.research.google.com/drive/1NNar…

对比结果以下所示:

根据上图可知:

  • GPU 版本的 PyTorch 是速度最快的;
  • 对于 numpy 和 pandas,采用 inplace 都比拷贝数据更快;
  • 默认的 pandas 的 quicksort 速度很快
  • 大部分 pandas 的相同排序算法实现都会慢过 numpy
  • TensorFlow 在 CPU 上速度很快,而 TensorFlow-gpu 版本在 CPU 上使用会变慢,在 GPU 上排序更慢,看起来这多是一个 bug;
  • 原生的 Python inplace 的排序速度很是慢,对比最快的 GPU 版的 PyTorch 要慢接近 100 倍。屡次测量这个方法来确保这不是异常状况。

另外,这就是一个小小的测试,绝对不是权威的结果。

总结

最后,一般咱们都不须要本身实现排序算法,目前各个库实现的方法以及很强大了。它们也并非只采用一种排序算法,都是经过对不一样类型的数据进行测试不一样的排序算法,从而选择不一样状况下最佳的排序算法,甚至有的实现会改进算法自己来提升排序的速度。

本文介绍了在不一样的 Python 库和 SQL 进行排序的方法,通常来讲只须要记得采用哪一个参数实现哪一个操做,而后下面是个人一些建议:

  • 对比较小的数据集,采用 Pandas 的默认的 sort_values() 进行数据探索分析;
  • 对于大数据集,或者须要优先考虑速度,尝试 numpy 的inplacemergesort ,或者 PyTorch 、TensorFlow 在 GPU 上的并行实现,或者是 SQL。

关于在 GPU 进行排序的作法,能够查看这篇文章:

devtalk.nvidia.com/default/top…


参考

  1. docs.python.org/3/library/s…
  2. docs.python.org/3/library/f…
  3. skerritt.blog/timsort-the…
  4. docs.scipy.org/doc/numpy-1…
  5. docs.scipy.org/doc/numpy-1…
  6. en.wikipedia.org/wiki/Intros…
  7. github.com/numpy/numpy…
  8. github.com/dask/dask
  9. www.tensorflow.org/versions/r2…
  10. towardsdatascience.com/which-deep-…
  11. nvlabs.github.io/cub/
  12. github.com/tensorflow/…
  13. thrust.github.io/
  14. madusudanan.com/blog/all-yo…
  15. wiki.postgresql.org/wiki/Tuning…
  16. stackoverflow.com/a/53026600/…
相关文章
相关标签/搜索