2009-12-21 4 views
11

Je suis en train d'exécuter ce qui suitConstruire un python mis d'une matrice numpy

>> from numpy import * 
>> x = array([[3,2,3],[4,4,4]]) 
>> y = set(x) 
TypeError: unhashable type: 'numpy.ndarray' 

Comment puis-je facilement et efficacement créer un ensemble à partir d'un tableau numpy?

Répondre

17

Si vous voulez un ensemble des éléments, voici une autre, probablement plus rapide w ay:

y = set(x.flatten()) 

PS: après avoir effectué des comparaisons entre x.flat, x.flatten() et x.ravel() sur un tableau de 10x100, j'ai découvert qu'ils remplissent tous à peu près la même vitesse. Pour un tableau 3x3, la version la plus rapide est la version iterator:

y = set(x.flat) 

que je recommande car il est la version moins chère mémoire (il adapte bien avec la taille du tableau).

PS: Il y a aussi une fonction NumPy qui fait quelque chose de similaire:

y = numpy.unique(x) 

Cela ne produit un tableau numpy avec le même élément que set(x.flat), mais comme un tableau numpy. C'est très rapide (presque 10 fois plus rapide), mais si vous avez besoin d'un set, alors faire set(numpy.unique(x)) est un peu plus lent que les autres procédures (la construction d'un ensemble vient avec un gros surcoût).

+2

Bonne suggestion! Vous pouvez également utiliser set (x.ravel()), qui fait la même chose mais ne crée une copie que si nécessaire. Ou, mieux, utilisez set (x.flat). x.flat est un itérateur sur les éléments du tableau aplati, mais ne perd pas de temps à réellement aplatir le tableau – musicinmybrain

+0

@musicinmybrain: de très bons points! Je vous remercie! – EOL

+2

ATTENTION: cette réponse * ne vous * donnera pas un ensemble de vecteurs, mais plutôt un ensemble de nombres. Si vous voulez un ensemble de vecteurs, voyez la réponse de miku ci-dessous qui convertit les vecteurs en tuples – conradlee

9

La contrepartie immuable à un tableau est le tuple, par conséquent, essayez de convertir le tableau de tableaux dans un tableau de tuples:

>> from numpy import * 
>> x = array([[3,2,3],[4,4,4]]) 

>> x_hashable = map(tuple, x) 

>> y = set(x_hashable) 
set([(3, 2, 3), (4, 4, 4)]) 
+0

et comment je pouvais facilement/transformer efficacement retour à une liste? – user989762

+0

'map (array, y)' – Manuel

3

Si vous voulez un ensemble des éléments:

>> y = set(e for r in x 
      for e in r) 
set([2, 3, 4]) 

Pour un ensemble de lignes:

>> y = set(tuple(r) for r in x) 
set([(3, 2, 3), (4, 4, 4)]) 
6

Les réponses ci-dessus fonctionnent si vous voulez créer un ensemble sur des éléments contenus dans un ndarray, mais si vous voulez créer un ensemble de ndarray objets - ou utiliser ndarray objets comme les clés dans un dictionnaire - vous Je vais devoir leur fournir une enveloppe pouvant être lavée. Voir le code ci-dessous pour un exemple simple:

from hashlib import sha1 

from numpy import all, array, uint8 


class hashable(object): 
    r'''Hashable wrapper for ndarray objects. 

     Instances of ndarray are not hashable, meaning they cannot be added to 
     sets, nor used as keys in dictionaries. This is by design - ndarray 
     objects are mutable, and therefore cannot reliably implement the 
     __hash__() method. 

     The hashable class allows a way around this limitation. It implements 
     the required methods for hashable objects in terms of an encapsulated 
     ndarray object. This can be either a copied instance (which is safer) 
     or the original object (which requires the user to be careful enough 
     not to modify it). 
    ''' 
    def __init__(self, wrapped, tight=False): 
     r'''Creates a new hashable object encapsulating an ndarray. 

      wrapped 
       The wrapped ndarray. 

      tight 
       Optional. If True, a copy of the input ndaray is created. 
       Defaults to False. 
     ''' 
     self.__tight = tight 
     self.__wrapped = array(wrapped) if tight else wrapped 
     self.__hash = int(sha1(wrapped.view(uint8)).hexdigest(), 16) 

    def __eq__(self, other): 
     return all(self.__wrapped == other.__wrapped) 

    def __hash__(self): 
     return self.__hash 

    def unwrap(self): 
     r'''Returns the encapsulated ndarray. 

      If the wrapper is "tight", a copy of the encapsulated ndarray is 
      returned. Otherwise, the encapsulated ndarray itself is returned. 
     ''' 
     if self.__tight: 
      return array(self.__wrapped) 

     return self.__wrapped 

Utilisation de la classe d'emballage est assez simple:

>>> from numpy import arange 

>>> a = arange(0, 1024) 
>>> d = {} 
>>> d[a] = 'foo' 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
TypeError: unhashable type: 'numpy.ndarray' 
>>> b = hashable(a) 
>>> d[b] = 'bar' 
>>> d[b] 
'bar'