Acessando itens em uma coleção.OrderedDict por índice

142

Digamos que eu tenha o seguinte código:

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

Existe uma maneira de acessar os itens de maneira numerada, como:

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

Respostas:

181

Se for um, OrderedDict()você pode acessar facilmente os elementos indexando, obtendo as tuplas de pares (chave, valor) da seguinte maneira

>>> 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')

Nota para Python 3.X

dict.itemsretornaria um objeto de exibição de ditado iterável em vez de uma lista. Precisamos agrupar a chamada em uma lista para possibilitar a indexação

>>> items = list(d.items())
>>> items
[('foo', 'python'), ('bar', 'spam')]
>>> items[0]
('foo', 'python')
>>> items[1]
('bar', 'spam')
Abhijit
fonte
21
Observe que no 3.x, o itemsmétodo retorna um objeto de exibição de dicionário interável em vez de uma lista e não oferece suporte a fatias ou indexação. Então você teria que transformá-lo em uma lista primeiro. docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper
8
A cópia de itens, valores ou chaves em listas pode ser bastante lenta para grandes ditames. Eu criei uma reescrita de OrderedDict () com uma estrutura de dados interna diferente para aplicações que têm de fazer isso muitas vezes: github.com/niklasf/indexed.py
Niklas
1
@PeterDeGlopper como faço para transformá-lo em uma lista?
Dejell
1
@Dejel - use o construtor:list(d.items())
Peter DeGlopper
9
Se você só um item de acesso, você pode evitar a sobrecarga de memória do list(d.items())usando next(islice(d.items(), 1))para obter('bar', 'spam')
Quantum7
24

Você precisa usar um OrderedDict ou deseja especificamente um tipo de mapa que é ordenado de alguma forma com indexação posicional rápida? Se este for o caso, considere um dos muitos tipos de ditado classificado do Python (que ordena pares de valor-chave com base na ordem de classificação). Algumas implementações também oferecem suporte à indexação rápida. Por exemplo, o projeto de contêineres classificados possui um tipo SortedDict para esse fim.

>>> 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
fonte
1
Você também pode usar SortedDictcom uma função de tecla para evitar comparações. Como: SortedDict(lambda key: 0, ...). As chaves serão desordenadas, mas permanecerão em uma ordem estável e são indexáveis.
usar o seguinte comando
19

Aqui está um caso especial se você deseja a primeira entrada (ou próxima a ela) em um OrderedDict, sem criar uma lista. (Isso foi atualizado para o Python 3):

>>> 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'

(A primeira vez que você diz "next ()", significa realmente "primeiro".)

No meu teste informal, next(iter(d.items()))com um pequeno OrderedDict é apenas um pouquinho mais rápido que items()[0]. Com um OrderedDict de 10.000 entradas, next(iter(d.items()))foi cerca de 200 vezes mais rápido que items()[0].

MAS, se você salvar a lista de itens () uma vez e depois usá-la muito, isso poderá ser mais rápido. Ou se você repetidamente {criar um iterador de itens () e percorrê-lo para a posição desejada}, isso poderá ser mais lento.

SteveWithamDuplicate
fonte
10
Python 3 OrderedDicts não tem um iteritems()método, então você vai precisar fazer o seguinte, a fim de obter o primeiro item: next(iter(d.items())).
Nathan Osman
No Python 3 d.items()não parece ser um iterador, então o iter na frente não ajudará? Ele ainda retornará a lista completa :(
askol
1
Atualização: Eu estava errado, iter (d.items ()) retorna odict_iteratore me foi confirmado no IRC #python que isso não faz uma cópia da lista.
asksol
@ Nathan Osman, obrigado pela cutucada. Finalmente me atualizei para o Python 3 recentemente!
SteveWithamDuplicate 16/04
14

É muito mais eficiente usar o IndexedOrderedDict do indexedpacote.

Após o comentário de Niklas, fiz um benchmark no OrderedDict e IndexedOrderedDict com 1000 entradas.

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 é ~ 100 vezes mais rápido nos elementos de indexação em uma posição específica nesse caso específico.

刘金国
fonte
Agradável! Ainda não está em Anaconda, infelizmente.
Konstantin
1
@ Konstantin O nome real do pacote é indexed.py . Tente instalar em indexed.pyvez de indexed.
Sven Haile
9

Este wiki da comunidade tenta coletar respostas existentes.

Python 2.7

Em python 2, as keys(), values()e items()funções de OrderedDictlistas de retorno. Usando valuescomo exemplo, a maneira mais simples é

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

Para grandes coleções onde você só se preocupam com um único índice, você pode evitar criar a lista completa usando as versões do gerador, iterkeys, itervaluese iteritems:

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

O pacote indexed.py fornece IndexedOrderedDict, projetado para esse caso de uso e será a opção mais rápida.

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

O uso de itervalues ​​pode ser consideravelmente mais rápido para dicionários grandes com acesso aleatório:

$ 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

O Python 3 tem as mesmas duas opções básicas (lista versus gerador), mas os métodos dict retornam geradores por padrão.

Método de lista:

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

Método gerador:

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

Os dicionários do Python 3 são uma ordem de magnitude mais rápida que o python 2 e têm acelerações semelhantes para usar geradores.

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

É uma nova era e, com os dicionários Python 3.6.1, agora eles mantêm sua ordem. Essa semântica não é explícita porque isso exigiria a aprovação do BDFL. Mas Raymond Hettinger é a próxima melhor coisa (e engraçado) e ele faz uma muito forte caso que os dicionários serão ordenados por um tempo muito longo.

Então agora é fácil criar fatias de um dicionário:

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

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

Nota: A preservação dictonária de ordem de inserção agora é oficial no Python 3.7 .

posto avançado
fonte
0

para OrderedDict (), você pode acessar os elementos indexando obtendo as tuplas dos pares (chave, valor) da seguinte forma ou usando '.values ​​()'

>>> 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
fonte