2016-05-02 20 views
3

Betrachten Sie ein numpy 2D-Array von Ganzzahlen, wobei einige Einträge 0 (Array1) sind. Betrachten Sie ein anderes 2D-Array (array2), in dem die erste Spalte die gleichen Werte ungleich null von array1 hat und eine andere Spalte, z. B. Index 2, unterschiedliche numerische Werte (floats) hat.Ersetzen von Einträgen eines Arrays mit denen eines anderen Arrays

Wie erstellen Sie ein neues Array3, indem Sie in Array1 jeden Eintrag ungleich Null durch den entsprechenden Wert von Spalte 2 von Array2 ersetzen? Wie machst du es super sauber?

Beispiel:

>>> array1 
array([[0, 27, 43, 10], 
     [0, 80, 15, 2], 
     [0, 3, 6, 9]]) 

>>> array2 
array([[ 10., 4., 88.], 
     [ 2., 2., 95.], 
     [ 9., 2., 65.], 
     [ 43., 1., 62.], 
     [ 15., 5., 64.], 
     [ 6., 6., 67.], 
     [ 27., 5., 62.], 
     [ 80., 8., 73.], 
     [ 3., 9., 59.]]) 

>>> array3 
array([[0., 62., 62., 88.], 
     [0., 73., 64., 95.], 
     [0., 59., 67., 65.]]) 

Antwort

1

Sie können boolean Indizierung verwenden zusammen mit erweiterten numpy Array-Indizierung:

array3 = array1.astype(float) # this copies the array by default. 
array3[array1 != 0] = array2[array1[array1 != 0]-1, 2] 

das Ergebnis:

array([[ 0, 62., 62., 88.], 
     [ 0, 73., 64., 95.], 
     [ 0, 59., 67., 65.]]) 

Erklärung

Sie zuerst einen boolean-Array erstellen, die anzeigt, wo es nicht-Null-Einträge :

>>> non_zero_mask = array1 != 0 
array([[False, True, True, True], 
     [False, True, True, True], 
     [False, True, True, True]], dtype=bool) 

Dies wird verwendet werden, um die Elemente zu finden, die Shoul d ersetzt werden.

Dann müssen Sie die Werte dieser Elemente finden:

>>> non_zero_values = array1[non_zero_mask] 
array([7, 4, 1, 8, 5, 2, 9, 6, 3]) 

Da Ihr array2 bestellt und beginnt mit dem Wert 1 brauchen wir eine subtrahieren Sie die entsprechende Zeile für den Wiederbeschaffungswert zu finden. Wenn Ihr array2 nicht sortiert Sie es sortieren könnte müssen oder eine andere Indizierung tun zwischendurch:

>>> replacement_rows = array2[non_zero_values-1] 
array([[ 7., 7., 62.], 
     [ 4., 4., 62.], 
     [ 1., 1., 88.], 
     [ 8., 8., 73.], 
     [ 5., 5., 64.], 
     [ 2., 2., 95.], 
     [ 9., 9., 59.], 
     [ 6., 6., 67.], 
     [ 3., 3., 65.]]) 

>>> replacement_values = array2[non_zero_values-1, 2] # third element of that row! 
array([ 62., 62., 88., 73., 64., 95., 59., 67., 65.]) 

und dann nur diese Werte auf den ursprünglichen oder neuen Array zuweisen:

array3[non_zero_mask] = replacement_values 

Dieser Ansatz beruht auf die Bestellung von array2 so wird es brechen, wenn es kompliziertere Bedingungen gibt. Aber das würde entweder erfordern, diese Beziehung zwischen Wert und Index zu finden und es anstelle der einfachen -1, die ich tat, oder eine andere Zwischenstufe np.where/boolean Indizierung einfügen.

Erweiterte

Falls Sie haben keine sortiert array2 und Sie können es nicht sortieren Sie tun können:

>>> array3 = array1.astype(float) 
>>> array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2] 
>>> array3 
array([[ 0., 62., 62., 88.], 
     [ 0., 73., 64., 95.], 
     [ 0., 59., 67., 65.]]) 

da dies mit Ausstrahlung der Arrays gegeneinander arbeitet man werde Erstellen Sie ein Array mit der Größe array1.size * array1.size.Dies ist möglicherweise nicht sehr memoreffizient, aber vollständig vektorisiert.

Numba (wenn Sie Geschwindigkeit wollen)

groß ist, wenn Sie beschleunigen, wollen die Dinge, die langsam sein würde, weil es keine native numpy oder scipy Version. Wenn Sie Anakonda oder CONDA es bereits installiert ist, so dass es eine sinnvolle Option sein könnte:

import numba as nb 
import numpy as np 

@nb.njit 
def nb_replace_values(array, old_new_array): 
    res = np.zeros(array.shape, dtype=np.float64) 

    rows = array.shape[0] 
    columns = array.shape[1] 
    rows_replace_array = old_new_array.shape[0] 

    for row in range(rows): 
     for column in range(columns): 
      val = array[row, column] 
      # only replace values that are not zero 
      if val != 0: 
       # Find the value to replace the element with 
       for ind_replace in range(rows_replace_array): 
        if old_new_array[ind_replace, 0] == val: 
         # Match found. Replace and break the innermost loop 
         res[row, column] = old_new_array[ind_replace, 2] 
         break 

    return res 

nb_replace_values(array1, array2) 
array([[ 0., 62., 62., 88.], 
     [ 0., 73., 64., 95.], 
     [ 0., 59., 67., 65.]]) 

Speziell für großen Arrays dies eindeutig die schnellste und speichereffiziente Lösung sein, da keine temporären Arrays erstellt werden. Der erste Aufruf wird viel langsamer, da die Funktion im laufenden Betrieb kompiliert werden muss.

Timings:

%timeit nb_replace_values(array1, array2) 

100000 Schleifen, am besten von 3: 6,23 & mgr; s pro Schleife

%%timeit 
array3 = array1.astype(float) 
array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2] 

10000 Schleifen, am besten von 3: 74,8 & mgr; s pro Schleife

# Solution provided by @PDRX 
%%timeit 
array3 = array1.astype(float) 
for i in array2[:,0]: 
    i_arr1,j_arr1 = np.where(array1 == i) 
    i_arr2 = np.where(array2[:,0] == i) 
    array3[i_arr1,j_arr1] = array2[i_arr2,2] 

1000 Schleifen, am besten von 3: 689 & mgr; s pro Schleife

+0

Nun, im Beispiel I gab array2 zwar sortiert, aber im Allgemeinen wird angenommen, jede Anordnung sein, nicht unbedingt sortiert, mit zufälligen Zahlen, die vielleicht nicht in einer einheitlichen Reihenfolge sortiert werden können, solange sich diese Zahlen nicht wiederholen - denken Sie an diese Spalte als ID. Kannst du deine Antwort darauf basierend verbessern? Ich werde mein Beispiel aktualisieren. – Bella

+0

@Bella Das ist viel schwieriger und ich habe nicht viel darüber nachgedacht. Aber siehe den letzten Teil der Antwort. Dies ist sehr ineffizient und ich würde wahrscheinlich eher "Pandas" oder eine benutzerdefinierte "numba" -Funktion für diese Fälle empfehlen. – MSeifert

+0

Oh, ich habe deine letzte Bearbeitung noch nicht gesehen! Als ich die andere Antwort sah, verstand ich sofort, was großartig war. Ihnen ist etwas schwieriger zu bekommen, aber Sie haben Recht, ich testete beide Lösungen für die Geschwindigkeit und Ihre ist schneller, obwohl es für größere Arrays nur eine Größenordnung schneller ist. Jetzt bin ich irgendwie unschlüssig welche Antwort ich annehmen soll, da ich wohl beide behalten werde. :/Ich habe das, was Sie über die Array-Größe erwähnt haben, nicht bekommen - von dem aus kann ich Array3 als die gleiche Größe wie Array1 sehen und in beiden Lösungen gibt mir getsizeof die gleiche Größe. – Bella

-1

Ich bin nicht sicher, ich Ihre Anforderungen verstanden, aber wir versuchen, mit list comprehensions:

array3 = [[array2[subitem1 - 1][2] if subitem1 != 0 else 0 for subitem1 in subarray1] for subarray1 in array1] 

Aber es ist schwer zu lesen, ziehe ich es tabellarisch:

array3 = [ 
    [ 
     array2[subitem1 - 1][2] if subitem1 != 0 else 0 
     for subitem1 in subarray1 
    ] 
    for subarray1 in array1 
] 
Verwandte Themen