2016-07-31 18 views
3

Ich habe eine regelmäßige list namens a, und ein NumPy Array von Indizes b.
(Nein, es ist mir nicht möglich, a zu einem NumPy Array zu konvertieren.)Slicing eine Python-Liste mit einem NumPy Array von Indizes - jeder schnelle Weg?

Gibt es für mich eine Möglichkeit, um die gleiche Wirkung wie „a[b]“ effizient? Um klar zu sein, bedeutet dies, dass ich nicht jedes Individuum int in b aufgrund seiner Auswirkungen auf die Leistung extrahieren möchte.

(Ja, das ist ein Engpass in meinem Code. Deshalb habe ich NumPy Arrays bin mit zu beginnen.)

+1

Ich weiß nicht, wie viel schneller (wenn überhaupt) 'operator.itemgetter()' wäre. –

+2

Was ist dein Plan für (was wäre) 'a [b]'? Es ist schwer, sich einen Gebrauch dafür vorzustellen, der nicht "ein Individuum' int' für immer in 'b'" ... schließlich extrahiert. Wenn Sie daran interessiert sind, Speicherplatz zu verschwenden, indem Sie eine Liste und eine Unterliste gleichzeitig herumliegen haben, scheint es, dass Sie (oder was auch immer) zum Zeitpunkt der Notwendigkeit über "b" iterieren könnten, anstatt das (wäre) 'a [b] '. – jedwards

+0

@jedwards: Mein Satz war ein wenig mehrdeutig (behoben), aber was ich sagte war, dass ich versuche, die einzelnen Elemente von 'b' zu entfernen (ich brauche es nicht und es verlangsamt meinen Code). Ich benutze die extrahierten Elemente von 'a' danach (z. B. schaue, welche' None' sind, etc ... es ist nicht wirklich relevant), aber das bedeutet kaum, dass ich 'b's Elemente manuell extrahieren muss . – Mehrdad

Antwort

3
a = list(range(1000000)) 
b = np.random.randint(0, len(a), 10000) 

%timeit np.array(a)[b] 
10 loops, best of 3: 84.8 ms per loop 

%timeit [a[x] for x in b] 
100 loops, best of 3: 2.93 ms per loop 

%timeit operator.itemgetter(*b)(a) 
1000 loops, best of 3: 1.86 ms per loop 

%timeit np.take(a, b) 
10 loops, best of 3: 91.3 ms per loop 

ich für numpy.take() große Hoffnungen hatte, aber es ist bei weitem nicht optimal. Ich habe auch einige Numba-Lösungen ausprobiert und ähnliche Zeiten erzielt - etwa 92 ms.

So ein einfaches Listenverständnis ist nicht weit von den besten hier, aber operator.itemgetter() gewinnt, zumindest für Eingangsgrößen in diesen Größenordnungen.

+0

Diese Methode scheint eine Inkonsistenz zu haben ... 'itemgetter (* (0,)) ([(0, 0)])' gibt ein Tupel von Tupeln zurück, aber 'itemgetter (* (0,)) ([(0 ,)]) 'gibt nur das Tupel zurück ... – Mehrdad

+0

Auch +1, aber interessanterweise wurde dies nicht tatsächlich schneller in meinem ursprünglichen Code (in der Tat war es ein bisschen langsamer). – Mehrdad

+0

@Mehrdad Ich bekomme ein Tupel von beiden oben zurückgegeben. –

3

schreiben cython Funktion:

import cython 
from cpython cimport PyList_New, PyList_SET_ITEM, Py_INCREF 

@cython.wraparound(False) 
@cython.boundscheck(False) 
def take(list alist, Py_ssize_t[:] arr): 
    cdef: 
     Py_ssize_t i, idx, n = arr.shape[0] 
     list res = PyList_New(n) 
     object obj 

    for i in range(n): 
     idx = arr[i] 
     obj = alist[idx] 
     PyList_SET_ITEM(res, i, alist[idx]) 
     Py_INCREF(obj) 

    return res 

Das Ergebnis% timeit:

import numpy as np 

al= list(range(10000)) 
aa = np.array(al) 

ba = np.random.randint(0, len(a), 10000) 
bl = ba.tolist() 

%timeit [al[i] for i in bl] 
%timeit np.take(aa, ba) 
%timeit take(al, ba) 

1000 loops, best of 3: 1.68 ms per loop 
10000 loops, best of 3: 51.4 µs per loop 
1000 loops, best of 3: 254 µs per loop 

numpy.take() ist die schnellste, wenn sowohl der Argumente ndarray Objekt sind. Die Cython-Version ist 5x schneller als das Listenverständnis.

+0

Schön. Jetzt frage ich mich, warum meine Numba-Versuche so langsam waren. Wenn Sie Numba kennen, können Sie versuchen, eine Numba-Funktion zu erstellen, die schneller ist als das Listenverständnis? Ich konnte nicht. –

+1

@JohnZwinck, numba eine Kopie der internen Liste erstellen: http://numba.pydata.org/numba-doc/dev/reference/pysupported.html#list – HYRY