2013-08-21 6 views
6

Mam dwie listy, x i y, i chcę posortować x i permutację y przez permutację sortowania x. Na przykład, biorąc pod uwagęNajszybszy sposób sortowania wielu list - Python

x = [4, 2, 1, 3] 
y = [40, 200, 1, 30] 

Chcę dostać

x_sorted = [1,2,3,4] 
y_sorted = [1, 200, 30, 40] 

Jak wspomniano w poprzednich pytaniach, w prosty sposób rozwiązać ten jest

x_sorted, y_sorted = zip(*sorted(zip(x,y))) 

Oto moje pytanie: Co to jest NAJSZYBSZY sposób na zrobienie tego?


Mam trzy metody, aby wykonać zadanie.

import numpy as np 
x = np.random.random(1000) 
y = np.random.random(1000) 

Metoda 1:

x_sorted, y_sorted = zip(*sorted(zip(x,y))) #1.08 ms 

Metoda 2:

foo = zip(x,y) 
foo.sort() 
zip(*foo)  #1.05 ms 

metoda 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 

Czy istnieje lepsza metoda, która wykonuje szybciej niż powyżej trzech metod?


Dodatkowe pytania.

  1. Dlaczego metoda 2 nie jest szybsza niż metoda 1, mimo że wykorzystuje metodę sortowania?
  2. Jeśli wykonam metodę 2 osobno, jest to szybciej. W terminalu ipython,

mam

%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 

Odpowiedz

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) 

Wydajność:

>>> 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 

Aby zobaczyć pełny obraz:

>>> 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 

metoda @falsetru - najszybciej dla np.Macierze

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

Jak @AshwiniChaudhary sugerowane w komentarzach, dla list istnieje sposób, aby ją przyspieszyć stosując itertools.izip zamiast 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 
+1

można użyć 'itertools.izip' na wewnętrznej zip, aby pamięć wydajny. –

+0

@AshwiniChaudhary zaznaczone :) –

+2

Nie używaj 'izip' poza posortowanym, ponieważ zwraca on nie listę iteratorów. –

7

Korzystanie 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]) 

>>> timeit('order = np.argsort(x); x_sorted = x[order]; y_sorted = y[order]', 'from __main__ import x, y, np', number=1000) 
0.030632019043 

UWAGA

Ma to sens, jeśli dane wejściowe są już numpy tablice.

+0

świetny, oczywisty zwycięzca tutaj :) –

+1

Ma to sens, jeśli są już numpy tablice –

+0

@gnibbler, masz rację. Wspomniałem o tym. Dziękuję Ci. – falsetru

4

Nie jesteś rozrządu to prawidłowo

%timeit foo.sort() 

po 1 pętli, to już posortowane dla pozostałej części. Timsort jest bardzo wydajny na listach z wyborami.

Byłem trochę zaskoczony, że użycie przez Romana kluczowej funkcji było o wiele szybsze. Można poprawić to dalej za pomocą itemgetter

from operator import itemgetter 
ig0 = itemgetter(0) 
zip(*sorted(zip(x, y), key=ig0)) 

Jest to około 9% szybciej niż przy użyciu funkcji lambda dla wykazów 1000 elementów

+0

Świetne, sprawdzone rozwiązanie, daje mi 0.7580892901514744, +1 dla ciebie –