Created
March 31, 2019 21:35
-
-
Save YannBouyeron/3eb3aa7a79fadb3350096dec0bb5b004 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright (c) 2019 Yann BOUYERON | |
| # | |
| # | |
| # licensed under GNU GPL version 3 (or later) | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| import os | |
| import matplotlib | |
| if 'DISPLAY' not in os.environ: | |
| matplotlib.use('Agg') | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| from Bio.Seq import Seq | |
| from Bio.Alphabet import IUPAC, generic_nucleotide, generic_protein, SingleLetterAlphabet | |
| from Bio.Data import CodonTable | |
| from Bio.SeqRecord import SeqRecord | |
| from Bio.SeqUtils import GC, seq1, seq3 | |
| from Bio import SeqIO | |
| from io import StringIO | |
| from Bio import AlignIO | |
| from Bio import Phylo | |
| from Bio import Restriction as zym | |
| from Bio.Phylo.TreeConstruction import * | |
| from Bio.Phylo.Consensus import * | |
| import fnmatch | |
| import pandas as pd | |
| import seaborn as sns | |
| import unicodedata | |
| import ipfsapi | |
| from Bio.Emboss.Applications import NeedleCommandline | |
| from Bio.Emboss.Applications import WaterCommandline | |
| from Bio import SeqIO | |
| from Bio.Align.Applications import ClustalwCommandline | |
| from Bio import AlignIO | |
| ipfs = ipfsapi.Client(host='https://ipfs.infura.io', port=5001) | |
| def recurliste(path): | |
| """ | |
| argument: path est une chaine de caracteres | |
| return: une liste recursive des dossiers et fichiers contenus dans path. | |
| c'est une alternative a glob.glob('**',recursive=True) qui ne fonctionne pas en python < 3.5""" | |
| r = [] | |
| for root, dir, files in os.walk(path): | |
| if root != path: | |
| r.append(root.replace(path+'/', '')) | |
| for f in fnmatch.filter(files, "*"): | |
| x = root+'/'+f | |
| r.append(x) | |
| return r | |
| ########################################### Alphabets et langages ########### | |
| def canbedna(seq): | |
| """ | |
| Teste si une séquence (str ou Seq ou SeqRecord) peut être un dna | |
| argument: seq (str, Seq, ou SeqRecord): la séquence à tester | |
| return: bool | |
| """ | |
| if type(seq) == SeqRecord: | |
| seq = seq.seq | |
| elif type(seq) != str and type(seq) != Seq: | |
| raise ValueError("L'argument seq doit être de type str, Seq ou SeqRecord") | |
| div = list(set(seq)) | |
| for i in div: | |
| if i not in list(IUPAC.unambiguous_dna.letters) and i.upper() not in list(IUPAC.unambiguous_dna.letters): | |
| return False | |
| return True | |
| def canberna(seq): | |
| """ | |
| Teste si une séquence (str ou Seq ou SeqRecord) peut être un rna | |
| argument: seq (str, Seq, ou SeqRecord): la séquence à tester | |
| return: bool | |
| """ | |
| if type(seq) == SeqRecord: | |
| seq = seq.seq | |
| elif type(seq) != str and type(seq) != Seq: | |
| raise ValueError("L'argument seq doit être de type str, Seq ou SeqRecord") | |
| div = list(set(seq)) | |
| for i in div: | |
| if i not in list(IUPAC.unambiguous_rna.letters) and i.upper() not in list(IUPAC.unambiguous_rna.letters): | |
| return False | |
| return True | |
| def canbeexprot(seq): | |
| """ | |
| Teste si une séquence (str ou Seq ou SeqRecord) peut être une extended proteine | |
| argument: seq (str, Seq, ou SeqRecord): la séquence à tester | |
| return: bool | |
| """ | |
| if type(seq) == SeqRecord: | |
| seq = seq.seq | |
| elif type(seq) != str and type(seq) != Seq: | |
| raise ValueError("L'argument seq doit être de type str, Seq ou SeqRecord") | |
| div = list(set(seq)) | |
| for i in div: | |
| if i not in list(IUPAC.extended_protein.letters) + ['*']: | |
| return False | |
| return True | |
| def canbeprot(seq): | |
| """ | |
| Teste si une séquence (str ou Seq ou SeqRecord) peut être une proteine | |
| argument: seq (str, Seq, ou SeqRecord): la séquence à tester | |
| return: bool | |
| """ | |
| if type(seq) == SeqRecord: | |
| seq = seq.seq | |
| elif type(seq) != str and type(seq) != Seq: | |
| raise ValueError("L'argument seq doit être de type str, Seq ou SeqRecord") | |
| div = list(set(seq)) | |
| for i in div: | |
| if i not in list(IUPAC.protein.letters) + ['*']: | |
| return False | |
| return True | |
| def testalpha(seq): | |
| """ | |
| Teste l'alphabet d'une sequence | |
| argument: Seq ou SeqRecord , la sequence à tester | |
| return: | |
| generic_nucleotide si il y'a un doute entre adn ou arn | |
| IUPAC.unambigous_dna | |
| IUPAC.unambigous_rna | |
| IUPAC.protein | |
| IUPAC.extended_protein | |
| ou None si la sequence n'est pas reconnue | |
| """ | |
| if canbedna(seq) and canberna(seq): | |
| return generic_nucleotide | |
| elif canbedna(seq): | |
| return IUPAC.unambiguous_dna | |
| elif canberna(seq): | |
| return IUPAC.unambiguous_rna | |
| elif canbeprot(seq): | |
| return IUPAC.protein | |
| elif canbeexprot(seq): | |
| return IUPAC.extended_protein | |
| else: | |
| return None | |
| def toSeq(seq): | |
| """Teste si une seq est de type str Seq ou SeqRecord. Return Seq object""" | |
| if type(seq) == SeqRecord: | |
| return seq.seq | |
| elif type(seq) != str and type(seq) != Seq: | |
| raise ValueError("L'argument seq doit être de type str, Seq ou SeqRecord") | |
| elif type(seq) == str: | |
| return Seq(seq, testalpha(seq)) | |
| else: | |
| return seq | |
| def langage(seq): | |
| """ | |
| Determine le mode d'ecriture à 1 ou 3 lettres des séquences peptidiques | |
| argument: | |
| seq: la sequence à tester (str) ou (objet Seq) ou (objet SeqRecord) | |
| return: | |
| 1 (int) si langage à une lettre | |
| 3 (int) si langage à trois lettres | |
| ou None si la séquence n'est pas reconnue comme une sequence proteique | |
| """ | |
| seq = toSeq(seq) | |
| seq = str(seq) | |
| alpha = testalpha(seq) | |
| if seq.isupper() and (alpha == IUPAC.protein or alpha == IUPAC.extended_protein): | |
| return 1 | |
| elif seq.isupper() == False and seq.islower() == False and (testalpha(seq1(seq)) == IUPAC.protein or testalpha(seq1(seq)) == IUPAC.extended_protein): | |
| return 3 | |
| else: | |
| return None | |
| def matrix(align, model="identity", frame = False): | |
| """ | |
| Construction d'une matrice de similitudes à partir de l'alignement | |
| argument: objet align retourné par clustal | |
| return: matrice de distances, DataFrame | |
| """ | |
| calc = DistanceCalculator(model) | |
| dm = calc.get_distance(align) | |
| if frame == True: | |
| df = pd.DataFrame(dm.matrix) | |
| df.columns = dm.names | |
| df.index = dm.names | |
| for i in range(len(dm.names)): | |
| for c in range(len(dm.names)): | |
| if df.isnull().iloc[c,i] == True: | |
| df.iloc[c,i] = df.iloc[i,c] | |
| return df | |
| elif frame == False: | |
| return dm | |
| def matrix2ipfs(matrix): | |
| """transforme une matrice de distance en html publié sur IPFS, retourne le hash ipfs | |
| La page html est accessible à l'adresse https://ipfs.io/ipfs/<hash> | |
| Pour plus d'informations sur ipfs: https://gist.github.com/YannBouyeron/53e6d67782dcff5995754b0a7333fa0b | |
| """ | |
| if type(matrix) == DistanceMatrix: | |
| try: | |
| df = pd.DataFrame(matrix.matrix) | |
| df.columns = matrix.names | |
| df.index = matrix.names | |
| except: | |
| return False | |
| elif type(matrix) == pd.DataFrame: | |
| df = matrix | |
| html = df.to_html() | |
| html = html.replace("\n", "") | |
| r = ipfs.add_str(html) | |
| return r | |
| def matrix2png(matrix, path = "", scale=0.6, cmap="YlGnBu"): | |
| """ | |
| Transforme une matrice de distance en image png | |
| arguments: | |
| matrix: pd.DataFrame ou distance matrice retournee par la fonction matrix | |
| path (otpionnel): path en .png de l'image | |
| scale (optionnel) [defaut=0.6] : taille du texte (légendes, titres, cellules) | |
| cmap (optionnel) [defaut="YlGnBu"] : palette de couleurs | |
| return: objet heatmap | |
| """ | |
| if type(matrix) == DistanceMatrix: | |
| try: | |
| df = pd.DataFrame(matrix.matrix) | |
| df.columns = matrix.names | |
| df.index = matrix.names | |
| except: | |
| return False | |
| elif type(matrix) == pd.DataFrame: | |
| df = matrix | |
| sns.set(font_scale = scale) | |
| plt.close() | |
| m = sns.heatmap(df, annot=True, cmap=cmap) | |
| if 'DISPLAY' in os.environ: | |
| plt.show() | |
| if path != "": | |
| plt.savefig(path) | |
| return m | |
| def transcribe(*seq, out = False): | |
| """ Transcription d'un ou plusieurs dna SeqRecord. Si out == True, chaque rna est sauvegardé dans un fasta. Return rna SeqRecord ou une liste de rna SeqRecord""" | |
| result = [] | |
| for i in seq: | |
| if type(i) == SeqRecord: | |
| if testalpha(i) == IUPAC.unambiguous_dna or testalpha(i) == generic_nucleotide: | |
| dna = i.seq | |
| try: | |
| rna = dna.transcribe() | |
| sr = creat(i.name + '.rna', rna, 'transcription de [{0}]'.format(i.description), out=out) | |
| result.append(sr) | |
| except: | |
| pass | |
| if len(result) == 1: | |
| return result[0] | |
| else: | |
| return result | |
| def translate(*seq, table_id = 1, to_stop = True, stop_symbol = " ", cds = True, out = False): | |
| """ Traduction d'un ou plusieurs dna ou rna SeqRecord. Si out == True, chaque proteine est sauvegardée dans un fasta. Return proteine SeqRecord ou une liste de proteines SeqRecord""" | |
| result = [] | |
| if table_id not in list(range(32))[1:]: | |
| table_id = 1 | |
| for i in seq: | |
| if type(i) == SeqRecord: | |
| if testalpha(i) == IUPAC.unambiguous_rna or testalpha(i) == generic_nucleotide or testalpha(i) == IUPAC.unambiguous_dna: | |
| nucl = i.seq | |
| try: | |
| prot = nucl.translate(table = table_id, to_stop = to_stop, stop_symbol = stop_symbol, cds = cds) | |
| sr = creat(i.name + '.prot', prot, 'traduction de [{0}]'.format(i.description), out=out) | |
| result.append(sr) | |
| except: | |
| pass | |
| if len(result) == 1: | |
| return result[0] | |
| else: | |
| return result | |
| def retro_transcribe(*seq, out = False): | |
| """ Retro transcription d'un ou plusieurs rna SeqRecord. Si out == True, chaque dna est sauvegardé dans un fasta. Return dna SeqRecord ou une liste de dna SeqRecord""" | |
| result = [] | |
| for i in seq: | |
| if type(i) == SeqRecord: | |
| if testalpha(i) == IUPAC.unambiguous_rna or testalpha(i) == generic_nucleotide: | |
| rna = i.seq | |
| try: | |
| dna = rna.back_transcribe() | |
| sr = creat(i.name + '.dnac', dna, 'retro transcription de [{0}]'.format(i.description), out=out) | |
| result.append(sr) | |
| except: | |
| pass | |
| if len(result) == 1: | |
| return result[0] | |
| else: | |
| return result | |
| def codegen(table_id = None, bydna = False): | |
| """Affiche et retourne la table du code génétique correspondant à l'id entré en argument | |
| Si aucun id entré en argument: retourne menu de choix d'id | |
| """ | |
| d = CodonTable.unambiguous_dna_by_id | |
| r = CodonTable.unambiguous_rna_by_id | |
| print('\n') | |
| print('\n') | |
| while table_id not in list(range(32))[1:]: | |
| for i in d: | |
| print(d[i].id, ':', d[i].names[0]) | |
| print('\n') | |
| print('\n') | |
| table_id = int(input("Entrez le numéro de la table à afficher: ")) | |
| if bydna == True: | |
| print('\n') | |
| print('DNA table: \n') | |
| print(d[table_id]) | |
| print('\n') | |
| return d[table_id] | |
| else: | |
| print('\n') | |
| print('RNA table: \n') | |
| print(r[table_id]) | |
| print('\n') | |
| return r[table_id] | |
| ################################### Représentation des séquences #################################### | |
| def show(seq, start = 0, stop = None, width = None, peprep = 3): | |
| """Affiche une représentation d'une séquence nucléotique ou peptidique avec regle graduée | |
| Les arguments start et stop doivent être des entiers (int); width doit être un multiple de 10. Par defaut, la representation des seq peptidique (peprep) et de 3 lettres/aa""" | |
| if testalpha(seq) == generic_nucleotide or testalpha(seq) == IUPAC.unambiguous_dna or testalpha(seq) == IUPAC.unambiguous_rna: | |
| return shown(seq, start=start, stop=stop, width=width) | |
| #elif testalpha(seq) == IUPAC.protein or testalpha(seq) == IUPAC.extended_protein: | |
| elif langage(seq) == 1 or langage(seq) == 3: | |
| if peprep == 3 and langage(seq) == 1: | |
| seq = toSeq(seq) | |
| seq = seq3(seq) | |
| return showp(seq, start=start, stop=stop, width=width) | |
| def shown(seq, start = 0, stop = None, width = None): | |
| """Présentation d'une séquence nucléotique découpée en tronçons avec regle graduée. | |
| Les arguments start et stop doivent être des entiers (int); width doit être un multiple de 10. | |
| """ | |
| seq = toSeq(seq) | |
| seq = str(seq) | |
| if width == None: | |
| width = 60 | |
| if width/10 != int(width/10): | |
| raise ValueError("Le parametre width doit être un multiple de 10") | |
| if stop == None: | |
| stop = len(seq) | |
| elif type(stop) == type(int()): | |
| pass | |
| else: | |
| raise ValueError("Le parametre stop doit être un entier") | |
| if type(start) != type(int()): | |
| raise ValueError("Le parametre start doit être un entier") | |
| d = start | |
| f = d + width | |
| txt = '' | |
| tir = "----:----|" * int(width/10) | |
| while d <= stop: | |
| if f > stop: | |
| tir = tir[:len(seq[d:stop])] | |
| num = " " | |
| for k in np.arange(10, width + 10, 10): | |
| num = num + """{0}{1}""".format(" " if k == 10 else " " if d+k <= 100 else " " if d+k <= 1000 else " ", d + k if d+k <= stop else "") | |
| if f <= stop: | |
| txt = txt + " " + seq[d:f] + "\n" + " " + tir + "\n" + num + "\n\n" | |
| else: | |
| txt = txt + " " + seq[d:stop] + "\n" + " " + tir + "\n" + num + "\n\n" | |
| d, f = d + width, f + width | |
| print("\n") | |
| print(txt) | |
| print('\n') | |
| def showp(seq, start = 0, stop = None, width = None): | |
| """Présentation d'une séquence peptidique découpée en tronçons avec regle graduée. | |
| Les arguments start et stop doivent être des entiers (int); width doit être un multiple de 10. | |
| """ | |
| seq = toSeq(seq) | |
| seq = str(seq) | |
| if langage(seq) == 1: | |
| if width == None: | |
| width = 60 | |
| if width/10 != int(width/10): | |
| raise ValueError("Le parametre width doit être un multiple de 10") | |
| if stop == None: | |
| stop = len(seq) | |
| elif type(stop) == type(int()): | |
| pass | |
| else: | |
| raise ValueError("Le parametre stop doit être un entier") | |
| if type(start) != type(int()): | |
| raise ValueError("Le parametre start doit être un entier") | |
| d = start | |
| f = d + width | |
| txt = '' | |
| tir = "----:----|" * int(width/10) | |
| while d <= stop: | |
| if f > stop: | |
| tir = tir[:len(seq[d:stop])] | |
| num = " " | |
| for k in np.arange(10, width + 10, 10): | |
| num = num + """{0}{1}""".format(" " if k == 10 else " " if d+k <= 100 else " " , d + k if d+k <= stop else "") | |
| if f <= stop: | |
| txt = txt + " " + seq[d:f] + "\n" + " " + tir + "\n" + num + "\n\n" | |
| else: | |
| txt = txt + " " + seq[d:stop] + "\n" + " " + tir + "\n" + num + "\n\n" | |
| d, f = d + width, f + width | |
| print("\n") | |
| print(txt) | |
| print('\n') | |
| elif langage(seq) == 3: | |
| if width == None: | |
| width = 30 | |
| if width/10 != int(width/10): | |
| raise ValueError("Le le parametre width doit être un multiple de 10") | |
| if stop == None: | |
| stop = int(len(seq)/3) | |
| elif type(stop) == type(int()): | |
| pass | |
| else: | |
| raise ValueError("Le parametre stop doit être un entier") | |
| if type(start) != type(int()): | |
| raise ValueError("Le parametre start doit être un entier") | |
| d = start | |
| f = d + width | |
| txt = '' | |
| #decoupage de la séquence | |
| w = 0 | |
| h = 3 | |
| z = [] | |
| while h <= len(seq): | |
| z.append(seq[w:h]) | |
| w, h = h, h + 3 | |
| tir = " - - - - : - - - - | " * int(width/10) | |
| while d <= stop: | |
| if f > stop: | |
| tir = tir[:len(''.join(z[d:stop]))] | |
| num = " " | |
| for k in np.arange(10, width + 10, 10): | |
| num = num + """{0}{1}""".format(" " if d+k <= 100 or k == 10 else " ", d + k if d+k <= stop else "") | |
| if f <= stop: | |
| txt = txt + " " + ''.join(z[d:f]) + "\n" + " " + tir + "\n" + num + "\n\n" | |
| else: | |
| txt = txt + " " + ''.join(z[d:stop]) + "\n" + " " + tir + "\n" + num + "\n\n" | |
| d, f = d + width, f + width | |
| print("\n") | |
| print(txt) | |
| print('\n') | |
| ############################### Représentation des alignements de séquences ############################ | |
| def reademboss(path = 'emb.aln'): | |
| """Cette fonction affiche le contenu de l'alignement emboss contenu dans le fichier à l'adresse "path", et return un objet align""" | |
| try: | |
| align = AlignIO.read(path, "emboss") | |
| except: | |
| return None | |
| else: | |
| with open(path, 'rb') as nd: | |
| x = nd.read() | |
| x = x.decode() | |
| print("\n", x) | |
| return align | |
| def showanag(align, start = 0, stop = None, width = 60): | |
| """Présentation d'un alignement nucléotique découpé en tronçons avec regle graduée , format anagene-like. | |
| start et stop doivent être des entiers; width doit être un multiple de 10 | |
| argument positionnel: | |
| align: l'alignement (str) retourné par la fonction AlignIO.read de biopython: exemple : align = AlignIO.read(path, "clustal") | |
| arguments facultatifs: | |
| start: (int) index du premier nucléotide représenté | |
| stop: (int) index du dernier nucléotide représenté | |
| width: (int) largeur des lignes; width doit être un multiple de 10 | |
| Legendes: _ deletion, - similitude avec la référence, * similitude | |
| """ | |
| s = list(align) | |
| #sequence de reférence | |
| sa = str(s[0].seq) | |
| #autres sequence de l'alignement | |
| ss = [str(i.seq) for i in s[1:]] | |
| #verification des options | |
| if width/10 != int(width/10): | |
| raise ValueError("Le parametre width doit être un multiple de 10") | |
| if stop == None: | |
| stop = len(sa) | |
| elif type(stop) == type(int()) and stop <= len(sa): | |
| pass | |
| else: | |
| raise ValueError("Le parametre stop doit être un entier inférieur ou égal à la longueur de l'alignement") | |
| if type(start) != type(int()): | |
| raise ValueError("Le parametre start doit être un entier") | |
| #modification de sa: remplacement des - par des _ | |
| san = "" | |
| for i in sa: | |
| if i == '-': san += "_" | |
| else: san += i | |
| #modif des sequences secondaires de ss, création des symboles * | |
| sss = [] | |
| for sb in ss: | |
| sbn = "" | |
| for a , b in zip(sa,sb): | |
| if b == "-": | |
| sbn += "_" | |
| elif b == a: | |
| sbn += "-" | |
| elif b != a: | |
| sbn += b | |
| sss.append(sbn) | |
| #récupération des noms des séquences | |
| #nom de la séquence de référence | |
| nsa = str(s[0].id) | |
| #liste des noms des autres séquences de l'alignement | |
| nnn = [str(i.id) for i in s[1:]] | |
| #recherche du nom de séquence le plus long et gestion des espaces entre nom et séquences | |
| lt = nnn | |
| lt.append(nsa) | |
| ltlen = [] | |
| for i in lt: | |
| ltlen.append(len(i)) | |
| lnm = max(ltlen) + 8 #c'est la longueur du nom le plus long plus 8 espaces | |
| #gestion des symboles * de similitudes | |
| symb = "" | |
| for i , j in enumerate(sa): | |
| count = 0 | |
| for k in ss: | |
| if j != k[i]: count += 1 | |
| if count == 0: symb += "*" | |
| else: symb += " " | |
| #gestion des tirets | |
| tir = "----:----|" * int(width/10) | |
| d = start | |
| f = d + width | |
| #initiation de la str devant contenir la repsrésentation de l'alignement | |
| txt = "" | |
| while d <= stop: | |
| if f > stop: | |
| tir = tir[:len(sa[d:stop])] | |
| num = " "*lnm | |
| for k in np.arange(10, width + 10, 10): | |
| num = num + """{0}{1}""".format(" " if k == 10 else " " if d+k <= 100 else " " if d+k <= 1000 else " " , d + k if d+k <= stop else "") | |
| if f <= stop: | |
| txt = txt + " "*lnm + symb[d:f] + "\n" | |
| txt = txt + nsa + " "*(lnm - len(nsa)) + san[d:f] + "\n" | |
| for nsb, sbn in zip(nnn,sss): | |
| txt = txt + nsb + " "*(lnm - len(nsb)) + sbn[d:f] + "\n" | |
| txt = txt + "\n" | |
| txt = txt + " "*lnm + tir + "\n" + num + "\n\n\n\n" | |
| else: | |
| txt = txt + " "*lnm + symb[d:stop] + "\n" | |
| txt = txt + nsa + " "*(lnm - len(nsa)) + san[d:stop] + "\n" | |
| for nsb, sbn in zip(nnn,sss): | |
| txt = txt + nsb + " "*(lnm - len(nsb)) + sbn[d:stop] + "\n" | |
| txt = txt + "\n" | |
| txt = txt + " "*lnm + tir + "\n" + num + "\n\n\n\n" | |
| d, f = d + width, f + width | |
| print("\n") | |
| print(txt) | |
| print('\n') | |
| def showgenix(align, start = 0, stop = None, width = 60, aaa = True): | |
| """Présentation d'un alignement nucléotique ou peptidique à une lettre découpé en tronçons avec regle graduée. | |
| start et stop doivent être des entiers; width doit être un multiple de 10 | |
| argument positionnel: | |
| align: l'alignement (str) retourné par la fonction AlignIO.read de biopython: exemple : align = AlignIO.read(path, "clustal") | |
| arguments facultatifs: | |
| start: (int) index du premier nucléotide représenté | |
| stop: (int) index du dernier nucléotide représenté | |
| width: (int) largeur des lignes; width doit être un multiple de 10 | |
| text: (str) description | |
| aaa: (bool) acides aminés à 3 lettres si True | |
| Legendes: - deletion, * similitude | |
| """ | |
| s = list(align) | |
| #sequence de reférence | |
| sa = str(s[0].seq) | |
| #autres sequence de l'alignement | |
| ss = [str(i.seq) for i in s[1:]] | |
| if aaa == True and (testalpha(sa) == IUPAC.protein or testalpha(sa) == IUPAC.extended_protein): | |
| return showgenixp3(align, start=start, stop=stop, stdout=stdout, text=text) | |
| #verification des options | |
| if width/10 != int(width/10): | |
| raise ValueError("Le parametre width doit être un multiple de 10") | |
| if stop == None: | |
| stop = len(sa) | |
| elif type(stop) == type(int()) and stop <= len(sa): | |
| pass | |
| else: | |
| raise ValueError("Le parametre stop doit être un entier inférieur ou égal à la longueur de l'alignement") | |
| if type(start) != type(int()): | |
| raise ValueError("Le parametre start doit être un entier") | |
| #récupération des noms des séquences | |
| #nom de la séquence de référence | |
| nsa = str(s[0].id) | |
| #liste des noms des autres séquences de l'alignement | |
| nnn = [str(i.id) for i in s[1:]] | |
| #recherche du nom de séquence le plus long et gestion des espaces entre nom et séquences | |
| lt = nnn | |
| lt.append(nsa) | |
| ltlen = [] | |
| for i in lt: | |
| ltlen.append(len(i)) | |
| lnm = max(ltlen) + 8 #c'est la longueur du nom le plus long plus 8 espaces | |
| #gestion des symboles * de similitudes | |
| #symb = align._star_info ca ne marche pas toujours !!! | |
| symb = "" | |
| for i , j in enumerate(sa): | |
| count = 0 | |
| for k in ss: | |
| if j != k[i]: count += 1 | |
| if count == 0: symb += "*" | |
| else: symb += " " | |
| #gestion des tirets | |
| tir = "----:----|" * int(width/10) | |
| #initiation des intervals | |
| d = start | |
| f = d + width | |
| #initiation de la str devant contenir la repsrésentation de l'alignement | |
| txt = "" | |
| while d <= stop: | |
| if f > stop: | |
| tir = tir[:len(sa[d:stop])] | |
| num = " "*lnm | |
| for k in np.arange(10, width + 10, 10): | |
| num = num + """{0}{1}""".format(" " if k == 10 else " " if d+k <= 100 else " " if d+k <= 1000 else " ", d + k if d+k <= stop else "") | |
| if f <= stop: | |
| txt = txt + " "*lnm + symb[d:f] + "\n" | |
| txt = txt + nsa + " "*(lnm - len(nsa)) + sa[d:f] + "\n" | |
| for nsb, sb in zip(nnn,ss): | |
| txt = txt + nsb + " "*(lnm - len(nsb)) + sb[d:f] + "\n" | |
| txt = txt + "\n" | |
| txt = txt + " "*lnm + tir + "\n" + num + "\n\n\n\n" | |
| else: | |
| txt = txt + " "*lnm + symb[d:stop] + "\n" | |
| txt = txt + nsa + " "*(lnm - len(nsa)) + sa[d:stop] + "\n" | |
| for nsb, sb in zip(nnn,ss): | |
| txt = txt + nsb + " "*(lnm - len(nsb)) + sb[d:stop] + "\n" | |
| txt = txt + "\n" | |
| txt = txt + " "*lnm + tir + "\n" + num + "\n\n\n\n" | |
| d, f = d + width, f + width | |
| print("\n") | |
| print(txt) | |
| print('\n') | |
| def treeliste(s): | |
| """Conversion d'une str representant une seq petpitidique à 3 lettres : MetValGlu en une liste d'acides aminés ['Met','Val','Glu']""" | |
| d = 0 | |
| f = 3 | |
| tl = [] | |
| while f <= len(s): | |
| tl.append(s[d:f]) | |
| d , f = f , f+3 | |
| return tl | |
| def showgenixp3(align, start = 0, stop = None, width = 30): | |
| """Présentation d'un alignement peptidique avec langage 3 lettres decoupé en tronçons avec regle graduée. | |
| start et stop doivent être des entiers; width doit être un multiple de 10 | |
| argument positionnel: | |
| align: l'alignement (str) retourné par la fonction AlignIO.read de biopython: exemple : align = AlignIO.read(path, "clustal") | |
| arguments facultatifs: | |
| start: (int) index du premier nucléotide représenté | |
| stop: (int) index du dernier nucléotide représenté | |
| width: (int) largeur des lignes; width doit être un multiple de 10 | |
| Legendes: - deletion, * similitude | |
| """ | |
| s = list(align) | |
| #sequence de reférence | |
| sa = str(s[0].seq) | |
| #autres sequences de l'alignement | |
| ss = [str(i.seq) for i in s[1:]] | |
| #verification des options | |
| if width/10 != int(width/10): | |
| raise ValueError("Le parametre width doit être un multiple de 10") | |
| if stop == None: | |
| stop = len(sa) | |
| elif type(stop) == type(int()) and stop <= len(sa): | |
| pass | |
| else: | |
| raise ValueError("Le parametre stop doit être un entier inférieur ou égal à la longueur de l'alignement") | |
| if type(start) != type(int()): | |
| raise ValueError("Le parametre start doit être un entier") | |
| #passage au langage a 3 lettres | |
| sa = seq3(sa, custom_map={"*": ""}, undef_code=' - ') | |
| #transformation en list d'acides amines ['Met','Val','Glu'] | |
| sa = treeliste(sa) | |
| #les deletions notées - dans l'alignement sont converties par seq3 en Xaa , il faut donc les corriger.... inutile avec undef_code=' - ' | |
| #for i, j in enumerate(sa): | |
| #if j == 'Xaa': | |
| #sa[i] = ' - ' | |
| #creation d'une liste de liste des autres sequences avec langage a 3 lettres | |
| sss = [] | |
| for i in ss: | |
| j = seq3(i, custom_map={"*": ""}, undef_code=' - ') | |
| ssl = treeliste(j) | |
| #for i, j in enumerate(ssl): | |
| #if j == 'Xaa': | |
| #ssl[i] = ' - ' | |
| sss.append(ssl) | |
| ss = sss | |
| #gestion des tirets | |
| tir = " - - - - : - - - - | " * int(width/10) | |
| #récupération des noms des séquences | |
| #nom de la séquence de référence | |
| nsa = str(s[0].id) | |
| #liste des noms des autres séquences de l'alignement | |
| nnn = [str(i.id) for i in s[1:]] | |
| #recherche du nom de séquence le plus long et gestion des espaces entre nom et séquences | |
| lt = nnn | |
| lt.append(nsa) | |
| ltlen = [] | |
| for i in lt: | |
| ltlen.append(len(i)) | |
| lnm = max(ltlen) + 8 #c'est la longueur du nom le plus long plus 8 espaces | |
| #gestion des symboles * de similitudes | |
| symb = "" | |
| for i , j in enumerate(sa): | |
| count = 0 | |
| for k in ss: | |
| if j != k[i]: | |
| count += 1 | |
| if count == 0: | |
| symb += " * " | |
| else: | |
| symb += " " | |
| symb = treeliste(symb) | |
| #initiation des intervals | |
| d = start | |
| f = d + width | |
| #initiation de la str devant contenir la repsrésentation de l'alignement | |
| txt = "" | |
| while d <= stop: | |
| if f > stop: | |
| tir = tir[:len(''.join(sa[d:stop]))] | |
| num = " "*lnm | |
| for k in np.arange(10, width + 10, 10): | |
| num = num + """{0}{1}""".format(" " if k == 10 else " " if d+k <= 100 else " " , d + k if d+k <= stop else "") | |
| if f <= stop: | |
| txt = txt + " "*lnm + "".join(symb[d:f]) + "\n" | |
| txt = txt + nsa + " "*(lnm - len(nsa)) + "".join(sa[d:f]) + "\n" | |
| for nsb, sb in zip(nnn,ss): | |
| txt = txt + nsb + " "*(lnm - len(nsb)) + "".join(sb[d:f]) + "\n" | |
| txt = txt + "\n" | |
| txt = txt + " "*lnm + tir + "\n" + num + "\n\n\n\n" | |
| else: | |
| txt = txt + " "*lnm + "".join(symb[d:stop]) + "\n" | |
| txt = txt + nsa + " "*(lnm - len(nsa)) + "".join(sa[d:stop]) + "\n" | |
| for nsb, sb in zip(nnn,ss): | |
| txt = txt + nsb + " "*(lnm - len(nsb)) + "".join(sb[d:stop]) + "\n" | |
| txt = txt + "\n" | |
| txt = txt + " "*lnm + tir + "\n" + num + "\n\n\n\n" | |
| d, f = d + width, f + width | |
| print("\n") | |
| print(txt) | |
| print('\n') | |
| ####################################### Outils de conversions edi / fasta ########################### | |
| def supraccent(x): | |
| y = unicodedata.normalize('NFKD', x).encode('ascii', 'ignore') | |
| return y.decode() | |
| def edi2fasta(path_edi, path_fas): | |
| """ | |
| Transforme un fichier edi en fasta | |
| Arguments positionnels: | |
| path_edi: path du fichier en .edi | |
| path_fasta: path du fichier en .fas | |
| les extensions sont obligatoires !!! | |
| """ | |
| #verification des extensions | |
| if path_edi[len(path_edi)-4:] != '.edi': | |
| raise ValueError('path_edi recquiert .edi extension !!') | |
| if path_fas[len(path_fas)-4:] != '.fas': | |
| #raise ValueError('path_fas recquiert .fas extension !!') | |
| path_fas += ".fas" | |
| #ouverture et lecture du fichier .edi | |
| with open(path_edi,'r', encoding='ISO-8859-1') as f: | |
| r = f.read() | |
| r = supraccent(r) | |
| #decoupage en liste de sequences (si plusieurs sequences dans le edi) | |
| l = r.split(';-') | |
| list_fasta = [] | |
| for i in l[:len(l)-1]: | |
| #decoupage de la sequence | |
| a = i.split('\n;') | |
| try: | |
| name = a[1][1:] #recuperation du nom de la sequence et suppression de l'espace | |
| if '-' in name: | |
| name = name.replace('-', '_') | |
| if len(a) == 6: | |
| des = a[4][1:] #description de la sequence | |
| elif len(a) > 6: | |
| des = a[4:len(a)-1] | |
| des = "".join(des) | |
| else: | |
| des = '' | |
| seq = a[len(a)-1] #la seq est en dernier dans la liste | |
| name = name.replace(" ","_") | |
| except IndexError: | |
| return 'Le fichier .edi presente une structure anormale' | |
| else: | |
| #suppression des espaces et des \n de la sequence | |
| y = list(seq) #conversion str to list | |
| while ' ' in y: | |
| y.remove(' ') | |
| while '\n' in y: | |
| y.remove('\n') | |
| seq = "".join(y) #reconversion list to str | |
| fas = SeqRecord(Seq(seq), id = name , description=des) | |
| list_fasta.append(fas) | |
| #ecriture du fasta | |
| SeqIO.write(list_fasta, path_fas, "fasta") | |
| ############################################################################################################################ | |
| def search(*keys, path = os.getcwd()): | |
| """ | |
| Recherche tous les fasta ayant dans leur nom l'un des mots clé recherché dans le repertoire et sous repertoires du path | |
| arguments: | |
| path: str : path du repertoire de recherche | |
| keys: str : mots clés de recherche | |
| return: | |
| une liste des SeqRecord | |
| """ | |
| lso = [] | |
| #si path ne se termine pas par un / on le rajoute de maniière à obtenir des path complets depuis la racine | |
| if path[len(path)-1] != '/': | |
| path = path + '/' | |
| #si la tuple de mot_cle est vide, on créé le mot_cle '' qui correspond à n'importe quel caractère | |
| if keys == (): | |
| keys = ('',) | |
| for i in recurliste(path): | |
| name = i.split('/') | |
| name = name[len(name)-1] | |
| if os.path.isfile(i) and '.fas' in name: | |
| for j in keys: | |
| if j in name: | |
| so = list(SeqIO.parse(i, "fasta")) | |
| lso = lso + so | |
| break | |
| #elimination des doublons | |
| list_SeqRec_open = [] | |
| list_name =[] | |
| for i in lso: | |
| if i.name not in list_name: | |
| list_SeqRec_open.append(i) | |
| list_name.append(i.name) | |
| return list_SeqRec_open | |
| def creat(seq_name, seq, des = "", out = False): | |
| """Création d'un SeqRecord et d'un fasta mono séquence | |
| arguments: | |
| seq_name: (str) nom de la séquence créée | |
| seq: (str) ou (Seq) ou (SeqRecord) la séquence | |
| des: (str) déscription du SeqRecord | |
| out (bool): si False (defaut) , le fasta n'est pas créé | |
| retrun: | |
| le SeqRecord créé | |
| """ | |
| seq = toSeq(seq) | |
| #creation d'un objet SeqRecord | |
| try: | |
| f = SeqRecord(seq, id= seq_name, name = seq_name, description = des) | |
| x = f.format('fasta') | |
| except: | |
| return None | |
| if out == True: | |
| SeqIO.write(f, os.getcwd() + '/' + f.name + '.fas', "fasta") | |
| return f | |
| def mkfas(*seq, path = os.getcwd()): | |
| """ | |
| Cette fonction permet de créer des fasta mono séquence | |
| arguments: | |
| *seq: str: liste de SeqRecord | |
| path: str: repertoire (absolu) dans lequel seront enregistrés les fasta créés. Le path par defaut est le repertoire courant | |
| return: | |
| cette fonction ne return rien, elle crée des fichiers fasta mono séquence | |
| les fichiers sont enregistrés dans le path indiqué en argument, ils portent leur nom de sequence en . fas | |
| """ | |
| if os.path.isdir(path): | |
| if path[len(path)-1] != '/': | |
| path = path + '/' | |
| for i in seq: | |
| try: | |
| SeqIO.write(i, path + i.name + '.fas', "fasta") | |
| except: | |
| pass | |
| def mkfasx(out, *seq): | |
| """ | |
| Création de fasta multi séquence | |
| arguments: | |
| *seq: liste de SeqRecord | |
| out: str: path absolu du fasta mutli séquence qui sera créé | |
| return: | |
| cette fonction ne retourne rien, elle crée un fichier fasta multi séquences contenant chaque séquence passée en argument | |
| """ | |
| SeqIO.write(seq, out, "fasta") | |
| def clustal(*seq, out = 'comp.aln', std = False): | |
| """ | |
| Alginement multiple ClustalW | |
| arguments: | |
| seq: liste des SeqRecord à aligner | |
| out: path du fichier contenant l'alignement créé | |
| std: bool, si True, return align et stdout , defaut = False | |
| Return: l'objet align | |
| """ | |
| mkfasx(out, *seq) | |
| cline = ClustalwCommandline("clustalw", infile=out, score='PERCENT') | |
| stdout, Stderr = cline() | |
| align = AlignIO.read(out, "clustal") | |
| if std == True: | |
| return align, stdout | |
| return align | |
| def phylo(*seq, out = "comp.aln"): | |
| """ | |
| Méthode simplifiée de création d'un arbre phylogenetique à partir d'une liste de SeqRecord | |
| argument: *seq: liste de SeqRecord à aligner | |
| return: (align, stdout, tree) | |
| """ | |
| align, stdout = clustal(*seq, out = out, std = True) | |
| mpd =matrix(align, frame = True) | |
| print('\n') | |
| print(mpd) | |
| tree = Phylo.read("comp.dnd", "newick") | |
| print('\n') | |
| draw_tree(tree) | |
| return (align, stdout, tree) | |
| def tree_nj(dm): | |
| """ | |
| Création d'un arbre nj à partir de la matrice de distance retournée par la fonction matrix | |
| return: tree | |
| """ | |
| cst = DistanceTreeConstructor() | |
| tree = cst.nj(dm) | |
| draw_tree(tree) | |
| return tree | |
| def tree_upgma(dm): | |
| """ | |
| Création d'un arbre upgma à partir de la matrice de distance retournée par la fonction matrix | |
| return: tree | |
| """ | |
| cst = DistanceTreeConstructor() | |
| tree = cst.upgma(dm) | |
| draw_tree(tree) | |
| return tree | |
| def parsimony_tree(align, starting_tree): | |
| """ | |
| Création d'un arbre basé sur la methode de parcimonie à partir de l'alignement et d'un arbre de départ (nj) | |
| return: tree | |
| """ | |
| scorer = ParsimonyScorer() #possibilité de passer une matrice de parsimony en argument ?!? | |
| searcher = NNITreeSearcher(scorer) | |
| cst = ParsimonyTreeConstructor(searcher, starting_tree) | |
| tree = cst.build_tree(align) | |
| draw_tree(tree) | |
| return tree | |
| def draw_tree(tree, distance=False): | |
| if "DISPLAY" not in os.environ: | |
| print('\n') | |
| Phylo.draw_ascii(tree) | |
| print('\n') | |
| if distance == True: | |
| Phylo.draw(tree, branch_labels=lambda c: c.branch_length) | |
| elif distance == False: | |
| Phylo.draw(tree) | |
| def needle(*seq, gapopen=10, gapextend=0.5, out ='emb.aln'): | |
| """Alignement global par la methode de Needleman | |
| arguments: | |
| seq: couple de 2 SeqRecord à aligner | |
| gapopen: pénalité de gap | |
| gapextend: pénalité d'expansion | |
| out: nom du fichier emboss créé | |
| return: un objet align | |
| """ | |
| mkfasx("seqa.fas", seq[0]) | |
| mkfasx("seqb.fas", *seq[1:]) | |
| needle_cline = NeedleCommandline(asequence='seqa.fas', bsequence='seqb.fas', gapopen=gapopen, gapextend=gapextend, outfile=out) | |
| stdout, stderr = needle_cline() | |
| os.remove('seqa.fas') | |
| os.remove('seqb.fas') | |
| if len(seq) < 3: | |
| align = AlignIO.read(out, "emboss") | |
| return align | |
| def water(*seq, gapopen=10, gapextend=0.5, out ='emb.aln'): | |
| """Alignement global par la methode de Needleman | |
| arguments: | |
| seq: couple de 2 SeqRecord à aligner | |
| gapopen: pénalité de gap | |
| gapextend: pénalité d'expansion | |
| out: nom du fichier emboss créé | |
| return: un objet align | |
| """ | |
| mkfasx('seqa.fas', seq[0]) | |
| mkfasx("seqb.fas", *seq[1:]) | |
| water_cline = WaterCommandline(asequence='seqa.fas', bsequence='seqb.fas', gapopen=gapopen, gapextend=gapextend, outfile=out) | |
| stdout, stderr = water_cline() | |
| os.remove('seqa.fas') | |
| os.remove('seqb.fas') | |
| if len(seq) < 3: | |
| align = AlignIO.read(out, "emboss") | |
| return align | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment