Filtrar una mega lista de objetos

Sabemos que filtrar una lista en Python puede hacerse con la función filter, que recibe una función booleana y un iterable, por ejemplo una lista.

La historia es esta. Tengo una lista de 455, 864 objetos Occurrence (llamada omega_tree.occurrences) , y quiero filtrar de aquí (remover) 16,380 (de una lista llamada ocs_of_interest). Originalmente pensé en simplemente hacer un filter:

ocs_complements = filter(lambda occurrence : occurrence not in ocs_of_interest , omega_tree.occurrences)

Estaba cansado y decidí dejar la compu corriendo. Cuando regresé a trabajar (12 horas después) el proceso seguía corriendo…

Pensé que tal vez se estaba lo que ocasionaba la demora era el método __hash__ y __eq__ que se utiliza para decidir si un elemento es igual al otro por lo que extraje solo el índice (un mapeo de los objectos Occurrence a Int). Esto hizo, efectivamente, más eficiente el filtrado pero igual, haciendo unas estimaciones a bote pronto me dijeron que posiblemente tardaría unas 10 horas… Rubish!

Me puse a buscar y encontré que la clase Set no solo es útil para remover elementos repetidos dentro de listas pero también hace muy eficiente la busqueda y, en mi caso, filtrado de objectos grandes.

Hice un cast (conversión) a lista para obtener un set. Los resultados fueron sorprendentes!!

%time ofi = set(map(lambda o : o.pk, ocs_of_interest))
%time oo = set(map(lambda o : o.pk ,omega_tree.occurrences))
%time ocs_complements = filter(lambda occurrence : occurrence not in ofi , oo)

CPU times: user 116 ms, sys: 0 ns, total: 116 ms
Wall time: 114 ms
CPU times: user 3.24 s, sys: 8 ms, total: 3.25 s
Wall time: 3.26 s
CPU times: user 136 ms, sys: 0 ns, total: 136 ms
Wall time: 133 ms

Conclusión

Para filtrar una lista grande de objetos conviene convertir la lista a conjunto (Set). El aumento de rendimiento es increiblemente grande!