Informations

1.11 : Modèles (expressions régulières) - Biologie


Dans les chapitres précédents, nous avons utilisé un simple fasta_stats programme pour effectuer des analyses de base sur un fichier FASTA appelé pz_cDNAs.fasta, principalement comme excuse pour en savoir plus sur les flux et outils standard commegrepetsorte. Il s'avère que les informations contenues dans lepz_cDNAs.fastadossier nous fournit de nombreuses questions potentielles à méditer.

Les séquences de ce fichier sont en fait un sous-ensemble de transcrits putatifs, produits à partir d'un assemblage de transcriptome de novo pour le papillon Papilio zelicaon. Chaque ligne d'en-tête de séquence code une variété d'informations : les deuxième et troisième colonnes des lignes d'en-tête révèlent le nombre de lectures contribuant à chaque séquence assemblée et la couverture moyenne de la séquence (définie comme le nombre total de bases apportées par les lectures, divisé par la longueur de la séquence assemblée). Même les identifiants de séquence codent certaines informations : ils commencent tous par un identifiant pseudo-aléatoire, mais certains ont un suffixe comme_TY.

Groupes de séquences qui partagent le même_suffixe ont été précédemment identifiés comme ayant des correspondances partagées à l'aide d'un auto-BLAST. Les ID de séquence sans un tel suffixe n'avaient aucune correspondance. On peut se demander : combien de séquences y a-t-il dans un tel groupe ? Cela pourrait être facilement résolu en utilisant d'abordgreppour extraire les lignes qui correspondent>(les lignes d'en-tête), puis en utilisant un autregreppour extraire ceux avec le motif_(ceux d'un groupe) avant d'envoyer le résultat àtoilettes.

Une question plus complexe serait de demander combien de groupes différents sont représentés dans le fichier. Si les informations du groupe étaient stockées dans une colonne séparée (par exemple, la deuxième colonne), cette question pourrait être répondue avec le même processus que ci-dessus, suivi d'untrier -k2,2d -upour supprimer les identifiants de groupe en double. Mais comment pouvons-nous contraindre les informations du groupe dans sa propre colonne ? Nous pourrions le faire en substituer cas de_avec des espaces. Lessed(Stream EDitor) peut nous aider. Voici le pipeline global que nous utiliserons :

Et voici une partie de la sortie, où seules les séquences en groupes sont représentées et chaque groupe n'est représenté qu'une seule fois (en remplaçant lemoins -Savectoilettescompterait ainsi le nombre de groupes) :

Lessedtool est un programme sophistiqué permettant de modifier l'entrée (à partir d'un fichier ou d'une entrée standard) et d'imprimer les résultats sur la sortie standard :sed '' ou... | sed ''.

Commeok,sedest originaire des années 1970 et fournit une grande variété de fonctionnalités et de syntaxes puissantes, dont nous ne couvrirons qu'une infime partie ici. En particulier, nous nous concentrerons sur less, ou substitution, opération.

Les-roption que nous avons utilisée permetsedsachez que nous voulons que notre modèle soit spécifié par la syntaxe « expression régulière étendue POSIX ».[1] Le schéma général du programme de substitution ests///g, où legspécifie que, pour chaque ligne, chaque instance du motif doit être remplacée. On peut alternativement utiliser1à cet endroit pour indiquer que seule la première instance doit être remplacée,2pour n'indiquer que le second, et ainsi de suite. Souvent,s///est utilisé, car il a la même signification ques///1.[2]

Expressions régulières

Le vrai pouvoir desedne vient pas de sa capacité à remplacer du texte, mais de son utilité à remplacer du texte basé sur des « modèles » ou, plus formellement, expressions régulières. Une expression régulière est une syntaxe pour décrire la correspondance de modèle dans les chaînes. Les expressions régulières sont décrites par les caractères individuels qui composent le modèle à rechercher et les « méta-opérateurs » qui modifient des parties du modèle pour plus de flexibilité. Dans[discuter, par exemple, les crochets fonctionnent comme un méta-opérateur signifiant "un de ces caractères", et ce modèle correspond aux deuxchatetchapeau, mais nondiscuter. Les expressions régulières sont souvent construites en enchaînant des expressions plus petites, comme dans[ch]at sur le [mh]at, correspondant àchat sur le chapeau,chat sur le tapis,chapeau sur le chapeau, etchapeau sur le tapis.

Dans l'exemple ci-dessus, le motif entier a été spécifié par_, qui n'est pas un méta-opérateur d'aucune sorte, et donc chaque instance de_a été remplacé par le remplacement (un espace). Les méta-opérateurs pris en charge par les expressions régulières sont nombreux et variés, mais voici une liste de base ainsi que quelques exemples d'inspiration biologique :

  • caractères ou chaînes non méta-opérateurs
    • La plupart des personnages qui ne fonctionnent pas de manière méta sont simplement appariés. Par exemple,_allumettes_,UNEallumettesUNE, etATGcorrespond à un codon de départ. (En réalité,ATGest trois modèles individuels spécifiés dans une ligne.) En cas de doute, il est généralement prudent d'échapper un caractère (en le préfixant avec une barre oblique inverse) pour s'assurer qu'il est interprété littéralement. Par exemple,[_]correspond à la chaîne littérale[_], plutôt que d'utiliser les crochets comme méta-opérateurs.
  • .
    • Un point correspond à n'importe quel caractère. Par exemple,CC.correspond à n'importe quel codon P (ACC,CCT,GCC,CCC), mais aussi des chaînes commeCCXetCC%.
  • []
    • Correspond à n'importe quel caractère spécifié dans<charset>. Par exemple,TACT]correspond à un codon Y (TACouTAT).
  • [^]
    • Placer un^car le premier caractère à l'intérieur des crochets du jeu de caractères annule la signification, de sorte que tout caractère unique ne pas nommé entre parenthèses correspond.TA[^CT]allumettesTAT,ÉTIQUETER,AT%, et ainsi de suite, mais pasTACouTAT.
  • ^(en dehors de[])
    • Placer un^en dehors des crochets du jeu de caractères correspond au début de la chaîne ou de la ligne d'entrée. À l'aide desed -r 's/^ATG/XXX/g', par exemple, remplace toutes les instances de codons de départ parXXX, mais seulement s'ils existent en début de ligne.
  • $
    • Semblable à^, mais$correspond à la fin de la chaîne ou de la ligne. Donc,sed -r 's/ATG$/XXX/g'remplace tous les codons de départ qui existent à la fin de leurs lignes respectives.

Jusqu'à présent, nos modèles ne sont pas vraiment flexibles, car la plupart des pièces couvertes jusqu'à présent correspondent à un seul personnage. Les cinq méta-opérateurs suivants résolvent cette limitation.

  • {x, y}
    • Modifie le modèle précédent afin qu'il corresponde s'il se produit entreXetouifois de suite, inclus. Par exemple,[CG]{4,8}correspond à n'importe quelle chaîne de C et/ou de G de quatre à huit caractères (tirer sur huit caractères, si possible). Donc,sed -r 's/[GC]{4,8}/_X_/g'entraînerait les substitutions suivantes :
      • ATCCGTCTàATCCGTCT(pas de remplacement)
        ATCCGCGGCTCàAT_X_TC
        ATCGCGCGGCCCGTTCGGGCCTàAT_X_CCGTT_X_T
    • À l'aide de{0,1}a pour effet de rendre ce qu'il suit facultatif dans le modèle, et{X,}a pour effet de permettre au motif de correspondreXou plusieurs fois sans limite supérieure.
  • *
    • Un astérisque modifie le modèle précédent afin qu'il corresponde s'il se produit zéro ou plusieurs fois ; il est donc équivalent à{0,}.

      L'utilisation de*mérite un exemple détaillé. Considérez le modèleATG[ATGC]*TGA, oùATGest le motif d'un codon de départ,[ATGC]*indique zéro ou plusieurs bases d'ADN d'affilée, etATGest l'un des codons stop canoniques. Ce modèle correspondATGTACCTTGA, et aussi des correspondancesATGTGA(où la partie médiane a été appariée zéro fois).

  • +
    • Le modificateur de répétition le plus important, un signe plus, modifie le modèle précédent de sorte qu'il soit mis en correspondance une ou plusieurs fois ; c'est équivalent à{1,}. Contrairement à l'exemple ci-dessus,ATG[ATGC]+TGAallumettesATGTACCTTGAetATGCTGA, mais nonATGTGA.
  • ()
    • Les parenthèses peuvent être utilisées pour regrouper une expression ou une série d'expressions en une seule unité afin qu'elles puissent être exploitées ensemble. Parce queÀest le modèleUNEsuivie parT, par exemple,AT+allumettesÀ,ATT,ATTT, etc. Si nous voulions plutôt correspondreÀrépétitions, nous pourrions souhaiter spécifier un modèle comme(À)+, ce qui correspondÀ,ATAT,ATATAT, etc. Les parenthèses « enregistrent » également la chaîne qui y correspond pour une utilisation ultérieure. Ceci est connu sous le nom de rétro-référencement, discuté ci-dessous.
  • <pattern x>|<pattern y>
    • Faites correspondre soit le motif<pattern x>ou le modèle<pattern y>. Plusieurs de ces modèles ou opérations peuvent être enchaînés ; par exemple,TAA|TAG|TGAcorrespond à l'un des trois codons stop canoniques. Cet exemple est cependant un peu ambigu : ce modèle lit-il « TA (A ou T) A (G ou T) GA » ou « TAA ou TAG ou TGA » ? Pour le rendre concret, nous voudrions probablement le spécifier comme((TAA)|(TAG)|(TGA)).

En utilisant ces éléments, nous pouvons créer une expression régulière qui sert de recherche de cadre de lecture ouvert simple (et pas vraiment utile dans la pratique). Pour les séquences procaryotes (où les introns ne sont pas pris en compte), nous les définirons comme un codon d'initiationATG, suivi d'un ou plusieurs codons, suivi de l'un des trois codons stop canoniquesAAT,ÉTIQUETER, ouATG. Le modèle de départ estATG, et nous avons vu comment encoder un stop ci-dessus, avec((TAA)|(TAG)|(TGA)). Que diriez-vous de « un ou plusieurs codons ? Eh bien, "un ou plusieurs" est incarné dans le+opérateur, et un codon correspond à trois A, T, C ou G. Ainsi, "un ou plusieurs codons" est codé comme([ACTG]{3,3})+. Ainsi, l'expression régulière de notre simple chercheur de cadre de lecture ouvert est :

En réalité, les expressions régulières ne sont pas souvent utilisées dans la recherche de régions de codage (bien qu'elles soient parfois utilisées pour identifier des motifs plus petits). Une partie de la raison est que les expressions régulières sont, par défaut, cupide: ils correspondent au premier motif qui se produit, et ils cherchent à faire correspondre autant de chaîne que possible. (La machinerie cellulaire qui traite les cadres de lecture ouverts n'est pas avide de cette manière.) Considérez la séquence suivante, qui a trois cadres de lecture ouverts selon notre définition simple et expression régulière ci-dessus.

Notez que la chaîneÉTIQUETERest à la fois un type de codon en général ([ACTG]{3,3}) et un arrêt, donc techniquement les deux premières options sont valides selon l'expression régulière. Par les règles de la gourmandise, le premier sera égalé, ce que nous pouvons vérifier avec un simpleéchoetsed.

La syntaxe des expressions régulières utilisée parsedest similaire à la syntaxe utilisée dans des langages tels que Perl, Python et R. En fait, tous les exemples que nous avons vus jusqu'à présent fonctionneraient de la même manière dans ces langages (bien qu'ils soient appliqués par leurs propres fonctions spécifiques plutôt que par un appeler poursed). Une fonctionnalité utile fournie par les moteurs d'expressions régulières plus modernes comme ceux-ci est que les opérateurs comme*et+peuvent être rendus non gourmands (bien que je préfère le terme plus clair « réticent ») en les faisant suivre d'un point d'interrogation. En Python, l'expression régulièreATG([ACTG]{3,3})+?((TAA)|(TAG)|(TGA))correspondrait à la deuxième option. (Lorsque vous ne suivez pas un*, ou+, il rend le précédent facultatif ; DoncTG(T)?CCest équivalent àTG(T){0,1}CC.) Des fonctionnalités plus sophistiquées permettent à l'utilisateur d'accéder à toutes les correspondances d'un modèle, même si elles se chevauchent, de sorte que la plus satisfaisante puisse être extraite par certains critères secondaires. Malheureusement,sedne prend pas en charge la correspondance non gourmande et plusieurs autres fonctionnalités avancées d'expression régulière.

Classes de caractères et expressions régulières dans d'autres outils

Nous souhaitons souvent utiliser des crochets de jeu de caractères pour correspondre à l'un quelconque d'une « classe » de caractères ; par exemple,[0123456789]correspond à n'importe quel chiffre. La plupart des syntaxes d'expressions régulières (y compris celle utilisée parsed) autoriser une version abrégée[0-9](si nous voulions faire correspondre uniquement un 0, 9 ou -, nous pourrions utiliser[09-]). De la même manière,[a-z]correspond à n'importe quelle lettre minuscule, et[A-Z]toute lettre majuscule. Ceux-ci peuvent même être combinés :[A-Za-z0-9]correspond à n'importe quel chiffre ou lettre. Dans la syntaxe étendue POSIX utilisée parsed,0-9peut également être spécifié comme[:chiffre:]. Notez l'absence de crochets dans le premier - pour correspondre à n'importe quel chiffre, l'expression régulière est[[:chiffre:]](ce qui, oui, est ennuyeux). Pour faire correspondre n'importe quel non-chiffre, nous pouvons annuler l'ensemble entre crochets comme[^[:chiffre:]].

Ces classes de caractères POSIX sont particulièrement utiles lorsque nous voulons faire correspondre des types de caractères difficiles à saisir ou à énumérer. En particulier,[[:espacer:]]correspond à l'un des caractères d'espacement (espaces, tabulations, sauts de ligne) et[[:ponct:]]correspond à n'importe quel caractère de « ponctuation », dont il existe un certain nombre. Les[[:espacer:]]La classe de caractères est particulièrement utile lorsque vous reformatez des données stockées dans des lignes et des colonnes, mais que vous ne savez pas si les séparateurs de colonnes sont des espaces, des tabulations ou une combinaison.

Dans de nombreuses syntaxes d'expressions régulières (y compris celles utilisées par Perl, Python, R et certaines versions desed), des raccourcis encore plus courts pour les classes de caractères sont disponibles. Dans ces, éest équivalent à[[:chiffre:]],est équivalent à[^[:chiffre:]],spour[[:espacer:]],Spour[^[:espace:]], entre autres.

Il s'avère que les expressions régulières peuvent être utilisées pargrepaussi bien queok. Lors de l'utilisationgrep, nous pouvons spécifier que le modèle doit être traité comme une expression régulière étendue en ajoutant le drapeau-E(par opposition à la-rutilisé poursed.) Ainsigrep -E '[[:chiffre:]]+'extrait les lignes qui contiennent un entier.

Dansok, nous pouvons utiliser le~comparateur au lieu du==comparateur dans une instruction if, comme dansawk '{if($1 ~ /PZ718[[:digit:]]+/) {print $3}}', qui imprime la troisième colonne de chaque ligne où la première colonne correspond au motifPZ718[[:chiffre:]]+.

Selon la définition ci-dessus pour les lignes d'en-tête dans lepz_cDNAs.fastafichier, les identifiants doivent être caractérisés par un identifiant pseudo-aléatoire suivi, éventuellement, d'un trait de soulignement et d'un ensemble de lettres majuscules spécifiant le groupe. À l'aide degrep '>'pour extraire uniquement les lignes d'en-tête, nous pouvons inspecter cela visuellement :

Si nous envoyons ces résultats partoilettes, nous voyons que ce fichier contient 471 lignes d'en-tête. Comment pouvons-nous vérifier que chacun d'eux suit ce schéma ? En utilisant une expression régulière avecgreppour le modèle, et en comparant le nombre à 471. Étant donné que les ID doivent commencer immédiatement après le>symbole dans un fichier FASTA, qui fera partie de notre modèle. Pour la section pseudo-aléatoire, qui peut ou non commencer parZPmais devrait au moins ne pas inclure un trait de soulignement ou un espace, nous pouvons utiliser le motif[^_[:espace:]]+pour spécifier un ou plusieurs caractères sans trait de soulignement et sans espace. Pour l'identifiant de groupe facultatif, le modèle serait(_[A-Z]+){0,1}(car{0,1}rend le précédent facultatif). En les rassemblant avecgrep -Eet compter les matchs devrait produire 471.

Tous les en-têtes correspondaient au modèle que nous attendions. Et s'ils ne l'avaient pas fait ? Nous avons pu inspecter ceux qui ne l'ont pas fait en utilisant ungrep -v -Epour imprimer les lignes qui ne correspondent pas au motif.

Maintenant, hypothétiquement, supposons qu'un collègue (têtu et plus expérimenté) ait identifié une liste d'identifiants de gènes importants et les ait envoyés dans un simple fichier texte.

Malheureusement, il semble que notre collègue ait décidé d'utiliser un schéma de nommage légèrement modifié, en ajoutant-gèneà la fin de chaque identifiant pseudo-aléatoire, avant le_, s'il est présent. Afin de poursuivre des collaborations pacifiques, il nous appartiendra peut-être de modifier notre fichier de séquence afin qu'il corresponde à ce schéma. Nous pouvons le faire avecsed, mais ce sera un défi, principalement parce que nous voulons effectuer une insertion plutôt qu'une substitution. En fait, nous effectuerons une substitution, mais nous remplacerons les correspondances par des contenus provenant d'eux-mêmes !

En ce qui concerne les références arrière, dans une expression régulière, les correspondances ou sous-correspondances qui sont regroupées et mises entre parenthèses ont leurs chaînes correspondantes enregistrées dans des variables1,2, etc. Le contenu de la première paire de parenthèses est enregistré dans1, le deuxième en2(certaines expérimentations peuvent être nécessaires pour identifier où les correspondances de parenthèses imbriquées sont enregistrées). L'intégralité de la correspondance d'expression est enregistrée dans.

Pour compléter l'exemple, nous allons modifier le modèle utilisé dans legreppour capturer les deux parties pertinentes du modèle, en les remplaçant par1-gène2.

Le contenu depz_cDNAs.fasta, après l'avoir parcouru dans lesedci-dessus, sont les suivants :

Des références arrière peuvent également être utilisées dans le modèle lui-même. Par exemple, unsed -r 's/([A-Za-z]+) 1/1/g'remplacerait les mots « doublés »([A-Za-z]+) 1avec une seule copie du mot1, un péchéj'aime beaucoup sed, résultant enj'aime beaucoup le sed. Mais méfiez-vous si vous envisagez d'utiliser une substitution de ce type comme vérificateur de grammaire, car cette syntaxe ne recherche pas au-delà des limites des lignes (bien que plus complexesedles programmes peuvent). Cet exemple ne modifierait pas la paire de lignes suivante (où le motlesapparaît deux fois):

L'expression régulière sed rapide modifie la sortie de lazy awk.

Quelques notes finales sursedet les expressions régulières en général aideront à conclure ce chapitre.

  1. Les expressions régulières, bien que puissantes, se prêtent à des erreurs. Travaillez progressivement et vérifiez régulièrement les résultats.
  2. Il est souvent plus facile d'utiliser plusieurs appels d'expressions régulières (par exemple, avec plusieurssedcommandes) plutôt que d'essayer de créer une seule expression complexe.
  3. Utilisez des expressions régulières le cas échéant, mais sachez qu'elles ne sont pas toujours appropriées. De nombreux problèmes qui peuvent sembler un ajustement naturel pour les expressions régulières sont également naturellement adaptés par d'autres stratégies, qui doivent être prises en considération étant donné la complexité que les expressions régulières peuvent ajouter à une commande ou à un morceau de code donné.

Certaines personnes, lorsqu'elles sont confrontées à un problème, pensent : « Je sais, je vais utiliser des expressions régulières ». Maintenant, ils ont deux problèmes.

~Jamie Zawinski

Des exercices

  1. Dans le fichier de statistiques d'assemblage de novo contig_stats.txt, les ID de contig sont nommés commeNOEUD_1,NOEUD_2, etc. Nous préférerions qu'ils soient nomméscontig1,contig2, etc. Produire uncontig_stats_renamed.txtavec ces changements effectués.
  2. Combien de séquences dans le fichier pz_cDNAs.fasta sont composés d'une seule lecture ? Vous devrez probablement utiliser les deuxoketsedici, et assurez-vous de vérifier attentivement les résultats de votre pipeline avecmoins.
  3. Un collègue particulièrement odieux insiste sur le fait que dans le dossier pz_cDNAs.fasta, séquences qui ne font partie d'aucun groupe (c'est-à-dire celles qui n'ont_suffixe) devrait avoir le suffixe_pas de groupe. Apaisez ce collègue en produisant un fichier selon cette spécification appelépz_cDNAs_fixed.fasta.
  4. Les lignes d'en-tête dans l'ensemble de protéines de levure orf_trans.fasta ressembler à cela lorsqu'il est vu avecmoins -Saprès avoir cherché>: Notamment, les lignes d'en-tête contiennent des informations sur les emplacements des exons individuels ; la séquence YAL001C a deux exons sur le chromosome I des emplacements 151006 à 147594 et 151166 à 151097 (les nombres vont de grand à petit car les emplacements sont spécifiés sur le brin avant, mais le gène est sur le brin inverse du complément). En revanche, la séquence YAL002W n'a qu'un seul exon sur le brin avant.

    Combien de séquences sont composées d'un seul exon ? Ensuite, produisez une liste d'ID de séquence dans un fichier appelémulti_exon_ids.txtcontenant tous ces identifiants de séquence avec plus d'un exon dans une seule colonne.

  5. Dans la continuité de la question 4, quelle séquence a le plus d'exons ? Quelle séquence d'exons unique est la plus longue, en termes de distance entre la première position et la dernière position notée dans la liste des exons ?