排序多个列表的最快方法 - Python

ywa*_*wat 6 python sorting performance zip list

我有两个列表,x和y,我想要排序x并通过x排序的排列来置换y.例如,给定

x = [4, 2, 1, 3]
y = [40, 200, 1, 30]
Run Code Online (Sandbox Code Playgroud)

我想得到

x_sorted = [1,2,3,4]
y_sorted = [1, 200, 30, 40]
Run Code Online (Sandbox Code Playgroud)

正如过去的问题所讨论的,解决这个问题的一个简单方法是

x_sorted, y_sorted = zip(*sorted(zip(x,y)))
Run Code Online (Sandbox Code Playgroud)

这是我的问题:最快的方法是什么?


我有三种方法来完成任务.

import numpy as np
x = np.random.random(1000)
y = np.random.random(1000)
Run Code Online (Sandbox Code Playgroud)

方法1:

x_sorted, y_sorted = zip(*sorted(zip(x,y))) #1.08 ms 
Run Code Online (Sandbox Code Playgroud)

方法2:

foo = zip(x,y)
foo.sort()
zip(*foo)       #1.05 ms
Run Code Online (Sandbox Code Playgroud)

方法3;

ind = range(1000)
ind.sort(key=lambda i:x[i])
x_sorted = [x[i] for i in ind]
y_sorted = [y[i] for i in ind]  #934us
Run Code Online (Sandbox Code Playgroud)

有没有比上述三种方法更快的执行方法?


其他问题.

  1. 为什么方法2不比方法1快,尽管它使用排序方法?
  2. 如果我单独执行方法2,它会更快.在IPython终端中,

我有

%timeit foo = zip(x,y)   #1000 loops, best of 3: 220 us per loop
%timeit foo.sort()       #10000 loops, best of 3: 78.9 us per loop
%timeit zip(*foo)        #10000 loops, best of 3: 73.8 us per loop
Run Code Online (Sandbox Code Playgroud)

fal*_*tru 7

使用numpy.argsort:

>>> import numpy as np
>>> x = np.array([4,2,1,3])
>>> y = np.array([40,200,1,30])
>>> order = np.argsort(x)
>>> x_sorted = x[order]
>>> y_sorted = y[order]
>>> x_sorted
array([1, 2, 3, 4])
>>> y_sorted
array([  1, 200,  30,  40])
Run Code Online (Sandbox Code Playgroud)
>>> timeit('order = np.argsort(x); x_sorted = x[order]; y_sorted = y[order]', 'from __main__ import x, y, np', number=1000)
0.030632019043
Run Code Online (Sandbox Code Playgroud)

注意

如果输入数据已经是numpy数组,这是有意义的.


Joh*_*ooy 5

你没有正确计时

%timeit foo.sort()
Run Code Online (Sandbox Code Playgroud)

在第一个循环之后,它已经为余数排序。Timsort 对于预先排序的列表非常有效。

我有点惊讶@Roman 使用 key 函数的速度要快得多。您可以使用以下方法进一步改进itemgetter

from operator import itemgetter
ig0 = itemgetter(0)
zip(*sorted(zip(x, y), key=ig0))
Run Code Online (Sandbox Code Playgroud)

这比对 1000 个元素的列表使用 lambda 函数快 9%


Rom*_*kar 4

>>> x = [4, 2, 1, 3]
>>> y = [40, 200, 1, 30]    
>>> x_sorted, y_sorted = zip(*sorted(zip(x, y), key=lambda a:a[0]))
>>> x_sorted
(1, 2, 3, 4)
>>> y_sorted
(1, 200, 30, 40)
Run Code Online (Sandbox Code Playgroud)

表现:

>>> timeit('foo = zip(x,y); foo.sort(); zip(*foo)', 'from __main__ import x, y', number=1000)
1.0197240443760691
>>> timeit('zip(*sorted(zip(x,y)))', 'from __main__ import x, y', number=1000)
1.0106219310922597
>>> timeit('ind = range(1000); ind.sort(key=lambda i:x[i]); x_sorted = [x[i] for i in ind]; y_sorteds = [y[i] for i in ind]', 'from __main__ import x, y', number=1000)
0.9043525504607857
>>> timeit('zip(*sorted(zip(x, y), key=lambda a:a[0]))', 'from __main__ import x, y', number=1000)
0.8288150863453723
Run Code Online (Sandbox Code Playgroud)

查看完整图片:

>>> timeit('sorted(x)', 'from __main__ import x, y', number=1000)
0.40415491505723367            # just getting sorted list from x
>>> timeit('x.sort()', 'from __main__ import x, y', number=1000)
0.008009909448446706           # sort x inplace
Run Code Online (Sandbox Code Playgroud)

@falsetru 方法 - np.arrays 最快

>>> timeit('order = np.argsort(x); x_sorted = x[order]; y_sorted = y[order]', 'from __main__ import x, y, np', number=1000)
0.05441799872323827
Run Code Online (Sandbox Code Playgroud)

正如 @AshwiniChaudhary 在评论中建议的那样,对于列表itertools.izip,有一种方法可以通过使用而不是来加快速度zip

>>> timeit('zip(*sorted(izip(x, y), key=itemgetter(0)))', 'from __main__ import x, y;from operator import itemgetter;from itertools import izip', number=1000)
0.4265049757161705
Run Code Online (Sandbox Code Playgroud)

  • 不要在排序之外使用“izip”,因为它返回一个迭代器而不是列表。 (2认同)