2013-05-29 21 views
15

Używam funkcji numpy.array() do tworzenia ndarrays numpy.float64 z list.Dlaczego funkcja numpy.array() jest czasami bardzo powolna?

Zauważyłem, że jest to bardzo powolne, gdy lista zawiera Brak lub wyświetlana jest lista list.

Poniżej znajduje się kilka przykładów razy. Istnieją oczywiste rozwiązania, ale dlaczego jest to tak powolne?

Przykłady listy None:

### Very slow to call array() with list of None 
In [3]: %timeit numpy.array([None]*100000, dtype=numpy.float64) 
1 loops, best of 3: 240 ms per loop 

### Problem doesn't exist with array of zeroes 
In [4]: %timeit numpy.array([0.0]*100000, dtype=numpy.float64) 
100 loops, best of 3: 9.94 ms per loop 

### Also fast if we use dtype=object and convert to float64 
In [5]: %timeit numpy.array([None]*100000, dtype=numpy.object).astype(numpy.float64) 
100 loops, best of 3: 4.92 ms per loop 

### Also fast if we use fromiter() insead of array() 
In [6]: %timeit numpy.fromiter([None]*100000, dtype=numpy.float64) 
100 loops, best of 3: 3.29 ms per loop 

Przykłady liście list:

### Very slow to create column matrix 
In [7]: %timeit numpy.array([[0.0]]*100000, dtype=numpy.float64) 
1 loops, best of 3: 353 ms per loop 

### No problem to create column vector and reshape 
In [8]: %timeit numpy.array([0.0]*100000, dtype=numpy.float64).reshape((-1,1)) 
100 loops, best of 3: 10 ms per loop 

### Can use itertools to flatten input lists 
In [9]: %timeit numpy.fromiter(itertools.chain.from_iterable([[0.0]]*100000),dtype=numpy.float64).reshape((-1,1)) 
100 loops, best of 3: 9.65 ms per loop 
+7

aby utworzyć pustą tablicę, użyj 'a = numpy.empty (100000)'. Aby utworzyć tablicę z zerami: 'a = numpy.zeros (100000)'. – jfs

+0

Moim celem nie jest utworzenie tablicy zerowej lub pustej, ale mam problemy z wydajnością, gdy Brak jest w zestawie danych. Jeśli utworzę tablicę, a najpierw i ustawię wartości, mam prawie ten sam problem. w [18]: A = numpy.empty ((100000)) w [19]:% timeit A [:] = [brak] * 100000 1 pętle, najlepiej od 3: 209 ms na pętli W [20]:% timeit a [:] = [0] * 100000 100 pętli, najlepiej 3: 9,59 ms na pętlę – MarkW

+0

faktycznie jest to interesujące pytanie: dlaczego '[3]' jest znacznie wolniejsze niż '[5 ] '(może to być błąd lub tylko rzadki przypadek użycia, który nie jest zoptymalizowany). Aby powtórzyć 'NaN', zadzwoń' numpy.repeat (float ("nan"), 100000) ' – jfs

Odpowiedz

1

Moje przypuszczenie byłoby, że kod do konwertowania list po prostu wywołuje float na wszystko. Jeśli argument definiuje __float__, nazywamy to, w przeciwnym razie traktujemy go jak ciąg (rzucając wyjątek na Brak, łapiemy to i umieszczamy w np.nan). Obsługa wyjątków powinna być względnie wolniejsza.

Timing wydaje się zweryfikować tę hipotezę:

import numpy as np 
%timeit [None] * 100000 
> 1000 loops, best of 3: 1.04 ms per loop 

%timeit np.array([0.0] * 100000) 
> 10 loops, best of 3: 21.3 ms per loop 
%timeit [i.__float__() for i in [0.0] * 100000] 
> 10 loops, best of 3: 32 ms per loop 


def flt(d): 
    try: 
     return float(d) 
    except: 
     return np.nan 

%timeit np.array([None] * 100000, dtype=np.float64) 
> 1 loops, best of 3: 477 ms per loop  
%timeit [flt(d) for d in [None] * 100000] 
> 1 loops, best of 3: 328 ms per loop 

Dodanie kolejnego sprawę po prostu być oczywisty o tym, gdzie idę z tym. Jeśli wystąpiłaby wyraźna kontrola dla Brak, to nie byłoby tak wolno powyżej:

def flt2(d):        
    if d is None: 
     return np.nan 
    try: 
     return float(d) 
    except: 
     return np.nan 

%timeit [flt2(d) for d in [None] * 100000] 
> 10 loops, best of 3: 45 ms per loop 
+0

Zauważyłem, że twoja hipoteza jest bardzo interesująca, chociaż różnice czasowe, które otrzymujesz (około 1.5 razy szybciej) nadal nie wspierają twoich argumentów, ponieważ różnice czasowe OP wynoszą około 35X ... może mógłbyś dalej pracować w tym kierunku –

+0

@sgpc Tak, gdybym chciał zagłębić się głębiej, następnym krokiem jest przyjrzenie się źródłu. Chociaż wiem: gdyby wyraźnie sprawdzali "Brak", to nie byłoby tak wolno. – U2EF1

+1

Wykopałem głębiej. Kod poświęca cały swój czas na rzucanie egzekucji. To nie traktuje rzeczy jak struny, ale nazywa getattr lub getbuffer dla wielu rzeczy, które nie mają atrybutu lub nie są buforami. Obie te tworzą i ignorują wyjątki. Wprowadziłem poprawki do common.c i ctor.c, aby uniknąć takich przypadków, a wydajność jest bardziej podobna do tej, której można się spodziewać. – MarkW

3

Zgłosiłem to jako problem numpy. Sprawozdanie i plaster pliki są tutaj:

https://github.com/numpy/numpy/issues/3392

Po łatanie:

# was 240 ms, best alternate version was 3.29 
In [5]: %timeit numpy.array([None]*100000) 
100 loops, best of 3: 7.49 ms per loop 

# was 353 ms, best alternate version was 9.65 
In [6]: %timeit numpy.array([[0.0]]*100000) 
10 loops, best of 3: 23.7 ms per loop 
Powiązane problemy