Skip to content

Commit db0b77f

Browse files
committed
igblastr 1.0.5: Some immprovements to igdata_info()
Also add read_igblastn_AIRR_output() and read_igblastn_fmt7_output().
1 parent 68e4477 commit db0b77f

16 files changed

Lines changed: 332 additions & 115 deletions

‎DESCRIPTION‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ Title: User-friendly R Wrapper to IgBLAST
33
Description: The igblastr package provides functions to conveniently install
44
and use a local IgBLAST installation from within R.
55
IgBLAST is described at <https://pubmed.ncbi.nlm.nih.gov/23671333/>.
6-
Online IgBLAST: <https://www.ncbi.nlm.nih.gov/igblast/>.
6+
IgBLAST web interface: <https://www.ncbi.nlm.nih.gov/igblast/>.
77
biocViews: Immunology, Immunogenetics, ImmunoOncology, CellBiology
88
URL: https://bioconductor.org/packages/igblastr
99
BugReports: https://github.com/HyrienLab/igblastr/issues
10-
Version: 1.0.4
10+
Version: 1.0.5
1111
License: Artistic-2.0
1212
Encoding: UTF-8
1313
Authors@R: c(
@@ -56,7 +56,8 @@ Collate:
5656
install_AIRR_germline_db.R
5757
augment_germline_db.R
5858
igblastn-args-utils.R
59-
outfmt7-utils.R
59+
read_igblastn_fmt7_output.R
60+
read_igblastn_AIRR_output.R
6061
igblastn.R
6162
igbrowser.R
6263
summarizeMismatches.R

‎NAMESPACE‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import(Biostrings)
2424
###
2525

2626
S3method(print, alignment_summary)
27+
S3method(print, aux_data_md5sum_df)
2728
S3method(print, c_region_dbs_df)
2829
S3method(print, fmt7footer)
2930
S3method(print, fmt7record)
@@ -46,6 +47,7 @@ S3method(summary, query_details)
4647

4748
export(
4849
print.alignment_summary,
50+
print.aux_data_md5sum_df,
4951
print.c_region_dbs_df,
5052
print.fmt7footer,
5153
print.fmt7record,
@@ -112,9 +114,13 @@ export(
112114
augment_germline_db_D,
113115
augment_germline_db_J,
114116

115-
## outfmt7-utils.R:
117+
## read_igblastn_fmt7_output.R:
116118
list_outfmt7_specifiers,
117119
parse_outfmt7,
120+
read_igblastn_fmt7_output,
121+
122+
## read_igblastn_AIRR_output.R:
123+
read_igblastn_AIRR_output,
118124

119125
## igblastn.R:
120126
igblastn, igblastn_help,
@@ -144,7 +150,7 @@ export(
144150
###
145151

146152
export(
147-
# outfmt7-utils.R:
153+
## read_igblastn_fmt7_output.R:
148154
qseqid
149155
)
150156

‎R/igblast_info.R‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ igblast_build <- function(igblast_root=get_igblast_root())
103103

104104
print.igblast_info <- function(x, ...)
105105
{
106-
cat(named_list_as_character(x), sep="\n")
106+
cat(named_list_as_pretty_keyvals(x), sep="\n")
107107
}
108108

109109
igblast_info <- function(igblast_root=get_igblast_root())

‎R/igblastn.R‎

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -117,68 +117,19 @@ print.igblastn_raw_output <- function(x, ...) cat(x, sep="\n")
117117

118118

119119
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120-
### .parse_igblastn_out()
120+
### .parse_igblastn_output()
121121
###
122122

123-
.sanitize_AIRR_df <- function(AIRR_df)
124-
{
125-
stopifnot(is.data.frame(AIRR_df),
126-
is.character(AIRR_df[ , "sequence_id"]),
127-
is.character(AIRR_df[ , "sequence"]),
128-
is.character(AIRR_df[ , "locus"]))
129-
130-
## AIRR field "sequence_aa" was added in igblast 1.21.0.
131-
sequence_aa <- AIRR_df$sequence_aa
132-
if (!is.null(sequence_aa))
133-
stopifnot(is.character(sequence_aa))
134-
135-
CHARACTER_COLS <- paste0(c("v", "d", "j"), "_call")
136-
for (colname in CHARACTER_COLS)
137-
AIRR_df[ , colname] <- as.character(AIRR_df[ , colname])
138-
c_call <- AIRR_df$c_call
139-
if (!is.null(c_call))
140-
AIRR_df$c_call <- as.character(c_call)
141-
142-
LOGICAL_COLS <- c("stop_codon", "vj_in_frame",
143-
"productive", "rev_comp", "complete_vdj")
144-
for (colname in LOGICAL_COLS)
145-
AIRR_df[ , colname] <- as.logical(AIRR_df[ , colname])
146-
147-
## AIRR field "v_frameshift" was added in igblast 1.17.0.
148-
v_frameshift <- AIRR_df$v_frameshift
149-
if (!is.null(v_frameshift))
150-
AIRR_df$v_frameshift <- as.logical(v_frameshift)
151-
152-
## AIRR field "d_frame" was added in igblast 1.21.0.
153-
d_frame <- AIRR_df$d_frame
154-
if (!is.null(d_frame))
155-
AIRR_df$d_frame <- as.logical(d_frame)
156-
157-
AIRR_df
158-
}
159-
160123
### TODO: Parse output format 3 and 4.
161-
.parse_igblastn_out <- function(out, outfmt_nb)
124+
.parse_igblastn_output <- function(out_lines, outfmt_nb)
162125
{
163-
stopifnot(isSingleNonWhiteString(out),
164-
isSingleInteger(outfmt_nb), outfmt_nb %in% c(3L, 4L, 7L, 19L))
165-
if (outfmt_nb == 19L) {
166-
## Make sure to use 'tryLogical=FALSE'. By default, i.e.
167-
## when 'tryLogical=TRUE', there's the risk that read.table()
168-
## will erroneously decide to interpret some columns as logical!
169-
## For example this can happen to column "d_sequence_alignment_aa"
170-
## if it contains only single-letter amino acid sequences T (Thr)
171-
## or F (Phe).
172-
AIRR_df <- read.table(out, header=TRUE, sep="\t",
173-
tryLogical=FALSE, na.strings="")
174-
return(.sanitize_AIRR_df(AIRR_df))
175-
}
176-
out_lines <- readLines(out)
126+
stopifnot(is.character(out_lines),
127+
inherits(out_lines, "igblastn_raw_output"),
128+
isSingleInteger(outfmt_nb), outfmt_nb %in% c(3L, 4L, 7L))
177129
if (outfmt_nb == 7L)
178130
return(parse_outfmt7(out_lines))
179131
warning(wmsg("parsing of igblastn output format ",
180132
outfmt_nb, " is not supported yet"))
181-
class(out_lines) <- "igblastn_raw_output"
182133
out_lines
183134
}
184135

@@ -305,7 +256,7 @@ igblastn <- function(query, outfmt="AIRR",
305256

306257
if (outfmt_nb == 19L) {
307258
if (show.in.browser || parse.out)
308-
AIRR_df <- .parse_igblastn_out(cmd_args$out, outfmt_nb)
259+
AIRR_df <- read_igblastn_AIRR_output(cmd_args$out)
309260
if (show.in.browser)
310261
display_data_frame_in_browser(AIRR_df)
311262
if (parse.out) {
@@ -319,12 +270,10 @@ igblastn <- function(query, outfmt="AIRR",
319270

320271
if (show.in.browser)
321272
display_local_file_in_browser(cmd_args$out)
322-
if (parse.out) {
323-
ans <- .parse_igblastn_out(cmd_args$out, outfmt_nb)
324-
} else {
325-
ans <- readLines(cmd_args$out)
326-
class(ans) <- "igblastn_raw_output"
327-
}
273+
ans <- readLines(cmd_args$out)
274+
class(ans) <- "igblastn_raw_output"
275+
if (parse.out)
276+
ans <- .parse_igblastn_output(ans, outfmt_nb)
328277
ans
329278
}
330279

‎R/igdata-utils.R‎

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,50 @@ update_live_igdata <- function(check.only=FALSE)
193193
}
194194

195195

196+
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
197+
### .make_aux_data_md5sum_df()
198+
###
199+
200+
.compute_aux_files_md5sums <- function(which=c("live", "original"))
201+
{
202+
aux_dir <- file.path(.path_to_igdata(which), "optional_file")
203+
aux_files <- list.files(aux_dir, pattern="\\.(aux|frame)$")
204+
setNames(md5sum(file.path(aux_dir, aux_files)), aux_files)
205+
}
206+
207+
.make_aux_data_md5sum_df <- function()
208+
{
209+
live_md5sums <- .compute_aux_files_md5sums("live")
210+
orig_md5sums <- .compute_aux_files_md5sums("original")
211+
files <- sort(union(names(live_md5sums), names(orig_md5sums)))
212+
ans <- data.frame(file=files,
213+
live=unname(live_md5sums[files]),
214+
original=unname(orig_md5sums[files]))
215+
class(ans) <- c("aux_data_md5sum_df", class(ans))
216+
ans
217+
}
218+
219+
.aux_data_md5sum_df_as_character <- function(x)
220+
{
221+
EXPECTED_COLNAMES <- c("file", "live", "original")
222+
stopifnot(is.data.frame(x), identical(colnames(x), EXPECTED_COLNAMES))
223+
note <- character(nrow(x))
224+
note[x$live != x$original] <- " (updated)"
225+
note[is.na(x$original)] <- " (new)"
226+
ans <- character(3L * nrow(x))
227+
ans[3L * seq_len(nrow(x)) - 2L] <- paste0("- file '", x$file, "'")
228+
ans[3L * seq_len(nrow(x)) - 1L] <- paste0(" live: ", x$live, note)
229+
ans[3L * seq_len(nrow(x)) ] <- paste0(" original: ", x$original)
230+
ans
231+
}
232+
233+
print.aux_data_md5sum_df <- function(x, margin="", ...)
234+
{
235+
y <- .aux_data_md5sum_df_as_character(x)
236+
cat(paste0(margin, y), sep="\n")
237+
}
238+
239+
196240
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
197241
### time_since_live_igdata_last_checked()
198242
### igdata_info()
@@ -212,56 +256,75 @@ update_live_igdata <- function(check.only=FALSE)
212256
if (file.exists(path)) readLines(path) else NA_character_
213257
}
214258

259+
.how_old <- function(datetime, units="days")
260+
{
261+
stopifnot(isSingleNonWhiteString(datetime))
262+
as.double(difftime(Sys.time(), as.POSIXct(datetime), units=units))
263+
}
264+
265+
### Used in .onLoad() hook.
215266
### By default, time is returned in number of days.
216267
time_since_live_igdata_last_checked <- function(units="days")
217268
{
218269
igdata_subdir <- file.path(igblastr_cache(LIVE_IGDATA), "optional_file")
219270
last_checked <- .get_last_checked(igdata_subdir)
220271
if (is.na(last_checked))
221272
return(Inf)
222-
as.double(difftime(Sys.time(), as.POSIXct(last_checked), units=units))
273+
.how_old(last_checked, units=units)
223274
}
224275

225-
.dt2msg <- function(dt)
276+
.annotate_how_old <- function(dt)
226277
{
227-
if (is.infinite(dt))
228-
return("n/a")
229-
msg <- paste0(sprintf("%.2f", dt), " days")
278+
msg <- paste0(sprintf("%.2f", dt), " days ago")
230279
if (dt > 30)
231280
msg <- paste0(msg, " --> time to run 'update_live_igdata()' again")
232281
msg
233282
}
234283

235-
print.igdata_info <- function(x, ...)
284+
.annotate_last_checked <- function(last_checked)
236285
{
237-
cat(named_list_as_character(x), sep="\n")
286+
if (is.na(last_checked))
287+
return(paste0("not checked yet --> ",
288+
"run 'update_live_igdata()' to check for ",
289+
"new updates available at NCBI"))
290+
dt <- .how_old(last_checked)
291+
paste0(last_checked, " (", .annotate_how_old(dt), ")")
238292
}
239293

240294
igdata_info <- function()
241295
{
242296
live_igdata <- .path_to_igdata("live")
243297
igdata_subdir <- file.path(live_igdata, "optional_file")
244298
last_checked <- .get_last_checked(igdata_subdir)
245-
if (is.na(last_checked))
246-
last_checked <- paste0("not checked yet --> ",
247-
"run 'update_live_igdata()' to check for\n",
248-
" new updates available at NCBI")
249299
last_updated <- .get_last_updated(igdata_subdir)
250-
dt <- time_since_live_igdata_last_checked()
251300
if (is.na(last_updated))
252301
last_updated <- "not updated yet"
253-
original_igdata <- .path_to_igdata("original")
254302
ans <- list(
255303
live_igdata=live_igdata,
256-
last_checked=last_checked,
257-
time_since_last_checked=.dt2msg(dt),
304+
last_checked=.annotate_last_checked(last_checked),
258305
last_updated=last_updated,
259-
original_igdata=original_igdata
306+
original_igdata=.path_to_igdata("original"),
307+
aux_data_md5sums=.make_aux_data_md5sum_df()
260308
)
261309
class(ans) <- "igdata_info"
262310
ans
263311
}
264312

313+
print.igdata_info <- function(x, ...)
314+
{
315+
stopifnot(is.list(x))
316+
md5sum_df <- x$aux_data_md5sums
317+
if (!is.null(md5sum_df))
318+
x$aux_data_md5sums <- NULL
319+
keyvals <- named_list_as_pretty_keyvals(x)
320+
if (!is.null(md5sum_df)) {
321+
y <- .aux_data_md5sum_df_as_character(md5sum_df)
322+
keyval <- paste0("aux_data_md5sums:\n", paste0(" ", y, collapse="\n"))
323+
keyvals <- c(keyvals, keyval)
324+
}
325+
cat(keyvals, sep="\n")
326+
}
327+
265328

266329
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
267330
### get_igblast_auxiliary_data()
@@ -271,11 +334,11 @@ get_igblast_auxiliary_data <- function(organism, which=c("live", "original"))
271334
{
272335
organism <- normalize_igblast_organism(organism)
273336
which <- match.arg(which)
274-
dirpath <- file.path(.path_to_igdata(which), "optional_file")
275-
auxiliary_data <- file.path(dirpath, paste0(organism, "_gl.aux"))
276-
if (!file.exists(auxiliary_data))
277-
stop(wmsg("no auxiliary data found in ", dirpath, " for ", organism))
278-
auxiliary_data
337+
aux_dir <- file.path(.path_to_igdata(which), "optional_file")
338+
aux_file <- file.path(aux_dir, paste0(organism, "_gl.aux"))
339+
if (!file.exists(aux_file))
340+
stop(wmsg("no auxiliary data found in ", aux_dir, " for ", organism))
341+
aux_file
279342
}
280343

281344

‎R/read_igblastn_AIRR_output.R‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
### =========================================================================
2+
### read_igblastn_AIRR_output()
3+
### -------------------------------------------------------------------------
4+
5+
6+
.sanitize_AIRR_df <- function(AIRR_df)
7+
{
8+
stopifnot(is.data.frame(AIRR_df),
9+
is.character(AIRR_df[ , "sequence_id"]),
10+
is.character(AIRR_df[ , "sequence"]),
11+
is.character(AIRR_df[ , "locus"]))
12+
13+
## AIRR field "sequence_aa" was added in igblast 1.21.0.
14+
sequence_aa <- AIRR_df$sequence_aa
15+
if (!is.null(sequence_aa))
16+
stopifnot(is.character(sequence_aa))
17+
18+
CHARACTER_COLS <- paste0(c("v", "d", "j"), "_call")
19+
for (colname in CHARACTER_COLS)
20+
AIRR_df[ , colname] <- as.character(AIRR_df[ , colname])
21+
c_call <- AIRR_df$c_call
22+
if (!is.null(c_call))
23+
AIRR_df$c_call <- as.character(c_call)
24+
25+
LOGICAL_COLS <- c("stop_codon", "vj_in_frame",
26+
"productive", "rev_comp", "complete_vdj")
27+
for (colname in LOGICAL_COLS)
28+
AIRR_df[ , colname] <- as.logical(AIRR_df[ , colname])
29+
30+
## AIRR field "v_frameshift" was added in igblast 1.17.0.
31+
v_frameshift <- AIRR_df$v_frameshift
32+
if (!is.null(v_frameshift))
33+
AIRR_df$v_frameshift <- as.logical(v_frameshift)
34+
35+
## AIRR field "d_frame" was added in igblast 1.21.0.
36+
d_frame <- AIRR_df$d_frame
37+
if (!is.null(d_frame))
38+
AIRR_df$d_frame <- as.logical(d_frame)
39+
40+
AIRR_df
41+
}
42+
43+
### Read igblastn output format 19 (AIRR format). This is a .tsv file.
44+
read_igblastn_AIRR_output <- function(out)
45+
{
46+
## Make sure to use 'tryLogical=FALSE'. By default, i.e.
47+
## when 'tryLogical=TRUE', there's the risk that read.table()
48+
## will erroneously decide to interpret some columns as logical!
49+
## For example this can happen to column "d_sequence_alignment_aa"
50+
## if it contains only single-letter amino acid sequences T (Thr)
51+
## or F (Phe).
52+
AIRR_df <- read.table(out, header=TRUE, sep="\t",
53+
tryLogical=FALSE, na.strings="")
54+
.sanitize_AIRR_df(AIRR_df)
55+
}
56+

0 commit comments

Comments
 (0)