1111
1212def split_into_codons (seq : str ) -> list :
1313 """Split the complete CDS feature in to a list of codons"""
14- codons = [seq [i :i + 3 ] for i in range (0 , len (seq ), 3 ) if "N" not in seq [i :i + 3 ]]
14+ codons = [
15+ seq [i :i + 3 ] for i in range (0 , len (seq ), 3 ) if "N" not in seq [i :i + 3 ]
16+ ]
1517 return codons
1618
1719
@@ -29,7 +31,7 @@ def calculate_potential_changes(genetic_code: dict) -> dict:
2931 for codon_p in range (0 , 3 ):
3032 nts = ["A" , "G" , "T" , "C" ]
3133 # Do not consider self substitutions, e.g. A->A
32- nts .remove (codon [codon_p ])
34+ nts .remove (codon [codon_p ])
3335 for nt in nts :
3436 codon_mutated = list (codon )
3537 # Mutate the basepair
@@ -43,49 +45,55 @@ def calculate_potential_changes(genetic_code: dict) -> dict:
4345
4446
4547def get_feature_codons (alignment : Gb2Alignment , annotation : list ) -> dict :
46- dct = {key :alignment .ntSequences (key )[1 ].sequence for key in annotation }
47- return {key :split_into_codons (item ) for key ,item in dct .items ()}
48+ dct = {key : alignment .ntSequences (key )[1 ].sequence for key in annotation }
49+ return {key : split_into_codons (item ) for key , item in dct .items ()}
4850
4951
50- def get_df (codons : dict , genetic_code : dict ) -> pd .DataFrame :
51- keys = []
52+ def calculate_ns_sites (codons : dict , genetic_code : dict ) -> pd .DataFrame :
53+ features = []
5254 N_sites = []
5355 S_sites = []
5456 values = calculate_potential_changes (genetic_code )
5557 for key , item in codons .items ():
56- keys .append (key )
58+ features .append (key )
5759 N = sum ([values ["N" ][x ] for x in item if x in values ["N" ].keys ()])
5860 S = sum ([values ["S" ][x ] for x in item if x in values ["S" ].keys ()])
5961 N_sites .append (N )
60- S_sites .append (S )
61- return pd .DataFrame ({"gene" : keys , "N" : N_sites , "S" : S_sites })
62+ S_sites .append (S )
63+ return pd .DataFrame ({"gene" : features , "N" : N_sites , "S" : S_sites })
6264
6365
6466def main ():
6567
66- logging .basicConfig (filename = snakemake .log [0 ], format = snakemake .config ["LOG_PY_FMT" ], level = logging .INFO )
67-
68+ logging .basicConfig (
69+ filename = snakemake .log [0 ], format = snakemake .config ["LOG_PY_FMT" ],
70+ level = logging .INFO
71+ )
72+
6873 logging .info ("Reading features" )
69- with open (snakemake .input .features ) as f :
70- feature_list = list (json .load (f ).keys ())
74+ feature_list = list (snakemake .params .gb_features .keys ())
7175
7276 logging .info ("Reading genetic code" )
7377 with open (snakemake .input .genetic_code ) as f :
7478 genetic_code = json .load (f )
75-
79+
7680 logging .info ("Create alignment object" )
7781 features = Features (snakemake .input .gb )
78- seq = list (FastaReads (snakemake .input .fasta ))[0 ]
82+ fasta_reads = list (FastaReads (snakemake .input .fasta ))
83+ if len (fasta_reads ) > 1 :
84+ logging .warning (
85+ f"More than one record found in { snakemake .input .fasta } , selecting the first one" )
86+ seq = fasta_reads [0 ]
7987 aln = Gb2Alignment (seq , features )
8088
81- logging .info ("Splitting ancestral sequence into codons" )
82- codons_dict = get_feature_codons (aln , feature_list )
89+ logging .info ("Splitting input sequence into codons" )
90+ codons = get_feature_codons (aln , feature_list )
8391
8492 logging .info ("Calculating synonymous and non synonymous sites" )
85- df = get_df ( codons_dict , genetic_code )
93+ df = calculate_ns_sites ( codons , genetic_code )
8694
8795 logging .info ("Saving results" )
88- df .to_csv (snakemake .output .csv ,index = False )
96+ df .to_csv (snakemake .output .csv , index = False )
8997
9098
9199if __name__ == "__main__" :
0 commit comments