2010-05-02 5 views
5

Ich bin ein 2D-Array Binning (x von y) in Python in die Behälter des x-Wertes (angegeben in "bins"), unter Verwendung von np.digitize:vektorisierten Ansatz mit Binning numpy/scipy in Python

elements_to_bins = digitize(vals, bins) 

wobei "vals" ist ein 2D-Array, das heißt:

vals = array([[1, v1], [2, v2], ...]). 

elements_to_bins sagt genau das, was ist jedes Element in fällt. Was ich dann machen möchte, ist eine Liste, deren Länge die Anzahl der Bins in "Bins" ist, und jedes Element gibt die Y-Dimension von "Vals" zurück, die in diesen Bins fällt. Ich mache es jetzt so:

points_by_bins = [] 
for curr_bin in range(min(elements_to_bins), max(elements_to_bins) + 1): 
    curr_indx = where(elements_to_bins == curr_bin)[0] 
    curr_bin_vals = vals[:, curr_indx] 
    points_by_bins.append(curr_bin_vals) 

gibt es eine elegantere/einfachere Möglichkeit, dies zu tun? Alles, was ich brauche, ist eine Liste von Listen der y-Werte, die in jede Klasse fallen.

danke.

+0

Wenn eine der Antworten Ihr Problem gelöst hat, markieren Sie es als akzeptiert (grünes Häkchen)! :) – EOL

Antwort

3

Wenn ich verstehe Ihre Frage richtig:

vals = array([[1, 10], [1, 11], [2, 20], [2, 21], [2, 22]]) # Example 

(x, y) = vals.T # Shortcut 
bin_limits = range(min(x)+1, max(x)+2) # Other limits could be chosen 
points_by_bin = [ [] for _ in bin_limits ] # Final result 
for (bin_num, y_value) in zip(searchsorted(bin_limits, x, "right"), y): # digitize() finds the correct bin number 
    points_by_bin[bin_num].append(y_value) 

print points_by_bin # [[10, 11], [20, 21, 22]] 

Numpy des schnellen Array-Betrieb searchsorted() wird für maximale Effizienz verwendet. Die Werte werden dann einzeln addiert (da das Endergebnis kein rechteckiges Array ist, kann Numpy dazu nicht viel beitragen). Diese Lösung sollte schneller als mehrere where() Aufrufe in einer Schleife sein, die Numpy zwingen, das gleiche Array mehrmals zu lesen.

+1

numpy.searchsorted sollte aus Leistungsgründen bevorzugt digitalisiert werden: https://github.com/numpy/numpy/issues/2656 – Alleo

+0

@Alleo: Sehr guter Punkt (für die aktuelle Implementierung von 'digitize()'). Ich habe die Antwort aktualisiert. – EOL

0

Sind die bin-Tasten nur Ganzzahlen, kein Binning, wie in Ihrem Beispiel? Dann könnte man nur das tun, ohne numpy:

from collections import defaultdict 
bins = defaultdict(list) # or [ [] ...] as in EOL 

vals = [[1, 10], [1, 11], [2, 20], [2, 21], [2, 22]] # nparray.tolist() 
for nbin, val in vals: 
    bins[nbin].append(val) 

print "bins:", bins 
# defaultdict(<type 'list'>, {1: [10, 11], 2: [20, 21, 22]}) 
+0

+1: das sieht gut aus, außer vielleicht, dass leere Bins keine leeren Listen enthalten (die mit einem Defaultdict behoben werden könnten). Aber hat das Originalplakat vielleicht eher allgemeine Bins im Hinterkopf? – EOL

1

Dies wird eine Datenstruktur analog zu IDL HISTOGRAMM der Reverse_Indices zurück:

ovec = np.argsort(vals) 
ivec = np.searchsorted(vals, bin_limits, sorter=ovec) 

dann die Liste der Elemente, die in den Behälter #i fallen ist

ovec[ ivec[i] : ivec[i+1] ] 

(meine schnellen Timing-Tests sagen, dass dies 5x schneller als EOL-Algorithmus ist, da es nicht unterschiedlich großen Erstellen von Listen stört)