while utiliza que para for español ejemplos con ciclo bucle anidado python for-loop optimization list-comprehension

utiliza - for i in range python español



Reemplazo de bucles anidados y asignación de valores para la comprensión de la lista (4)

Puedes usar zip() :

In [10]: a = ''ACGG'' In [11]: b = ''CAGT'' In [12]: chars = [''A'', ''C'', ''G'', ''T''] In [13]: [[(ch==i) + (ch==j) for i, j in zip(a, b)] for ch in chars] Out[13]: [[1, 1, 0, 0], [1, 1, 0, 0], [0, 0, 2, 1], [0, 0, 0, 1]]

Si quieres un diccionario puedes usar una comprensión dict:

In [25]: {ch:[(ch==i) + (ch==j) for i, j in zip(a, b)] for ch in chars} Out[25]: {''T'': [0, 0, 0, 1], ''G'': [0, 0, 2, 1], ''C'': [1, 1, 0, 0], ''A'': [1, 1, 0, 0]}

O si desea el resultado en el mismo orden que su lista de caracteres, puede usar collections.OrderedDict :

In [26]: from collections import OrderedDict In [27]: OrderedDict((ch, [(ch==i) + (ch==j) for i, j in zip(a, b)]) for ch in chars) Out[28]: OrderedDict([(''A'', [1, 1, 0, 0]), (''C'', [1, 1, 0, 0]), (''G'', [0, 0, 2, 1]), (''T'', [0, 0, 0, 1])])

Si aún necesita más rendimiento y / o está trabajando con cadenas largas y conjuntos de datos más grandes, puede usar Numpy para sortear este problema mediante un método vectorizado.

In [61]: pairs = np.array((list(a), list(b))).T In [62]: chars Out[62]: array([''A'', ''C'', ''G'', ''T''], dtype=''<U1'') In [63]: (chars[:,None,None] == pairs).sum(2) Out[63]: array([[1, 1, 0, 0], [1, 1, 0, 0], [0, 0, 2, 1], [0, 0, 0, 1]])

He escrito una función para contar las ocurrencias de ciertos caracteres ( A , C , G y T ) dentro de múltiples cadenas en la misma posición y guardar el número de ocurrencias en un diccionario.

Por ejemplo, con estas dos cadenas ''ACGG'' y ''CAGT'', debería devolver:

{''A'': [1, 1, 0, 0], ''C'': [1, 1, 0, 0], ''G'': [0, 0, 2, 1], ''T'': [0, 0, 0, 1]}

Quiero convertir el código a continuación para enumerar la comprensión para optimizarla en velocidad. Utiliza dos bucles for anidados, y los Motifs de entrada son una lista de cadenas que contienen los G y T de A de C.

def CountWithPseudocounts(Motifs): count = {} k = len(Motifs[0]) t = len(Motifs) for s in ''ACGT'': count[s] = [0] * k for i in range(t): for j in range(k): symbol = Motifs[i][j] count[symbol][j] += 1 return count

He intentado reemplazar los bucles for anidados en la parte inferior de la función para esta lista de comprensión:

count = [ [ count[Motifs[i][j]][j] += 1 ] for i in range(0, t) ] for j in range(0, k)]

No funciona, probablemente porque no puedo hacer la asignación de valores de + = 1 dentro de la lista de comprensión. ¿Cómo puedo solucionar esto?


Sugiero el enfoque numpy proporcionado por @Kasramvs si el rendimiento de velocidad realmente importa.

Además, el conteo de caracteres no es amigable ni siquiera para las computadoras modernas y tal vez puedas jugar con algunos trucos sobre la indexación / hash de las cadenas de entrada antes de contar. Por ejemplo, dado que cada cuerda tiene solo 4 caracteres, y cada carácter solo contiene 4 letras posibles, ''A'', ''C'', ''G'', ''T'', por lo tanto, puede representar fácilmente cada una de las combinaciones ''ACGT'' de ''AAAA'' a ''TTTT'' con un número, un hash o un código misterioso. El volumen de las combinaciones debe ser igual o inferior a 4x4x4x4 = 256 números diferentes aquí.

Y luego cuente el código en su lugar. Por ejemplo, cada vez que ve un ''AAAA'' y luego lo cuenta como 0x0 en una lista de Python o en una matriz numpy, vea un ''AAAC'' y cuente como 0x1, y viceversa. Después de eso, obtendrás una matriz de agrupación con índices que van desde 0x0 ~ 0xFF (255) y con las ocurrencias correspondientes, ¿verdad? Ahora recuerde, un 0x0 significa A: {1, 1, 1, 1} en su caso, o siete 0x1 para A: {7, 7, 7, 0} junto con C: {0, 0, 0, 7} ... Suma todos con cuidado y es el resultado.

Este tipo de trucos posiblemente deberían ayudar a aumentar el rendimiento de velocidad bastante en este caso. La velocidad se beneficia en dos factores: el primero es que ahora las computadoras se ocupan de números en lugar de caracteres, mientras que los números son mucho más fáciles de ordenar, contar, agrupar, particionar o indexar que los caracteres; el segundo proviene de una tasa de aciertos de caché mucho más alta por naturaleza ya que la traza de memoria se reduce mucho en estos trucos.

Espero que esto pueda ayudar. :)

Bueno, agregue algunos códigos de la siguiente manera para ser una referencia clara.

En primer lugar, las importaciones:

import itertools import numpy as np

Y la función encode_sequence() continuación debe ser lo suficientemente rápida siempre que dict t02 no sea demasiado grande, por ejemplo, típicamente menos de 1M pares clave-valor:

def encode_sequence(tsq): t00 = itertools.product(''ACGT'', repeat=4) t01 = np.array(list(t00)).view(''|U4'').ravel() t02 = dict(zip(t01, np.arange(len(t01)))) t03 = [t02[i] for i in tsq] return t03

Y use el siguiente fragmento para generar un tensor, map_decode , para representar cosas sobre `` contar el código '''' ... Además, hay un truco matemático llamado transformación de matriz aumentada debajo de esta parte, que dice que transforma ll0 y ''ACGT'' de la misma manera para abarcar map_decode para su uso posterior.

ll0 = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) map_decode = np.array(list(itertools.product(ll0, repeat=4)))

Alimentar una secuencia de prueba y traducir,

test_seq = (''ACGG'', ''CAGT'', ''CCGA'', ''AAAT'', ''TTGC'', ''GCAT'', ''ACGG'', ''AAAT'') u01 = encode_sequence(test_seq)

Cuente las ocurrencias; tenga en cuenta que el bloque de abajo debe ser la principal fuente de ganancia de velocidad porque la computadora es buena para manejar los números en u01 ,

p01, q01 = np.unique(u01, return_counts=True)

Después de todo, generar la salida ... Es un poco complicado aquí, como p01 es el código hash ordenado de test_seq y q01 es de hecho el conteo correspondiente, mientras que map_decode sirve como lo que dije, un tensor para mapear un código hash en p01 a otro vector que queremos, por ejemplo, mapeando 0x0 (o ''AAAA'') a A: [1, 1, 1, 1], C: [0, 0, 0, 0], G: [0, 0, 0, 0] y T: [0, 0, 0, 0]. El map_decode[p01] asignado map_decode[p01] se pondera por los conteos q01 y está listo para sumar para el informe:

np.sum(map_decode[p01]*q01[:, None, None], axis=0).T

Y dice:

array([[4, 3, 3, 1], [2, 4, 0, 1], [1, 0, 5, 2], [1, 1, 0, 4]])

que es equivalente a A: {4, 3, 3, 1}, C: {2, 4, 0, 1}, G: {1, 0, 5, 2} y T: {1, 1, 0, 4 }. Verifique si cumple con la respuesta.

Esa es la implementación numpy; no contiene ningún bucle explícito en el cuerpo principal. Y más que eso, encode_sequence() puede ser reemplazado por algunas preparaciones de entradas fuera de línea para aumentar el rendimiento por adelantado. Aunque no tenía la medida de velocidad de los fragmentos anteriores, creo que deberían acelerarse hasta cierto punto. :)

Ok, veamos qué pasará si hay cadenas largas.

Usamos esta secuencia como ejemplo,

test_seq0 = (( ''A''*40, ''A''*40, ''A''*40, ''C''*40, ''C''*40, ''C''*40, ''C''*40, ''G''*40, ''G''*40, ''T''*40 ))*4

El test_seq0 contiene 40 cadenas y cada cadena tiene 40 caracteres. Se parece a esto,

In: len(test_seq0), test_seq0 Out: (40, (''AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'', ''AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'', ''AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'', ''CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'', ''CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'', ... skip 30 lines ... ''CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'', ''CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC'', ''GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG'', ''GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG'', ''TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT''))

Una vista bastante divertida, ¿verdad?

Entonces tenemos que volver a encode_sequence() la encode_sequence() para una versión de cadena larga,

def encode_sequence_longstring(tsq_np): t00 = itertools.product(''ACGT'', repeat=4) t01 = np.array(list(t00)).view(''|U4'').ravel() t02 = dict(zip(t01, np.arange(len(t01)))) t03 = np.empty_like(tsq_np, dtype=np.uint) t03.ravel()[:] = [t02[i] for i in tsq_np.ravel()] return t03

Tenga cuidado de que tsq_np aquí ya no sea una simple lista de cadenas. El postfix _np significa que ahora es una matriz numpy.

Y divide el test_seq0 original de una manera numpy,

In: v01 = np.asarray(test_seq0).view(''|U4'').reshape(-1, int(40/4)) In: v01 Out: array([[''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA''], [''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA''], [''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA'', ''AAAA''], [''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC''], [''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC''], ... skip 30 lines ... [''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC''], [''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC'', ''CCCC''], [''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG''], [''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG'', ''GGGG''], [''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'', ''TTTT'']], dtype=''<U4'')

Otra vista divertida en v01 . :)

Y use v01 para calcular los códigos hash u02 esta manera. Implica algunas convenciones numpy alrededor de estas variables y funciones. Solo acostúmbrate a esos trucos de fantasía; ellos lo valen,

In: u02 = encode_sequence_longstring(v01) In: u02 Out: array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85], [ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85], ... skip 30 lines ... [ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85], [ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85], [170, 170, 170, 170, 170, 170, 170, 170, 170, 170], [170, 170, 170, 170, 170, 170, 170, 170, 170, 170], [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]], dtype=uint64)

Por observación, puede decir que u02 es en realidad un mapeo de 1 a 1 de v01 . Simplemente asigna cada ''AAAA'' a 0x0 como se esperaba.

A partir de ahora, el mapa u02 contiene toda la información que necesita con respecto a test_seq0 . u02 de u02 con la ayuda de Numpy,

s01 = np.empty((4, 0)) for u03 in u02.T: p02, q02 = np.unique(u03, return_counts=True) s02 = np.sum(map_decode[p02]*q02[:, None, None], axis=0).T s01 = np.hstack((s01, s02))

Hay una regla de pulgar para el bucle nativo de Python: puede usarlo, pero úselo fuera de cualquier región sensible al rendimiento. Pero, eso necesita experiencias para juzgar la situación en la mano.

Ahora s01 es el informe esperado de la siguiente manera:

In: s01 Out: array([[ 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12.], [ 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16.], [ 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8.], [ 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.]])

Lea las 4 filas de arriba a abajo y son ''A'', ''C'', ''G'', ''T'', respectivamente.

Al mismo tiempo, probé un test_seq0 test_seq0 así,

test_seq0 = (( ''A''*40, ''A''*40, ''A''*40, ''C''*40, ''C''*40, ''C''*40, ''C''*40, ''G''*40, ''G''*40, ''T''*40 ))*4000

El informe dice:

array([[ 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000., 12000.], [ 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000., 16000.], [ 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000., 8000.], [ 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000., 4000.]])

y está terminado en menos de 1 segundo (presione ENTER, y listo) en mi MacBook Pro, que no es un monstruo. :)


collections.Counter es tu amigo :)

s1 = ''ACGG'' s2 = ''CAGT'' from collections import Counter counter = Counter(enumerate(s1)) counter += Counter(enumerate(s2))

Formato de salida: ((posición, carácter), número de apariciones)

sorted(counter.items()) [((0, ''A''), 1), ((0, ''C''), 1), ((1, ''A''), 1), ((1, ''C''), 1), ((2, ''G''), 2), ((3, ''G''), 1), ((3, ''T''), 1)] [counter[i,''A''] if (i,''A'') in counter else 0 for i in range(4)] [1, 1, 0, 0] [counter[i,''C''] if (i,''C'') in counter else 0 for i in range(4)] [1, 1, 0, 0] [counter[i,''G''] if (i,''G'') in counter else 0 for i in range(4)] [0, 0, 2, 1] [counter[i,''T''] if (i,''T'') in counter else 0 for i in range(4)] [0, 0, 0, 1]


De hecho, no puede hacer asignaciones en la lista de comprensión (bien puede - llamando a funciones - realizar efectos secundarios). Una lista de comprensión espera una expresión. Además, es extraño que quieras asignar para count y, al mismo tiempo, actualizar un count .

Una forma de hacer esto con la comprensión del diccionario y la comprensión de la lista que no es muy eficiente es:

chars = ''ACGT'' a = ''ACGG'' b = ''CAGT'' sequences = list(zip(a,b)) counts = {char:[seq.count(char) for seq in sequences] for char in chars}

(créditos a @Chris_Rands para la seq.count(char) )

Esto produce:

{''G'': [0, 0, 2, 1], ''A'': [1, 1, 0, 0], ''C'': [1, 1, 0, 0], ''T'': [0, 0, 0, 1]}

Puede generalizar fácilmente la solución para contar más cadenas llamando a zip(..) con más cadenas.

También puede decidir optimizar su algoritmo en sí mismo. Probablemente esto sea más efectivo, ya que solo tiene que recorrer las cadenas una vez y puede usar la búsqueda de un diccionario, como por ejemplo:

def CountWithPseudocounts(sequences): k = len(sequences[0]) count = {char:[0]*k for char in ''ACGT''} for sequence in sequences: j = 0 for symbol in sequence: count[symbol][j] += 1 j += 1 return count

EDITAR :

Si desea agregar uno a todos los elementos en los conteos, puede usar:

counts = {char:[seq.count(char)+1 for seq in sequences] for char in chars}