Saltar al contenido principal
LibreTexts Español

2.9: Knick-knacks bioinformáticos y expresiones regulares

  • Page ID
    55125
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    El capítulo 14, “Tipos de datos elementales”, mencionó que las cadenas en Python son inmutables, lo que significa que no se pueden modificar después de haber sido creadas. Para la biología computacional, esta característica suele ser un obstáculo. Para sortear esta limitación, frecuentemente convertimos cadenas en listas de cadenas de un solo carácter, modificamos las listas según sea necesario y convertimos las listas de caracteres en cadenas cuando sea necesario. Python viene incorporado con una serie de funciones que operan en listas y cadenas útiles para la biología computacional.

    Convertir una cadena en una lista de cadenas de un solo carácter

    Recuerda que debido a que las cadenas de Python son inmutables, no podemos ejecutar algo así como

    II.9_1_py_88_inmutable_string_2

    Para convertir una cadena en una lista de cadenas de un solo carácter, podemos pasar la cadena como parámetro a la función list () (que, cuando se llama sin parámetros, crea una lista vacía):

    II.9_2_PY_89_LISTA_BASE_LISTA

    Convertir una lista de cadenas en una cadena

    Si por casualidad tenemos una lista de cadenas (como la lista de cadenas de un solo carácter anterior), podemos unirlas en una sola cadena usando el método .join () de cualquier cadena que queramos usar como separador para la cadena de salida.

    II.9_3_py_90_base_list_join

    La sintaxis parece un poco hacia atrás, “separator” .join (list_variable), pero tiene sentido a la hora de considerar cuál es el método. -sintaxis dice: pide a la cadena “separator” que produzca una cadena dada la lista de entrada list_variable. Debido a que la salida es de tipo string, tiene sentido que le pidamos a una cadena que haga esta producción.

    Revertir una lista

    A veces es posible que queramos invertir el orden de una lista. Podemos hacer esto con el método .reverse () de la lista que pide a la lista que se revierta en su lugar (pero devuelve None).

    alt

    Al igual que con los métodos .sort () y .append () del capítulo 15, “Collections and Looping, Part 1: Lists and for”, .reverse () devuelve None, por lo que una línea como base_list = base_list.reverse () casi seguramente sería un error.

    Invertir una cadena

    Hay dos formas de revertir una cadena en Python: (1) primero, convertirla en una lista de cadenas de un solo carácter, luego revertir la lista, luego unirla de nuevo en una cadena, o (2) usar “sintaxis de rebanada extendida”. La primera solución utiliza los métodos descritos anteriormente, mientras que el segundo método extiende la sintaxis de [] slice para que los corchetes puedan contener valores para [start:end:step], donde start y end pueden estar vacíos (para indicar el primer y último índice de la cadena), y el paso puede ser -1 (para indicar un paso atrás en la cadena).

    alt

    Aunque la sintaxis de slice extendida es bastante más rápida y requiere menos escritura, es un poco más difícil de leer. Una búsqueda en línea de “python string reverse” probablemente revelaría primero esta solución.

    Búsqueda y reemplazo simple en una cadena

    Ya hemos visto cómo dividir una cadena en una lista de cadenas con el método.split (). Un método similar, .replace (), nos permite reemplazar todas las subcadenas coincidentes de una cadena por otra cadena. Debido a que las cadenas son inmutables, este método devuelve una copia modificada de la cadena original.

    II.9_6_py_93_string_replace

    También debido a que las cadenas son inmutables, los datos originales se dejan sin cambios. Debido a que una variable es simplemente un nombre que usamos para referirnos a algunos datos, sin embargo, podemos hacer que parezca que hemos modificado la cadena original reasignando la variable seq a lo que sea devuelto por el método.

    II.9_7_py_94_string_replace_reref

    Comandos versus consultas

    ¿Por qué algunas operaciones (como el método .sort () de una lista) cambian los datos en su lugar pero devuelven None, mientras que otras (como el método .split () de una cadena o la función len ()) devuelven algo pero dejan solos los datos originales? ¿Por qué es tan raro ver operaciones que hagan ambas cosas? El motivo es que los diseñadores de Python (generalmente) intentan seguir lo que se conoce como el principio de separación comando-consulta, un principio filosófico en el diseño de lenguajes de programación que establece que las operaciones individuales deben o bien modificar datos o devolver respuestas a las consultas, pero no ambas.

    La idea detrás de este principio es que al leer código, debería quedar inmediatamente obvio lo que hace, característica que se logra fácilmente si cada operación solo tiene una cosa que puede hacer. Cuando las operaciones cambian los datos y devuelven una respuesta, existe la tentación de “codificar por efecto secundario”, es decir, hacer uso de efectos simultáneos para minimizar la escritura a costa de la claridad. Comparado con muchos otros lenguajes, Python hace un intento más fuerte de seguir este principio.

    Expresiones Regulares

    Las expresiones regulares, comunes a muchos lenguajes de programación e incluso herramientas de línea de comandos como sed, son sintaxis para hacer coincidir patrones sofisticados en cadenas. Los patrones más simples son solo cadenas simples; por ejemplo, “ATG” es el patrón para un codón de inicio. Debido a que Python trata las barras inversas como especiales en cadenas (por ejemplo, “\ t” no es en realidad “\” seguido de una “t”, sino más bien un carácter de tabulación), los patrones para expresiones regulares en Python generalmente se expresan como “cadenas sin procesar”, lo que se indica prefijándolas con una r. Entonces, R"atg” es también el patrón para un codón de inicio, pero r "\ t” es la expresión regular para “\” y “t” en lugar del carácter tabulador.

    II.9_8_PY_94_2_Raw_VS_Tab_len

    La funcionalidad de expresión regular de Python se importa con el módulo re mediante el uso de import re cerca de la parte superior del script. Hay muchas funciones en el módulo re para trabajar con cadenas y patrones de expresión regular, pero cubriremos solo las tres más importantes: (1) buscar un patrón en una cadena, (2) reemplazar un patrón en una cadena, y (3) dividir una cadena en una lista de cadenas basada en un patrón.

    Búsqueda de un patrón en una cadena

    La búsqueda de un patrón se realiza con la función re.search (). La mayoría de las funciones del módulo re devuelven un tipo de datos SRE_match especial, o una lista de ellos (con su propio conjunto de métodos), o Ninguno si no se encontró ninguna coincidencia. La sentencia if- (y los bucles de tiempo) en Python trata a None como falso, por lo que podemos usar fácilmente re.search () en una sentencia if. El resultado de una coincidencia exitosa puede indicarnos la ubicación de la coincidencia en la consulta utilizando el método .start ():

    II.9_9_PY_96_RE_Match

    Reemplazar un patrón en una cadena

    La función re.subn () se puede utilizar para buscar y reemplazar un patrón dentro de una cadena. Se necesitan al menos cuatro argumentos importantes (y algunos opcionales que no discutiremos aquí): (1) el patrón para buscar, (2) la cadena de reemplazo con la que reemplazar las coincidencias, (3) la cadena para buscar y (4) el número máximo de reemplazos para hacer (0 para reemplazar todas las coincidencias). Esta función devuelve una lista [1] que contiene dos elementos: en el índice 0, la copia modificada de la cadena, y en el índice 1, el número de reemplazos realizados.

    II.9_10_py_97_re_subn

    Dividir una cadena en una lista de cadenas basada en un patrón

    Ya hemos visto que podemos dividir una cadena basada en cadenas simples con .split (). Ser capaz de dividirse en expresiones regulares complejas a menudo también es útil, y la función re.split () proporciona esta funcionalidad.

    II.9_11_PY_98_RE_Split

    Lo dejaremos como un ejercicio para que el lector determine qué sería la salida para una secuencia donde las coincidencias están espalda con espalda, o donde las coincidencias se superponen, como en re.split (r"ATA”, “GCATATAGG”).

    El lenguaje de las expresiones regulares, en Python

    En el capítulo 11, “Patrones (expresiones regulares)”, cubrimos las expresiones regulares con cierto detalle al discutir el motor de expresión regular de línea de comandos sed. Las expresiones regulares en Python funcionan de manera similar: cadenas simples como R"atg” coinciden con sus expresiones, los puntos coinciden con cualquier carácter único (R"cc.” coincide con cualquier codón P), y los corchetes se pueden usar para emparejar uno de un conjunto de caracteres (r "[ACTG]” coincide con una de una secuencia de ADN, y r "[a-Za-Z0-9_]” es la abreviatura de “cualquier carácter alfanumérico y el guión bajo”). Los paréntesis agrupan patrones, y el signo más modifica el grupo anterior (o grupo implícito) para que coincida una o más veces (* para cero o más), y las tuberías verticales | funcionan como un “o”, como en r "([ACTG]) + (TAG|TAA|TGA)”, que coincidirá con cualquier secuencia de ADN terminada por una parada codón (TAG, TAA o TGA). Completando la discusión de capítulos anteriores, las expresiones regulares de Python también admiten corchetes (por ejemplo, r "(AT) {10,100}” coincide con un “AT” repetido de 10 a 100 veces) y notación estándar para el inicio y el final de la cadena. (Tenga en cuenta que r"^ ([ACTG]) +$” coincide con una cadena de ADN y solo ADN. Para ejemplos más detallados de estos constructos de expresión regular, consulte el capítulo 11.)

    La sintaxis de expresión regular de Python, sin embargo, difiere de la sintaxis extendida POSIX que discutimos para sed, y de hecho proporciona una sintaxis extra extendida conocida como expresiones regulares “estilo Perl”. Estos admiten una serie de características sofisticadas, dos de las cuales se detallan aquí.

    Primero, los operadores que especifican que se debe repetir una coincidencia, como signos más, llaves y asteriscos, son por defecto “codiciosos”. Lo mismo es cierto en la sintaxis POSIX extendida utilizada por sed. En Python, sin embargo, tenemos la opción de hacer que el operador no sea codicioso, o más exactamente, reacio. Considera el patrón r "([ACTG]) + (TAG|TAA|TGA)”, que coincide con una secuencia de ADN terminada por un codón de parada. La parte codiciosa, ([ACTG]) +, consumirá todo menos el último codón de parada, dejando lo menos posible de la cadena restante para hacer coincidir el resto del patrón.

    II.9_12_Regex_Greedy_ex

    En Python, si queremos hacer que el signo más sea reacio, podemos seguirlo con un signo de interrogación, lo que hace que el partido deje lo más posible para partes posteriores del patrón.

    II.9_13_Regex_renuentes

    El operador de reluctancia también puede seguir el asterisco y las llaves, pero tenga cuidado: cuando se usa sin seguir a uno de estos tres operadores de repetición, el operador de signo de interrogación funciona como un operador “opcional” (por ejemplo, R"c (ATG)? C” coincide con “CC” y “CATGC”).

    La segunda diferencia principal entre las expresiones regulares Python (estilo Perl) y las expresiones regulares extendidas por POSIX está en cómo especifican las clases de caracteres. Como se mencionó anteriormente, un patrón como r "[a-Za-z0-9_]” es la abreviatura de “cualquier alfanumérico y el guión bajo”, por lo que una serie de alfanuméricos se puede emparejar con r "[a-Za-Z0-9_] +”. En las expresiones regulares POSIX, la clase de caracteres A-zA-z0-9_ se puede especificar con [:alnum:], y entonces el patrón tendría que usarse como [[:alnum:]] + en sed.

    La sintaxis de expresión regular de estilo PERL utilizada por Python introdujo una serie de códigos abreviados para las clases de caracteres. Por ejemplo,\ w es la abreviatura de “cualquier alfanumérico y el guión bajo”, así que r "\ w+” es el patrón para una serie de estos y es equivalente a r "[a-za-Z0-9_] +”. El patrón\ W coincide con cualquier carácter que no sea alfanumérico o el guión bajo, por lo que r "\ W+” coincide con una serie de estos (equivalente a r "[^a-Za-z0-9_] +”). Quizás la clase taquigráfica más importante es\ s, que coincide con un solo carácter de espacio en blanco: un carácter de tabulación, espacio o nueva línea (\ S coincide con cualquier carácter que no sea de espacio en blanco). Esto es más útil cuando se analiza la entrada, donde no se puede garantizar que las palabras estén separadas por tabulaciones, espacios o alguna combinación de los mismos.

    II.9_14_py_99_re_split_whitespace

    En el código anterior, hemos reemplazado la división en “\ t” a la que estamos acostumbrados por un re.split () en r "\ s+”, lo que asegurará que analizamos correctamente las piezas de la línea aunque estén separadas con múltiples tabulaciones, espacios, o alguna combinación de los mismos.

    Contando Elementos Promotor

    Considere el archivo grape_promoters.txt, que contiene en cada línea 1000bp aguas arriba de las regiones génicas en el genoma de Vitis vinifera:

    II.9_15_PY_99_2_Grape_Promotores

    Estas columnas no están separadas por un carácter tabulador, sino por un número variable de espacios.

    Los motivos promotores son pequeños patrones de ADN cercanos a las secuencias génicas a las que se une la maquinaria celular para ayudar a iniciar el proceso de transcripción génica. Por ejemplo, la proteína ABF se une al patrón de ADN “CACGTGGC” si está cerca de un gen en algunas plantas. Algunos motivos son flexibles y pueden describirse mediante expresiones regulares; la proteína GATA se une a cualquier secuencia de ADN corta que coincida con el patrón “[AT] GATA [GA]”. Se desea analizar las regiones aguas arriba de V. vinifera y contar para cada una el número de ocurrencias del motivo GATA. Nuestra salida debería verse así:

    II.9_16_py_99_3_ex_out

    Después de importar los módulos io y re, primero escribiremos una función count_motif () que toma dos parámetros: primero una secuencia en la que contar coincidencias de motivos, y segundo un motivo para buscar. Hay una variedad de formas en las que podríamos codificar esta función, pero una solución simple será usar re.split () para dividir la secuencia en la expresión regular del motivo; el número de motivos será entonces la longitud del resultado menos uno (porque si no se encuentran motivos, no se hará división; si se encuentra uno, se hará una división, y así sucesivamente).

    Con esta función escrita y probada, podemos abrir el archivo usando io.open () y bucle sobre cada línea, llamando a la función count_motif () en cada secuencia con el motivo r "[AT] GATA [GA]”. Debido a que las columnas están separadas con un número variable de espacios en lugar de una sola tabulación o caracteres de espacio, usaremos re.split () para dividir cada línea en pedazos.

    Primero, escribiremos y probaremos la función que cuenta motivos y ofrece un ejemplo de uso, donde el número de coincidencias debe ser dos.

    alt

    A continuación podemos terminar el programa, usando re.split () para procesar cada línea.

    alt

    Cuando se ejecuta, este sencillo programa (grape_count_gata.py) produce la salida deseada anteriormente. Debido a que la salida se envía a la salida estándar, podemos filtrar aún más los resultados a través de las utilidades de línea de comandos sort y head para identificar qué promotores tienen más probabilidades de ser encontrados por GATA:. /grape_count_gata.py | ordenar -k2,2nr | cabeza -n 10.

    Ejercicios

    1. Escribe una función llamada reverse_complements () que tome un parámetro de secuencia de ADN y devuelva su complemento inverso (es decir, invierte la cadena, cambia A y T, y cambia G y C).
    2. Las secuencias de ADN que están destinadas a convertirse en proteínas son “leídas” por la maquinaria celular en uno de los seis “marcos de lectura” con longitudes que son múltiplos de tres. Los tres primeros se derivan de la propia secuencia, comenzando en el índice 0, índice 1 e índice 2; los tres primeros marcos de lectura de “ACTAGACG” son “ACTAGACG”, “CTAGAC” y “TAGACG”.

      Para derivar los marcos de lectura tres, cuatro y cinco, primero complementamos de forma inversa la secuencia (“CGTCTAGT”) y luego producimos marcos a partir de los índices 0, 1 y 2, dando como resultado “CGTCTA”, “GTCTAG” y “TCTAGT”.

      Usando la función reverse_complements () desde arriba (y potencialmente la función get_windows () del capítulo 18, “Funciones de Python”), escriba una función seq_to_six_frames () que tome una secuencia de ADN como parámetro y devuelva una lista de los seis marcos de lectura (como cadenas).

    3. Escribe una función llamada longest_non_stop () que tome una secuencia de ADN como parámetro y devuelva la secuencia de aminoácidos más larga que se pueda producir a partir de ella. Esto se hace generando los seis marcos de lectura a partir de la secuencia, convirtiendo cada uno en una secuencia de aminoácidos (probablemente usando la función dna_to_aa () del capítulo 20, “Diccionarios”), y luego recortando las secuencias resultantes al primer codón de “parada” (“*”) si hay alguno.

      En la secuencia de ADN seq = “AGCTATAGGAAGATATAGACGATTAGAC”, por ejemplo, las seis traducciones son SY*EDRRLD (Frame 1), ATRKIDD* (Frame 2), LLGR*TIR (Frame 3), V*SSIFLVA (Frame 4), SNRLSS** (Frame 5), y LIVYLPSS (Frame 6). En orden de longitudes, las posibilidades son así “V”, “SY”, “LLGR”, “SNRLSS”, “ATRKIDD” y “LIVYLPSS”. Como resultado, longest_non_stop (seq) debería devolver “LIVYLPSS”.

    4. Modifique el programa grape_count_gata.py, llamándolo motifs_count.py para que pueda leer el nombre del archivo y los motivos a procesar (este último como una lista separada por comas) en sys.argv. Cuando se ejecuta como. /motifs_count.py grape_promoters.txt [AT] GATA [GA], [CGT] ACGTG [GT] [AC], TTGAC, la salida debe tener el siguiente aspecto:II.9_19_py_99_6_motifs_ex_out
    5. El genotipado por secuenciación (GBS) es un método para identificar variantes genéticas en una muestra de ADN dividiendo primero los cromosomas en ubicaciones pseudoaleatorias mediante la aplicación de enzimas de restricción y luego secuenciando lecturas cortas de los extremos de los fragmentos resultantes:alt En lo anterior, líneas negras representan la secuencia de ADN de entrada, las líneas rojas son sitios de corte y las líneas verdes representan la salida de la máquina de secuenciación. (Se han hecho algunas simplificaciones a la figura anterior en aras de la claridad.) El resultado es una profundidad mucho mayor de secuenciación en menos ubicaciones, lo que puede ser útil en una variedad de contextos, incluyendo la búsqueda de variantes genéticas entre diferentes versiones del mismo cromosoma (lo que requiere muchas lecturas de la misma ubicación para confirmar estadísticamente esas variantes).

      Las enzimas de restricción cortan en base a patrones; por ejemplo, la enzima aPEK1 corta moléculas de ADN en el patrón “GC [AT] GC”. Con base en los patrones reconocidos, algunas enzimas son “cortadoras frecuentes” y otras no; los cortadores más frecuentes muestran más ubicaciones en un genoma pero sacrifican la profundidad de secuenciación. Por esta razón, los investigadores suelen querer conocer, dado un patrón enzimático y una secuencia genómica, la distribución de las longitudes de los fragmentos que resultarán.

      Escribe una función gbs_cut () que tome una secuencia de ADN, un patrón de expresión regular y un “tamaño de bin”. Debe devolver las longitudes de secuencia de mapeo de un diccionario, redondeadas hacia abajo al tamaño de bin más cercano, al número de fragmentos producidos por el cortador en ese bin. Como ejemplo, “AAAAGCAGCAAAAAAAAGCTGCAAGCAGCAAAAA" cuando se procesa con “GC [AT] GC" produce tamaños de fragmento de 4, 6, 2 y 5. Si se agrupa en un tamaño de bin de 3, el diccionario tendría las claves 0, 3 y 6, y los valores 1, 1 y 2.


    1. En realidad, devuelve una tupla, que funciona de manera muy parecida a una lista pero es inmutable. Las tuplas se tratan brevemente en el capítulo 15.

    This page titled 2.9: Knick-knacks bioinformáticos y expresiones regulares is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Shawn T. O’Neil (OSU Press) .