2016-12-26 2 views
21

Ich möchte Outliner aus einer Liste ersetzen. Daher definiere ich eine obere und untere Grenze. Jetzt wird jeder Wert über upper_bound und unter lower_bound durch den gebundenen Wert ersetzt. Mein Ansatz bestand darin, dies in zwei Schritten mit einem numply-Array zu tun.Pythonischer Weg, Listenwerte durch obere und untere Grenze zu ersetzen (Klemmen, Clipping, Thresholding)?

Jetzt frage ich mich, ob es möglich ist, dies in einem Schritt zu tun, wie ich denke, es könnte Leistung und Lesbarkeit verbessern.

Gibt es einen kürzeren Weg, dies zu tun?

import numpy as np 

lowerBound, upperBound = 3, 7 

arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

arr[arr > upperBound] = upperBound 
arr[arr < lowerBound] = lowerBound 

# [3 3 3 3 4 5 6 7 7 7] 
print(arr) 
+1

Während es schön ist, dass es eine kompilierte "Clip" -Methode gibt, gibt es nichts Un-Pythonic über Ihren Code. Es ist eine vollkommen gute Verwendung von "numpy" und genauso lesbar (für einen erfahrenen Benutzer). Behalte dieses Konzept in deiner Toolbox. Es funktioniert in Fällen, die nicht ganz zum Clip-Modell passen. – hpaulj

+1

Dieser Vorgang wird allgemein als *** clamping ***, *** clipping *** oder *** *** thresholding *** – smci

+2

bezeichnet. Sie sollten die 'clip' Methode verwenden, aber es gibt einen anderen Grund als die Geschwindigkeit; Ihr Code ist elegant, erzeugt aber ein intermediäres Array mit 'arr> upperBound', das ein Problem darstellen kann, wenn das Array groß wird. –

Antwort

31

können Sie verwenden numpy.clip:

In [1]: import numpy as np 

In [2]: arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

In [3]: lowerBound, upperBound = 3, 7 

In [4]: np.clip(arr, lowerBound, upperBound, out=arr) 
Out[4]: array([3, 3, 3, 3, 4, 5, 6, 7, 7, 7]) 

In [5]: arr 
Out[5]: array([3, 3, 3, 3, 4, 5, 6, 7, 7, 7]) 
+0

Hallo @arthur, danke das ist genau das, was ich gesucht habe! Ich vermisste irgendwie das Schlüsselwort 'clip' und fand die Methode selbst nicht ... – ppasler

+3

Ich frage mich, wie' clip' geschrieben wurde. Es könnte das Gleiche tun, nur in einem Funktionsaufruf verpackt. – hpaulj

+0

@hpaulj hast du es herausgefunden? – djechlin

13

Für eine Alternative, die nicht auf numpy angewiesen ist, können Sie immer

arr = [max(lower_bound, min(x, upper_bound)) for x in arr] 

tun könnte, wenn Sie nur eine obere setzen wollten gebunden, könnte man natürlich arr = [min(x, upper_bound) for x in arr] schreiben. Oder ähnlich, wenn Sie nur eine untere Grenze wollten, würden Sie stattdessen max verwenden.

Hier habe ich gerade beide Operationen angewendet, geschrieben zusammen.

Edit: Hier ist eine etwas tiefer gehende Erklärung:

ein Element x des Arrays Given (und unter der Annahme, dass Ihr upper_bound mindestens so groß wie Ihre lower_bound!), Sie eine haben werde von drei Fällen:

i) x < lower_bound

ii) x > upper_bound

iii) lower_bound <= x <= upper_bound.

In Fall (i) wertet der Ausdruck max/min zunächst max(lower_bound, x) aus, der dann in aufgelöst wird. Im Fall (ii) wird der Ausdruck zuerst max(lower_bound, upper_bound), was dann upper_bound wird.

Im Fall (iii) erhalten wir max(lower_bound, x), die nur x löst.

In allen drei Fällen ist die Ausgabe, was wir wollen.

+1

nur meine Beschwerde (keine Abstimmung), ich muss eher * wirklich * schwer denken, wenn ich Max/Min-Kombinationen sehe und sie nicht so gut lesbar finde. – djechlin

+6

@djechlin Sicher, ich stimme dem nicht zu. Auf der anderen Seite verwendet die andere Antwort zu diesem Punkt 'numpy.clip', was für mich nicht sofort lesbar wäre, wenn ich irgendwo darauf stoßen würde - ich würde wahrscheinlich die numpy Dokumentation überprüfen wollen, oder einfach nur Raten Sie, was es getan hat, und hoffen Sie, dass der Autor es richtig gemacht hat. – mathmandan

+0

Was ist seltsam ist die Verschachtelung. Es ist eine sehr symmetrische Operation, die aus "Clip einmal," Clip zweimal besteht. "Dies ist" Clip einmal, dann wieder das Clip. " – djechlin

Verwandte Themen