Il y a toujours un moment ou nous avons besoin d’aligner des séquences protéiques, cela va de la modélisation par homologie à un simple calcul de fluctuation/écart entre deux structures protéiques. Heureusement il existe toute une série d’outils, dont certains sont prévus pour être utilisés avec un code Python. C’est le cas de Biopython (paquetage Bio) qui permet de réaliser des alignements simples ou multiples en utilisant différents algorithmes, en propre (partie intégrante de la bibliothèque) ou à travers des interfaces en ligne de commande. Dans ce cas, un binaire du logiciel de comparaison de séquences doit être installé (ex: NCBI standalone BLAST). Le paquetage peut travailler avec d’autres paquetages tels que ReportLab ou des SGBD relationnels.
D’après un article publié dans buildblog.buidez.net en 2014.
1. Deux séquences protéiques
Nous allons comparer deux séquences protéiques, donc à priori 2 chaines de caractères. Si les séquences proviennent d’une base de séquences, nous restons dans le cas standard. Si les séquences proviennent de deux structures 3D de protéines, il faudra soit les télécharger à partir d’un site (PDB, Uniprot), soit les extraire à la main à partir du fichier PDB, soit via un logiciel de visualisation moléculaire s’il permet un copier-coller ou un export. En mode full Python, Bio permettra d’obtenir la séquence à partir de fichiers, avec des raffinements liés au formats, dans le cas le plus simple (exemple au format FASTA) :
from Bio import SeqIOfor record in SeqIO.parse("example.fasta", "fasta"): print(record.id) |
Dans le contexte buildez, le paquetage buildez.pdb offre plusieurs structures de données, la plus facile à utiliser étant la resmap, car elle peut être annotée (ce qui peut être utile pour la suite des calculs). Dans ce cas on peut déléguer à Bio la partie alignement puis reprendre la main avec notre propre code et l’annotation d’un structure de données telle que la resmap qui est positionnée entre séquence et structure, nous fournira le lien entre les deux paquetages. A cet effet, un paquetage d’interface buildez.bio est disponible avec un module buildez.bio.aln pour les alignements.
Mais une fois que la séquence est disponible, il faut la valider, car si on travaille à partir d’une structure, des résidus pouvant être non standard (par exemple des sélénométhionines). Puis enfin il faudra transformer l’alphabet a 3 lettres/résidu en séquence protéique (1 lettre/résidu).
2. Gestion des matrices de similarité
Qui dit alignement de séquences, dit matrice de similarité (substitution matrix). Le module bio.aln gère les matrices de Biopython, qui sont identifiées sous forme de chaines de caractères dont on peut connaître la liste, par exemple le code suivant :
|
1 2 3 4 5 6 7 8 9 10 |
print("<span style="color: #008000;">*** Substitution matrices</span>\n") print("-> Get available names") print(bio_interface_getsubstmatrix_list()) print("Is 'BLOSUM42' available ?", bio_interface_getsubstmatrix_info('BLOSUM42')) print("Is 'BLOSUM40' available ?", bio_interface_getsubstmatrix_info('BLOSUM40')) print("Is 'BLOSUM62' available ?", bio_interface_getsubstmatrix_info('BLOSUM62')) print("-> Get blosum62 matrix") print("-"*70) print(bio_interface_getsubstmatrix('blosum62')) print("-"*70) |
Donnera le résultat suivant, grace aux fonctions bio_interface_getsubstmatrix_list (renvoie la liste des matrices), bio_interface_getsubstmatrix_info (renvoie True ou False si la matrice existe) et bio_interface_getsubstmatrix (qui renvoie la matrice) :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
** Substitution matrices -> Get available names ['BENNER22', 'BENNER6', 'BENNER74', 'BLASTN', 'BLASTP', 'BLOSUM45', 'BLOSUM50', 'BLOSUM62', 'BLOSUM80', 'BLOSUM90', 'DAYHOFF', 'FENG', 'GENETIC', 'GONNET1992', 'HOXD70', 'JOHNSON', 'JONES', 'LEVIN', 'MCLACHLAN', 'MDM78', 'MEGABLAST', 'NUC.4.4', 'PAM250', 'PAM30', 'PAM70', 'RAO', 'RISLER', 'SCHNEIDER', 'STR', 'TRANS'] Is 'BLOSUM42' available ? False Is 'BLOSUM40' available ? False Is 'BLOSUM62' available ? True -> Get blosum62 matrix ---------------------------------------------------------------------- # Matrix made by matblas from blosum62.iij # * column uses minimum score # BLOSUM Clustered Scoring Matrix in 1/2 Bit Units # Blocks Database = /data/blocks_5.0/blocks.dat # Cluster Percentage: >= 62 # Entropy = 0.6979, Expected = -0.5209 A R N D C Q E G H I L K M F P S T W Y V B Z X * A 4.0 -1.0 -2.0 -2.0 0.0 -1.0 -1.0 0.0 -2.0 -1.0 -1.0 -1.0 -1.0 -2.0 -1.0 1.0 0.0 -3.0 -2.0 0.0 -2.0 -1.0 0.0 -4.0 R -1.0 5.0 0.0 -2.0 -3.0 1.0 0.0 -2.0 0.0 -3.0 -2.0 2.0 -1.0 -3.0 -2.0 -1.0 -1.0 -3.0 -2.0 -3.0 -1.0 0.0 -1.0 -4.0 N -2.0 0.0 6.0 1.0 -3.0 0.0 0.0 0.0 1.0 -3.0 -3.0 0.0 -2.0 -3.0 -2.0 1.0 0.0 -4.0 -2.0 -3.0 3.0 0.0 -1.0 -4.0 ... B -2.0 -1.0 3.0 4.0 -3.0 0.0 1.0 -1.0 0.0 -3.0 -4.0 0.0 -3.0 -3.0 -2.0 0.0 -1.0 -4.0 -3.0 -3.0 4.0 1.0 -1.0 -4.0 Z -1.0 0.0 0.0 1.0 -3.0 3.0 4.0 -2.0 0.0 -3.0 -3.0 1.0 -1.0 -3.0 -1.0 0.0 -1.0 -3.0 -2.0 -2.0 1.0 4.0 -1.0 -4.0 X 0.0 -1.0 -1.0 -1.0 -2.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -2.0 0.0 0.0 -2.0 -1.0 -1.0 -1.0 -1.0 -1.0 -4.0 * -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 -4.0 1.0 |
Biopython a suivi des évolutions, par exemple les noms de matrices pouvaient être en minuscules et il pouvait y avoir plus de matrices disponibles. Dans une ancienne version du module j’avais :
[benner6, benner22, benner74, blosum100, blosum30, blosum35, blosum40, blosum45, blosum50, blosum55, blosum60, blosum62, blosum65, blosum70, blosum75, blosum80, blosum85, blosum90, blosum95, feng, fitch, genetic, gonnet, grant, ident, johnson, levin, mclach, miyata, nwsgappep, pam120, pam180, pam250, pam30, pam300, pam60, pam90, rao, risler, structure] |
C’est comme cela, les codes de référence évoluent et c’est justement pour cela que l’on fait des paquetages d’interface qui nous permettent d’assurer une stabilité dans le temps, le code appelant ne change pas, le code d’interface change en fonction de la librairie qui est appelée. Le format de la matrice pouvait changer aussi, par exemple la fonction bio_interface_getsubstmatrix('blosum62') renvoyait un dictionnaire :
{('B', 'N'): 3, ('W', 'L'): -2, ('G', 'G'): 6, ('X', 'S'): 0, ('X', 'D'): -1, ('K', 'G'): -2, ('S', 'E'): 0, ('X', 'M'): -1, ('Y', 'E'): -2, ... ('Y', 'Y'): 7, ('T', 'K'): -1, ('Z', 'I'): -3, ('T', 'P'): -1, ('V', 'L'): 1, ('F', 'I'): 0, ('G', 'Q'): -2, ('L', 'A'): -1, ('M', 'I'): 1} |
Dans tous les cas ces structures de données sont simple et on arrive toujours à les convertir, soit pour continuer à les utiliser, soit pour les injecter dans des applications non Biopython mais qui utilisent ces matrices (ce qui évite de les recoder).
3. Switcher n’est pas forker
A partir de la version 1.84 l’utilisation du module pairwise2 est démodée (Biopython utilise maintenant PairwiseAligner) et la structure de données sur laquelle on travaille pour les alignements est différente. Il faut s’adapter et cela commence par un import qui est capable de capter la version du paquetage via la chaine __version__ (l’intégrer dans un paquetage est une très bonne pratique) :
|
1 2 3 4 5 6 7 8 |
import warnings from buildez.str import str_substr, str_replace, str_uc, str_lc from Bio import __version__ as package_Bio_version from Bio import BiopythonDeprecationWarning warnings.simplefilter('ignore', BiopythonDeprecationWarning) from Bio import pairwise2 from Bio.Align import PairwiseAligner from Bio.Align import substitution_matrices |
Si le code contient encore des appels à pairwise2, cela fonctionne mais Biopython envoie un message sur la sortie standard, mais que l’on peut désactiver grâce au paquetage warnings et à un filtre (fonction warnings.simplefilter) associé à un tag dans le message d’erreur (une autre très bonne pratique). Puis il nous faut une fonction pour définir un mode d’exécution :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def bio_get_package_mode(): """ The subroutine is internally used to set the operating mode. The module pairwise2 is deprecated in new versions of Bio, the module Bio.Align.PaiwiseAligner is used. Checking the __version__ of package Bio, the mode is set to 'pairwise2' or 'PairwiseAligner' and returned as string bay the subroutine. This string will be used as an operating switch by bio_interface_2str2aln_localds and bio_interface_2str2aln_globalds. """ ver = float(package_Bio_version) mode = "pairwise2" if (ver > 1.84): mode = "PairwiseAligner" return(mode) |
La nous sommes calés, le code d’interface aura un switch pour se mettre en mode pairwise2 ou Pairwise Aligner et renvoyer les mêmes données dans les deux cas.
4. En mode pairwise2
Prenons le cas de l’appel en mode pairwise2 pour la fonction bio_interface_2str2aln_globalds qui effectue un alignement global avec quelques paramètres facilement reconnaissables (séquence ce référence, séquence query, matrice, gap penalties et mode de fonctionnement) :
bio_interface_2str2aln_globalds(r_seq, q_seq, matrix_name='blosum62', gap_open=-10, gap_extend=-0.5, opmode='') |
La portion de code correspondante sera:
|
1 2 3 4 5 6 7 8 9 |
#+ Using pairwise2 (working if bio <= 1.84) if (opmode == "pairwise2"): matrix = bio_interface_getsubstmatrix(matrix_name) if (len(matrix) <= 0): return ('','', 0.0, 0, 0) alns = pairwise2.align.globalds(r_seq, q_seq, matrix, gap_open, gap_extend) if (len(alns) <= 0): return ('','', 0.0, 0, 0) r_aln, q_aln, score, begin, end = alns[0] ... return (r_aln, q_aln, score, begin, end) |
On exploite les résultats en sachant que alns est une liste car il peut y avoir plusieurs alignements possibles, chaque item est une liste de 4 chaines de caractères incluant des simples quotes pour encadrer les séquences et des = pour définir les égalités.
[Alignment(seqA='TGLLDGKRIL-SGIITDSSIAFHIARVAQEQGAQLVLTGFDRLQVLRRLTDRLPAKAPLLELDLQNEEHLASLAGRVTEAIGA', seqB='TGLLDGKRILVSGIITDSSIAFHIA-----QGAQLVLTGFDRLRLIQRITDRLPAKAPLLELDVQNEEHLASLAGRVTEAIGA', score=333.0, start=0, end=83)] |
Mais le code de bio_interface_2str2aln_globalds ne renvoie que le premier élément de la liste. Si nous affichons les variables (r_aln, q_aln, score, begin, end) nous avons :
-> bio_interface_2str2aln_globalds Score [333.000000] begin [0] end [83] : for first alignmentTGLLDGKRIL-SGIITDSSIAFHIARVAQEQGAQLVLTGFDRLQVLRRLTDRLPAKAPLLELDLQNEEHLASLAGRVTEAIGATGLLDGKRILVSGIITDSSIAFHIA-----QGAQLVLTGFDRLRLIQRITDRLPAKAPLLELDVQNEEHLASLAGRVTEAIGA |
5. En mode PairwiseAligner
Dans ce cas la portion de code est différente, car en entrée il faut paramétrer (matrice, gap penalties) un objet aligner qui va ensuite nous donner un résultat via la méthode aligner.align(r_seq, q_seq). Le résultat est soit itérable (construction d’une liste) soit directement transformé en liste pour être exploité, chaque item de cette liste est traité par une fonction d’interface bio_interface_get_PairwiseAligner :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def bio_interface_get_PairwiseAligner(alignment): """ The subroutine exploits the result (alignment) after application of PairwiseAligner.aligner.align method. """ r_aln = alignment[0, :] q_aln = alignment[1, :] score = alignment.score # r_seq = alignment.target # q_sec = alignment.query begin = 0 end = len(alignment[0]) if (len(alignment[1]) > end): end = len(alignment[1]) return([r_aln, q_aln, score, begin, end]) |
Et l’ensemble reconstitue la même liste aln que dans le cas pairwise2, nous constatons également la modification dans l’entrée des paramètres (attributs de l’objet aligner) :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#+ Using PairwiseAligner if (opmode == "PairwiseAligner"): matrix = bio_interface_getsubstmatrix(matrix_name) aligner = PairwiseAligner() aligner.substitution_matrix = matrix aligner.open_gap_score = gap_open aligner.extend_gap_score = gap_extend aligner.mode = 'local' alignments = aligner.align(r_seq, q_seq) ## alternative : alns = list(alignments) alns = [] for alignment in sorted(alignments): aln = bio_interface_get_PairwiseAligner(alignment) alns.append(aln) |
Cette liste aln sera traitée de la même manière par les modules en aval.
C’est le point clé, le code en aval doit rester stable, c’est les fonctions d’interface qui s’adaptent aux changement dans les codes/librairies métier.
Ce qui suppose les bons choix en amont, notamment pour les structures de données (listes, listes de dictionnaires, listes d’objets) qui vont transiter entre ces couches. La motivation du changement dans Biopython se comprennent tout de suite si on veut faire un dump de l’itérable alignments :
|
1 2 |
mlist = list(alignments) print(mlist[0]) |
Ce qui va donner :
target 0 TGLLDGKRIL-SGIITDSSIAFHIARVAQEQGAQLVLTGFDRLQVLRRLTDRLPAKAPLL 0 ||||||||||-||||||||||||||-----|||||||||||||....|.|||||||||||query 0 TGLLDGKRILVSGIITDSSIAFHIA-----QGAQLVLTGFDRLRLIQRITDRLPAKAPLL
|
Qui est plus proche de ce que l’on a l’habitude de voir en termes d’alignement. A noter que nous n’avons pas discuté sur les paramètres de l’alignement, en particulier les pénalités pour l’ouverture et pour l’extension d’un gap, qui peuvent aussi changer le résultat. Nous pourrions penser qu’utiliser un alignement de séquences, au sein de processus structure-based est anecdotique, en fait ce n’est pas le cas, il y a plusieurs applications, par exemple lorsqu’on veut calculer des fluctuations d’éléments structuraux d’une protéine.
Liens et lectures
- Biopython – [ https://biopython.org/ ].
- Download BLAST Software and Databases [ https://blast.ncbi.nlm.nih.gov/doc/blast-help/downloadblastdata.html ].
- Biopython Tutorial and Cookbook [ https://biopython.org/docs/latest/Tutorial/index.html ].
- Alignement de séquences [ https://fr.wikipedia.org/wiki/Alignement_de_s%C3%A9quences ].
- Wikipedia [ https://fr.wikipedia.org/wiki/Matrice_de_similarit%C3%A9 ].
- Wikipedia [ https://en.wikipedia.org/wiki/Substitution_matrix ].
- API Biopython, module Bio.pairwise2 [ https://biopython.org/docs/1.75/api/Bio.pairwise2.html ].
- API Biopython, Pairwise sequence alignment [ https://biopython.org/docs/latest/Tutorial/chapter_pairwise.html ].
- Algorithme de Needleman-Wunsch [ https://fr.wikipedia.org/wiki/Algorithme_de_Needleman-Wunsch ].
- Algorithme de Smith-Waterman [ https://fr.wikipedia.org/wiki/Algorithme_de_Smith-Waterman ].
- API Biopython, paquetage Bio.Emboss [ http://biopython.org/DIST/docs/api/Bio.Emboss-module.html ].