Module 1 · Section 1.4
Recherche de motifs par expressions régulières
55 min · niveau intermediaire · dernière révision : 2026-11-01
Objectifs d'apprentissage
- Expliquer pourquoi un site de restriction palindromique se lit de façon identique sur les deux brins
- Convertir un motif IUPAC dégénéré en classes de caractères et l'utiliser avec le module re
- Distinguer re.findall et re.finditer, puis énumérer les occurrences chevauchantes avec un lookahead
- Écrire une recherche sur les deux brins en convertissant correctement les coordonnées 0-based et 1-based
- Reconnaître les limites du backtracking et choisir entre regex, algorithme exact et index
Prérequis : 1.1 Représentations computationnelles du dogme central, 1.2 Formats FASTA et FASTQ
Python 3.11+
Prérequis
Cette section suppose que vous savez lire une séquence, distinguer le brin direct du brin complémentaire et calculer un complément inverse, comme dans 1.1. Elle réutilise le parsing FASTA de 1.2 : une séquence coupée sur plusieurs lignes doit être concaténée avant toute recherche. Les positions sont 1-based dans le texte biologique et 0-based dans le code Python ; chaque conversion est explicitée. Aucune connaissance préalable du module re n'est nécessaire.
Introduction
Les génomes sont parsemés de motifs courts qui portent une information précise : sites de restriction utilisés en clonage, boîtes promotrices, sites d'épissage, signaux de polyadénylation, motifs de liaison de facteurs de transcription. Retrouver ces motifs dans une séquence est l'une des opérations les plus fréquentes de la bioinformatique, et l'une des plus faciles à mal faire. Un site de restriction de six paires de bases apparaît en moyenne une fois tous les 4 096 nucléotides si les bases sont équiprobables ; dans un génome de plusieurs millions de paires de bases, il peut donc y avoir des centaines de candidats. La question n'est pas seulement « le motif est-il présent ? », mais « où, sur quel brin, combien de fois, et avec quelles coordonnées ? ».
Un hit n'est pas une fonction. La présence de GAATTC ne garantit pas que l'enzyme coupe l'ADN in vivo, et la présence d'un motif dégénéré ne garantit pas la liaison d'un facteur de transcription. La séquence est une couche d'information parmi d'autres : méthylation, chromatine, conformation et contexte cellulaire peuvent modifier l'effet d'un site. La recherche de motifs produit donc des candidats, qu'il faut ensuite confronter à l'annotation, à la conservation ou à une expérience.
Le problème computationnel a plusieurs couches. Un motif peut être exact, comme GAATTC, ou dégénéré, comme G[AT]TC, qui décrit deux mots possibles. Il peut se trouver sur le brin direct ou sur le brin complémentaire, et deux occurrences peuvent se chevaucher. Les formats biologiques numérotent les positions à partir de 1 et incluent la fin, alors que Python indexe à partir de 0 et exclut la fin. Enfin, une expression régulière (regular expression, regex) est un petit langage de description de motifs : elle est compacte, mais son moteur peut avoir un comportement inattendu sur les chevauchements, les ancres ou les quantificateurs imbriqués.
Nous construisons dans cette section une recherche de motifs vérifiable, puis nous l'appliquons à une région réelle d'Escherichia coli K-12 MG1655, accession NC_000913.3. Le fil rouge reste le même que dans les sections précédentes. L'objectif n'est pas de remplacer les bibliothèques spécialisées, mais de comprendre ce qu'elles font, de savoir quand une regex suffit et quand il faut un algorithme exact ou un index.
Théorie
Sites de restriction et symétrie
Un motif est une chaîne courte sur un alphabet, éventuellement avec des symboles qui représentent plusieurs bases. Un site de restriction est un motif d'ADN reconnu par une enzyme de restriction. Les enzymes de type II reconnaissent souvent des séquences de quatre à huit paires de bases et coupent l'ADN à proximité. Cinq exemples classiques suffisent à couvrir la logique :
| Enzyme | Site reconnu | Complément inverse |
|---|---|---|
| EcoRI | GAATTC | GAATTC |
| BamHI | GGATCC | GGATCC |
| HindIII | AAGCTT | AAGCTT |
| PstI | CTGCAG | CTGCAG |
| NotI | GCGGCCGC | GCGGCCGC |
Ces cinq sites sont palindromiques au sens de l'ADN : la séquence est égale à son complément inverse, ce que l'on écrit . Attention au vocabulaire : GAATTC n'est pas un palindrome de lecture, puisque l'inverse est CTTAAG. C'est un palindrome de complément inverse, propriété qui découle de l'antiparallélisme des deux brins. Lorsqu'on lit le brin complémentaire de 5' vers 3', on retrouve exactement GAATTC. C'est pourquoi une enzyme comme EcoRI reconnaît le même site quelle que soit l'orientation locale de l'ADN.
Cette symétrie a une conséquence pratique : pour ces cinq enzymes, chercher le site sur le brin direct suffit, car le brin complémentaire porte la même séquence. Toutes les enzymes ne sont pas dans ce cas. BsaI, une enzyme de type IIS, reconnaît GGTCTC, dont le complément inverse est GAGACC ; le site n'est donc pas palindromique, et une recherche sur un seul brin manque la moitié des occurrences. De plus, la position de coupure d'une enzyme de type IIS est décalée par rapport au site de reconnaissance : trouver le motif ne revient pas à prédire la coupure. Il faut consulter le catalogue de l'enzyme et ne pas confondre reconnaissance et clivage.
Dans un génome circulaire, un site peut chevaucher l'origine de numérotation. La région 1–3000 que nous étudions est un extrait linéaire : aucun site ne traverse la jonction. Pour un chromosome complet, il faut soit dupliquer les premières bases à la fin, soit utiliser une recherche circulaire. C'est un détail qui devient un bug silencieux dès qu'on passe d'un exercice à un vrai génome.
Motifs dégénérés et alphabet IUPAC
Un motif dégénéré décrit un ensemble de mots, pas une séquence unique. La notation IUPAC (International Union of Pure and Applied Chemistry) étend l'alphabet ADN avec des symboles d'ambiguïté. Les plus utiles sont :
| Code | Bases autorisées | Classe regex |
|---|---|---|
| R | A ou G | [AG] |
| Y | C ou T | [CT] |
| S | G ou C | [GC] |
| W | A ou T | [AT] |
| K | G ou T | [GT] |
| M | A ou C | [AC] |
| B | C, G ou T | [CGT] |
| D | A, G ou T | [AGT] |
| H | A, C ou T | [ACT] |
| V | A, C ou G | [ACG] |
| N | A, C, G ou T | [ACGT] |
Ainsi, G[AT]TC correspond au motif IUPAC GWTC : il reconnaît GATC et GTTC. La conversion en classe de caractères est directe, et chaque symbole produit exactement une classe qui consomme un caractère. La longueur du motif ne change donc pas : un motif de quatre symboles produit une regex qui reconnaît quatre bases, ce qui simplifie le calcul des intervalles.
Les motifs de liaison des facteurs de transcription sont souvent résumés de cette façon : chaque position autorise un sous-ensemble de bases, et la séquence consensus n'est qu'une des lectures possibles. La carte IUPAC reste une description discrète ; elle ne pondère pas les bases et ne capture pas les préférences graduelles.
Le symbole N mérite une attention particulière. Dans un motif de recherche, N signifie « n'importe quelle base » et devient [ACGT]. Dans une séquence cible, N signifie « base inconnue » ; une classe [ACGT] ne le reconnaît pas. Ce n'est pas une incohérence : une base inconnue n'est pas une preuve de présence du motif. Si vous décidez de traiter les N de la cible comme des jokers, il faut l'écrire explicitement et documenter la perte de spécificité. Le même raisonnement vaut pour les autres symboles IUPAC présents dans la cible.
Un motif dégénéré n'est pas une recherche approximative. G[AT]TC énumère deux mots exacts ; il ne tolère ni substitution arbitraire, ni insertion, ni délétion. Pour autoriser une ou deux erreurs, il faut une autre machinerie : programmation dynamique, distance de Hamming, ou matrice de poids positionnels. Nous y revenons avec la note d'actualité.
Une table de conversion est un contrat. Si un symbole est oublié, le programme doit lever une erreur explicite plutôt que produire une regex qui ne correspond jamais ; si N est traduit par . au lieu de [ACGT], le motif devient trop permissif et peut franchir des frontières de lignes. Les tests unitaires doivent couvrir chaque symbole, les minuscules, un motif vide et un symbole inconnu. La conversion est triviale, mais c'est précisément le genre de code trivial qu'on ne teste pas et qui casse silencieusement.
re.findall, re.finditer et chevauchements
Le module re de la bibliothèque standard compile un motif en un automate de recherche. re.findall renvoie une liste de chaînes trouvées ; re.finditer renvoie des objets Match qui portent les positions. Pour répondre à « où ? », il faut re.finditer et m.start(), m.end(), m.span() ou m.group(). re.findall est pratique pour compter ou lister des mots, mais elle ne donne aucune coordonnée.
Les deux fonctions avancent après chaque match : par défaut, les occurrences ne se chevauchent pas. Sur ATATAT, le motif ATAT n'est trouvé qu'une fois, à la position 0, alors qu'une seconde occurrence commence à la position 2. Pour énumérer les chevauchements, on utilise un lookahead (assertion avant) : (?=(ATAT)). Le lookahead vérifie que le motif suit la position courante sans consommer de caractère. Le moteur avance donc d'un caractère à la fois, et m.start() parcourt toutes les positions de départ possibles. Sans parenthèses de capture, m.group() est vide ; avec (?=(ATAT)), m.group(1) contient le motif. C'est un détail qui explique pourquoi re.findall(r"(?=ATAT)", seq) renvoie une liste de chaînes vides : il n'y a rien à capturer.
re.escape protège un motif littéral. Sans échappement, le point . est un joker qui reconnaît n'importe quel caractère : A.T reconnaît AAT, ACT, AGT et ATT. Pour chercher littéralement un point, il faut re.escape("A.T"). En ADN, le point n'est pas un symbole officiel, mais des motifs importés d'un fichier ou saisis par un utilisateur peuvent contenir des métacaractères. La règle est simple : échapper les parties littérales, construire les classes IUPAC à partir d'une table contrôlée, ne jamais concaténer une entrée utilisateur dans une regex sans validation.
Lorsque la même recherche est répétée, compilez le motif une fois avec re.compile et réutilisez l'objet obtenu. Le module re conserve un cache interne des derniers motifs compilés, mais un objet explicite documente l'intention, évite les reconstructions et permet de passer des drapeaux comme re.IGNORECASE ou re.MULTILINE. Pour une séquence d'ADN, la normalisation en majuscules reste préférable à IGNORECASE, car elle rend la casse explicite et évite les surprises sur les alphabets étendus.
Les ancres ^ et $ ne signifient pas « début et fin de la séquence biologique » dans tous les contextes. Par défaut, elles désignent le début et la fin de la chaîne, ou de chaque ligne avec re.MULTILINE. re.match ancre implicitement au début, re.search balaie toute la chaîne, et re.fullmatch exige que toute la chaîne corresponde. Pour une séquence FASTA non concaténée, ^ATG ne trouve que le premier codon de la première ligne, et un motif coupé par un retour à la ligne est invisible. La concaténation préalable n'est pas une commodité : c'est une condition de correction.
Coordonnées, brins et chevauchements
Python utilise des indices 0-based et des intervalles semi-ouverts : seq[start:end] inclut start et exclut end. Les formats biologiques utilisent des positions 1-based et des intervalles fermés. Pour un match de longueur commençant à l'indice :
Un site GAATTC trouvé à l'indice 3 couvre donc les positions 4 à 9, et non 3 à 8. L'erreur d'un décalage est particulièrement vicieuse parce qu'elle reste plausible : toutes les positions semblent cohérentes entre elles, mais elles sont décalées par rapport à l'annotation de référence.
Sur le brin complémentaire, la conversion est plus subtile. Si un motif de longueur commence à l'indice de la séquence complémentaire inverse, l'intervalle correspondant sur le brin direct est :
en coordonnées 0-based. La position 1-based de début vaut donc , et la fin 1-based vaut . On ne peut pas réutiliser comme coordonnée directe : est une position dans une chaîne inversée, pas dans la séquence d'origine. Un site palindromique trouvé sur les deux brins correspond au même locus physique ; il faut le compter une fois. Un site non palindromique trouvé sur le brin complémentaire est une occurrence distincte, et son matched doit être lu sur ce brin.
Complexité, backtracking et alternatives
La recherche naïve d'un motif de longueur dans une séquence de longueur coûte dans le pire des cas, mais les implémentations modernes sont souvent meilleures. str.find de CPython utilise un algorithme linéaire en pratique, et des algorithmes comme Knuth-Morris-Pratt garantissent . Pour plusieurs motifs simultanés, Aho-Corasick construit un automate en et scanne la séquence une fois. Pour des requêtes répétées sur un génome fixe, un index (table de suffixes, FM-index) déplace le coût vers la construction et répond en temps proportionnel à la longueur du motif plus le nombre d'occurrences.
Le moteur de re est un moteur à backtracking. Pour un motif littéral ou une classe simple, il est rapide. Pour des quantificateurs imbriqués, le pire cas peut être exponentiel : un motif comme (?:A+)+B sur une longue suite de A sans B peut essayer un nombre combinatoriel de découpages. Les motifs d'ORF avec (?:[ACGT]{3})*? suivis d'un codon stop cumulent deux difficultés : le quantificateur étoile peut revenir en arrière, et la structure ne suit pas explicitement l'état du cadre. Python 3.11 ajoute les groupes atomiques (?>...) et les quantificateurs possessifs, qui limitent certains retours arrière, mais ils ne remplacent pas un algorithme déterministe quand la sémantique l'exige.
Le choix dépend de la question. Une regex est excellente pour un motif court, dégénéré, cherché une fois dans une région de quelques kilobases. Elle est moins adaptée à des millions de requêtes, à des motifs avec de nombreuses erreurs, ou à l'énumération exhaustive d'ORFs. Dans ce dernier cas, une boucle qui avance de trois en trois et suit le cadre est plus simple à raisonner et souvent plus rapide.
Pour fixer les ordres de grandeur, chercher un site de six bases dans un génome bactérien de quelques millions de paires de bases reste confortable sur un ordinateur portable. Le coût devient problématique lorsque la recherche est répétée pour des milliers de motifs ou de séquences, ou lorsque le motif contient des quantificateurs imbriqués. Un index construit une fois, comme une table de suffixes ou un FM-index, déplace le coût vers la préparation et répond ensuite sans rescanner le génome à chaque requête. C'est le même arbitrage que pour une base de données : un parcours séquentiel est simple, un index devient rentable dès que les requêtes se multiplient.
Exemple travaillé à la main
Prenons la séquence CGCGAATTCCGG, de longueur 12. Le motif GAATTC commence à l'indice 0-based 3 : les bases 3 à 8 sont G A A T T C. Les coordonnées 1-based sont donc 4 à 9. Le complément inverse de est CCGGAATTCGCG. Le même motif y commence à l'indice 3 ; la formule donne pour le début 0-based, soit 4 en 1-based. Le site est palindromique : les deux recherches désignent le même locus physique, qu'il ne faut pas compter deux fois.
Passons au motif dégénéré G[AT]TC, qui reconnaît GATC et GTTC. Dans la séquence GATCCGGTTCGATC, les occurrences commencent aux indices 0 (GATC), 6 (GTTC) et 10 (GATC). En 1-based, elles couvrent 1–4, 7–10 et 11–14. Le motif IUPAC équivalent est GWTC : la conversion W → [AT] redonne G[AT]TC. Ces trois occurrences ne se chevauchent pas, mais le motif ne permet pas de conclure que les chevauchements sont impossibles en général : ATAT sur ATATAT donne bien les départs 0 et 2.
Le complément inverse de la séquence est GATCGAACCGGATC. Les occurrences de G[AT]TC y sont GATC aux indices 0 et 10. En les ramenant sur le brin direct, on obtient les intervalles 10–13 et 0–3 : ce sont exactement les deux loci GATC déjà trouvés sur le brin direct. Le GTTC de l'indice 6 n'a pas de contrepartie sur le brin complémentaire, car son complément inverse est GAAC, absent de la séquence. Une recherche naïve sur les deux brins renverrait cinq hits (trois plus deux), alors qu'il n'y a que trois loci physiques. Le dédoublonnage des palindromes n'est pas une optimisation cosmétique : c'est une correction biologique.
Implémentation
Les blocs suivants utilisent uniquement la bibliothèque standard de Python 3.11 ou une version ultérieure. Ils normalisent la casse, valident l'alphabet, échappent les motifs littéraux et utilisent un lookahead pour les chevauchements. Chaque bloc est autonome.
Trois choix structurants méritent d'être explicités. D'abord, la validation de l'alphabet est séparée de la recherche : une séquence contenant X ou un chiffre doit être rejetée, pas silencieusement ignorée. Ensuite, les motifs littéraux passent par re.escape, alors que les motifs IUPAC passent par une table de classes : mélanger les deux conduit soit à un motif trop permissif, soit à une regex invalide. Enfin, les coordonnées sont calculées au moment où le hit est créé, avec les deux conventions affichées côte à côte ; on évite ainsi les conversions tardives, qui sont la principale source de décalage.
Aucune sortie. Exécutez le code pour voir le résultat.
Le code s'exécute dans votre navigateur (Web Worker isolé). Il n'a pas accès au serveur ; l'accès réseau est désactivé pendant l'exécution. Le premier clic sur « Exécuter » télécharge Pyodide depuis jsDelivr (plusieurs mégaoctets, version 0.26.4 épinglée) ; rien n'est chargé avant ce clic.
La sortie vérifiée le 2026-11-01 est :
[0, 2]
[3]
G[AT]TC
G[AG]TCfind_motif_positions renvoie des positions 0-based, triées par ordre de lecture. Le lookahead capture le motif pour que m.group(1) reste disponible, même si nous n'utilisons ici que m.start(). re.escape garantit qu'un motif comme A.T est traité littéralement. iupac_to_regex refuse les symboles inconnus plutôt que de produire une regex silencieusement fausse : c'est une validation peu coûteuse qui évite des heures de débogage.
Le bloc suivant illustre les différences de comportement entre findall, finditer, les ancres et l'échappement. Il est volontairement petit : chaque ligne isole un piège.
Aucune sortie. Exécutez le code pour voir le résultat.
Le code s'exécute dans votre navigateur (Web Worker isolé). Il n'a pas accès au serveur ; l'accès réseau est désactivé pendant l'exécution. Le premier clic sur « Exécuter » télécharge Pyodide depuis jsDelivr (plusieurs mégaoctets, version 0.26.4 épinglée) ; rien n'est chargé avant ce clic.
La sortie vérifiée est :
['ATAT']
[0]
[0, 2]
['ATAT', 'ATAT']
['', '']
['AAT', 'ACT']
[]
None
(3, 6)
[]
['ATG']Les deux premières lignes montrent le comportement non chevauchant. La troisième montre le lookahead ; la quatrième montre que findall renvoie les groupes capturés, pas les positions ; la cinquième montre qu'un lookahead sans groupe capture la chaîne vide. Le point non échappé reconnaît trois mots, alors que re.escape ne reconnaît rien dans AATACT. Enfin, re.match échoue dès que le motif n'est pas au début, re.search trouve ATG à l'indice 3, et les ancres ^ et $ contraignent la recherche aux extrémités de la chaîne.
Pour chercher sur les deux brins, il faut convertir les coordonnées et dédoublonner les palindromes. La fonction suivante prend un motif littéral, mais la même logique s'applique à une regex IUPAC.
Aucune sortie. Exécutez le code pour voir le résultat.
Le code s'exécute dans votre navigateur (Web Worker isolé). Il n'a pas accès au serveur ; l'accès réseau est désactivé pendant l'exécution. Le premier clic sur « Exécuter » télécharge Pyodide depuis jsDelivr (plusieurs mégaoctets, version 0.26.4 épinglée) ; rien n'est chargé avant ce clic.
La sortie vérifiée est :
[(4, 9, '+')]
[(13, 18, '-')]Le site EcoRI est palindromique : la recherche sur le brin complémentaire le retrouve au même locus, et la condition plus_word == reverse_complement(plus_word) l'écarte. Le site BsaI GGTCTC n'est présent que sur le brin complémentaire de l'extrait ; la conversion n - (i + k) le replace correctement aux positions 13 à 18 du brin direct. Sans cette conversion, on obtiendrait un intervalle faux, souvent situé de l'autre côté de la séquence.
Une regex peut aussi servir de complément pour les ORFs. Le motif suivant reconnaît un codon ATG, un nombre entier de codons, puis un codon stop. Le quantificateur paresseux *? cherche le stop le plus proche tout en restant dans le cadre.
Aucune sortie. Exécutez le code pour voir le résultat.
Le code s'exécute dans votre navigateur (Web Worker isolé). Il n'a pas accès au serveur ; l'accès réseau est désactivé pendant l'exécution. Le premier clic sur « Exécuter » télécharge Pyodide depuis jsDelivr (plusieurs mégaoctets, version 0.26.4 épinglée) ; rien n'est chargé avant ce clic.
La sortie vérifiée est :
[(4, 12, 9, 'ATGAAATAG'), (16, 24, 9, 'ATGCCCTAA')]
[]
[]La regex trouve deux ORFs de neuf nucléotides. Le filtre de longueur écarte le second appel, et la séquence contenant N ne produit aucun match, car [ACGT] ne reconnaît pas une base inconnue. Cette approche a des limites structurelles : finditer ne renvoie pas les ORFs imbriqués ou chevauchants, le motif ignore les codons d'initiation alternatifs, et il ne parcourt pas le brin complémentaire. Pour un génome, une boucle par cadre qui avance de trois en trois reste préférable ; la regex est utile pour filtrer ou pour exprimer une contrainte locale, pas pour remplacer un annotateur.
Étude de cas
Nous travaillons sur la région 1–3000 du chromosome d'E. coli K-12 MG1655, accession NC_000913.3. Cette région réelle a été téléchargée depuis NCBI le 2026-11-01 avec EFetch ; elle contient 3 000 pb et un contenu en GC de 51,93 %. Les résultats ci-dessous sont des mesures réelles sur cette séquence, pas des sorties illustratives.
La question est double : la région contient-elle un site EcoRI, et combien de fois le motif dégénéré G[AT]TC y apparaît-il sur chaque brin ? La région se télécharge avec EFetch, puis le script suivant concatène le FASTA, calcule les compositions et énumère les occurrences chevauchantes.
efetch -db nuccore -id NC_000913.3 -seq_start 1 -seq_stop 3000 -format fasta > ecoli_1_3000.fastafrom collections import Counter
from pathlib import Path
from re import finditer
def read_fasta(path):
lines = Path(path).read_text().splitlines()
return "".join(line.strip() for line in lines if not line.startswith(">")).upper()
def starts(seq, pattern):
return [m.start() for m in finditer(f"(?=({pattern}))", seq)]
seq = read_fasta("ecoli_1_3000.fasta")
counts = Counter(seq)
print(len(seq), counts["A"], counts["C"], counts["G"], counts["T"])
print(round(100 * (counts["G"] + counts["C"]) / len(seq), 2))
print(len(starts(seq, "GAATTC")))
print(len(starts(seq, r"G[AT]TC")))
rc = seq.translate(str.maketrans("ACGT", "TGCA"))[::-1]
plus = set(starts(seq, r"G[AT]TC"))
minus = {len(seq) - (i + 4) for i in starts(rc, r"G[AT]TC")}
print(len(plus), len(minus), len(plus & minus), len(plus | minus))
print(sorted(x + 1 for x in plus)[:5])La sortie vérifiée est :
3000 710 740 818 732
51.93
0
25
25 30 13 42
[351, 588, 619, 726, 781]Aucun site EcoRI n'apparaît dans la région. Ce zéro est un résultat, pas un échec : sous un modèle uniforme, un site de six bases a une probabilité par position, soit environ 0,73 site attendu dans 3 000 pb. La probabilité de n'en observer aucun est proche de , soit environ 48 %. Une fenêtre de 3 kb n'a donc pas la puissance nécessaire pour conclure qu'EcoRI est rare dans cette région, et encore moins dans le génome. Le choix de la fenêtre influence directement l'interprétation : les mêmes 3 000 pb donneraient un autre verdict si elles étaient riches en GC, et une recherche sur plusieurs fenêtres multiplierait les tests. Sans correction pour tests multiples, un excès apparent dans une fenêtre peut n'être qu'une fluctuation.
Pour vérifier que le pipeline détecte bien un site réel, prenons le premier site EcoRI de la séquence de référence complète, à la position 3842 (1-based). L'extrait 3812–3877 contient le site en son milieu.
from re import finditer
extract = "CAGGGGCTGTTTTTTCCGCACGACCTGCCGGAATTCAGCCTGACTGAAATTGATGAGATGCTGAAG"
print(len(extract))
print([(m.start() + 1, m.end(), m.group()) for m in finditer("GAATTC", extract)])La sortie vérifiée est :
66
[(31, 36, 'GAATTC')]Le site commence à la position 31 de l'extrait, ce qui correspond à la position 3842 du génome. La recherche est donc sensible et les coordonnées sont cohérentes. Cette vérification positive ne prouve pas que le site est coupé in vivo : la méthylation, la chromatine et la disponibilité de l'enzyme ne sont pas dans la séquence.
Le motif G[AT]TC est plus fréquent : 25 occurrences sur le brin direct, 30 sur le brin complémentaire, mais 13 loci sont partagés parce que GATC est palindromique. Le nombre de loci physiques distincts est 42, et non 55. Les cinq premières positions directes sont 351, 588, 619, 726 et 781. Le motif mélange deux familles : GATC, qui est aussi le site de méthylation Dam chez E. coli, et GTTC (ou GAAC sur l'autre brin), qui n'a pas le même statut biologique. Compter les hits d'un motif dégénéré sans regarder les mots effectivement reconnus peut donc mélanger des signaux différents. Pour G[AT]TC, la fréquence attendue par position est sous un modèle uniforme, soit environ 23,4 hits sur un brin de 3 000 pb ; les 25 observés sont proches de cette valeur. Cette concordance ne doit pas être surinterprétée : le génome n'est pas uniforme, et le motif mélange deux familles de mots.
Ces résultats restent descriptifs. Une recherche de motifs ne dit rien sur la conservation, la liaison d'une protéine ou l'effet d'une mutation. Pour aller plus loin, on croiserait les positions avec l'annotation, on testerait la significativité par rapport à un modèle de fond, et on utiliserait une matrice de poids positionnels si le motif est dégénéré de façon probabiliste. La leçon méthodologique est la même que pour les ORFs : un hit est une hypothèse, pas une conclusion.
Erreurs fréquentes
Quiz
Exercices
Énoncé
Écrire find_motif_positions(seq, motif) qui renvoie la liste triée des positions 0-based de toutes les occurrences chevauchantes d'un motif littéral, insensible à la casse. La fonction doit utiliser re.escape et un lookahead, renvoyer [] pour une séquence vide et lever ValueError pour un motif vide ou une séquence contenant un caractère hors alphabet IUPAC ADN. Écrire aussi iupac_to_regex(pattern) qui convertit chaque symbole IUPAC en classe regex, refuse un motif vide ou un symbole inconnu, et renvoie G[AT]TC pour GWTC.
Comment vérifier votre travail
Les tests se trouvent dans exercises/module-1/1.4-recherche-motifs-regex/test_1_4.py. Depuis la racine du dépôt :
python -m pytest exercises/module-1/1.4-recherche-motifs-regex -qCorrigé verrouillé
Le corrigé est chargé uniquement après confirmation. Il n'est pas présent dans la page avant cette action.
Énoncé
Écrire restriction_sites(seq) qui cherche EcoRI (GAATTC), BamHI (GGATCC), HindIII (AAGCTT), PstI (CTGCAG) et NotI (GCGGCCGC) sur les deux brins. Chaque résultat est un dictionnaire avec enzyme, site, start et end (0-based, fin exclusive), start_1based et end_1based (1-based, fin inclusive), strand ("+" ou "-") et matched. Les occurrences chevauchantes sont incluses. Un site palindromique trouvé sur les deux brins ne doit apparaître qu'une fois. La fonction valide l'alphabet IUPAC ADN et renvoie une liste triée par position.
Comment vérifier votre travail
Les tests se trouvent dans exercises/module-1/1.4-recherche-motifs-regex/test_1_4.py. Depuis la racine du dépôt :
python -m pytest exercises/module-1/1.4-recherche-motifs-regex -qCorrigé verrouillé
Le corrigé est chargé uniquement après confirmation. Il n'est pas présent dans la page avant cette action.
Énoncé
Mini-projet : rapport de sites pour plusieurs enzymes. Écrire motif_report(seq, enzyme_dict) où enzyme_dict associe un nom d'enzyme à un site IUPAC. Le rapport contient la longueur, le GC, un dictionnaire enzymes (site, regex, hits), le nombre total de hits et des avertissements. Chaque hit contient start et end 1-based fin inclusive, strand, matched (motif lu sur le brin trouvé) et plus_sequence (tranche du brin de référence). Les occurrences chevauchantes sont incluses, les sites palindromiques ne sont comptés qu'une fois, et les entrées invalides lèvent ValueError. Le rapport doit être déterministe.
Comment vérifier votre travail
Les tests se trouvent dans exercises/module-1/1.4-recherche-motifs-regex/projet/test_projet_1_4.py. Depuis la racine du dépôt :
python -m pytest exercises/module-1/1.4-recherche-motifs-regex/projet -qCorrigé verrouillé
Le corrigé est chargé uniquement après confirmation. Il n'est pas présent dans la page avant cette action.
Résumé
- Un site de restriction est un motif court ; les cinq sites étudiés sont palindromiques au sens du complément inverse.
- L'alphabet IUPAC décrit des ensembles de mots ; chaque symbole se convertit en une classe regex qui consomme un caractère.
re.finditerfournit les positions ;re.findallfournit des chaînes et ne suffit pas pour localiser un motif.- Le lookahead
(?=(...))énumère les occurrences chevauchantes, à condition de capturer le motif. - Les positions Python sont 0-based et semi-ouvertes ; les formats biologiques sont 1-based et fermés.
- Sur le brin complémentaire, la conversion est ; un palindrome trouvé deux fois ne compte qu'une fois.
re.escapeprotège les motifs littéraux ; les ancres et les retours à la ligne faussent les recherches sur un FASTA brut.- Une regex est exacte et adaptée aux motifs courts ; pour l'échelle, les erreurs ou les ORFs, préférez un algorithme exact, un index ou un scan déterministe.
Références
- Python Software Foundation. re — Regular expression operations. Documentation officielle : https://docs.python.org/3/library/re.html
- Python Software Foundation. Regular Expression HOWTO. Documentation officielle : https://docs.python.org/3/howto/regex.html
- NCBI RefSeq. Escherichia coli str. K-12 substr. MG1655, complete genome, accession NC_000913.3. https://www.ncbi.nlm.nih.gov/nuccore/NC_000913.3
- NCBI. E-utilities et EFetch. Documentation officielle : https://www.ncbi.nlm.nih.gov/books/NBK25501/
- Cock, P. J. A. et al. (2009). Biopython: freely available Python tools for computational molecular biology and bioinformatics. Bioinformatics, 25(11), 1422–1423.
- Biopython. Bio.Restriction — restriction enzyme analysis. Documentation officielle : https://biopython.org/docs/latest/api/Bio.Restriction.html
- Roberts, R. J. et al. (2003). A nomenclature for restriction enzymes, DNA methyltransferases, homing endonucleases and their genes. Nucleic Acids Research, 31(7), 1805–1812.