2016-05-11 6 views
4

Ich habe ein großes numpy Array k, von unspezifizierter Form, und ich möchte ein identisch geformtes Array d erstellen, die 1.0 ist, wenn der entsprechende Eintrag in k zwischen zwei Konstanten lo ist und hi, 0,0 sonst. (Aufgrund dessen, was der größere Code tut, das tue ich nicht wollen ein Boolean-Wert von Array.)Der effizienteste Weg (lo <= k && k <= hi)? 1: 0 für ka numpy array, lo, hi Konstanten

Der offensichtliche Weg, dies zu tun ist

d = np.ones_like(k) 
d[np.less(k, lo)] = 0 
d[np.greater(k, hi)] = 0 

jedoch die np.less und np.greater Anrufe betreffen die Erstellen von großen Booleschen Scratch-Arrays, und ich habe dies als signifikanten Overhead gemessen. Gibt es eine Möglichkeit, diese Operation auszuführen, bei der keine großen Scratch-Objekte erstellt werden müssen, während vollständig vektorisiert bleibt?

+3

Wenn die großen Scratch-Arrays ein Problem darstellen, das groß genug ist, dass Sie sie loswerden müssen, haben Sie das gleiche Problem mit den meisten üblichen NumPy-Techniken. NumPy baut gerne riesige Scratch-Arrays. – user2357112

+1

Ich glaube nicht, dass es einen Weg gibt, irgendwelche Scratch-Arrays für diese Operation zu vermeiden, wenn man einfach NumPy verwendet. Möglicherweise müssen Sie nach C (benutzerdefinierte ufuncs schreiben), Cython oder Numba suchen. –

Antwort

0

Sie können boolean-Array erstellen auf der Grundlage des Vergleichs und dann konvertieren Typ zu schweben, die alle in einem Rutsch, wie so -

d = ((k >=lo) & (k <= hi)).astype(float) 
+0

Das erstellt immer noch Scratch-Objekte. In der Tat, ich glaube, es schafft * mehr * Scratch-Objekte als das, was ich habe (ich zähle drei von ihnen). – zwol

+0

@zwol Ja, ich ging mehr mit der Leistungseffizienz, da es eine gute Beschleunigung zeigt (basierend auf einem schnellen Laufzeittest an meinem Ende). In Bezug auf die Speichernutzung muss man entsprechend profilieren. Ja, ich denke, es gibt drei hier und dann wird einer von ihnen als Ausgabe zugewiesen, also ist es derselbe wie der Original, wenn der Zuordnungsteil keine Kopie macht? Ich denke schon! – Divakar

0

less und greater nehmen einen out Parameter:

out=np.ones_like(k) 
np.less(k,80,out=out); 
out &= np.greater(k,20); 
# np.logical_and(np.greater(k,20),out,out=out); 

Das könnte am Ende ein Zwischenarray speichern. Obwohl mein Eindruck mit der ufunc out ist, dass es immer noch ein temporäres Array erstellt, aber dann kopiert es nur auf out.

Auf einem kleinen (10x10) Array ist dies schneller als @zwols Methode, aber langsamer als @ Divakar. Aber die Unterschiede sind nicht groß.

1

Wie andere sagten, ist 0plus schwer auf temporäre Puffer und es bietet nicht viel Kontrolle über es. Wenn der Speicherabdruck wirklich ein Blocker ist, können Sie Ihre eigene kleine Routine einfügen. Zum Beispiel

def process(x, lo, hi): 
    """ lo <= x < hi ? 1.0 : 0.0.""" 
    x_shape = x.shape 
    xx = np.ascontiguousarray(x).ravel() 
    out = np.empty_like(xx) 
    _process(xx, lo, hi, out) 
    return out.reshape(x_shape) 

wo _process in cython ist:

%%cython --annotate 

import cython 

@cython.boundscheck(False) 
@cython.wraparound(False) 
def _process(double[::1] x, double lo, double hi, double[::1] out): 
    """ lo <= x < hi ? 1.0 : 0.0.""" 
    cdef: 
     Py_ssize_t j 
     double xj 

    for j in range(x.shape[0]): 
     xj = x[j] 
     if lo <= xj < hi: 
      out[j] = 1.0 
     else: 
      out[j] = 0.0 

Here I jupyter Notebook verwendet (daher die lustige %%cython Syntax). In einem echten Projekt müssen Sie einen setup.py einwerfen, um die Erweiterung usw. zu kompilieren. Ob der Nutzen davon den Aufwand wert ist, bleibt Ihnen überlassen.

Verwandte Themen