Utwórz macierz numpy wypełnioną NaN


195

Mam następujący kod:

r = numpy.zeros(shape = (width, height, 9))

Tworzy width x height x 9macierz wypełnioną zerami. Zamiast tego chciałbym wiedzieć, czy istnieje funkcja lub sposób na ich zainicjowanie zamiast NaNs w łatwy sposób.


2
Jednym zastrzeżeniem jest to, że NumPy nie ma wartości całkowitej NA (w przeciwieństwie do R). Zobacz listę pand gotch . Stąd np.nanźle idzie po konwersji na int.
smci

smci ma rację. Dla NumPy nie ma takiej wartości NaN. Zależy więc od typu i od NumPy, która wartość będzie dla NaN. Jeśli nie jesteś tego świadomy, spowoduje to kłopoty
MasterControlProgram

Odpowiedzi:


271

Rzadko potrzebujesz pętli do operacji wektorowych w Numpy. Możesz utworzyć niezainicjowaną tablicę i przypisać do wszystkich wpisów jednocześnie:

>>> a = numpy.empty((3,3,))
>>> a[:] = numpy.nan
>>> a
array([[ NaN,  NaN,  NaN],
       [ NaN,  NaN,  NaN],
       [ NaN,  NaN,  NaN]])

Sprawdziłem alternatywne opcje a[:] = numpy.nantutaj i a.fill(numpy.nan)opublikowane przez Blaenka:

$ python -mtimeit "import numpy as np; a = np.empty((100,100));" "a.fill(np.nan)"
10000 loops, best of 3: 54.3 usec per loop
$ python -mtimeit "import numpy as np; a = np.empty((100,100));" "a[:] = np.nan" 
10000 loops, best of 3: 88.8 usec per loop

Czasy pokazują preferencje dla ndarray.fill(..) jako najszybszą alternatywę. OTOH, podoba mi się implementacja wygody numpy, w której możesz przypisywać wartości do całych wycinków w tym czasie, intencja kodu jest bardzo jasna.

Pamiętaj, że ndarray.fillwykonuje swoją operację w miejscu, więc numpy.empty((3,3,)).fill(numpy.nan)wróci None.


8
Zgadzam się, że intencja twojego kodu jest jaśniejsza. Ale dziękuję za bezstronne terminy (a raczej za to, że wciąż je publikujesz), doceniam to :)
Jorge Israel Peña

2
I jak ten: a = numpy.empty((3, 3,)) * numpy.nan. fillMierzył czas szybciej niż, ale wolniej niż metoda przypisania, ale jest oneliner !!
heltonbiker

2
Proszę spojrzeć na tę odpowiedź: stackoverflow.com/questions/10871220/...
Ivan

3
Wolę tę .fill()metodę, ale różnica prędkości zmniejsza się praktycznie do zera, ponieważ tablice stają się większe.
naught101

4
... ponieważ np.empty([2, 5])tworzy tablicę, a następnie fill()modyfikuje tablicę w miejscu, ale nie zwraca kopii ani referencji. Jeśli chcesz wywoływać np.empty(2, 5)według nazwy („przypisać do zmiennej”), musisz to zrobić przed wykonaniem na nim operacji w miejscu. To samo dzieje się, jeśli to zrobisz [1, 2, 3].insert(1, 4). Lista jest tworzona, a 4 wstawiane, ale nie można uzyskać odniesienia do listy (i dlatego można założyć, że została ona wyrzucona). W przypadku niezmiennych danych, takich jak ciągi, zwracana jest kopia, ponieważ nie można operować w miejscu. Pandy mogą robić obie rzeczy.
flutefreak7

164

Inną opcją jest użycie numpy.fullopcji dostępnej w NumPy 1.8+

a = np.full([height, width, 9], np.nan)

Jest to dość elastyczne i możesz wypełnić go dowolną inną liczbą.


19
Uznałbym to za najbardziej poprawną odpowiedź, ponieważ jest to właściwie to, co fulljest przeznaczone. np.empy((x,y))*np.nanjest dobrym drugim miejscem (i kompatybilnością ze starszymi wersjami numpy).
travc

jest to wolniejsze niżfill python -mtimeit "import numpy as np; a = np.empty((100,100));" "a.fill(np.nan)" 100000 loops, best of 3: 13.3 usec per loop python -mtimeit "import numpy as np; a = np.full((100,100), np.nan);" 100000 loops, best of 3: 18.5 usec per loop
Farnabaz,

5
@Farnabaz Jeśli umieścisz równoważny kod wewnątrz pętli czasowej, będą one mniej więcej takie same. Dwie metody są w zasadzie równe, po prostu masz „np.empty” poza licznikiem czasu w pierwszej. python -mtimeit "import numpy as np; a = np.empty((1000,1000)); a.fill(np.nan)" 1000 loops, best of 3: 381 usec per loop $ python -mtimeit "import numpy as np; a = np.full((1000,1000), np.nan);" 1000 loops, best of 3: 383 usec per loop
Scott Staniewiczowa

48

Porównałem sugerowane alternatywy prędkości i stwierdziłem, że dla wystarczająco dużych wektorów / macierzy do wypełnienia wszystkie alternatywy oprócz val * onesi array(n * [val])są równie szybkie.

wprowadź opis zdjęcia tutaj


Kod do odtworzenia fabuły:

import numpy
import perfplot

val = 42.0


def fill(n):
    a = numpy.empty(n)
    a.fill(val)
    return a


def colon(n):
    a = numpy.empty(n)
    a[:] = val
    return a


def full(n):
    return numpy.full(n, val)


def ones_times(n):
    return val * numpy.ones(n)


def list(n):
    return numpy.array(n * [val])


perfplot.show(
    setup=lambda n: n,
    kernels=[fill, colon, full, ones_times, list],
    n_range=[2 ** k for k in range(20)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

Dziwne, że numpy.full(n, val)jest wolniejsze niż a = numpy.empty(n) .. a.fill(val)ponieważ robi to samo wewnętrznie
endolith

26

Czy znasz numpy.nan?

Możesz stworzyć własną metodę, taką jak:

def nans(shape, dtype=float):
    a = numpy.empty(shape, dtype)
    a.fill(numpy.nan)
    return a

Następnie

nans([3,4])

wyszedłby

array([[ NaN,  NaN,  NaN,  NaN],
       [ NaN,  NaN,  NaN,  NaN],
       [ NaN,  NaN,  NaN,  NaN]])

Znalazłem ten kod w wątku listy adresowej .


1
Wygląda na przesadę.
Szalony fizyk

@MadPhysicist To zależy całkowicie od twojej sytuacji. Jeśli musisz zainicjować tylko jedną pojedynczą tablicę NaN, to tak, funkcja niestandardowa prawdopodobnie przesadza. Jeśli jednak musisz zainicjować tablicę NaN w kilkudziesięciu miejscach w kodzie, posiadanie tej funkcji stanie się całkiem wygodne.
Xukrao

1
@Xukaro. Nie bardzo, biorąc pod uwagę, że istnieje już bardziej elastyczna i wydajna wersja takiej funkcji, o której wspominano w wielu innych odpowiedziach.
Mad Physicist

10

Zawsze możesz użyć mnożenia, jeśli nie przywołujesz natychmiast metod .emptylub .full:

>>> np.nan * np.ones(shape=(3,2))
array([[ nan,  nan],
       [ nan,  nan],
       [ nan,  nan]])

Oczywiście działa również z dowolną inną wartością liczbową:

>>> 42 * np.ones(shape=(3,2))
array([[ 42,  42],
       [ 42,  42],
       [ 42, 42]])

Ale zaakceptowana odpowiedź @ u0b34a0f6ae jest 3 razy szybsza (cykle procesora, a nie cykle mózgowe do zapamiętania składni numpy;):

$ python -mtimeit "import numpy as np; X = np.empty((100,100));" "X[:] = np.nan;"
100000 loops, best of 3: 8.9 usec per loop
(predict)laneh@predict:~/src/predict/predict/webapp$ master
$ python -mtimeit "import numpy as np; X = np.ones((100,100));" "X *= np.nan;"
10000 loops, best of 3: 24.9 usec per loop

6

Inną alternatywą jest numpy.broadcast_to(val,n) która powraca w stałym czasie niezależnie od wielkości i jest również najbardziej wydajna pod względem pamięci (zwraca widok powtarzanego elementu). Zastrzeżenie polega na tym, że zwrócona wartość jest tylko do odczytu.

Poniżej znajduje się porównanie wyników wszystkich innych metod, które zostały zaproponowane przy użyciu tego samego testu porównawczego, co w odpowiedzi Nico Schlömera .

wprowadź opis zdjęcia tutaj


5

Jak powiedziano, numpy.empty () jest właściwą drogą. Jednak w przypadku obiektów funkcja fill () może nie działać dokładnie tak, jak myślisz:

In[36]: a = numpy.empty(5,dtype=object)
In[37]: a.fill([])
In[38]: a
Out[38]: array([[], [], [], [], []], dtype=object)
In[39]: a[0].append(4)
In[40]: a
Out[40]: array([[4], [4], [4], [4], [4]], dtype=object)

Jednym sposobem może być np .:

In[41]: a = numpy.empty(5,dtype=object)
In[42]: a[:]= [ [] for x in range(5)]
In[43]: a[0].append(4)
In[44]: a
Out[44]: array([[4], [], [], [], []], dtype=object)

Poza tym, że praktycznie nie ma nic wspólnego z pierwotnym pytaniem, schludnie.
Szalony fizyk

1
Cóż, chodzi o „Inicjowanie macierzy numpy na coś innego niż zero lub jeden”, w przypadku, gdy „coś innego” jest obiektem :) (Praktycznie, Google zaprowadził mnie tutaj do inicjowania z pustą listą)
NT

3

Jeszcze inną możliwością, o której jeszcze nie wspomniano, jest użycie płytki NumPy:

a = numpy.tile(numpy.nan, (3, 3))

Daje również

array([[ NaN,  NaN,  NaN],
       [ NaN,  NaN,  NaN],
       [ NaN,  NaN,  NaN]])

Nie wiem o porównaniu prędkości.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.