Encontrar elementos en listas de asociación de python eficientemente.

Tengo un conjunto de listas que se ven así:

conditions = [ ["condition1", ["sample1", "sample2", "sample3"]], ["condition2", ["sample4", "sample5", "sample6"], ...] 

¿Cómo puedo hacer las siguientes cosas de manera eficiente y elegante en Python?

  1. ¿Encontrar todos los elementos en una determinada condición?

    Por ejemplo, obtener todas las muestras en condición2. Ahora mismo puedo hacer:

     for cond in conditions: cond_name, samples = cond if cond_name == requested_cond: return samples 

    pero eso es torpe

  2. ¿Encuentra la unión ordenada de una lista de condiciones? Por ejemplo, ordered_union(["condition1", "condition2"], conditions) debe devolver:

     ["sample1", "sample2", "sample3", "sample4", "sample5", "sample6"] 

¿Cómo puedo hacer esto de manera eficiente en Python? Probablemente hay trazadores de líneas inteligentes?

Ah, bueno, si te ves obligado a mantener esa estructura de datos torpe, no puedes esperar mucho. El equivalente de una sola línea de su primera solución será algo así como:

 def samplesof(requested_cond, conditions): return next(s for c, s in conditions if c==requested_cond) 

y para la segunda, si insistes en las frases de una sola línea, será algo como:

 def ordered_union(the_conds, conditions): return [s for c in the_conds for s in samplesof(c, conditions)] 

Hay formas más rápidas de resolver el segundo problema, pero todas son multilínea, por ejemplo:

 aux_set = set(the_conds) samples_by_cond = dict((c, s) for c, s in conditions if c in aux_set) return [s for c in the_conds for s in samples_by_cond[c]] 

Tenga en cuenta que la razón por la cual este último enfoque es más rápido es que utiliza las estructuras de datos correctas (un conjunto y un dictado); desafortunadamente, tiene que comstackrlas por sí misma, porque la lista anidada de las conditions entrantes es realmente la estructura de datos incorrecta.

¿No podría encapsular las conditions como una variable miembro de una clase que construye las estructuras de datos auxiliares cruciales (correctas, rápidas) solo una vez? P.ej:

 class Sensible(object): def __init__(self, conditions): self.seq = [] self.dic = {} for c, s in conditions: self.seq.append(c) self.dic[c] = s def samplesof(self, requested_condition): return self.dic[requested_condition] def ordered_union(self, the_conds): return [s for c in the_conds for s in self.dic[c]] 

¡Ahora que es rápido y elegante!

self.seq que necesitas self.seq (la secuencia de condiciones) para otra cosa (¡ciertamente no es necesaria para las dos operaciones que mencionas!), Y que no hay repeticiones en esa secuencia y en las muestras (cualquiera que sea tu Las especificaciones son que no serán difíciles de acomodar, pero tratar de adivinarlas ciegamente cuando no se menciona nada sobre ellas sería muy difícil e inútil ;-).

Esto se parece más a un trabajo para un dict :

 conditions = { "condition1": ["sample1", "sample2", "sample3"], "condition2": ["sample4", "sample5", "sample6"], ...} 

A continuación, puede obtener el “sindicato ordenado” utilizando

 >>> conditions["condition1"]+conditions["condition2"] ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6'] 

En Python 3.1 o 2.7, puedes conservar el orden usando un OrderedDict en OrderedDict lugar:

 from collections import OrderedDict conditions = OrderedDict([ ["condition1", ["sample1", "sample2", "sample3"]], ["condition2", ["sample4", "sample5", "sample6"]] ]) 

Luego puede obtener el “sindicato ordenado”, también para OrderedDicts de tamaño arbitrario:

 >>> import itertools >>> [item for item in itertools.chain(*conditions.values())] ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6'] 

Necesita usar un dict (diccionario) en lugar de una list . Además, puede mantener las muestras en un set si desea operaciones basadas en conjuntos eficientes.

 conditions = { "condition1" : set(["sample1", "sample2", "sample3"]), "condition2" : set(["sample4", "sample5", "sample6"]) } print conditions["condition2"] # set(['sample5', 'sample4', 'sample6']) union = conditions["condition1"].union(conditions["condition2"]) print sorted(union) # ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6'] 

En la primera pregunta:

 >>> dict(conditions)['condition1'] ['sample1', 'sample2', 'sample3'] 

En el # 2 (no queda muy claro lo que quiere decir con “unión ordenada”, así que estoy haciendo suposiciones “listas ordenadas concatenadas en orden”):

 >>> tmpdict = dict(conditions) >>> sum( map(tmpdict.get, ["condition1", "condition2"]), [] ) ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6'] 

PD. El ejemplo se depreció para abordar las críticas legítimas de AM, que debido a problemas de implementación sum() muestra un comportamiento cuadrático con un aumento del tamaño de la lista. En su lugar sugiero el siguiente código:

 >>> import operator >>> tmpdict = dict(conditions) >>> reduce(operator.iadd, map(tmpdict.get, ["condition1", "condition2"]), [] ) ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6']