Python ha un dizionario ordinato . Che dire di un set ordinato?
collections.Counter
è la borsa di Python.
Python ha un dizionario ordinato . Che dire di un set ordinato?
collections.Counter
è la borsa di Python.
Risposte:
C'è una ricetta ordinata (possibile nuovo collegamento ) per questo, a cui si fa riferimento dalla documentazione di Python 2 . Funziona su Py2.6 o successivo e 3.0 o successivo senza alcuna modifica. L'interfaccia è quasi esattamente la stessa di un set normale, tranne per il fatto che l'inizializzazione dovrebbe essere fatta con un elenco.
OrderedSet([1, 2, 3])
Questo è un MutableSet, quindi la firma per .union
non corrisponde a quella del set, ma poiché include __or__
qualcosa di simile può essere facilmente aggiunta:
@staticmethod
def union(*sets):
union = OrderedSet()
union.union(*sets)
return union
def union(self, *sets):
for set in sets:
self |= set
update
, union
, intersection
.
union
nella stessa classe. L'ultimo "vincerà" e il primo non esisterà in fase di esecuzione. Questo perché OrderedSet.union
(senza parentesi) deve fare riferimento a un singolo oggetto.
Le chiavi di un dizionario sono uniche. Pertanto, se si ignorano i valori in un dizionario ordinato (ad es. Assegnandoli None
), si ha essenzialmente un set ordinato.
A partire da Python 3.1 c'è collections.OrderedDict
. Di seguito è riportata un'implementazione di esempio di un OrderedSet. (Si noti che solo pochi metodi devono essere definiti o ignorati: collections.OrderedDict
ed collections.MutableSet
eseguire il sollevamento pesante.)
import collections
class OrderedSet(collections.OrderedDict, collections.MutableSet):
def update(self, *args, **kwargs):
if kwargs:
raise TypeError("update() takes no keyword arguments")
for s in args:
for e in s:
self.add(e)
def add(self, elem):
self[elem] = None
def discard(self, elem):
self.pop(elem, None)
def __le__(self, other):
return all(e in other for e in self)
def __lt__(self, other):
return self <= other and self != other
def __ge__(self, other):
return all(e in self for e in other)
def __gt__(self, other):
return self >= other and self != other
def __repr__(self):
return 'OrderedSet([%s])' % (', '.join(map(repr, self.keys())))
def __str__(self):
return '{%s}' % (', '.join(map(repr, self.keys())))
difference = __sub__
difference_update = __isub__
intersection = __and__
intersection_update = __iand__
issubset = __le__
issuperset = __ge__
symmetric_difference = __xor__
symmetric_difference_update = __ixor__
union = __or__
OrderedSet
che sottoclassi OrderedDict
e abc.Set
quindi definire __len__
, __iter__
e __contains__
.
collections
, ma per il resto un buon suggerimento
OrderedSet([1,2,3])
genera un errore di tipo. Come funziona il costruttore? Esempio di utilizzo mancante.
La risposta è no, ma puoi usare collections.OrderedDict
dalla libreria standard di Python con solo chiavi (e valori come None
) per lo stesso scopo.
Aggiornamento : A partire da Python 3.7 (e CPython 3.6), lo standard dict
è garantito per preservare l'ordine ed è più performante di OrderedDict
. (Per compatibilità con le versioni precedenti e soprattutto leggibilità, tuttavia, potresti voler continuare a utilizzare OrderedDict
.)
Ecco un esempio di come utilizzare dict
un set ordinato per filtrare gli articoli duplicati preservando l'ordine, emulando così un set ordinato. Usa il dict
metodo class fromkeys()
per creare un dict, quindi chiedi semplicemente il keys()
retro.
>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']
>>> list(dict.fromkeys(keywords))
['foo', 'bar', 'baz']
dict.fromkeys()
. Ma in quel caso, l'ordine delle chiavi viene preservato solo nelle implementazioni di CPython 3.6+, quindi OrderedDict
è una soluzione più portatile quando l'ordine conta.
keys = (1,2,3,1,2,1)
list(OrderedDict.fromkeys(keys).keys())
-> [1, 2, 3]
, python-3.7. Funziona.
dict
, set
in Python 3.7+ purtroppo non si conserva l'ordine.
Posso farti meglio di un OrderedSet: boltons ha un Python puro, compatibile 2/3IndexedSet
tipo che non è solo un insieme ordinato, ma supporta anche l'indicizzazione (come con le liste).
Semplicemente pip install boltons
(o copia setutils.py
nel tuo codebase), importa il IndexedSet
e:
>>> from boltons.setutils import IndexedSet
>>> x = IndexedSet(list(range(4)) + list(range(8)))
>>> x
IndexedSet([0, 1, 2, 3, 4, 5, 6, 7])
>>> x - set(range(2))
IndexedSet([2, 3, 4, 5, 6, 7])
>>> x[-1]
7
>>> fcr = IndexedSet('freecreditreport.com')
>>> ''.join(fcr[:fcr.index('.')])
'frecditpo'
Tutto è unico e mantenuto in ordine. Divulgazione completa: ho scritto il IndexedSet
, ma ciò significa anche che puoi darmi bug in caso di problemi . :)
Mentre altri hanno sottolineato che non esiste un'implementazione integrata di un set di conservazione degli ordini di inserzione in Python (ancora), sento che a questa domanda manca una risposta che afferma cosa si può trovare su PyPI .
Ci sono i pacchetti:
Alcune di queste implementazioni si basano sulla ricetta pubblicata da Raymond Hettinger su ActiveState che è menzionata anche in altre risposte qui.
my_set[5]
)remove(item)
Entrambe le implementazioni hanno O (1) per add(item)
e __contains__(item)
( item in my_set
).
set.union
non ci lavorano, anche se eredita collections.abc.Set
.
OrderedSet
ora supportaremove
Se stai usando il set ordinato per mantenere un ordine ordinato, considera l'utilizzo di un'implementazione del set ordinato da PyPI. Il modulo sortcontainers fornisce un SortedSet proprio per questo scopo. Alcuni vantaggi: pure-Python, implementazioni fast-as-C, copertura del test unitario al 100%, ore di stress test.
L'installazione da PyPI è semplice con pip:
pip install sortedcontainers
Si noti che se non è possibile pip install
, è sufficiente estrarre i file sortlist.py e sortset.py dal repository open source .
Una volta installato puoi semplicemente:
from sortedcontainers import SortedSet
help(SortedSet)
Il modulo dei contenitori ordinati mantiene anche un confronto delle prestazioni con diverse implementazioni alternative.
Per il commento che ha chiesto del tipo di dati bag di Python, c'è in alternativa un tipo di dati SortedList che può essere utilizzato per implementare in modo efficiente un bag.
SortedSet
classe lì richiede che i membri siano comparabili e hash.
set
e frozenset
richiedono anche elementi per essere hash. Il vincolo comparabile è l'aggiunta per SortedSet
, ma è anche un vincolo ovvio.
Nel caso in cui stai già utilizzando i panda nel tuo codice, il suo Index
oggetto si comporta in modo simile a un set ordinato, come mostrato in questo articolo .
Esempi dall'articolo:
indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])
indA & indB # intersection
indA | indB # union
indA - indB # difference
indA ^ indB # symmetric difference
indA.difference(indB)
, il segno meno esegue la sottrazione standard
Un po 'in ritardo al gioco, ma ho scritto una classe setlist
come parte di collections-extended
ciò che implementa completamente sia Sequence
eSet
>>> from collections_extended import setlist
>>> sl = setlist('abracadabra')
>>> sl
setlist(('a', 'b', 'r', 'c', 'd'))
>>> sl[3]
'c'
>>> sl[-1]
'd'
>>> 'r' in sl # testing for inclusion is fast
True
>>> sl.index('d') # so is finding the index of an element
4
>>> sl.insert(1, 'd') # inserting an element already in raises a ValueError
ValueError
>>> sl.index('d')
4
GitHub: https://github.com/mlenzen/collections-extended
Documentazione: http://collections-extended.lenzm.net/en/latest/
Non ce n'è OrderedSet
nella biblioteca ufficiale. Faccio un completo cheatsheet di tutta la struttura dei dati per riferimento.
DataStructure = {
'Collections': {
'Map': [
('dict', 'OrderDict', 'defaultdict'),
('chainmap', 'types.MappingProxyType')
],
'Set': [('set', 'frozenset'), {'multiset': 'collection.Counter'}]
},
'Sequence': {
'Basic': ['list', 'tuple', 'iterator']
},
'Algorithm': {
'Priority': ['heapq', 'queue.PriorityQueue'],
'Queue': ['queue.Queue', 'multiprocessing.Queue'],
'Stack': ['collection.deque', 'queue.LifeQueue']
},
'text_sequence': ['str', 'byte', 'bytearray']
}
Il pacchetto ParallelRegression fornisce una classe set ordinata setList () che è più completa di metodo rispetto alle opzioni basate sulla ricetta ActiveState. Supporta tutti i metodi disponibili per gli elenchi e la maggior parte se non tutti i metodi disponibili per gli insiemi.
Come menzionano altre risposte, come per Python 3.7+, il dict è ordinato per definizione. Invece di sottoclasse OrderedDict
possiamo sottoclassare abc.collections.MutableSet
o typing.MutableSet
usare le chiavi del dict per memorizzare i nostri valori.
class OrderedSet(typing.MutableSet[T]):
"""A set that preserves insertion order by internally using a dict."""
def __init__(self, iterable: t.Iterator[T]):
self._d = dict.fromkeys(iterable)
def add(self, x: T) -> None:
self._d[x] = None
def discard(self, x: T) -> None:
self._d.pop(x)
def __contains__(self, x: object) -> bool:
return self._d.__contains__(x)
def __len__(self) -> int:
return self._d.__len__()
def __iter__(self) -> t.Iterator[T]:
return self._d.__iter__()
Quindi solo:
x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]
Ho inserito questo codice in una piccola libreria , quindi chiunque può pip install
farlo.
Per molti scopi sarà sufficiente semplicemente chiamare ordinati. Per esempio
>>> s = set([0, 1, 2, 99, 4, 40, 3, 20, 24, 100, 60])
>>> sorted(s)
[0, 1, 2, 3, 4, 20, 24, 40, 60, 99, 100]
Se lo utilizzerai ripetutamente, si verificherà un sovraccarico chiamando la funzione ordinata, quindi potresti voler salvare l'elenco risultante, a condizione che tu abbia finito di cambiare il set. Se è necessario mantenere elementi unici e ordinati, sono d'accordo con il suggerimento di utilizzare OrderedDict da raccolte con un valore arbitrario come Nessuno.
Quindi avevo anche un piccolo elenco in cui avevo chiaramente la possibilità di introdurre valori non univoci.
Ho cercato l'esistenza di un elenco univoco di qualche tipo, ma poi ho capito che testare l'esistenza dell'elemento prima di aggiungerlo funziona bene.
if(not new_element in my_list):
my_list.append(new_element)
Non so se ci sono avvertimenti in questo semplice approccio, ma risolve il mio problema.