2012-08-23 16 views
8

Chciałbym dodać tysiące elementów 4D mądry i rozliczenia dla nans. Prosty przykład stosując 1D tablic będzie:szybkie numpy addnan

X = array([4,7,89,nan,89,65, nan]) 
Y = array([0,5,4, 9, 8, 100,nan]) 
z = X+Y 
print z = array([4,12,93,9,97,165,nan]) 

Pisałem prosty dla pętli wokół tego, ale trwa wiecznie - nie inteligentne rozwiązanie. Innym rozwiązaniem może być utworzenie większej macierzy i użycie wąskiego gardła, ale zajmie to zbyt dużo pamięci dla mojego laptopa. Potrzebuję sumy kontrolnej ponad 11000 przypadków.

Czy ktoś ma inteligentny i szybki sposób to zrobić?

Odpowiedz

10

Oto jedna możliwość:

>>> x = np.array([1, 2, np.nan, 3, np.nan, 4]) 
... y = np.array([1, np.nan, 2, 5, np.nan, 8]) 
>>> x = np.ma.masked_array(np.nan_to_num(x), mask=np.isnan(x) & np.isnan(y)) 
>>> y = np.ma.masked_array(np.nan_to_num(y), mask=x.mask) 
>>> (x+y).filled(np.nan) 
array([ 2., 2., 2., 8., nan, 12.]) 

Prawdziwą trudnością jest to, że wydają się chce nan należy interpretować jako zero, chyba że wszystkie wartości w określonym położeniu są nan. Oznacza to, że musisz spojrzeć na X i Y, aby określić, które nans ma zastąpić. Jeśli nie masz nic przeciwko zastąpieniu wszystkich nanowych wartości, możesz po prostu zrobić np.nan_to_num(x) + np.nan_to_num(y).

+0

Zamaskowane tablice są tutaj, jeśli twoja implementacja numpy jest wystarczająco nowa, aby ją obsłużyć (moja nie jest - może czas na uaktualnienie) (+1). – mgilson

+0

@mgilson: Heh, prawdopodobnie jest czas! Sądzę, że maskowane tablice od paru lat są odrętwiałe. – BrenBarn

+0

Cóż, mój komputer ma kilka lat; ^) – mgilson

1

Nie wiem, jak byłoby to wykonać, ale warto spróbować :)

def nan_to_zero(array): 
    new_arr = array.copy() 
    new_arr[np.isnan(array)] = 0. 
    return new_arr 

sum(nan_to_zero(arr) for arr in array_generator) 

nie spowodować NaN w ostatnim miejscu macierzy chociaż. Skutkuje to 0 ...

+2

Numpy zapewnia to już w funkcji 'nan_to_num'. – BrenBarn

+0

@mgilson: Zrozumienie listy po usunięciu nans. Nigdy nie myślałem o części ze zrozumieniem listy. Ale podejrzewam, że zakłada to tablicę 1D. Nie widzę sposobu, w jaki mogę zakodować tę metodę dla macierzy 4D. – Shejo284

+1

@ Shejo284 - W rzeczywistości jest to wyrażenie generujące, ale działa podobnie. Nie widzę żadnego powodu, dla którego nie można tego użyć z macierzami 4D. Naprawdę, tablice 4D są w pamięci tylko tablicami 1D (chyba, że ​​naprawdę masz obiekty widokowe, ale powinno to nadal działać z tymi). – mgilson

3

Można zrobić coś takiego:

arr1 = np.array([1.0, 1.0, np.nan, 1.0, 1.0, np.nan]) 
arr2 = np.array([1.0, 1.0, 1.0, 1.0, 1.0, np.nan]) 
flags = np.isnan(arr1) & np.isnan(arr2) 
copy1 = arr1.copy() 
copy2 = arr2.copy() 
copy1[np.isnan(copy1)] = 0.0 
copy2[np.isnan(copy2)] = 0.0 
out = copy1 + copy2 
out[flags] = np.NaN 
print out 
array([ 2., 2., 1., 2., 2., NaN]) 

znaleźć lokalizacje w tablicach gdzie obie mają NaN w tym indeksie. Następnie wykonaj zasadniczo to, co @mgilson zasugerował, jak w przypadku tworzenia kopii i zastąpienie NaN s przy pomocy 0.0, dodaj razem dwie tablice, a następnie zastąp wskaźniki oznaczone powyżej wyżej np.NaN.

+0

@mgilson: Próbuję napisać wyrażenie generatora, ponieważ zużywa ono mniej pamięci, ale jestem nieco zdezorientowany, jak to działa, gdy ma do czynienia z bardzo dużymi liczbami i czyta plik netcdf, plaster dla plasterka: dla mnie w przypadki: array = np.array (netcdfvar [i]) # Następnie podsumuj te plasterki odpowiadające nan , nie wiesz jak wyglądałby ten generator. – Shejo284

+0

@ Shejo284 - Wydaje mi się, że wysłałeś to na niewłaściwą odpowiedź ;-). W każdym razie nie jestem zaznajomiony z czytaniem plasterków z pliku netcdf, ale możesz wypróbować następujące: 'sum (nan_to_zero (np.array (netcdfvar [i])) dla i w przypadkach)' lub jak wskazuje BrenBarn : 'suma (np.nan_to_num (netcdfvar [i]) dla i w przypadkach)' – mgilson

+0

@mgilson: tak, masz rację. Nadal uczę się korzystać z tej strony. Dzięki. Próbowałem kilku odmian z różnym sukcesem. Twoje rozwiązanie jest nieco sprzeczne z intuicją. Testuję to. – Shejo284

1

widzę kilka prostszych rozwiązań:

  • (ZMIENIONĄ) Korzystanie np.ma

    mX = np.ma.masked_array(X, mask=np.isnan(X)) 
    mY = np.ma.masked_array(Y, mask=np.isnan(Y)) 
    mZ = np.ma.masked_array(mX.filled(0) + mY.filled(0), 
             mask=mX.mask * mY.mask) 
    Z = mZ.filled(np.nan) 
    
  • (EDITED) nieużywanie np.ma

    mx = np.isnan(x) 
    my = np.isnan(y) 
    z = np.where(mx,0,x) + np.where(my,0,y) 
    z[mx&my] = np.nan 
    
+1

Rozwiązania te nie dają pożądanych wyników. Chce, aby dodane zostały terminy inne niż nan, przy czym nan pojawia się w wyniku tylko wtedy, gdy * wszystkie * wartości w danej pozycji są nan. Twoje rozwiązania produkują dodatkowe nans na stanowiskach, gdzie tylko jeden z dwóch wektorów wejściowych ma nan. – BrenBarn

+0

OK, naprawione. Dzięki za trzymanie mnie na palcach –

+0

Zauważ też, że twoje ostatnie rozwiązanie jest czymś, co OP wyraźnie powiedział, że nie chciał zrobić (utwórz większą tablicę zawierającą oba). Drugie rozwiązanie wygląda jednak ładnie. – BrenBarn

3
import numpy as np 
z=np.nansum([X,Y],axis=0) 
+1

To prawie działa. Problem polega na tym, że to rozwiązanie nie zapewnia pożądanej wydajności. Wynik powinien zawierać NaNs, gdzie * oba * wektory wejściowe mają NaNs w tych samych pozycjach. Możemy przywrócić NaNs z dodatkiem trzeciej linii do tego rozwiązania: 'z [np.isnan (x) & np.isnan (y)] = np.NaN' –