python - dict to ordereddict
OrderedDict vs defaultdict vs dict (2)
En la biblioteca de Python, ahora tenemos dos implementaciones de Python de diccionarios cuyas subclases dict
más allá del tipo de dict
nativo.
Los defensores de Python siempre han preferido el dict.setdefault
en dict.setdefault
usar dict.setdefault
cuando sea posible. Incluso el doc cita que This technique is simpler and faster than an equivalent technique using dict.setdefault():
De manera similar, dado que los diccionarios no mantienen el orden, es OrderedDict
usar OrderedDict
sobre el uso de dict
seguido de ordenar los elementos, siempre que sea posible para el uso alternativo.
En ambos casos, el código es definitivamente más limpio, pero a costa de una penalización de rendimiento.
Mientras respondía y comentaba una de las preguntas de la lista única de Python basada en un ítem , me topé con la penalización de rendimiento sobre el dict
nativo al usar defaultdict
y OrderedDict
. También parece que el tamaño de los datos tampoco es inmaterial para la ventaja de rendimiento que la solución dict
tiene sobre otros.
Creo que There should be one-- and preferably only one --obvious way to do it.
, entonces, ¿cuál es la forma preferida?
No hay una sola respuesta ni un solo dict verdadero y único. Entre muchas variables, depende de:
- Tamaño del conjunto de datos;
- Número de claves únicas
- Velocidad de la fábrica subyacente para el incumplimiento;
- Velocidad de OrderDict vs algún paso posterior de pedido;
- Versión de Python.
No me gusta generalizar, pero he aquí algunas generalidades:
- La declaración
This technique is simpler and faster than an equivalent technique using dict.setdefault()
simplemente está mal. Depende de los datos; -
setdefault
es más rápido y simple con pequeños conjuntos de datos; -
defaultdict
es más rápido para conjuntos de datos más grandes con conjuntos de claves más homogéneos; -
setdefault
tiene una ventaja con conjuntos de claves más heterogéneas; - estos resultados son diferentes para Python 3 vs Python 2;
-
OrderedDict
es más lento en todos los casos que no sea un algoritmo que depende del orden y el orden no es fácil de reconstruir o ordenar; - Python 3 generalmente es más rápido para la mayoría de las operaciones de
dict
; - El dict de Python 3.6 ahora está ordenado por orden de inserción.
La única verdad: ¡Depende! Las tres técnicas son útiles.
Aquí hay un código de tiempo para mostrar:
from __future__ import print_function
from collections import defaultdict
from collections import OrderedDict
try:
t=unichr(100)
except NameError:
unichr=chr
def f1(li):
''''''defaultdict''''''
d = defaultdict(list)
for k, v in li:
d[k].append(v)
return d.items()
def f2(li):
''''''setdefault''''''
d={}
for k, v in li:
d.setdefault(k, []).append(v)
return d.items()
def f3(li):
''''''OrderedDict''''''
d=OrderedDict()
for k, v in li:
d.setdefault(k, []).append(v)
return d.items()
if __name__ == ''__main__'':
import timeit
import sys
print(sys.version)
few=[(''yellow'', 1), (''blue'', 2), (''yellow'', 3), (''blue'', 4), (''red'', 1)]
fmt=''{:>12}: {:10.2f} micro sec/call ({:,} elements, {:,} keys)''
for tag, m, n in [(''small'',5,10000), (''medium'',20,1000), (''bigger'',1000,100), (''large'',5000,10)]:
for f in [f1,f2,f3]:
s = few*m
res=timeit.timeit("{}(s)".format(f.__name__), setup="from __main__ import {}, s".format(f.__name__), number=n)
st=fmt.format(f.__doc__, res/n*1000000, len(s), len(f(s)))
print(st)
s = [(unichr(i%0x10000),i) for i in range(1,len(s)+1)]
res=timeit.timeit("{}(s)".format(f.__name__), setup="from __main__ import {}, s".format(f.__name__), number=n)
st=fmt.format(f.__doc__, res/n*1000000, len(s), len(f(s)))
print(st)
print()
Resultado de Python 2.7:
2.7.5 (default, Aug 25 2013, 00:04:04)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)]
defaultdict: 10.20 micro sec/call (25 elements, 3 keys)
defaultdict: 21.08 micro sec/call (25 elements, 25 keys)
setdefault: 13.41 micro sec/call (25 elements, 3 keys)
setdefault: 18.24 micro sec/call (25 elements, 25 keys)
OrderedDict: 49.47 micro sec/call (25 elements, 3 keys)
OrderedDict: 102.16 micro sec/call (25 elements, 25 keys)
defaultdict: 28.28 micro sec/call (100 elements, 3 keys)
defaultdict: 79.78 micro sec/call (100 elements, 100 keys)
setdefault: 45.68 micro sec/call (100 elements, 3 keys)
setdefault: 68.66 micro sec/call (100 elements, 100 keys)
OrderedDict: 117.78 micro sec/call (100 elements, 3 keys)
OrderedDict: 343.17 micro sec/call (100 elements, 100 keys)
defaultdict: 1123.60 micro sec/call (5,000 elements, 3 keys)
defaultdict: 4250.44 micro sec/call (5,000 elements, 5,000 keys)
setdefault: 2089.86 micro sec/call (5,000 elements, 3 keys)
setdefault: 3803.03 micro sec/call (5,000 elements, 5,000 keys)
OrderedDict: 4399.16 micro sec/call (5,000 elements, 3 keys)
OrderedDict: 16279.14 micro sec/call (5,000 elements, 5,000 keys)
defaultdict: 5609.39 micro sec/call (25,000 elements, 3 keys)
defaultdict: 25351.60 micro sec/call (25,000 elements, 25,000 keys)
setdefault: 10267.00 micro sec/call (25,000 elements, 3 keys)
setdefault: 24091.51 micro sec/call (25,000 elements, 25,000 keys)
OrderedDict: 22091.98 micro sec/call (25,000 elements, 3 keys)
OrderedDict: 94028.00 micro sec/call (25,000 elements, 25,000 keys)
Resultado de Python 3.3:
3.3.2 (default, May 21 2013, 11:50:47)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))]
defaultdict: 8.58 micro sec/call (25 elements, 3 keys)
defaultdict: 21.18 micro sec/call (25 elements, 25 keys)
setdefault: 10.42 micro sec/call (25 elements, 3 keys)
setdefault: 14.58 micro sec/call (25 elements, 25 keys)
OrderedDict: 45.43 micro sec/call (25 elements, 3 keys)
OrderedDict: 92.69 micro sec/call (25 elements, 25 keys)
defaultdict: 20.47 micro sec/call (100 elements, 3 keys)
defaultdict: 77.48 micro sec/call (100 elements, 100 keys)
setdefault: 34.22 micro sec/call (100 elements, 3 keys)
setdefault: 54.86 micro sec/call (100 elements, 100 keys)
OrderedDict: 107.37 micro sec/call (100 elements, 3 keys)
OrderedDict: 318.98 micro sec/call (100 elements, 100 keys)
defaultdict: 714.70 micro sec/call (5,000 elements, 3 keys)
defaultdict: 3892.92 micro sec/call (5,000 elements, 5,000 keys)
setdefault: 1502.91 micro sec/call (5,000 elements, 3 keys)
setdefault: 2888.08 micro sec/call (5,000 elements, 5,000 keys)
OrderedDict: 3912.95 micro sec/call (5,000 elements, 3 keys)
OrderedDict: 14863.02 micro sec/call (5,000 elements, 5,000 keys)
defaultdict: 3649.02 micro sec/call (25,000 elements, 3 keys)
defaultdict: 22313.17 micro sec/call (25,000 elements, 25,000 keys)
setdefault: 7447.28 micro sec/call (25,000 elements, 3 keys)
setdefault: 18426.88 micro sec/call (25,000 elements, 25,000 keys)
OrderedDict: 19202.17 micro sec/call (25,000 elements, 3 keys)
OrderedDict: 85946.45 micro sec/call (25,000 elements, 25,000 keys)
Siento que su suposición, solo una forma preferible , no es válida. Veo al menos dos casos con diferentes requisitos:
En el código de mantenimiento intensivo (por ejemplo, un analizador de opciones de una clase de utilidad en evolución), siempre buscaría un código más limpio , para que otros y yo podamos implementar nuevas características más fácilmente. El rendimiento no es crítico, ya que solo se procesan pequeñas cantidades (por ejemplo, un dict de la configuración del usuario).
mientras en
la implementación de un algoritmo de rendimiento crítico en una tarea de procesamiento de datos, no me importaría escribir un código más detallado para una ejecución mucho más rápida . Si es poco probable que el aloritmo cambie, el código poco menos legible no se convertirá en un problema.