Skip to content

Commit 575ee81

Browse files
committed
igblastr 1.0.28
o Rename makeogrannote() --> extract_intdata_from_ogrdb_json() o Add extract_auxdata_from_ogrdb_json() o Add auxdata to all the OGRDB built-in germline dbs for mouse.
1 parent 2fafde6 commit 575ee81

54 files changed

Lines changed: 690 additions & 144 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎DESCRIPTION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Description: The igblastr package provides functions to conveniently install
1717
biocViews: Immunology, Immunogenetics, ImmunoOncology, CellBiology
1818
URL: https://bioconductor.org/packages/igblastr
1919
BugReports: https://github.com/HyrienLab/igblastr/issues
20-
Version: 1.0.27
20+
Version: 1.0.28
2121
License: Artistic-2.0
2222
Encoding: UTF-8
2323
Authors@R: c(

‎NAMESPACE‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ export(
165165

166166
## download_OGRDB_germline_json.R:
167167
download_OGRDB_germline_json,
168-
makeogrannote,
168+
extract_intdata_from_ogrdb_json,
169+
extract_auxdata_from_ogrdb_json,
169170

170171
## install_OGRDB_germline_db.R:
171172
#install_OGRDB_germline_db,

‎R/auxdata-utils.R‎

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -95,32 +95,3 @@ compute_germline_db_auxdata <- function(db_name, ...)
9595
.do_compute_germline_db_auxdata(db_path, ...)
9696
}
9797

98-
99-
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
100-
### add_computed_auxdata_if_missing()
101-
###
102-
103-
### Not exported!
104-
### If the auxdata in germline db at 'db_path' is missing then
105-
### add_computed_auxdata_if_missing() computes and adds it.
106-
### Returns FALSE if the germline db already includes auxdata, and TRUE if
107-
### if doesn't and the auxdata could be computed and added sucessfully.
108-
### Raises an error if the auxdata was missing but the computed auxdata
109-
### could not be added because its "coding_frame_start" column contains NAs.
110-
add_computed_auxdata_if_missing <- function(db_path, verbose=FALSE)
111-
{
112-
stopifnot(isTRUEorFALSE(verbose))
113-
auxdata_path <- make_germline_db_auxdata_path(db_path)
114-
if (file.exists(auxdata_path))
115-
return(FALSE)
116-
if (verbose)
117-
message(wmsg("Computing the auxdata"), " ... ", appendLF=FALSE)
118-
auxdata <- .do_compute_germline_db_auxdata(db_path)
119-
ok <- add_computed_auxdata_to_db(auxdata, db_path, verbose=verbose)
120-
if (!ok)
121-
stop(wmsg("failed to add the computed auxdata to ", basename(db_path)))
122-
if (verbose)
123-
message("ok.")
124-
TRUE
125-
}
126-

‎R/clean_allele_set.R‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ checkarg_with.intdata <- function(with.intdata, gapped)
252252
stopifnot(identical(intdata[ , "allele_name"], allele_names))
253253

254254
## Add "chain_type" column.
255-
intdata$chain_type <- paste0("V", substr(locus, 3L, 3L))
255+
intdata$chain_type <- make_chain_type("V", locus)
256256

257257
mcols(allele_set) <- cbind(allele_set_mcols, intdata)
258258
allele_set
@@ -321,7 +321,7 @@ checkarg_with.intdata <- function(with.intdata, gapped)
321321

322322
## Sanity checks.
323323
stopifnot(identical(auxdata[ , "allele_name"], allele_names))
324-
expected_chain_type <- paste0("J", substr(locus, 3L, 3L))
324+
expected_chain_type <- make_chain_type("J", locus)
325325
stopifnot(identical(auxdata$chain_type, expected_chain_type))
326326

327327
mcols(allele_set) <- cbind(allele_set_mcols, auxdata)

‎R/compute_auxdata.R‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
} else {
107107
allele_groups <- substr(allele_names, 1L, 4L)
108108
stopifnot(all(allele_groups == J_group))
109-
chain_type <- paste0("J", substr(J_group, 3L, 3L))
109+
chain_type <- make_chain_type("J", substr(J_group, 1L, 3L))
110110
}
111111
if (J_group == "IGHJ") {
112112
fwr4_starts <- .find_heavy_fwr4_starts(J_alleles)

‎R/create_region_db.R‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,6 @@ add_computed_auxdata_to_db <- function(auxdata, destdir, verbose=FALSE)
308308
auxdata_dir <- dirname(auxdata_path)
309309
stopifnot(!dir.exists(auxdata_dir))
310310
dir.create(auxdata_dir)
311-
312311
write_auxdata(auxdata, auxdata_path)
313312

314313
if (verbose)

‎R/download_OGRDB_germline_json.R‎

Lines changed: 119 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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+

‎R/loci-utils.R‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ IG_GROUPS <- .make_groups(.IG_LOCI_2_REGION_TYPES) # 7 IG groups
5050
TR_GROUPS <- .make_groups(.TR_LOCI_2_REGION_TYPES) # 10 TR groups
5151

5252

53+
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54+
### make_chain_type()
55+
###
56+
57+
### Vectorized w.r.t. 'locus'.
58+
make_chain_type <- function(region_type, locus)
59+
{
60+
stopifnot(isSingleNonWhiteString(region_type),
61+
region_type %in% VDJ_REGION_TYPES,
62+
is.character(locus), all(nchar(locus, keepNA=FALSE) == 3L))
63+
paste0(region_type, substr(locus, 3L, 3L), recycle0=TRUE)
64+
}
65+
66+
5367
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5468
### checkarg_loci()
5569
###

0 commit comments

Comments
 (0)