@@ -78,16 +78,16 @@ download_OGRDB_germline_json <- function(organism, germline_sets,
7878
7979
8080# ## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
81- # ## makeogrannote ()
81+ # ## extract_intdata_from_ogrdb_json ()
8282# ##
8383
8484# ## Returns an unnamed list with 1 list element per allele.
8585# ## Each list element is itself a named list (nested lists).
86- .extract_allele_descriptions <- function (ogrdb_json_file )
86+ .extract_allele_descriptions <- function (json_path )
8787{
88- if (! isSingleNonWhiteString(ogrdb_json_file ))
89- stop(wmsg(" 'ogrdb_json_file ' must be a single (non-empty) string" ))
90- parsed_json <- fromJSON(ogrdb_json_file , simplifyDataFrame = FALSE )
88+ if (! isSingleNonWhiteString(json_path ))
89+ stop(wmsg(" 'json_path ' must be a single (non-empty) string" ))
90+ parsed_json <- fromJSON(json_path , simplifyDataFrame = FALSE )
9191 stopifnot(is.list(parsed_json ), length(parsed_json ) == 1L ,
9292 identical(names(parsed_json ), " GermlineSet" ))
9393 germline_set <- parsed_json [[1L ]]
@@ -108,9 +108,7 @@ download_OGRDB_germline_json <- function(organism, germline_sets,
108108 return (NULL )
109109 allele_name <- allele_description $ label
110110 stopifnot(isSingleNonWhiteString(allele_name ))
111- locus <- allele_description $ locus
112- stopifnot(isSingleNonWhiteString(locus ),
113- locus %in% IG_LOCI )
111+ chain_type <- make_chain_type(sequence_type , allele_description $ locus )
114112 v_gene_delineations <- allele_description $ v_gene_delineations
115113 stopifnot(is.list(v_gene_delineations ),
116114 is.null(names(v_gene_delineations )))
@@ -121,8 +119,6 @@ download_OGRDB_germline_json <- function(organism, germline_sets,
121119 names(v_gene_delineation )))
122120 starts_ends <- v_gene_delineation [V_GENE_DELINEATION_COLNAMES ]
123121 starts_ends <- setNames(as.character(starts_ends ), names(starts_ends ))
124- nc <- nchar(locus )
125- chain_type <- paste0(sequence_type , substr(locus , nc , nc ))
126122 return (c(allele_name = allele_name , starts_ends , chain_type = chain_type ))
127123 }
128124 stop(wmsg(" no IMGT v_gene_delineation information found " ,
@@ -143,20 +139,23 @@ download_OGRDB_germline_json <- function(organism, germline_sets,
143139# ## in its infancy, so maybe the early JSON files that they generated at the
144140# ## time had the FR1/CDR1/FR2/CDR2/FR3 boundaries relative to the **gapped**
145141# ## V allele sequences? Is this the reason why makeogrannote.py adjusts them?
146- # ## Returns a data.frame with 1 row per V allele in JSON file 'ogrdb_json_file '
142+ # ## Returns a data.frame with 1 row per V allele in JSON file 'json_path '
147143# ## and the same columns as the data.frame returned by load_intdata() (see
148144# ## R/intdata-utils.R).
149- makeogrannote <- function (ogrdb_json_file )
145+ extract_intdata_from_ogrdb_json <- function (json_path )
150146{
151- allele_descriptions <- .extract_allele_descriptions(ogrdb_json_file )
147+ allele_descriptions <- .extract_allele_descriptions(json_path )
152148 data <- lapply(allele_descriptions , .extract_IMGT_v_gene_delineation )
153- data <- unlist(data , use.names = FALSE )
154- if (is.null(data )) {
149+ data <- S4Vectors ::: delete_NULLs(data )
150+ col2class <- head(NDM_DATA_COL2CLASS , n = - 1L ) # no coding_frame_start yet
151+ if (length(data ) == 0L ) {
155152 warning(wmsg(" no V allele descriptions found " ,
156- " in JSON file: " , ogrdb_json_file ))
153+ " in JSON file: " , json_path ))
157154 data <- character (0 )
155+ } else {
156+ stopifnot(identical(names(data [[1L ]]), names(col2class )))
157+ data <- unlist(data , use.names = FALSE )
158158 }
159- col2class <- head(NDM_DATA_COL2CLASS , n = - 1L )
160159 m <- matrix (data , ncol = length(col2class ), byrow = TRUE )
161160 df <- matrix2df(m , col2class )
162161 cbind(df , coding_frame_start = integer(nrow(df )))
@@ -189,21 +188,20 @@ makeogrannote <- function(ogrdb_json_file)
189188
190189 intdata <- compute_V_gene_delineations(V_fasta_file )
191190 locus <- extract_loci_from_OGRDB_set_names(organism , names(germline_set ))
192- intdata $ chain_type <- paste0 (" V" , substr( locus , 3L , 3L ) )
191+ intdata $ chain_type <- make_chain_type (" V" , locus )
193192 intdata [ , names(NDM_DATA_COL2CLASS )]
194193}
195194
196195# ## Returns the intdata in a data.frame.
197- .extract_intdata_from_OGRDB_json_file <-
196+ .fetch_intdata_from_OGRDB <-
198197 function (organism , germline_set , source_set = FALSE , recache = FALSE , ... )
199198{
200- # # Download OGRDB germline set to local store if it's not
201- # # already there.
199+ # # Download OGRDB germline set to local store if it's not already there.
202200 local_file <- download_OGRDB_germline_set_to_OGRDB_store(organism ,
203201 names(germline_set ), germline_set ,
204202 format = " airr" , source_set = source_set ,
205203 recache = recache , ... )
206- makeogrannote (local_file )
204+ extract_intdata_from_ogrdb_json (local_file )
207205}
208206
209207# ## Not exported!
@@ -226,18 +224,110 @@ validate_OGRDB_intdata <- function(organism, germline_set, source_set=FALSE,
226224
227225 # # Method 1: Infer intdata from the gaps in the V allele sequences.
228226 intdata1 <- .infer_intdata_from_OGRDB_gapped_V_sequences(
229- organism , germline_set ,
230- source_set = source_set ,
231- recache = recache , ... )
227+ organism , germline_set ,
228+ source_set = source_set ,
229+ recache = recache , ... )
232230
233231
234232 # # Method 2: Extract intdata from OGRDB json file.
235- intdata2 <- .extract_intdata_from_OGRDB_json_file (
236- organism , germline_set ,
237- source_set = source_set ,
238- recache = recache , ... )
233+ intdata2 <- .fetch_intdata_from_OGRDB (
234+ organism , germline_set ,
235+ source_set = source_set ,
236+ recache = recache , ... )
239237
240238 # # Compare.
241239 same_ndm_data(intdata1 , intdata2 )
242240}
243241
242+
243+ # ## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
244+ # ## extract_auxdata_from_ogrdb_json()
245+ # ##
246+
247+ .extract_J_allele_length <- function (allele_description )
248+ {
249+ stopifnot(is.list(allele_description ), ! is.null(names(allele_description )))
250+ coding_sequence <- allele_description $ coding_sequence
251+ stopifnot(isSingleNonWhiteString(coding_sequence ))
252+ seq_len <- nchar(coding_sequence )
253+ # # Some sanity checks.
254+ gene_start <- allele_description $ gene_start
255+ gene_end <- allele_description $ gene_end
256+ stopifnot(isSingleInteger(gene_start ), isSingleInteger(gene_end ),
257+ gene_end - gene_start + 1L == seq_len )
258+ seq_len
259+ }
260+
261+ # ## IMPORTANT NOTE about the j_cdr3_end reported in OGRDB json files:
262+ # ## The j_cdr3_end reported in OGRDB json files seems to be 2-based i.e.
263+ # ## it's set to the 1-based value + 1 or to the 0-based value + 2.
264+ # ## For example, for human allele IGHJ1*01, the json file reports:
265+ # ##
266+ # ## j_codon_frame = 1 and j_cdr3_end = 19
267+ # ##
268+ # ## IGHJ1*01 is the first J allele annotated in human_gl.aux in IgBLAST and
269+ # ## the file entry for this allele is:
270+ # ##
271+ # ## allele name | first coding | chain type | CDR3 stop | extra bps
272+ # ## | frame start | | | beyond J
273+ # ## | position | | | coding end
274+ # ## ------------|--------------|------------|-----------|-----------
275+ # ## IGHJ1*01 | 0 | JH | 17 | 1
276+ # ##
277+ # ## All positions in human_gl.aux are 0-based so, according to the above
278+ # ## entry, the 1-based position of the CDR3 end would be 18.
279+ # ## Given that j_codon_frame is 1, the 1-based CDR3 end is a multiple of 3,
280+ # ## as expected.
281+ # ## Furthermore, the coding sequence for IGHJ1*01 is:
282+ # ##
283+ # ## GCTGAATACTTCCAGCACTGGGGCCAGGGCACCCTGGTCACCGTCTCCTCAG
284+ # ##
285+ # ## which translates to AEYFQHWGQGTLVTVSS if we consider that the first 3
286+ # ## nucleotides of the sequence form the first codon (as suggested by the
287+ # ## value of j_codon_frame).
288+ # ## As expected, amino acids WGQG at position 7-10 match the WGXG motif that
289+ # ## marks the start of the FWR4 on the heavy chain. This would mean that the
290+ # ## first 6 amino acids (AEYFQH) are the last 6 amino acids of the CDR3,
291+ # ## confirming that the 18th nucleotide in the coding sequence is where the
292+ # ## CDR3 ends.
293+ .extract_j_annotation <- function (allele_description )
294+ {
295+ stopifnot(is.list(allele_description ), ! is.null(names(allele_description )))
296+ sequence_type <- allele_description $ sequence_type
297+ stopifnot(isSingleNonWhiteString(sequence_type ),
298+ sequence_type %in% VDJC_REGION_TYPES )
299+ if (sequence_type != " J" )
300+ return (NULL )
301+ allele_name <- allele_description $ label
302+ stopifnot(isSingleNonWhiteString(allele_name ))
303+ chain_type <- make_chain_type(sequence_type , allele_description $ locus )
304+ j_codon_frame <- allele_description $ j_codon_frame
305+ j_cdr3_end <- allele_description $ j_cdr3_end
306+ stopifnot(isSingleInteger(j_codon_frame ), isSingleInteger(j_cdr3_end ))
307+ coding_frame_start <- j_codon_frame - 1L
308+ seq_len <- .extract_J_allele_length(allele_description )
309+ extra_bps <- (seq_len - coding_frame_start ) %% 3L
310+ c(allele_name = allele_name ,
311+ coding_frame_start = coding_frame_start ,
312+ chain_type = chain_type ,
313+ cdr3_end = j_cdr3_end - 2L , # see IMPORTANT NOTE above
314+ extra_bps = extra_bps )
315+ }
316+
317+ extract_auxdata_from_ogrdb_json <- function (json_path )
318+ {
319+ allele_descriptions <- .extract_allele_descriptions(json_path )
320+ data <- lapply(allele_descriptions , .extract_j_annotation )
321+ data <- S4Vectors ::: delete_NULLs(data )
322+ if (length(data ) == 0L ) {
323+ warning(wmsg(" no J allele descriptions found " ,
324+ " in JSON file: " , json_path ))
325+ data <- character (0 )
326+ } else {
327+ stopifnot(identical(names(data [[1L ]]), names(AUXDATA_COL2CLASS )))
328+ data <- unlist(data , use.names = FALSE )
329+ }
330+ m <- matrix (data , ncol = length(AUXDATA_COL2CLASS ), byrow = TRUE )
331+ matrix2df(m , AUXDATA_COL2CLASS )
332+ }
333+
0 commit comments