Como un nuevo conjunto de pruebas para mostrar @ EriF89 todavía tiene razón después de todos estos años:
$ python -m timeit -s "l={k:k for k in xrange(5000)}" "[i for i in xrange(10000) if i in l]"
1000 loops, best of 3: 1.84 msec per loop
$ python -m timeit -s "l=[k for k in xrange(5000)]" "[i for i in xrange(10000) if i in l]"
10 loops, best of 3: 573 msec per loop
$ python -m timeit -s "l=tuple([k for k in xrange(5000)])" "[i for i in xrange(10000) if i in l]"
10 loops, best of 3: 587 msec per loop
$ python -m timeit -s "l=set([k for k in xrange(5000)])" "[i for i in xrange(10000) if i in l]"
1000 loops, best of 3: 1.88 msec per loop
Aquí también comparamos a tuple
, que se sabe que son más rápidos que lists
(y usan menos memoria) en algunos casos de uso. En el caso de la tabla de búsqueda, el tuple
carenado no mejoró.
Tanto el dict
y set
funcionó muy bien. Esto trae un punto interesante relacionado con la respuesta de @SilentGhost sobre la unicidad: si el OP tiene valores de 10M en un conjunto de datos, y se desconoce si hay duplicados en ellos, entonces valdría la pena mantener un conjunto / dict de sus elementos en paralelo con el conjunto de datos real y las pruebas de existencia en ese conjunto / dict. ¡Es posible que los 10M puntos de datos solo tengan 10 valores únicos, que es un espacio mucho más pequeño para buscar!
El error de SilentGhost sobre los dictos es realmente esclarecedor porque uno podría usar un dict para correlacionar datos duplicados (en valores) en un conjunto no duplicado (claves) y, por lo tanto, mantener un objeto de datos para contener todos los datos, pero aún así ser rápido como una tabla de búsqueda. Por ejemplo, una clave dict podría ser el valor que se busca, y el valor podría ser una lista de índices en una lista imaginaria donde ocurrió ese valor.
Por ejemplo, si la lista de datos de origen a buscar fuera l=[1,2,3,1,2,1,4]
, podría optimizarse tanto para la búsqueda como para la memoria reemplazándola con este dict:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> l=[1,2,3,1,2,1,4]
>>> for i, e in enumerate(l):
... d[e].append(i)
>>> d
defaultdict(<class 'list'>, {1: [0, 3, 5], 2: [1, 4], 3: [2], 4: [6]})
Con este dict, uno puede saber:
- Si un valor estaba en el conjunto de datos original (es decir,
2 in d
devuelve True
)
- Cuando el valor era en el conjunto de datos original (es decir,
d[2]
devuelve la lista de índices, donde se encontró datos en la lista de datos original: [1, 4]
)