2016-01-26 13 views
11

Ich habe ein Array myA wie folgt aus:Wie werden nur die ersten n Elemente in einem numpy Array ersetzt, die größer als ein bestimmter Wert sind?

array([ 7, 4, 5, 8, 3, 10]) 

Wenn ich alle Werte ersetzen wollen, die val 0 von größer als ein Wert sind, habe ich einfach tun können:

myA[myA > val] = 0 

, die das gibt mir gewünschter Ausgang (für val = 5):

array([0, 4, 5, 0, 3, 0]) 

aber mein Ziel ist es zu ersetzen, nicht alle, sondern nur die erste n Elemente dieses Arrays, die größer als ein Wert val sind.

Also, wenn n = 2 mein gewünschtes Ergebnis würde wie folgt aussehen (10 das dritte Element ist und daher nicht ersetzt worden ist):

array([ 0, 4, 5, 0, 3, 10]) 

Eine einfache Implementierung sei:

import numpy as np 

myA = np.array([7, 4, 5, 8, 3, 10]) 
n = 2 
val = 5 

# track the number of replacements 
repl = 0 

for ind, vali in enumerate(myA): 

    if vali > val: 

     myA[ind] = 0 
     repl += 1 

     if repl == n: 
      break 

That funktioniert, aber vielleicht kann jemand mit einer intelligenten Art der Maskierung!

Antwort

5

Die folgenden sollte funktionieren:

myA[(myA > val).nonzero()[0][:2]] = 0 

seit nonzero wird die Indizes zurückgeben, wo das boolesche Array myA > val ungleich Null ist, z.B. True.

Zum Beispiel:

In [1]: myA = array([ 7, 4, 5, 8, 3, 10]) 

In [2]: myA[(myA > 5).nonzero()[0][:2]] = 0 

In [3]: myA 
Out[3]: array([ 0, 4, 5, 0, 3, 10]) 
+0

Sehr elegant, danke. Ich habe es vorläufig aufgewertet und könnte es später akzeptieren, abhängig von der Qualität der anderen Antworten. – Cleb

1

Sie konnten die gleiche Lösung wie here verwenden mit der Umwandlung Sie np.array zu pd.Series:

s = pd.Series([ 7, 4, 5, 8, 3, 10]) 
n = 2 
m = 5 
s[s[s>m].iloc[:n].index] = 0 

In [416]: s 
Out[416]: 
0  0 
1  4 
2  5 
3  0 
4  3 
5 10 
dtype: int64 

Schritt für Schritt erklärt:

In [426]: s > m 
Out[426]: 
0  True 
1 False 
2 False 
3  True 
4 False 
5  True 
dtype: bool 

In [428]: s[s>m].iloc[:n] 
Out[428]: 
0 7 
3 8 
dtype: int64 

In [429]: s[s>m].iloc[:n].index 
Out[429]: Int64Index([0, 3], dtype='int64') 

In [430]: s[s[s>m].iloc[:n].index] 
Out[430]: 
0 7 
3 8 
dtype: int64 

Ausgabe in In[430] sieht genauso aus wie In[428] aber in 428 es ist eine Kopie und in 430 Originalserien.

Wenn Sie benötigen np.array Sie values Methode verwenden:

In [418]: s.values 
Out[418]: array([ 0, 4, 5, 0, 3, 10], dtype=int64) 
+0

Großartig, das funktioniert gut! Danke auch für die ausführliche Erklärung. Ich habe es vorläufig aufgewertet und könnte es später akzeptieren, abhängig von der Qualität der anderen Antworten. – Cleb

2

Endlösung ist sehr einfach:

import numpy as np 
myA = np.array([7, 4, 5, 8, 3, 10]) 
n = 2 
val = 5 

myA[np.where(myA > val)[0][:n]] = 0 

print(myA) 

Ausgang:

[ 0 4 5 0 3 10] 
+0

Das scheint zu scheitern, wenn n = 3 ist. – Cleb

+0

Ja, das sollte 'np.where (mask) [0] [n:]' –

+0

Ja, jetzt funktioniert es auch gut, also habe ich es auch hochgeladen und könnte akzeptieren später abhängig von der Qualität der anderen Antworten. – Cleb

2

Hier ist eine andere Möglichkeit (nicht getestet), wahrscheinlich nicht besser als nonzero:

def truncate_mask(m, stop): 
    m = m.astype(bool, copy=False) # if we allow non-bool m, the next line becomes nonsense 
    return m & (np.cumsum(m) <= stop) 

myA[truncate_mask(myA > val, n)] = 0 

von Gebäude zu vermeiden und einen expliziten Index verwenden Sie könnten sich mit etwas bessere Leistung beenden ... aber du müsstest es testen, um es herauszufinden.

Edit 1: während wir auf das Thema der Möglichkeiten sind, könnten Sie auch versuchen:

def truncate_mask(m, stop): 
    m = m.astype(bool, copy=True) # note we need to copy m here to safely modify it 
    m[np.searchsorted(np.cumsum(m), stop):] = 0 
    return m 

Edit 2 (am nächsten Tag): Ich habe das gerade getestet und es scheint, das cumsum ist eigentlich schlimmer als nonzero, zumindest mit der kinds of values die ich benutzt habe (also ist keiner der obigen Ansätze wert, benutzt zu werden). Aus Neugier habe ich versucht, es auch mit numba:

import numba 

@numba.jit 
def set_first_n_gt_thresh(a, val, thresh, n): 
    ii = 0 
    while n>0 and ii < len(a): 
     if a[ii] > thresh: 
      a[ii] = val 
      n -= 1 
     ii += 1 

dies nur eine Iteration über das Array einmal, oder vielmehr nur iteriert über den notwendigen Teil des Arrays einmal, nie auch nur den letzten Teil zu berühren. Dies gibt Ihnen sehr viel bessere Leistung für kleine n, aber selbst für den schlimmsten Fall von n>=len(a) ist dieser Ansatz schneller.

+0

Es gibt einen kleinen "Fehler" im Code: Es sollte "<= Stop" statt "Stop" sein, das scheint gut zu funktionieren. Danke für den Vorschlag, ich habe es auch verbessert. – Cleb

+0

Ahh, ja (ich dachte in Slice Notation). Ich habe eine zweite Variation hinzugefügt, die möglicherweise auch Fehler enthält. –

Verwandte Themen