Estoy haciendo un curso de bioinformática. Se suponía que íbamos a crear una función que tomara una lista de cadenas como esta:
Motifs =[ "AACGTA", "CCCGTT", "CACCTT", "GGATTA", "TTCCGG"]y conviértalo en una matriz de conteo que cuente la aparición de los nucleótidos (las letras A, C, G y T) en cada columna y le agregue un pseudoconteo 1, representado por un diccionario con múltiples valores para cada clave como este:
count ={ 'A': [2, 3, 2, 1, 1, 3], 'C': [3, 2, 5, 3, 1, 1], 'G': [2, 2, 1, 3, 2, 2], 'T': [2, 2, 1, 2, 5, 3]}Por ejemplo A ocurre 1 + 1 pseudocuenta = 2 en la primera columna. C aparece 2 + 1 pseudocuenta = 3 en la cuarta columna.
Aquí está mi solución:
def CountWithPseudocounts(Motifs): t = len(Motifs) k = len(Motifs[0]) count = {} for symbol in "ACGT": count[symbol] = [1 for j in range(k)] for i in range(t): for j in range(k): symbol = Motifs[i][j] count[symbol][j] += 1 return countEl primer conjunto de bucles for genera un diccionario con las claves A,C,G,T y los valores iniciales 1 para cada columna como este:
count ={ 'A': [1, 1, 1, 1, 1, 1], 'C': [1, 1, 1, 1, 1, 1], 'G': [1, 1, 1, 1, 1, 1], 'T': [1, 1, 1, 1, 1, 1]}El segundo conjunto de bucles for cuenta la ocurrencia de los nucleótidos y los agrega a los valores del diccionario existente como se ve arriba.
Esto funciona y hace su trabajo, pero quiero saber cómo comprimir aún más ambos bucles usando comprensiones de dictado.
NOTA: Soy plenamente consciente de que hay una multitud de módulos y bibliotecas como biopython, scipy y numpy que probablemente pueden convertir toda la función en una sola línea. El problema con los módulos es que su formato de salida a menudo no coincide con lo que espera la verificación de solución automatizada del curso.
Este
count = {} for symbol in "ACGT": count[symbol] = [1 for j in range(k)]se puede cambiar a comprensión de la siguiente manera
count = {symbol:[1 for j in range(k)] for symbol in "ACGT"} y luego simplificado aún más mediante el uso de la capacidad de python para multiplicar la lista por un número entero para
count = {symbol:[1]*k for symbol in "ACGT"}comprimiendo el primer bucle:
count = {symbol: [1 for j in range(k)] for symbol in "ACGT"} Este método se llama generador (o comprensión de dictado): genera un dict usando un bucle for .
No estoy seguro de que pueda comprimir el segundo bucle (anidado), ya que no genera nada, sino que cambia el primer dict.
Puedes comprimir mucho tu código usando collections.Counter y collections.defaultdict :
from collections import Counter, defaultdict out = defaultdict(list) bases = 'ACGT' for m in zip(*Motifs): c = Counter(m) for b in bases: out[b].append(c[b]+1) dict(out)producción:
{'A': [2, 3, 2, 1, 1, 3], 'C': [3, 2, 5, 3, 1, 1], 'G': [2, 2, 1, 3, 2, 2], 'T': [2, 2, 1, 2, 5, 3]}