Zugriff auf Elemente in einer Sammlung.OrderedDict nach Index

142

Nehmen wir an, ich habe den folgenden Code:

import collections
d = collections.OrderedDict()
d['foo'] = 'python'
d['bar'] = 'spam'

Gibt es eine Möglichkeit, wie ich nummeriert auf die Elemente zugreifen kann, z.

d(0) #foo's Output
d(1) #bar's Output
Billjk
quelle

Antworten:

181

Wenn dies der OrderedDict()Fall ist, können Sie einfach auf die Elemente zugreifen, indem Sie sie indizieren, indem Sie die Tupel von (Schlüssel-, Wert-) Paaren wie folgt abrufen

>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>> d.items()[0]
('foo', 'python')
>>> d.items()[1]
('bar', 'spam')

Hinweis für Python 3.X.

dict.itemswürde ein iterierbares Diktatansichtsobjekt anstelle einer Liste zurückgeben. Wir müssen den Aufruf in eine Liste einschließen, um die Indizierung zu ermöglichen

>>> items = list(d.items())
>>> items
[('foo', 'python'), ('bar', 'spam')]
>>> items[0]
('foo', 'python')
>>> items[1]
('bar', 'spam')
Abhijit
quelle
21
Beachten Sie, dass die itemsMethode in 3.x ein interaktives Wörterbuchansichtsobjekt anstelle einer Liste zurückgibt und kein Slicing oder Indizieren unterstützt. Sie müssten es also zuerst in eine Liste umwandeln. docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper
8
Das Kopieren von Elementen, Werten oder Schlüsseln in Listen kann für große Diktaturen sehr langsam sein. Ich habe eine Neufassung von OrderedDict () mit einer anderen internen Datenstruktur für Anwendungen erstellt, die dies sehr oft tun müssen: github.com/niklasf/indexed.py
Niklas
1
@PeterDeGlopper Wie mache ich daraus eine Liste?
Dejell
1
@ Dejel - verwenden Sie den Konstruktor:list(d.items())
Peter DeGlopper
8
Wenn Sie nur auf ein Element zugreifen, können Sie den Speicheraufwand vermeiden, list(d.items())indem Sie verwenden next(islice(d.items(), 1)), um('bar', 'spam')
Quantum7
24

Müssen Sie ein OrderedDict verwenden oder möchten Sie speziell einen kartenähnlichen Typ, der in irgendeiner Weise mit schneller Positionsindizierung geordnet ist? Wenn letzteres der Fall ist, betrachten Sie einen der vielen sortierten Diktattypen von Python (der Schlüssel-Wert-Paare basierend auf der Sortierreihenfolge der Schlüssel sortiert). Einige Implementierungen unterstützen auch die schnelle Indizierung. Zu diesem Zweck verfügt das sortedcontainers- Projekt beispielsweise über einen SortedDict- Typ.

>>> from sortedcontainers import SortedDict
>>> sd = SortedDict()
>>> sd['foo'] = 'python'
>>> sd['bar'] = 'spam'
>>> print sd.iloc[0] # Note that 'bar' comes before 'foo' in sort order.
'bar'
>>> # If you want the value, then simple do a key lookup:
>>> print sd[sd.iloc[1]]
'python'
GrantJ
quelle
1
Sie können auch SortedDicteine Schlüsselfunktion verwenden, um Vergleiche zu vermeiden. Wie : SortedDict(lambda key: 0, ...). Die Schlüssel werden dann unsortiert, bleiben jedoch in einer stabilen Reihenfolge und sind indizierbar.
GrantJ
19

Hier ist ein Sonderfall, wenn Sie den ersten Eintrag (oder einen ähnlichen Eintrag) in einem OrderedDict wünschen, ohne eine Liste zu erstellen. (Dies wurde auf Python 3 aktualisiert):

>>> from collections import OrderedDict
>>> 
>>> d = OrderedDict()
>>> d["foo"] = "one"
>>> d["bar"] = "two"
>>> d["baz"] = "three"
>>> next(iter(d.items()))
('foo', 'one')
>>> next(iter(d.values()))
'one'

(Wenn Sie zum ersten Mal "next ()" sagen, bedeutet dies wirklich "first".)

In meinem informellen Test ist next(iter(d.items()))mit einem kleinen OrderedDict nur ein kleines bisschen schneller als items()[0]. Mit einem OrderedDict von 10.000 Einträgen next(iter(d.items()))war das etwa 200-mal schneller als items()[0].

ABER wenn Sie die Liste items () einmal speichern und die Liste dann häufig verwenden, kann dies schneller sein. Oder wenn Sie wiederholt {einen items () - Iterator erstellen und ihn an die gewünschte Position bringen}, kann dies langsamer sein.

SteveWithamDuplicate
quelle
10
Python 3 OrderedDicts haben keine iteritems()Methode, daher müssen Sie Folgendes tun, um das erste Element zu erhalten : next(iter(d.items())).
Nathan Osman
In Python 3 d.items()scheint es kein Iterator zu sein, also hilft der Iter vor nicht? Es wird immer noch die vollständige Liste zurückgegeben :(
fragtol
1
Update: Ich habe mich geirrt, iter (d.items ()) kehrt zurück odict_iteratorund wurde mir im IRC #python bestätigt, dass dies keine Kopie der Liste erstellt.
fragtol
@ Nathan Osman, danke für den Schubs. Ich habe mich kürzlich endlich auf Python 3 aktualisiert!
SteveWithamDuplicate
14

Die Verwendung von IndexedOrderedDict aus dem indexedPaket ist erheblich effizienter .

Nach Niklas 'Kommentar habe ich einen Benchmark für OrderedDict und IndexedOrderedDict mit 1000 Einträgen durchgeführt.

In [1]: from numpy import *
In [2]: from indexed import IndexedOrderedDict
In [3]: id=IndexedOrderedDict(zip(arange(1000),random.random(1000)))
In [4]: timeit id.keys()[56]
1000000 loops, best of 3: 969 ns per loop

In [8]: from collections import OrderedDict
In [9]: od=OrderedDict(zip(arange(1000),random.random(1000)))
In [10]: timeit od.keys()[56]
10000 loops, best of 3: 104 µs per loop

IndexedOrderedDict ist in diesem speziellen Fall ~ 100-mal schneller bei der Indizierung von Elementen an einer bestimmten Position.

刘金国
quelle
Nett! Leider noch nicht in Anaconda.
Konstantin
1
@Konstantin Der tatsächliche Name des Pakets lautet indexed.py . Versuchen Sie indexed.pystattdessen zu installieren indexed.
Sven Haile
9

Dieses Community-Wiki versucht, vorhandene Antworten zu sammeln.

Python 2.7

In Python 2, das keys(), values()und items()Funktionen der OrderedDictRückkehr Listen. Am valueseinfachsten ist es am Beispiel

d.values()[0]  # "python"
d.values()[1]  # "spam"

Für große Sammlungen , wo man nur über einen einzigen Index kümmern, können Sie vermeiden , um die vollständige Liste der Erstellung der Generator - Versionen verwenden, iterkeys, itervaluesund iteritems:

import itertools
next(itertools.islice(d.itervalues(), 0, 1))  # "python"
next(itertools.islice(d.itervalues(), 1, 2))  # "spam"

Das Paket indexed.py bietet IndexedOrderedDict, das für diesen Anwendungsfall entwickelt wurde und die schnellste Option ist.

from indexed import IndexedOrderedDict
d = IndexedOrderedDict({'foo':'python','bar':'spam'})
d.values()[0]  # "python"
d.values()[1]  # "spam"

Die Verwendung von itervalues ​​kann für große Wörterbücher mit wahlfreiem Zugriff erheblich schneller sein:

$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 1000;   d = OrderedDict({i:i for i in range(size)})'  'i = randint(0, size-1); d.values()[i:i+1]'
1000 loops, best of 3: 259 usec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 10000;  d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i:i+1]'
100 loops, best of 3: 2.3 msec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 100000; d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i:i+1]'
10 loops, best of 3: 24.5 msec per loop

$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 1000;   d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
10000 loops, best of 3: 118 usec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 10000;  d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
1000 loops, best of 3: 1.26 msec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 100000; d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
100 loops, best of 3: 10.9 msec per loop

$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 1000;   d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.19 usec per loop
$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 10000;  d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.24 usec per loop
$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 100000; d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.61 usec per loop

+--------+-----------+----------------+---------+
|  size  | list (ms) | generator (ms) | indexed |
+--------+-----------+----------------+---------+
|   1000 | .259      | .118           | .00219  |
|  10000 | 2.3       | 1.26           | .00224  |
| 100000 | 24.5      | 10.9           | .00261  |
+--------+-----------+----------------+---------+

Python 3.6

Python 3 hat die gleichen zwei grundlegenden Optionen (Liste gegen Generator), aber die dict-Methoden geben standardmäßig Generatoren zurück.

Listenmethode:

list(d.values())[0]  # "python"
list(d.values())[1]  # "spam"

Generatormethode:

import itertools
next(itertools.islice(d.values(), 0, 1))  # "python"
next(itertools.islice(d.values(), 1, 2))  # "spam"

Python 3-Wörterbücher sind um eine Größenordnung schneller als Python 2 und haben ähnliche Beschleunigungen für die Verwendung von Generatoren.

+--------+-----------+----------------+---------+
|  size  | list (ms) | generator (ms) | indexed |
+--------+-----------+----------------+---------+
|   1000 | .0316     | .0165          | .00262  |
|  10000 | .288      | .166           | .00294  |
| 100000 | 3.53      | 1.48           | .00332  |
+--------+-----------+----------------+---------+
Quantum7
quelle
7

Es ist eine neue Ära und mit Python 3.6.1 behalten Wörterbücher jetzt ihre Reihenfolge bei. Diese Semantik ist nicht explizit, da dies eine BDFL-Genehmigung erfordern würde. Aber Raymond Hettinger ist das nächstbeste (und witzigere) und er macht ein ziemlich starkes Argument dafür, dass Wörterbücher für eine sehr lange Zeit bestellt werden.

So ist es jetzt einfach, Slices eines Wörterbuchs zu erstellen:

test_dict = {
                'first':  1,
                'second': 2,
                'third':  3,
                'fourth': 4
            }

list(test_dict.items())[:2]

Hinweis: Die Beibehaltung der diktonaren Einfügereihenfolge ist jetzt in Python 3.7 offiziell .

Hochpfosten
quelle
0

Für OrderedDict () können Sie auf die Elemente zugreifen, indem Sie sie indizieren, indem Sie die Tupel von (Schlüssel-, Wert-) Paaren wie folgt abrufen oder '.values ​​()' verwenden.

>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>>d.values()
odict_values(['python','spam'])
>>>list(d.values())
['python','spam']
Mehar Rahim
quelle