TP 8 : Bioinformatique appliqué à l’ADN du choléra#

Lors de la réplication de l’ADN, deux chaînes de l’ADN se séparent pour produire deux copies identiques. La réplication commence en une séquence particulière de l’ADN appelée origine de réplication (ori).
Un problème important est de localiser l’ori d’un génome. On sait par la biologie que l’ori contient de nombreuses de la même séquence de nucléotides, qui est appelée dnaA et permet de localiser le début de l’ori.

Exercice

  1. Télécharger (clic droit puis enregistrer sous) l’ADN de Vibrio cholerae (bactérie du choléra) et le sauvegarder dans le même dossier que votre fichier Python.

  2. Exécuter le code ci-dessous pour charger l’ADN dans une variable cholerae (sous forme de chaîne de caractères, chaque caractère étant un nucléotide).

  3. Afficher les 10 premiers nucléotides de l’ADN de Vibrio cholerae.

  4. Combien de nucléotides contient l’ADN de Vibrio cholerae ?

with open("Vibrio_cholerae.txt") as f:
    cholerae = f.read()

Considérons aussi l’ori de l’ADN de Vibrio cholerae, sous forme de chaîne de caractères (chaque caractère étant un nucléotide) :

cholerae_ori = "ATCAATGATCAACGTAAGCTTCTAAGCATGATCAAGGTGCTCACACAGTTTATCCACAACCTGAGTGGATGACATCAAGATAGGTCGTTGTATCTCCTTCCTCTCGTACTCTCATGACCACGGAAAGATGATCAAGAGAGGATGATTTCTTGGCCATATCGCAATGAATACTTGTGACTTGTGCTTCCAATTGACATCTTCAGCGCCATATTGCGCTGGCCAAGGTGACGGAGCGGGATTACGAAAGCATGATCATGGCTGTTGTTCTGTTTATCTTGTTTTGACTGAGACTTGTTAGGATAGACGGTTTTTCATCACTGACTAGCCAAAGCCTTACTCTGCCTGACATCGACCGTAAATTGATAATGAATTTACATGCTTCCGCGACGATTTACCTCTTGATCATCGATCCGATTGAAGATCTTCAATTGTTAATTCTCTTGCCTCGACTCATAGCCATGATGAGCTCTTGATCATGTTTCCTTAACCCTCTATTTTTTACGGAAGAATGATCAAGCTGCTGCTCTTGATCATCGTTTC"

Question

Quel est le nombre de nucléotides de l’ori de Vibrio cholerae ?

Question

La règle de Chargaff dit que la proportion de nucléotides \(A\) est environ égale à celle de nucléotides \(T\) et que la proportion de nucléotides \(C\) est environ égale à celle de nucléotides \(G\). Vérifiez que cette règle est respectée pour Vibrio cholerae.

Motif dans un ADN#

On rappelle que, Comme pour les listes, s[i:j] extrait une sous-chaîne de s allant du caractère d’indice i (inclus) au caractère d’indice j (exclus) :

cholerae_ori[0:3] # 3 premiers nucléotides
'ATC'

On peut comparer deux chaînes de caractères avec l’opérateur == :

cholerae_ori[0:3] == "ATC" # est-ce que les 3 premiers nucléotides sont égaux à "ATC" ?
True

Question

À quel indice l’ori de Vibrio cholerae commence t-il dans le génome de Vibrio cholerae ?

Question

Écrire une fonction compter(adn, motif) qui compte le nombre d’apparitions de motif dans adn.

compter(cholerae_ori, "ATG")
15

k-mer le plus fréquent#

Un k-mer est une séquence de \(k\) nucléotides. Par exemple, ATG est un 3-mer.

Version naïve#

Question

Écrire une fonction kmer_plus_frequent(adn, k) qui renvoie le k-mer qui apparaît le plus souvent dans adn (s’il y a plusieurs k-mers de fréquence maximum, on en renverra un quelconque).
Pour cela, on parcourt tous les k-mers de adn et on compte le nombre d’occurences de chacun d’eux, avec la fonction compter écrite précédemment.

kmer_plus_frequent(cholerae_ori, 3)
'TGA'

Si adn est de longueur \(n\), kmer_plus_frequent(adn, k) appelle environ \(n\) fois compter, qui effectue elle-même environ \(n\) opérations. Donc kmer_plus_frequent(adn, k) effectue de l’ordre de \(n^2\) opérations, ce qui demanderait trop de temps pour le génome complet de Vibrio cholerae.

Version avec un dictionnaire#

Pour avoir une fonction plus efficace, on va utiliser un dictionnaire.
Un dictonnaire est une structure de données qui associe à chaque clé une valeur. Par exemple, on peut créer un dictonnaire qui à chaque utilisateur associe son âge :

age = {"michel" : 42, "pierre" : 12, "marie" : 32} # définition d'un dictionnaire, sous forme d'association clé-valeur

On peut accéder à la valeur associée à une clé dans un dictionnaire d avec d[cle] :

age["marie"] # donne la valeur associée à la clé "marie", c'est-à-dire 32
32

On peut ajouter une nouvelle paire clé-valeur avec d[cle] = valeur :

age["jean"] = 22 # ajoute une nouvelle association clé-valeur
age # age a été modifié
{'michel': 42, 'pierre': 12, 'marie': 32, 'jean': 22}

Question

Créer un dictionnaire complement qui associe à chaque nucléotide son complément. Ainsi, complement['A'] doit valoir 'T', complement['T'] doit valoir 'A', complement['C'] doit valoir G et complement['G'] doit valoir 'C'.

On peut parcourir les clés d’un dictionnaire avec une boucle for :

for k in age: # parcours des clés du dictionnaire
    print(k, age[k]) # affiche la clé et la valeur associée
michel 42
pierre 12
marie 32
jean 22

Question

Écrire une fonction cle_max(d) qui renvoie la clé associée à la plus grande valeur dans le dictionnaire d.

cle_max(age) # michel est la personne dont l'âge est le plus élevé
'michel'

Pour obtenir plus efficacement le k-mer le plus fréquent, on va calculer un dictionnaire qui à chaque k-mer associe le nombre de fois qu’il apparaît. Puis on utilise la fonction cle_max pour obtenir le k-mer le plus fréquent de ce dictionnaire.

Question

Réécrire une fonction kmer_plus_frequent(adn, k) qui renvoie le k-mer qui apparaît le plus souvent dans adn, en utilisant un dictionnaire.

kmer_plus_frequent(cholerae_ori, 3)
'TGA'

Question

Quel est le 9-mer le plus fréquent dans l’ADN de Vibrio cholerae ?

Remarques :

  • Le DnaA est souvent de taille 9 pour une bactérie.

  • Avec notre première version de kmer_plus_frequent, il n’aurait pas été possible d’obtenir la réponse à cette dernière question en temps raisonnable.

Nucléotide complémentaire#

L’ori est composé de deux brins d’ADN complémentaires et dans des sens opposés. Pour que la cellule sache où se trouve l’ori, il faut donc que le dnaA soit présent sur les deux brins, dans des sens opposés.

Ainsi, dans l’ori de Vibrio cholerae, le DnaA ATGATCAAG est présent ainsi que son complémentaire inversé CTTGATCAT.

Pour la question suivante, on pourra utiliser + sur des chaînes de caractères pour les concaténer :

s = "" # s est une chaîne de caractères vide
s += "Bonne " # on ajoute "Bonne " à s
s += "année" # on ajoute "année" à s
s
'Bonne année'

On pourra aussi utiliser la fonction s[::-1] pour inverser une chaîne de caractères :

s[::-1] # on inverse la chaîne de caractères
'eénna ennoB'

Question

Écrire une fonction complementaire_inverse(adn) qui renvoie le complémentaire inversé de adn. Ainsi, si adn vaut \(n_1n_2\ldots n_k\), complementaire_inverse(adn) doit renvoyer \(n_k^*n_{k-1}^*\ldots n_1^*\), où \(n_i^*\) est le complémentaire de \(n_i\) (A <-> T, C -> G).

complementaire_inverse("ATGATCAAG")
'CTTGATCAT'

Question

Écrire une fonction plus_frequent_inverse(adn, k) qui renvoie le k-mer s le plus fréquent dans adn, en comptant aussi les occurrences de complementaire_inverse(s). On pourra s’inspirer de la fonction kmer_plus_frequent écrite précédemment.

plus_frequent_inverse(cholerae_ori, 9)
'ATGATCAAG'

Recherche de phase ouverte de lecture (open reading frame)#

Un codon est une séquence de trois nucléotides (par exemple ATT).
Une “phase ouverte de lecture” (ORF) est une séquence d’ADN commençant par un codon d’initiation ATG, se terminant par un codon STOP (TAA, TAG, TGA), et ne contenant aucun codon STOP en son sein.

Question

Écrire une fonction orf(adn) qui renvoie la liste des ORFs de adn.

orf(cholerae_ori)
['ATGATCAACGTAAGCTTCTAA',
 'ATGATCAAGGTGCTCACACAGTTTATCCACAACCTGAGTGGATGA',
 'ATGACATCAAGATAG',
 'ATGACCACGGAAAGATGA',
 'ATGATCAAGAGAGGATGA',
 'ATGATTTCTTGGCCATATCGCAATGAATACTTGTGA',
 'ATGAATACTTGTGACTTGTGCTTCCAATTGACATCTTCAGCGCCATATTGCGCTGGCCAAGGTGACGGAGCGGGATTACGAAAGCATGATCATGGCTGTTGTTCTGTTTATCTTGTTTTGACTGAGACTTGTTAG',
 'ATGATCATGGCTGTTGTTCTGTTTATCTTGTTTTGA',
 'ATGGCTGTTGTTCTGTTTATCTTGTTTTGA',
 'ATGAATTTACATGCTTCCGCGACGATTTACCTCTTGATCATCGATCCGATTGAAGATCTTCAATTGTTAATTCTCTTGCCTCGACTCATAGCCATGATGAGCTCTTGA',
 'ATGCTTCCGCGACGATTTACCTCTTGA',
 'ATGATGAGCTCTTGA',
 'ATGAGCTCTTGA',
 'ATGTTTCCTTAA']