数据科学家令人惊叹的排序技巧

2019 年第 80 篇文章,总第 104 篇文章

本文大约 7800 字,阅读大约需要20分钟

原题 | Surprising Sorting Tips for Data Scientists

作者 | Jeff Hale

原文 | https://towardsdatascience.com/surprising-sorting-tips-for-data-scientists-9c360776d7e

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

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

导读

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

前言

现在其实有很大基础的排序算法,其中有的算法速度很快而且只需要很少的内存,有的算法更适合用于数据量很大的数据,有的算法适合特定排序的数据,下面的表格给出了大部分常用的排序算法的时间复杂度和空间复杂度:

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

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

python 3.6.8numpy 1.16.4pandas 0.24.2tensorflow==2.0.0-beta1  #tensorflow-gpu==2.0.0-beta1 slows sortingpytorch 1.1

Python

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

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

sort 方法是两者中速度更快的,因为是修改列表本身的关系。但这种操作是非常危险的,因为会修改原始数据。

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

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

key 也是一个参数名字,可以用于创建自己的排序标准,比如sort(key=len) 表示根据元素的长度进行排序。

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

https://skerritt.blog/timsort-the-fastest-sorting-algorithm-youve-never-heard-of/

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 的文档解释,以及作者的部分修改:

https://github.com/numpy/numpy/blob/v1.16.1/numpy/core/fromnumeric.py#L815-L935

在上述介绍的几个库中,只有 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 地址:

https://github.com/dask/dask

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

TensorFlow

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

https://towardsdatascience.com/which-deep-learning-framework-is-growing-fastest-3f77f14aa318?source=friends_link&sk=0a10207f22f4dbc143e7a90a3f843515

下面的介绍都是 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 ,详细介绍可以查看:

https://github.com/tensorflow/tensorflow/issues/288

TensorFlow 的 GPU 信息可以查看:

https://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 来增加可用的内存,具体查看:

https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server

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

https://stackoverflow.com/a/53026600/4590385

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

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

SELECT Names FROM CustomersORDER BY Names DESC;

比较

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

源码地址:https://colab.research.google.com/drive/1NNarscUZHUnQ5v-FjbfJmB5D3kyyq9Av

对比结果如下所示:

根据上图可知:

  • 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 进行排序的做法,可以查看这篇文章:

https://devtalk.nvidia.com/default/topic/951795/fastest-sorting-algorithm-on-gpu-currently/


参考

  1. https://docs.python.org/3/library/stdtypes.html#list.sort
  2. https://docs.python.org/3/library/functions.html#sorted
  3. https://skerritt.blog/timsort-the-fastest-sorting-algorithm-youve-never-heard-of/
  4. https://docs.scipy.org/doc/numpy-1.16.0/reference/generated/numpy.ndarray.sort.html#numpy.ndarray.sort
  5. https://docs.scipy.org/doc/numpy-1.16.0/reference/generated/numpy.sort.html#numpy.sort
  6. https://en.wikipedia.org/wiki/Introsort
  7. https://github.com/numpy/numpy/blob/v1.16.1/numpy/core/fromnumeric.py#L815-L935
  8. https://github.com/dask/dask
  9. https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/sort
  10. https://towardsdatascience.com/which-deep-learning-framework-is-growing-fastest-3f77f14aa318?source=friends_link&sk=0a10207f22f4dbc143e7a90a3f843515
  11. https://nvlabs.github.io/cub/
  12. https://github.com/tensorflow/tensorflow/issues/288
  13. https://thrust.github.io/
  14. https://madusudanan.com/blog/all-you-need-to-know-about-sorting-in-postgres/
  15. https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
  16. https://stackoverflow.com/a/53026600/4590385

因为翻译水平有限,所以可能有些翻译不是很准确,请见谅,可以点击下方阅读原文查看原始的英文表达意思。

欢迎关注我的微信公众号--算法猿的成长,或者扫描下方的二维码,大家一起交流,学习和进步!

如果觉得不错,在看、转发就是对小编的一个支持!

推荐阅读

  • [Python技巧]是时候用 defaultdict 和 Counter 代替 dictionary 了
  • 快速入门 Jupyter notebook
  • Jupyter 进阶教程
  • 只需几行代码,即可实现多线程和多进程操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几个有趣的python技巧

2019 年第 82 篇文章,总第 106 篇文章标题 | python-is-cool作者 | chiphuyen原文 | https://github.com/chiphuyen/python-is-cool译者 | kbsc13("算法猿的成长"公众号作者)声明 | 翻译是出于交流学习的目的,欢迎转载,但请保留本文…

20191215周学习总结

最近会打算每周总结一下学习的内容,主要内容可能是看过的书的一些学习笔记、论文阅读、学习的知识点以及推荐一些文章。这周的学习包括:推荐系统的知识点整理机器学习的技巧学习linux下两台机器的ssh免密登陆方式书籍阅读效率方法推荐系统因为工作方向的…

玩转12306之系统登录

【申明:本文所涉及的技术和分析的目的都是为了学习和交流,任何人使用文中所提的技术或成果做出的违法事情与我无关,大家购买火车票还是去12306官网上去购买。】 从今天起,我开始分析12306网站的Http请求,以及编写一个客…

Nginx快速搭建和基本使用

2019年第 83 篇文章,总第 107 篇文章最近在工作中项目需要上线,所以也了解到关于一些部署上线的知识内容,Nginx 就是其中一个知识点,主要是可以用它来进行负载均衡,本文的目录如下:简介安装配置基本使用简介…

第二期周总结

第二期的周总结,这次学习的内容可能没有上次那么广泛,主要是因为这周我负责的模块需要测试并进行上线,所以主要学习了解的就是工程开发方面的内容,准确说是部署上线的内容,所以本周主要简单总结这次上线过程的一些内容…

AI知识点(1)--激活函数

2019年第 84 篇文章,总第 108 篇文章本文大约 5000 字,阅读大约需要 15 分钟AI知识点(AI Knowledge)系列第一篇文章--激活函数。本文主要的目录如下:激活函数的定义为什么需要激活函数常见的激活函数1. 激活函数的定义…

Linux 定时执行shell 脚本

2019年第 85 篇文章,总第 109 篇文章本文大约2000字,阅读大约需要6分钟crontab 可以在指定的时间执行一个shell脚本以及执行一系列 Linux 命令。定时执行shell 脚本简单给出执行 shell 脚本的步骤。首先是编写一个测试脚本--test.sh# 创建脚本 $ vim tes…

RS(1)--10分钟了解什么是推荐系统

总第 110 篇文章,本文大约 3200 字,阅读大约需要 10 分钟2020 年第一篇技术文章,以一个新的系列开始--推荐系统(Recommend System),第一篇文章会简单介绍推荐系统的定义和应用,目录如下&#xf…

当搭配遇上个性化推荐

总第 111 篇文章,本文大约 3000 字,阅读大约需要 10 分钟今天介绍的是一篇个性化搭配推荐的论文,是 2017 年时候的论文,这也是比较早的开始结合搭配和个性化推荐的一个工作,基于度量学习和排序学习的方法。论文题目&am…

2020年周记(1/50)

总第 112 篇文章,本文大约 1200 字,阅读大约需要 3 分钟正如标题所言,希望 2020 年能写满 50 篇周记吧,刚好前两周没有发,所以希望接下来每周完成一篇。周记的内容主要是这几方面的内容:工作学习阅读&…

python版代码整洁之道

总第 113 篇文章,本文大约 8000 字,阅读大约需要 20 分钟原文:https://github.com/zedr/clean-code-pythonpython 版的代码整洁之道。目录如下所示:介绍变量函数1. 介绍软件工程的原则,来自 Robert C. Martins 的书--《…

MVC 3.0错误 HTTP 404您正在查找的资源(或者它的一个依赖项)可能已被移除,或其名称已更改,或暂时不可用。请检查以下 URL 并确保其拼写正确。...

MVC3.0框架开发项目: 有时在程序运行的时候会出现“HTTP 404。您正在查找的资源(或者它的一个依赖项)可能已被移除,或其名称已更改,或暂时不可用。请检查以下 URL 并确保其拼写正确。”的错误提示。 在这里我们以运行时打开登录页面&#xff…

2020年1月总结

总第 114 篇文章,本文大约 1300 字,阅读大约需要 4 分钟这是 2020 年的第一篇月总结,总结的内容和周记差不多,也还是从这几个方面进行总结:工作学习阅读&写作2月计划工作这个月的工作时间大概是2周多一点&#xff…

python技巧(1)--如何转换itertools.chain对象为数组

总第 115 篇文章,本文大约 900 字,阅读大约需要 3 分钟之前做1月总结的时候说过希望每天或者每2天开始的更新一些学习笔记,这是开始的第一篇。这篇介绍的是如何把一个 itertools.chain 对象转换为一个数组。参考 stackoverflow 上的一个回答&…

python技巧(2)--碾平列表和列表去重

总第 116 篇文章,本文大约 1000 字,阅读大约需要 3 分钟今天介绍和列表相关的两个小技巧:碾平列表(flatten list),也就是列表里的元素也带有列表的情况;列表去重,保留原始顺序和不保…

原来电脑并不需要重装系统才能恢复出厂设置,这个操作学起来!

前言 小伙伴们应该都知道手机上有恢复出厂设置的功能,如果想要把手机送给朋友或者卖给别人,就会先恢复出厂设置。 但换到Windows电脑上之后,如果出现同样的情况,就会第一时间想到重装系统。就好像Windows电脑上不存在恢复出厂设…

2020年周记(2/50)

总第 117 篇文章,本文大约 1400 字,阅读大约需要 5 分钟因为春节假期的延长,中间还是休息过长,少了两周的周记了,这是2020年的第二篇周记,内容还是这几个方面:工作学习阅读&写作其他下周计划…