251 votes

Trouver des lignes uniques dans numpy.array

Je dois trouver des lignes uniques dans un numpy.array .

Par exemple:

 >>> a # I have
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])
 

Je sais que je peux créer un ensemble et une boucle sur le tableau, mais je recherche une solution efficace pure numpy . Je crois qu’il existe un moyen de définir le type de données sur null et que je pourrais alors utiliser numpy.unique , mais je ne savais pas comment le faire fonctionner.

144voto

Encore une autre solution possible

 np.vstack({tuple(row) for row in a})
 

115voto

Jaime Points 25540

Une autre option pour l'utilisation de structuré à l'aide de tableaux est une vue d'un void type qui se joint à l'ensemble de la ligne dans un article:

a = np.array([[1, 1, 1, 0, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [1, 1, 1, 0, 0, 0],
              [1, 1, 1, 1, 1, 0]])

b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)

unique_a = a[idx]

>>> unique_a
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

MODIFIER Ajouté np.ascontiguousarray suivant @seberg, de la recommandation. Cela va ralentir la méthode à la baisse si le tableau n'est pas déjà contigu.

MODIFIER Le ci-dessus peuvent être légèrement accéléré, peut-être au détriment de la clarté, en faisant:

unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])

Aussi, au moins sur mon système, la performance sage, il est sur le pair, ou même mieux, que la lexsort méthode:

a = np.random.randint(2, size=(10000, 6))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop

a = np.random.randint(2, size=(10000, 100))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop

31voto

Joe Kington Points 68089

Si vous voulez éviter de la mémoire, le coût de la conversion à une série de n-uplets ou d'une autre structure de données similaires, vous pouvez utiliser numpy est structuré tableaux.

L'astuce est de consulter votre tableau d'origine comme une structure du tableau où chaque élément correspond à une ligne du tableau original. Ce n'est pas une copie, et est très efficace.

Comme un exemple rapide:

import numpy as np

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])

ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)

uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq

Pour comprendre ce qui se passe, avoir un regard sur les résultats intermédiaires.

Une fois que nous voyons les choses comme une structure du tableau, chaque élément du tableau est une ligne dans votre tableau d'origine. (En gros, c'est une structure de données similaires à une liste de tuples.)

In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(1, 1, 1, 0, 0, 0)],
       [(1, 1, 1, 1, 1, 0)]],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Une fois que nous courons numpy.unique, nous aurons une structurés tableau:

In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Que nous avons alors besoin à considérer comme une "normale", array (_ stocke le résultat du dernier calcul en ipython, ce qui est pourquoi vous voyez _.view...):

In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])

Et puis, remodeler à l'arrière dans un tableau 2D (-1 est un espace réservé qui dit numpy pour calculer le bon nombre de lignes, de donner le nombre de colonnes):

In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

Évidemment, si vous voulez être plus concis, vous pouvez l'écrire comme:

import numpy as np

def unique_rows(data):
    uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
    return uniq.view(data.dtype).reshape(-1, data.shape[1])

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])
print unique_rows(data)

Qui se traduit par:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]

20voto

Ryan Saxe Points 2664

np.unique lorsque je l'exécute sur np.random.random(100).reshape(10,10) renvoie tous les éléments individuels uniques, mais vous voulez les lignes uniques, vous devez donc d'abord les mettre en tuples:

 array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)
 

C’est la seule façon pour moi de vous voir changer les types pour faire ce que vous voulez, et je ne suis pas sûr que l’itération de liste à changer en tuples soit acceptable si vous «ne passez pas en boucle»

16voto

cge Points 1830

np.des œuvres uniques par tri aplatie tableau, puis regarder si chaque élément est égale à la précédente. Cela peut être fait manuellement, sans l'aplatissement:

ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]

Cette méthode ne permet pas l'utilisation de n-uplets, et devrait être beaucoup plus simple et plus rapide que les autres méthodes présentées ici.

NOTE: UNE version précédente de ce fait pas l'ind droit après une[, ce qui signifie que le mauvais indices ont été utilisés. Aussi, Joe kingston portant fait un bon moment que ce n'est faire une variété intermédiaire de copies. La méthode suivante permet de moins en moins, en faisant une triés copier, puis à l'aide de vues de:

b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]

C'est plus rapide et utilise moins de mémoire.

Aussi, si vous voulez trouver des lignes uniques dans un ndarray indépendamment du nombre de dimensions dans le tableau, les éléments suivants:

b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=tuple(range(1,a.ndim)))))]

Une intéressante question qui reste en suspens serait si vous vouliez de tri/unique le long d'un axe arbitraire, d'un arbitraire de la dimension du tableau, quelque chose qui serait plus difficile.

Edit:

Pour démontrer les différences de vitesse, j'ai couru quelques tests dans ipython de l'une des trois méthodes décrites dans les réponses. Avec votre exact, il n'y a pas trop de différence, même si cette version est un peu plus rapide:

In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop

In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop

In [89]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop

Avec un grand a, cependant, cette version est beaucoup, beaucoup plus rapide:

In [96]: a = np.random.randint(0,2,size=(10000,6))

In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop

In [98]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop

In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X