1515 num_glycosylation_sites Count of NxS/T motifs in query (x != Pro)
1616 glycosylation_imgt_positions IMGT positions of N residues in query, comma-separated
1717 glycosylation_motifs Corresponding NxS/T triplets in query, comma-separated
18+ glycosylation_imgt_regions IMGT FWR/CDR region of each query site, comma-separated
1819 num_acquired_glycosylation_sites Sites in query absent from germline (SHM-acquired)
1920 acquired_glycosylation_imgt_positions IMGT positions of acquired sites, comma-separated
21+ acquired_glycosylation_imgt_regions IMGT FWR/CDR region of each acquired site, comma-separated
22+ num_acquired_glycosylation_sites_cdr Acquired sites falling in CDR1/CDR2/CDR3
23+ manntype_ags POS/NEG: >=1 acquired site in a CDR (Tatterton et al. 2025
24+ AGS criterion). This is only the sequence-derived half of
25+ manntype classification; combine with a genomic FL signature
26+ (EZB LymphGen subtype or BCL2 translocation) downstream, as
27+ GAMBLR.results::collate_tatterton does for the published
28+ Tatterton cohort, to get the full manntype call.
2029 germline_aa_sequence Germline amino acid sequence used for numbering
2130 num_germline_glycosylation_sites Count of NxS/T motifs in germline
2231 germline_glycosylation_imgt_positions IMGT positions of N residues in germline
@@ -60,6 +69,27 @@ def _imgt_pos_str(pos_tuple):
6069 return f"{ num } { ins .strip ()} " if ins .strip () else str (num )
6170
6271
72+ # IMGT unique numbering V-domain region boundaries (Lefranc et al.), inclusive.
73+ IMGT_REGION_BOUNDARIES = [
74+ (1 , 26 , "FWR1" ),
75+ (27 , 38 , "CDR1" ),
76+ (39 , 55 , "FWR2" ),
77+ (56 , 65 , "CDR2" ),
78+ (66 , 104 , "FWR3" ),
79+ (105 , 117 , "CDR3" ),
80+ (118 , 128 , "FWR4" ),
81+ ]
82+
83+
84+ def _imgt_region (pos_tuple ):
85+ """Map an ANARCI IMGT position (number, insertion_letter) to its FWR/CDR region."""
86+ num , _ = pos_tuple
87+ for lo , hi , name in IMGT_REGION_BOUNDARIES :
88+ if lo <= num <= hi :
89+ return name
90+ return "NA"
91+
92+
6393def number_with_imgt (aa_seq ):
6494 """
6595 Align aa_seq to IG/TCR germline HMMs with ANARCI using the IMGT scheme.
@@ -75,15 +105,15 @@ def find_glycosylation_sites(numbered):
75105 """
76106 Scan IMGT-numbered residues for N-linked glycosylation motifs (NxS/T, x != Pro).
77107
78- Returns [(imgt_position_string, motif_string), ...] for each site.
108+ Returns [(imgt_position_string, motif_string, region_string ), ...] for each site.
79109 """
80110 sites = []
81111 for i in range (len (numbered ) - 2 ):
82- _ , n_aa = numbered [i ]
83- _ , x_aa = numbered [i + 1 ]
84- _ , st_aa = numbered [i + 2 ]
112+ n_pos , n_aa = numbered [i ]
113+ _ , x_aa = numbered [i + 1 ]
114+ _ , st_aa = numbered [i + 2 ]
85115 if n_aa == "N" and x_aa != "P" and st_aa in ("S" , "T" ):
86- sites .append ((_imgt_pos_str (numbered [ i ][ 0 ] ), f"N{ x_aa } { st_aa } " ))
116+ sites .append ((_imgt_pos_str (n_pos ), f"N{ x_aa } { st_aa } " , _imgt_region ( n_pos ) ))
87117 return sites
88118
89119
@@ -92,8 +122,13 @@ def classify_sites(query_sites, germline_sites):
92122 Return the subset of query_sites whose IMGT position is absent in germline_sites.
93123 These are SHM-acquired glycosylation sites.
94124 """
95- germline_positions = {pos for pos , _ in germline_sites }
96- return [(pos , motif ) for pos , motif in query_sites if pos not in germline_positions ]
125+ germline_positions = {pos for pos , _ , _ in germline_sites }
126+ return [site for site in query_sites if site [0 ] not in germline_positions ]
127+
128+
129+ def sites_in_cdr (sites ):
130+ """Return the subset of sites (as returned by find_glycosylation_sites) in a CDR."""
131+ return [site for site in sites if site [2 ].startswith ("CDR" )]
97132
98133
99134def _make_lookup_fn (seq_lookup ):
@@ -139,8 +174,12 @@ def read_fasta(path):
139174 "num_glycosylation_sites" ,
140175 "glycosylation_imgt_positions" ,
141176 "glycosylation_motifs" ,
177+ "glycosylation_imgt_regions" ,
142178 "num_acquired_glycosylation_sites" ,
143179 "acquired_glycosylation_imgt_positions" ,
180+ "acquired_glycosylation_imgt_regions" ,
181+ "num_acquired_glycosylation_sites_cdr" ,
182+ "manntype_ags" ,
144183 "germline_aa_sequence" ,
145184 "num_germline_glycosylation_sites" ,
146185 "germline_glycosylation_imgt_positions" ,
@@ -154,6 +193,9 @@ def _sites_str(sites):
154193def _motifs_str (sites ):
155194 return "," .join (s [1 ] for s in sites ) if sites else "NA"
156195
196+ def _regions_str (sites ):
197+ return "," .join (s [2 ] for s in sites ) if sites else "NA"
198+
157199
158200def main ():
159201 parser = argparse .ArgumentParser ()
@@ -182,8 +224,12 @@ def main():
182224 "num_glycosylation_sites" : 0 ,
183225 "glycosylation_imgt_positions" : "NA" ,
184226 "glycosylation_motifs" : "NA" ,
227+ "glycosylation_imgt_regions" : "NA" ,
185228 "num_acquired_glycosylation_sites" : 0 ,
186229 "acquired_glycosylation_imgt_positions" : "NA" ,
230+ "acquired_glycosylation_imgt_regions" : "NA" ,
231+ "num_acquired_glycosylation_sites_cdr" : 0 ,
232+ "manntype_ags" : "NA" ,
187233 "germline_aa_sequence" : "NA" ,
188234 "num_germline_glycosylation_sites" : 0 ,
189235 "germline_glycosylation_imgt_positions" : "NA" ,
@@ -200,15 +246,20 @@ def main():
200246 query_sites = find_glycosylation_sites (numbered ) if numbered else []
201247 germline_sites = find_glycosylation_sites (gl_numbered ) if gl_numbered else []
202248 acquired_sites = classify_sites (query_sites , germline_sites )
249+ acquired_cdr_sites = sites_in_cdr (acquired_sites )
203250
204251 writer .writerow ({
205252 "sequence_id" : seq_id ,
206253 "aa_sequence" : aa_seq ,
207254 "num_glycosylation_sites" : len (query_sites ),
208255 "glycosylation_imgt_positions" : _sites_str (query_sites ),
209256 "glycosylation_motifs" : _motifs_str (query_sites ),
257+ "glycosylation_imgt_regions" : _regions_str (query_sites ),
210258 "num_acquired_glycosylation_sites" : len (acquired_sites ),
211259 "acquired_glycosylation_imgt_positions" : _sites_str (acquired_sites ),
260+ "acquired_glycosylation_imgt_regions" : _regions_str (acquired_sites ),
261+ "num_acquired_glycosylation_sites_cdr" : len (acquired_cdr_sites ),
262+ "manntype_ags" : "POS" if acquired_cdr_sites else "NEG" ,
212263 "germline_aa_sequence" : gl_seq if gl_seq else "NA" ,
213264 "num_germline_glycosylation_sites" : len (germline_sites ),
214265 "germline_glycosylation_imgt_positions" : _sites_str (germline_sites ),
0 commit comments