def dpam2dpam_strands(dpam,pams): """ Duplicates dpam dataframe to be compatible for searching PAMs on - strand :param dpam: dataframe with pam information :param pams: pams to be used for actual designing of guides. """ dpam=del_Unnamed(dpam) dpam['rPAM']=dpam.apply(lambda x : s2re(x['PAM'],multint2reg) ,axis=1) dpam=set_index(dpam,'PAM') dpam['strand']='+' dpamr=pd.DataFrame(columns=dpam.columns) dpam.loc[:,'reverse complement']=np.nan dpam.loc[:,'original']=np.nan for pam in dpam.index: pamr=reverse_complement_multintseq(pam,nt2complement) dpam.loc[pam,'reverse complement']=pamr dpam.loc[pam,'original']=pam dpamr.loc[pamr,'original']=pam dpam.loc[pam,'original position']=dpam.loc[pam,'position'] dpamr.loc[pamr,'original position']=dpam.loc[pam,'position'] dpamr.loc[pamr,['position','guide length','Description']]=dpam.loc[pam,['position','guide length','Description']] dpamr.loc[pamr,['rPAM']]=reverse_complement_multintseqreg(pam,multint2regcomplement,nt2complement) dpamr['position']= dpamr.apply(lambda x: 'up' if x['position']=='down' else 'down',axis=1) dpamr['strand']='-' dpam_strands=dpam.append(dpamr,sort=True) dpam_strands.index.name='PAM' dpam_strands.loc[:,'is a reverse complement']=pd.isnull(dpam_strands.loc[:,'reverse complement']) pams_strands=pams+dpam_strands.loc[pams,'reverse complement'].dropna().tolist() dpam_strands=dpam_strands.loc[pams_strands,:] return dpam_strands
def get_possible_mutagenesis( dcodontable, dcodonusage, BEs, pos_muts, host, ): """ Assesses possible mutagenesis strategies, given the set of Base editors and positions of mutations. :param dcodontable: Codon table :param dcodonusage: Codon usage table :param BEs: Base editors (dict), see global_vars.py :param pos_muts: positions of mutations :param host: host organism :returns: possible mutagenesis strategies as a pandas dataframe """ def write_dmutagenesis(cdni, posi, codon, codonmut, ntwt, ntmut, aa, aamut, method): """ Write dmutagenesis table for each iteraction in get_possible_mutagenesis. """ dmutagenesis.loc[cdni, 'codon'] = codon dmutagenesis.loc[cdni, 'position of mutation in codon'] = int(posi) dmutagenesis.loc[cdni, 'codon mutation'] = codonmut dmutagenesis.loc[cdni, 'nucleotide'] = ntwt dmutagenesis.loc[cdni, 'nucleotide mutation'] = ntmut dmutagenesis.loc[cdni, 'amino acid'] = aa dmutagenesis.loc[cdni, 'amino acid mutation'] = aamut dmutagenesis.loc[cdni, 'mutation on strand'] = method.split(' on ')[1] dmutagenesis.loc[cdni, 'strand: mutation'] = method.split(' on ')[1].replace( ' strand', '') dmutagenesis.loc[cdni, 'method'] = method.split(' on ')[0] dmutagenesis.loc[cdni, 'codon mutation usage Fraction'] = dcodonusage.loc[ codonmut, 'Fraction'] dmutagenesis.loc[cdni, 'codon mutation usage Frequency'] = dcodonusage.loc[ codonmut, 'Frequency'] return dmutagenesis def get_sm(dmutagenesis, BEs, positions, codon, muti, cdni): """ Fetches single nucleotide mutagenesis strategies. """ for method in BEs: for posi in positions: if BEs[method][0] == codon[posi]: for ntmut in BEs[method][1]: if posi == 0: codonmut = '{}{}{}'.format(ntmut, codon[1], codon[2]) elif posi == 1: codonmut = '{}{}{}'.format(codon[0], ntmut, codon[2]) elif posi == 2: codonmut = '{}{}{}'.format(codon[0], codon[1], ntmut) aamut = str( Seq.Seq(codonmut, Alphabet.generic_dna).translate(table=1)) # if (aamut!='*') and (aamut!=aa): # nonsence and synonymous if muti == 0: cdni = cdni else: cdni = len(dmutagenesis) + 1 muti += 1 ntwt = BEs[method][0] if '-' in method.split(' on ')[1]: ntwt = str( Seq.Seq( ntwt, Alphabet.generic_dna).reverse_complement()) ntmut = str( Seq.Seq( ntmut, Alphabet.generic_dna).reverse_complement()) dmutagenesis = write_dmutagenesis( **{ 'cdni': cdni, 'posi': posi + 1, 'codon': codon, 'codonmut': codonmut, 'ntwt': ntwt, 'ntmut': ntmut, 'aa': aa, 'aamut': aamut, 'method': method }) return dmutagenesis, muti def get_dm(dmutagenesis, BEs, positions_dm, codon, muti, cdni): """ Fetches double nucleotide mutagenesis strategies. """ for method in BEs: for posi1, posi2 in positions_dm: if (BEs[method][0] == codon[posi1]) and (BEs[method][0] == codon[posi2]): for ntmut1, ntmut2 in itertools.product(''.join( BEs[method][1]), repeat=2): if (posi1 == 0) and (posi2 == 1): codonmut = '{}{}{}'.format(ntmut1, ntmut2, codon[2]) elif (posi1 == 1) and (posi2 == 2): codonmut = '{}{}{}'.format(codon[0], ntmut1, ntmut2) elif (posi1 == 0) and (posi2 == 2): codonmut = '{}{}{}'.format(ntmut1, codon[1], ntmut2) aamut = str( Seq.Seq(codonmut, Alphabet.generic_dna).translate(table=1)) # if (aamut!='*') and (aamut!=aa): # nonsence and synonymous if muti == 0: cdni = cdni else: cdni = len(dmutagenesis) + 1 muti += 1 ntwt = '{}{}'.format(BEs[method][0], BEs[method][0]) ntmut = '{}{}'.format(ntmut1, ntmut2) if '-' in method.split(' on ')[1]: ntwt = str( Seq.Seq( ntwt, Alphabet.generic_dna).reverse_complement()) ntmut = str( Seq.Seq( ntmut, Alphabet.generic_dna).reverse_complement()) dmutagenesis = write_dmutagenesis( **{ 'cdni': cdni, 'posi': '{}{}'.format(posi1, posi2), 'codon': codon, 'codonmut': codonmut, 'ntwt': ntwt, 'ntmut': ntmut, 'aa': aa, 'aamut': aamut, 'method': method }) return dmutagenesis, muti def get_tm(dmutagenesis, BEs, positions_tm, codon, muti, cdni): """ Fetches triple nucleotide mutagenesis strategies. """ for method in BEs: for posi1, posi2, posi3 in positions_tm: if (BEs[method][0] == codon[posi1]) and ( BEs[method][0] == codon[posi2]) and (BEs[method][0] == codon[posi3]): for ntmut1, ntmut2, ntmut3 in itertools.product(''.join( BEs[method][1]), repeat=3): codonmut = '{}{}{}'.format(ntmut1, ntmut2, ntmut3) aamut = str( Seq.Seq(codonmut, Alphabet.generic_dna).translate(table=1)) # if (aamut!='*') and (aamut!=aa): # nonsence and synonymous if muti == 0: cdni = cdni else: cdni = len(dmutagenesis) + 1 muti += 1 ntwt = '{}{}{}'.format(BEs[method][0], BEs[method][0], BEs[method][0]) ntmut = '{}{}{}'.format(ntmut1, ntmut2, ntmut3) if '-' in method.split(' on ')[1]: ntwt = str( Seq.Seq( ntwt, Alphabet.generic_dna).reverse_complement()) ntmut = str( Seq.Seq( ntmut, Alphabet.generic_dna).reverse_complement()) dmutagenesis = write_dmutagenesis( **{ 'cdni': cdni, 'posi': '123', 'codon': codon, 'codonmut': codonmut, 'ntwt': ntwt, 'ntmut': ntmut, 'aa': aa, 'aamut': aamut, 'method': method }) return dmutagenesis, muti def get_dm_combo(dmutagenesis, BEs, positions_dm, codon, muti, cdni, method): """ Fetches double nucleotide mutagenesis strategies utilising 2 different base editors simultaneously. """ methods = [ m for m in itertools.product(BEs.keys(), repeat=2) if (( m[0].split('on')[1] == m[1].split('on')[1])) and (m[0] != m[1]) ] for method1, method2 in methods: for posi1, posi2 in positions_dm: if (BEs[method1][0] == codon[posi1]) and (BEs[method2][0] == codon[posi2]): ntmuts = [(n1, n2) for n1 in ''.join(BEs[method1][1]) for n2 in ''.join(BEs[method2][1])] for ntmut1, ntmut2 in ntmuts: if (posi1 == 0) and (posi2 == 1): codonmut = '{}{}{}'.format(ntmut1, ntmut2, codon[2]) elif (posi1 == 1) and (posi2 == 2): codonmut = '{}{}{}'.format(codon[0], ntmut1, ntmut2) elif (posi1 == 0) and (posi2 == 2): codonmut = '{}{}{}'.format(ntmut1, codon[1], ntmut2) aamut = str( Seq.Seq(codonmut, Alphabet.generic_dna).translate(table=1)) # if (aamut!='*') and (aamut!=aa): # nonsence and synonymous if muti == 0: cdni = cdni else: cdni = len(dmutagenesis) + 1 muti += 1 ntwt = '{}{}'.format(BEs[method1][0], BEs[method2][0]) ntmut = '{}{}'.format(ntmut1, ntmut2) if '-' in method1.split(' on ')[1]: ntwt = str( Seq.Seq( ntwt, Alphabet.generic_dna).reverse_complement()) ntmut = str( Seq.Seq( ntmut, Alphabet.generic_dna).reverse_complement()) dmutagenesis = write_dmutagenesis( **{ 'cdni': cdni, 'posi': '{}{}'.format(posi1, posi2), 'codon': codon, 'codonmut': codonmut, 'ntwt': ntwt, 'ntmut': ntmut, 'aa': aa, 'aamut': aamut, 'method': method + ' on ' + method1.split('on')[1] }) return dmutagenesis, muti def get_tm_combo(dmutagenesis, BEs, positions_tm, codon, muti, cdni, method): """ Fetches triple nucleotide mutagenesis strategies utilising 2 different base editors simultaneously. """ methods = [ m for m in itertools.product(BEs.keys(), repeat=3) if ((m[0].split('on')[1] == m[1].split('on')[1] == m[2].split('on') [1])) and not (m[0] == m[1] == m[2]) ] for method1, method2, method3 in methods: for posi1, posi2, posi3 in positions_tm: if (BEs[method1][0] == codon[posi1]) and ( BEs[method2][0] == codon[posi2]) and (BEs[method3][0] == codon[posi3]): ntmuts = [(n1, n2, n3) for n1 in ''.join(BEs[method1][1]) for n2 in ''.join(BEs[method2][1]) for n3 in ''.join(BEs[method3][1])] for ntmut1, ntmut2, ntmut3 in ntmuts: codonmut = '{}{}{}'.format(ntmut1, ntmut2, ntmut3) aamut = str( Seq.Seq(codonmut, Alphabet.generic_dna).translate(table=1)) # if (aamut!='*') and (aamut!=aa): # nonsence and synonymous if muti == 0: cdni = cdni else: cdni = len(dmutagenesis) + 1 muti += 1 ntwt = '{}{}{}'.format(BEs[method1][0], BEs[method2][0], BEs[method3][0]) ntmut = '{}{}{}'.format(ntmut1, ntmut2, ntmut3) if '-' in method1.split(' on ')[1]: ntwt = str( Seq.Seq( ntwt, Alphabet.generic_dna).reverse_complement()) ntmut = str( Seq.Seq( ntmut, Alphabet.generic_dna).reverse_complement()) dmutagenesis = write_dmutagenesis( **{ 'cdni': cdni, 'posi': '123', 'codon': codon, 'codonmut': codonmut, 'ntwt': ntwt, 'ntmut': ntmut, 'aa': aa, 'aamut': aamut, 'method': method + ' on ' + method1.split('on')[1] }) return dmutagenesis, muti #double nucleotide mutations positions = {0: '@1st position', 1: '@2nd position', 2: '@3rd position'} #double nucleotide mutations positions_dm = [(i, j) for i in positions.keys() for j in positions.keys() if i < j] #double nucleotide mutations positions_tm = [[0, 1, 2]] dmutagenesis = dcodontable.copy() # test=True test = False for cdni in dmutagenesis.index: codon = dmutagenesis.loc[cdni, 'codon'] aa = dmutagenesis.loc[cdni, 'amino acid'] muti = 0 if test: print(codon) #single nucleuotide mutations dmutagenesis, muti = get_sm(dmutagenesis, BEs, positions, codon, muti, cdni) #double nucleotide mutations dmutagenesis, muti = get_dm(dmutagenesis, BEs, positions_dm, codon, muti, cdni) #triple nucleotide mutations dmutagenesis, muti = get_tm(dmutagenesis, BEs, positions_tm, codon, muti, cdni) # #double nucleotide mutations combinations # dmutagenesis,muti=get_dm_combo(dmutagenesis,BEs,positions_dm,codon,muti,cdni,method='undefined') # #triple nucleotide mutations combinations # dmutagenesis,muti=get_tm_combo(dmutagenesis,BEs,positions_tm,codon,muti,cdni,method='undefined') dmutagenesis['nucleotide mutation: count'] = [ len(s) for s in dmutagenesis['nucleotide mutation'] ] dmutagenesis = dmutagenesis.sort_values('codon') # Adding information of Allowed activity window dmutagenesis = dmutagenesis.set_index('method').join(pos_muts) dmutagenesis = dmutagenesis.reset_index() from beditor.lib.io_seqs import reverse_complement_multintseq from beditor.lib.global_vars import nt2complement dmutagenesis['nucleotide: wild-type'] = dmutagenesis.apply( lambda x: x['nucleotide'] if x['strand: mutation'] == '+' else reverse_complement_multintseq( x['nucleotide'], nt2complement), axis=1) dmutagenesis['nucleotide: mutation'] = dmutagenesis.apply( lambda x: x['nucleotide mutation'] if x['strand: mutation'] == '+' else reverse_complement_multintseq( x['nucleotide mutation'], nt2complement), axis=1) return dmutagenesis
def make_guides(cfg,dseq,dmutagenesis,dpam, test=False, dbug=False): """ Wrapper around submodules that design guides by 1. searching all PAM sequences on 'both' the strands, 2. filtering guides by all possible strategies (given in dmutagenesis) e.g. activity window, Finally generates a table. :param cfg: configuration dict :param dseq: dsequences dataframe :param dmutagenesis: dmutagenesis dataframe :param dpam: dpam dataframe :param test: debug mode on :param dbug: more verbose """ from beditor.lib.io_strs import s2re flankaas=7#FIXME if flank length changes dseq=dseq.reset_index() dseq.index=range(len(dseq)) dpam=set_index(dpam,'PAM') # for gi in trange(len(dseq),desc='designing guides'): gierrfltmutpos=[] gierrdenan=[] gierrfltguidel=[] gierrpamnotfound=[] gierrcannotmutate=[] dseq_cols=['transcript: id','aminoacid: position','aminoacid: wild-type','codon: wild-type','id',] for gi in dseq.index: if cfg['mutations']=='mutations': dseqi=pd.DataFrame(dseq.loc[gi,dseq_cols+['amino acid mutation']]).T dmutagenesis_gi=pd.merge(dseqi, dmutagenesis, how='inner', left_on=['aminoacid: wild-type','codon: wild-type','amino acid mutation'], right_on=['amino acid','codon','amino acid mutation']) else: dseqi=pd.DataFrame(dseq.loc[gi,dseq_cols]).T dmutagenesis_gi=pd.merge(dseqi, dmutagenesis, how='inner', left_on=['aminoacid: wild-type','codon: wild-type'], right_on=['amino acid','codon']) if len(dmutagenesis_gi)!=0: logging.info(f"working on {dseq.loc[gi,'id']}") # codon=dseq.loc[gi,'codon: wild-type'] pos_codon=(flankaas)*3 dpamsearches=get_pam_searches(dpam=dpam, seq=dseq.loc[gi,'transcript: sequence'], pos_codon=pos_codon, test=test) # print(dpamsearches.columns) #RMME if dpamsearches is None: continue if len(dpamsearches)!=0: # filter by guide length dpamsearchesflt=dpamsearches.loc[dpamsearches['guide length']==dpamsearches['guide sequence length'],:] if len(dpamsearchesflt)!=0: dpamsearches_strategy=pd.merge(dpamsearchesflt.reset_index(),dmutagenesis_gi.reset_index(), how='inner', on=['strand'],suffixes=['',': dmutagenesis_gi']) if len(dpamsearches_strategy)!=0: if not 'dguides' in locals(): dguides=dpamsearches_strategy.copy() else: dguides=dguides.append(dpamsearches_strategy) del dpamsearches_strategy else: gierrdenan.append(gi) if dbug: print('empty after removing nan seqs') else: gierrfltguidel.append(gi) if dbug: print(f"empty after filtering by guide length. {dpamsearches['guide sequence length'].tolist()}") else: gierrpamnotfound.append(gi) if dbug: print(f"no pam among {dpam.index.tolist()} were found {dseq.loc[gi,'transcript: sequence']}") else: gierrcannotmutate.append(gi) if dbug: print(f"can not mutate {dseqi['codon: wild-type'].tolist()}. its not in {dmutagenesis['codon'].tolist()}") gierrfltmutpos=[] gierrdenan=[] gierrfltguidel=[] gierrpamnotfound=[] gierrcannotmutate=[] err2idxs={'gierrfltmutpos':gierrfltmutpos, 'gierrdenan':gierrdenan, 'gierrfltguidel':gierrfltguidel, 'gierrpamnotfound':gierrpamnotfound, 'gierrcannotmutate':gierrcannotmutate, } if 'dguides' in locals(): # 0-based indexing 'position of guide ini/end', 'position of PAM ini/end' # 1-based indexing 'position of mutation in codon' logging.info('#reverse complement guides on negative strand sequences') dguides.loc[:,'PAM']=dguides.apply(lambda x : reverse_complement_multintseq(x['PAM'],nt2complement) if x['is a reverse complement'] else x['PAM'],axis=1) for colseq in ['guide+PAM sequence','guide sequence','PAM sequence']: dguides.loc[:,colseq]=dguides.apply(lambda x : str(str2seq(x[colseq]).reverse_complement()) if x['is a reverse complement'] else x[colseq],axis=1) logging.info('get dposition') dpositions=dguides.apply(lambda x: guide2dpositions(x),axis=1).apply(pd.Series) # posmut,posmutfrompam,distmutfrompam,posguideini,posguideend,activity_sequence dpositions.columns=['position of mutation','position of mutation from PAM','distance of mutation from PAM', 'position of guide ini','position of guide end','activity sequence'] # dguides.to_csv('test_dguides.csv',sep='\t') # dpositions.to_csv('test_dposition.csv',sep='\t') for col in dpositions: dguides[col]=dpositions[col] logging.info('filter by # of editable nts in activity seq') logging.info(dguides.shape) dguides_noflt=dguides.copy() dguides=dguides.loc[(dguides.apply(lambda x : np.sum([x['activity sequence'].count(nt) for nt in x['nucleotide']])==len(x['nucleotide']),axis=1)),:] logging.info(dguides.shape) if len(dguides)!=0: # if dbug: # dguides.to_csv('test.tsv',sep='\t') logging.info('#filter by location of mutation within guide') dguides=dguides.loc[dguides.apply(lambda x : True if (x['distance of mutation from PAM: minimum']<=abs(x['distance of mutation from PAM'])<=x['distance of mutation from PAM: maximum']) else False,axis=1),:] if len(dguides)!=0: dguides.loc[:,'strategy']=dguides.apply(lambda x: f"{x['method']};{x['strand']};@{int(x['distance of mutation from PAM'])};{x['PAM']};{x['codon']}:{x['codon mutation']};{x['amino acid']}:{x['amino acid mutation']};",axis=1) dguides.loc[:,'guide: id']=dguides.apply(lambda x: f"{x['id']}|{int(x['aminoacid: position']) if not pd.isnull(x['aminoacid: position']) else 'nucleotide'}|({x['strategy']})",axis=1) dguides.loc[:,'guide+PAM length']=dguides.apply(lambda x: len(x['guide+PAM sequence']),axis=1) dguides=dguides.drop_duplicates(subset=['guide: id']) return dguides,dguides_noflt,err2idxs else: return None,dguides_noflt,None else: return None,dguides_noflt,None else: return None,None,None
def get_possible_mutagenesis( cfg, dcodontable, dcodonusage, BEs, pos_muts, host, ): """ Assesses possible mutagenesis strategies, given the set of Base editors and positions of mutations. :param dcodontable: Codon table :param dcodonusage: Codon usage table :param BEs: Base editors (dict), see global_vars.py :param pos_muts: positions of mutations :param host: host organism :returns: possible mutagenesis strategies as a pandas dataframe """ def write_dmutagenesis(cdni, posi, codon, codonmut, ntwt, ntmut, aa, aamut, method): """ Write dmutagenesis table for each iteraction in get_possible_mutagenesis. """ dmutagenesis.loc[cdni, 'codon'] = codon dmutagenesis.loc[cdni, 'position of mutation in codon'] = int(posi) dmutagenesis.loc[cdni, 'codon mutation'] = codonmut dmutagenesis.loc[cdni, 'nucleotide'] = ntwt dmutagenesis.loc[cdni, 'nucleotide mutation'] = ntmut dmutagenesis.loc[cdni, 'amino acid'] = aa dmutagenesis.loc[cdni, 'amino acid mutation'] = aamut dmutagenesis.loc[cdni, 'mutation on strand'] = method.split(' on ')[1] dmutagenesis.loc[cdni, 'strand: mutation'] = method.split(' on ')[1].replace( ' strand', '') dmutagenesis.loc[cdni, 'method'] = method.split(' on ')[0] dmutagenesis.loc[cdni, 'codon mutation usage Fraction'] = dcodonusage.loc[ codonmut, 'Fraction'] dmutagenesis.loc[cdni, 'codon mutation usage Frequency'] = dcodonusage.loc[ codonmut, 'Frequency'] return dmutagenesis def get_sm(dmutagenesis, BEs, positions, codon, muti, cdni): """ Fetches single nucleotide mutagenesis strategies. """ for method in BEs: for posi in positions: if BEs[method][0] == codon[posi]: for ntmut in BEs[method][1]: if posi == 0: codonmut = '{}{}{}'.format(ntmut, codon[1], codon[2]) elif posi == 1: codonmut = '{}{}{}'.format(codon[0], ntmut, codon[2]) elif posi == 2: codonmut = '{}{}{}'.format(codon[0], codon[1], ntmut) aamut = str( Seq.Seq(codonmut, Alphabet.generic_dna).translate(table=1)) # if (aamut!='*') and (aamut!=aa): # nonsence and synonymous if muti == 0: cdni = cdni else: cdni = len(dmutagenesis) + 1 muti += 1 ntwt = BEs[method][0] if '-' in method.split(' on ')[1]: ntwt = str( Seq.Seq( ntwt, Alphabet.generic_dna).reverse_complement()) ntmut = str( Seq.Seq( ntmut, Alphabet.generic_dna).reverse_complement()) dmutagenesis_row = { 'cdni': cdni, 'posi': posi + 1, 'codon': codon, 'codonmut': codonmut, 'ntwt': ntwt, 'ntmut': ntmut, 'aa': aa, 'aamut': aamut, 'method': method } # print(dmutagenesis_row) dmutagenesis = write_dmutagenesis(**dmutagenesis_row) # else: # logging.warning(f"BEs[{method}][0]!=codon[{posi}]") return dmutagenesis, muti #double nucleotide mutations positions = {0: '@1st position', 1: '@2nd position', 2: '@3rd position'} #double nucleotide mutations positions_dm = [(i, j) for i in positions.keys() for j in positions.keys() if i < j] #double nucleotide mutations positions_tm = [[0, 1, 2]] dmutagenesis = dcodontable.copy() # test=True test = False for cdni in dmutagenesis.index: codon = dmutagenesis.loc[cdni, 'codon'] aa = dmutagenesis.loc[cdni, 'amino acid'] muti = 0 if test: print(codon) #single nucleuotide mutations dmutagenesis, muti = get_sm(dmutagenesis, BEs, positions, codon, muti, cdni) if len(dmutagenesis) == 0: from beditor.lib.global_vars import saveemptytable logging.warning('no guides designed; saving an empty table.') dmutagenesis = saveemptytable(cfg) else: # to_table(dmutagenesis,'test.tsv') #FIXME # print(dmutagenesis.shape) dmutagenesis = dmutagenesis.dropna() #FIXME # print(dmutagenesis.shape) dmutagenesis['nucleotide mutation: count'] = [ len(s) for s in dmutagenesis['nucleotide mutation'] ] dmutagenesis = dmutagenesis.sort_values('codon') # Adding information of Allowed activity window dmutagenesis = dmutagenesis.set_index('method').join(pos_muts) dmutagenesis = dmutagenesis.reset_index() from beditor.lib.io_seqs import reverse_complement_multintseq from beditor.lib.global_vars import nt2complement dmutagenesis['nucleotide: wild-type'] = dmutagenesis.apply( lambda x: x['nucleotide'] if x['strand: mutation'] == '+' else reverse_complement_multintseq( x['nucleotide'], nt2complement), axis=1) dmutagenesis['nucleotide: mutation'] = dmutagenesis.apply( lambda x: x['nucleotide mutation'] if x['strand: mutation'] == '+' else reverse_complement_multintseq( x['nucleotide mutation'], nt2complement), axis=1) return dmutagenesis