2014-11-18 10 views
7

Ich versuche, eine Reihe jede Zeile in einem np.array zeigt zu zählen, zum Beispiel:zählen, wie oft jede Zeile in numpy.array ist

import numpy as np 
my_array = np.array([[1, 2, 0, 1, 1, 1], 
        [1, 2, 0, 1, 1, 1], # duplicate of row 0 
        [9, 7, 5, 3, 2, 1], 
        [1, 1, 1, 0, 0, 0], 
        [1, 2, 0, 1, 1, 1], # duplicate of row 0 
        [1, 1, 1, 1, 1, 0]]) 

Row [1, 2, 0, 1, 1, 1] zeigt 3 Mal.

wäre eine einfache naive Lösung beinhalten alle meine Zeilen zu Tupeln konvertieren und collections.Counter, wie diese Anwendung:

from collections import Counter 
def row_counter(my_array): 
    list_of_tups = [tuple(ele) for ele in my_array] 
    return Counter(list_of_tups) 

Welche ergibt:

In [2]: row_counter(my_array) 
Out[2]: Counter({(1, 2, 0, 1, 1, 1): 3, (1, 1, 1, 1, 1, 0): 1, (9, 7, 5, 3, 2, 1): 1, (1, 1, 1, 0, 0, 0): 1}) 

Ich bin aber besorgt über die Effizienz der mein Ansatz. Und vielleicht gibt es eine Bibliothek, die eine eingebaute Möglichkeit bietet, dies zu tun. Ich habe die Frage als pandas markiert, weil ich denke, dass pandas das Werkzeug haben könnte, das ich suche.

+0

Ich mag dieses Problem! Sie können 'np.lexsort' zu Ihrem Vorteil verwenden, aber ich bin nicht sicher, ob die Sammlung nach dem Sortieren schnell genug durchgeführt werden kann. – eickenberg

Antwort

8

Sie können the answer to this other question of yours verwenden, um die Anzahl der eindeutigen Elemente zu erhalten.

In Nummer 1.9 ist ein return_counts optionale Schlüsselwort Argument, so können Sie einfach tun:

>>> my_array 
array([[1, 2, 0, 1, 1, 1], 
     [1, 2, 0, 1, 1, 1], 
     [9, 7, 5, 3, 2, 1], 
     [1, 1, 1, 0, 0, 0], 
     [1, 2, 0, 1, 1, 1], 
     [1, 1, 1, 1, 1, 0]]) 
>>> dt = np.dtype((np.void, my_array.dtype.itemsize * my_array.shape[1])) 
>>> b = np.ascontiguousarray(my_array).view(dt) 
>>> unq, cnt = np.unique(b, return_counts=True) 
>>> unq = unq.view(my_array.dtype).reshape(-1, my_array.shape[1]) 
>>> unq 
array([[1, 1, 1, 0, 0, 0], 
     [1, 1, 1, 1, 1, 0], 
     [1, 2, 0, 1, 1, 1], 
     [9, 7, 5, 3, 2, 1]]) 
>>> cnt 
array([1, 1, 3, 1]) 

In früheren Versionen können Sie es tun, wie:

>>> unq, _ = np.unique(b, return_inverse=True) 
>>> cnt = np.bincount(_) 
>>> unq = unq.view(my_array.dtype).reshape(-1, my_array.shape[1]) 
>>> unq 
array([[1, 1, 1, 0, 0, 0], 
     [1, 1, 1, 1, 1, 0], 
     [1, 2, 0, 1, 1, 1], 
     [9, 7, 5, 3, 2, 1]]) 
>>> cnt 
array([1, 1, 3, 1]) 
+0

Die letzte Umformung kann etwas vereinfacht werden mit: 'unq.view ((my_array.dtype, my_array.shape [1]))'; es verwendet dieselbe Art von Multi-Item-Typ wie die erste "View". – hpaulj

2

Ein Pandas Ansatz wie dieser

import pandas as pd 

df = pd.DataFrame(my_array,columns=['c1','c2','c3','c4','c5','c6']) 
df.groupby(['c1','c2','c3','c4','c5','c6']).size() 

Hinweis aussehen könnte: Spaltennamen Versorgung nicht notwendig ist,

+0

Ich habe keine Ahnung, warum das abgelehnt wurde. Dies ist ein gutes Beispiel dafür, wie man das mit Pandas macht. –

+0

Danke, JD. Ich schätze es –

+0

Können Sie zeigen, wie Sie es tun würden, ohne Spaltennamen zu liefern? – Akavall

3

Du Lösung ist nicht schlecht, aber wenn Ihre Matrix Sie wahrscheinlich will groß ist eine verwenden, effizienter Hash (verglichen mit dem Standard, den ein Counter verwendet) für die Zeilen vor dem Zählen. Sie können das tun mit joblib:

A = np.random.rand(5, 10000) 

%timeit (A[:,np.newaxis,:] == A).all(axis=2).sum(axis=1) 
10000 loops, best of 3: 132 µs per loop 

%timeit Counter(joblib.hash(row) for row in A).values() 
1000 loops, best of 3: 1.37 ms per loop 

%timeit Counter(tuple(ele) for ele in A).values() 
100 loops, best of 3: 3.75 ms per loop 

%timeit pd.DataFrame(A).groupby(range(A.shape[1])).size() 
1 loops, best of 3: 2.24 s per loop 

Die Pandas Lösung ist extrem langsam (ca. 2s pro Loop) mit so vielen Spalten. Für eine kleine Matrix wie die zeigen Sie Ihre Methode ist schneller als JOBLIB Hashing aber langsamer als numpy:

numpy: 100000 loops, best of 3: 15.1 µs per loop 
joblib:1000 loops, best of 3: 885 µs per loop 
tuple: 10000 loops, best of 3: 27 µs per loop 
pandas: 100 loops, best of 3: 2.2 ms per loop 

Wenn Sie eine große Anzahl von Zeilen haben, dann können Sie wahrscheinlich einen besseren Ersatz für Counter finden Hash-Frequenzen zu finden .

Bearbeiten: Anzahl der Benchmarks von @acjr's Lösung in meinem System hinzugefügt, so dass es einfacher zu vergleichen ist. Die numpige Lösung ist in beiden Fällen die schnellste.

4

(Dies setzt voraus, dass die Anordnung ziemlich klein ist, beispielsweise weniger als 1000 Zeilen.)

Hier eine kurze NumPy Art und Weise zu zählen, wie oft jede Zeile in einem Array erscheint:

>>> (my_array[:, np.newaxis] == my_array).all(axis=2).sum(axis=1) 
array([3, 3, 1, 1, 3, 1]) 

Diese Zählt, wie oft jede Zeile in my_array erscheint, wobei ein Array zurückgegeben wird, bei dem der erste Wert angibt, wie oft die erste Zeile angezeigt wird, der zweite Wert zeigt, wie oft die zweite Zeile angezeigt wird, und so weiter.

+0

Mit 'n = np.arange (my_array.shape [0])' kann man ein schönes Ergebnis erhalten, indem man '[n [ui] für ui schreibt in (my_array [:, np.newaxis,]] == my_array) .all (axis = 2)] '... Schöne Antwort, ich habe es schon halb verstanden, aber was mich verwirrt, ist, wie du mit der Lösung herauskommst! – gboffi

0

Eine Lösung identisch mit Jaimes in der numpy_indexed finden Paket (Disclaimer: Ich bin der Autor)

import numpy_indexed as npi 
npi.count(my_array) 
Verwandte Themen