2014-04-29 14 views
8

Come posso uniquify l'elenco seguente in Python:Ottenere elenco di unici multi-set

all_the_ways = [(5,), (2, 2, 1), (2, 1, 2), (2, 1, 1, 1), (1, 2, 2),\ 
       (1, 2, 1, 1), (1, 1, 2, 1), (1, 1, 1, 2), (1, 1, 1, 1, 1)] 

output desiderato è:

[(5,), (2, 2, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)] 

cioè ho bisogno di sbarazzarsi di tuple che hanno lo stesso insieme di numeri ma in ordine diverso.

ho cercato

set(all_the_ways) 

ma trasporre solo elementi.

E quando lo faccio

list(map(set, all_the_ways)) 

le cose che ottengono solo peggio:

[{5}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1}] 

In altre parole ho bisogno di convertire tuple interiore per una collezione che consente a più elementi uguali (set non è adatto) e per le quali permutazioni di elementi non cambiano la collezione stessa (un po 'come C++' s multiset)

+0

Quale dovrebbe essere l'uscita quando 'all_the_ways = [(2, 1, 2), (2, 2, 1)]'? – thefourtheye

+0

prima o seconda tupla, non importa – tsionyx

+0

Quindi, il risultato dovrebbe essere in 'all_the_ways'? – thefourtheye

risposta

5

ne dite di questo:

list(set(tuple(sorted(s)) for s in all_the_ways)) 

uscita:

[(1, 2, 2), (5,), (1, 1, 1, 1, 1), (1, 1, 1, 2)] 

Sarà storpiare l'ordine di ciascuna tupla però. Presumo che ciò non contenga, in quanto le tuple contenenti lo stesso insieme di numeri sono considerate uguali per il tuo caso. Ciò implica che, alla fine, l'elenco di output potrebbe contenere tuple che non sono tra l'ingresso originale, per esempio (credito @thefourtheye):

all_the_ways = [(2, 1, 2), (2, 2, 1)] 
# Output: [(1, 2, 2)] 

Questo può o non può essere un problema, e se è, è possibile utilizzare le soluzioni più solide che sono già menzionate nelle altre risposte eccellenti.

+1

Se la combinazione '(1, 2, 2)' non esiste in 'all_the_ways', questo potrebbe essere un problema. Ma non sono sicuro se va bene con l'OP. Già + 1 – thefourtheye

+0

Questo è molto vero, come ho menzionato nella risposta. Ho deciso di non affrontare il problema degli ordini per fornire una prospettiva più semplice, nel caso in cui non fosse un vincolo in questo problema. + 1s a tutte le soluzioni di conservazione degli ordini! :) –

+1

In realtà, non riguarda l'ordine. Quando 'all_the_ways = [(2, 1, 2), (2, 2, 1)]', l'output sarà '[(1, 2, 2)]', che non è presente in 'all_the_ways'. Questo potrebbe essere un problema, immagino. – thefourtheye

0

Io prendo che consideri due elementi "uguali" se contengono gli stessi valori, indipendentemente dall'ordine.

Così si può "canonicalize" ogni tupla di classificare esso, convertire torna a tuple (in modo che siano hashable), e rimuovere i duplicati con set ancora:

set(tuple(sorted(tup)) for tup in all_the_ways) 

Si può anche conservare il "esterno" originale ordine, utilizzando OrderedSet anziché set.

3

Usa collections.Counter() per identificare i multinsiemi unici:

>>> from collections import Counter 

>>> all_the_ways = [(5,), (2, 2, 1), (2, 1, 2), (2, 1, 1, 1), (1, 2, 2),\ 
       (1, 2, 1, 1), (1, 1, 2, 1), (1, 1, 1, 2), (1, 1, 1, 1, 1)] 
>>> result = [] 
>>> seen = set() 
>>> for tup in all_the_ways: 
     key = tuple(sorted(Counter(tup).items())) # unique signature 
     if key not in seen: 
      result.append(tup) 
     seen.add(key) 

>>> result 
[(5,), (2, 2, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)] 
+0

stavo pensando a questo solo, ma non è possibile procedere ulteriormente in quanto non sono lavabili ... :( – thefourtheye

+0

@thefourtheye Una volta che il conteggio è fatto, una sorta di elementi rende ordinamento canonico, e tuplizing lo rende lavabile :-) –

+0

Perché non semplicemente ' Counter (tupla (sort (i)) per i in all_the_ways) .keys() '? –

1

Può essere questo?:

result = {tuple(sorted(x)) for x in all_the_ways} 
2

Se l'ordine non importa è possibile utilizzare questo

from collections import Counter 
>>> {frozenset(Counter(tup).items()):tup for tup in data}.values() 
# [(1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1), (5,)] 

Se si desidera mantenere l'ordine,

from collections import Counter, OrderedDict 
OrderedDict([frozenset(Counter(tup).items()),tup] for tup in data).values() 
# [(5,), (1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1)] 

In entrambe le soluzioni ci affidiamo frozenset, perché gli oggetti set non sono lavabili in quanto sono modificabili. Nel primo caso, costruiamo un dizionario con la frequenza dei numeri (determinata con Counter) come chiave e la tupla corrente come valore corrispondente a quello. Una volta completata la costruzione del dizionario, prendiamo tutti i valori, che corrispondono alle tuple.

Nel secondo caso, è sufficiente utilizzare OrderedDict per mantenere l'ordine.

+1

+1 Per la bella combinazione di OrderedDict, frozenset e Counter. –

+0

@RaymondHettinger Grazie :-) – thefourtheye

1

Prova

from collections import OrderedDict 
print OrderedDict.fromkeys(map(lambda x: tuple(sorted(x)), all_the_ways)).keys() 

o

print set(map(lambda x: tuple(sorted(x)), all_the_ways))