2010-08-01 23 views

Respuesta

35

Cronometré las sugerencias anteriores y aquí están mis resultados.

En primer lugar, las funciones:

def f(seq): 
    # http://stackoverflow.com/questions/3382352/equivalent-of-numpy-argsort-in-basic-python/3383106#3383106 
    #non-lambda version by Tony Veijalainen 
    return [i for (v, i) in sorted((v, i) for (i, v) in enumerate(seq))] 

def g(seq): 
    # http://stackoverflow.com/questions/3382352/equivalent-of-numpy-argsort-in-basic-python/3383106#3383106 
    #lambda version by Tony Veijalainen 
    return [x for x,y in sorted(enumerate(seq), key = lambda x: x[1])] 


def h(seq): 
    #http://stackoverflow.com/questions/3382352/equivalent-of-numpy-argsort-in-basic-python/3382369#3382369 
    #by unutbu 
    return sorted(range(len(seq)), key=seq.__getitem__) 

Ahora, la sesión IPython:

In [16]: seq = rand(10000).tolist() 

In [17]: %timeit f(seq) 
100 loops, best of 3: 10.5 ms per loop 

In [18]: %timeit g(seq) 
100 loops, best of 3: 8.83 ms per loop 

In [19]: %timeit h(seq) 
100 loops, best of 3: 6.44 ms per loop 

Fwiw

+3

Interesante - probablemente el promedio es más importante que el "mejor" de 3 (?) – JPH

+1

El promedio se ve afectado por valores atípicos. No desea que los resultados sean contaminados por otros programas en ejecución o falta de memoria caché de hardware casualidades. –

51

No hay una función incorporada, pero es fácil de montar una de las herramientas fenomenales Python tiene disponibles:

def argsort(seq): 
    # http://stackoverflow.com/questions/3071415/efficient-method-to-calculate-the-rank-vector-of-a-list-in-python 
    return sorted(range(len(seq)), key=seq.__getitem__) 

x = [5,2,1,10] 

print(argsort(x)) 
# [2, 1, 0, 3] 

Funciona en Python array.array s de la misma manera:

import array 
x = array.array('d', [5, 2, 1, 10]) 
print(argsort(x)) 
# [2, 1, 0, 3] 
+4

+1 Muy Pythonic! – katrielalex

+1

En lugar de usar el (teóricamente privado) __getitem__, también puedes usar 'operator.itemgetter' /' operator.attrgetter' http://docs.python.org/library/operator.html – Ender

+0

Si 'operator.itemgetter' podría estar usado como un reemplazo directo para '__getitem__', creo que estuve de acuerdo contigo, Ender, pero por lo que puedo ver,' operator.itemgetter' también necesitaría envolverlo en una expresión 'lambda'. Prefiero evitar el 'lambda' extra si pudiera. – unutbu

2

encontrado esta pregunta, pero argsort necesario para una lista de objetos basado en una propiedad del objeto.

La extensión de la respuesta de unutbu, esto sería:

sorted(range(len(seq)), key = lambda x: seq[x].sort_property)