From 879489eff3f8435305782cc84dea076edce6eb14 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 21 Nov 2019 00:37:32 -0700 Subject: [PATCH 01/51] update installation help in readme --- README.Rmd | 6 ++++++ README.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/README.Rmd b/README.Rmd index 52385838..dfad80f7 100644 --- a/README.Rmd +++ b/README.Rmd @@ -35,6 +35,12 @@ This package is intended as a unified one-stop command line interface to all com You can install isoreader from github with the devtools package (version > 1.13.2 required for bioconductor support). ```{r gh-installation, eval = FALSE} +# restart your R session (this command only works in RStudio) +.rs.restartR() + +# installs the development tools package if not yet installed +if(!requireNamespace("devtools", quietly = TRUE)) install.packages("devtools") + # install.packages("devtools") # only needed once devtools::install_github("isoverse/isoreader") ``` diff --git a/README.md b/README.md index 2101f61c..79f58c9d 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ You can install isoreader from github with the devtools package (version \> 1.13.2 required for bioconductor support). ``` r +# restart your R session (this command only works in RStudio) +.rs.restartR() + +# installs the development tools package if not yet installed +if(!requireNamespace("devtools", quietly = TRUE)) install.packages("devtools") + # install.packages("devtools") # only needed once devtools::install_github("isoverse/isoreader") ``` From c1ebaa20cbeb83042dd18113c3ec96ab0953b032 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Fri, 7 Feb 2020 20:40:57 -0700 Subject: [PATCH 02/51] make finding gas configurations in dxf files more robust closes #72 --- DESCRIPTION | 2 +- R/isoread_dxf.R | 23 ++++++++++++----------- R/utils_binary_files.R | 5 ++++- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index d93b2221..42da514b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: isoreader Title: Read IRMS data files Description: R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry. -Version: 1.0.12 +Version: 1.0.13 Authors@R: person("Sebastian", "Kopf", email = "sebastian.kopf@colorado.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2044-0201")) URL: https://github.com/isoverse/isoreader BugReports: https://github.com/isoverse/isoreader/issues diff --git a/R/isoread_dxf.R b/R/isoread_dxf.R index 314edece..bd7f4a04 100644 --- a/R/isoread_dxf.R +++ b/R/isoread_dxf.R @@ -47,25 +47,23 @@ iso_read_dxf <- function(ds, options = list()) { # extract voltage data in dxf file extract_dxf_raw_voltage_data <- function(ds) { - # move to beginning of intensity information (the larger block coming - # afterwards is not always present so not used as max pos here) + # search gas configurations + # (the larger block coming afterwards is not always present so not used as max pos here) ds$binary <- ds$binary %>% set_binary_file_error_prefix("cannot identify measured masses") %>% - move_to_C_block_range("CEvalDataItemTransferPart", "CBinary") + move_to_C_block_range("CEvalDataIntTransferPart", "CBinary") + # find all gas configurations configs <- list() - gas_config_re <- re_text("Overwritten") - config_positions <- ds$binary %>% find_next_patterns(gas_config_re) - if (length(config_positions) == 0) stop("could not find gas configurations", call. = FALSE) + gas_config_name_re <- re_combine(re_block("fef-x"), re_block("alpha"), re_block("fef-0"), re_block("fef-x")) + config_positions <- ds$binary %>% find_next_patterns(gas_config_name_re) config_caps <- c(config_positions[-1], ds$binary$max_pos) + if (length(config_positions) == 0) return(list()) for(i in 1:length(config_positions)) { - # find name of gas configuration ds$binary <- ds$binary %>% - move_to_pos(config_positions[i] + gas_config_re$size) %>% - move_to_next_pattern(re_block("etx"), re_text("/"), re_block("fef-0")) %>% - move_to_next_pattern(re_block("fef-x"), re_block("text"), re_block("fef-0"), re_block("fef-x"), move_to_end = FALSE) %>% + move_to_pos(config_positions[i]) %>% move_to_next_pattern(re_block("fef-x"), max_gap = 0) %>% capture_data("gas", "text", re_block("fef-0"), re_block("fef-x")) @@ -77,7 +75,10 @@ extract_dxf_raw_voltage_data <- function(ds) { # new config configs[[ds$binary$data$gas]] <- list(pos = config_positions[i], cap = config_caps[i], masses = c()) } - } + } + + # safety check + if (length(configs) == 0) stop("could not find gas configurations", call. = FALSE) # find all masses for (config in names(configs)) { diff --git a/R/utils_binary_files.R b/R/utils_binary_files.R index a6345e70..f84cd1fb 100644 --- a/R/utils_binary_files.R +++ b/R/utils_binary_files.R @@ -66,7 +66,8 @@ skip_pos <- function(bfile, nbyte) { } # move to position -move_to_pos <- function(bfile, pos) { +move_to_pos <- function(bfile, pos, reset_cap = FALSE) { + if (reset_cap) bfile$max_pos <- length(bfile$raw) if (pos > bfile$max_pos) { op_error( bfile, sprintf("cannot move to position %.0f as it exceeds position max set at %.0f", @@ -472,6 +473,8 @@ get_ctrl_blocks_config <- function() { `C-block` = list(size = 20L, auto = FALSE, regexp = "\xff\xff(\\x00|[\x01-\x0f])\\x00.\\x00\x43[\x20-\x7e]+"), # text block (not auto processed) - note that this does NOT include non standard characters + latin = list(size = 20L, auto = FALSE, regexp = "([\x41-\x5a\x61-\x7a]\\x00)+"), #a-zA-Z + alpha = list(size = 20L, auto = FALSE, regexp = "([\x41-\x5a\x61-\x7a\x30-\x39]\\x00)+"), #a-zA-Z0-9 text = list(size = 20L, auto = FALSE, regexp = "([\x20-\x7e]\\x00)+"), `text0` = list(size = 20L, auto = FALSE, regexp = "([\x20-\x7e]\\x00)*"), # allows no text permil = list(size = 2L, auto = FALSE, regexp = "\x30\x20") From 4fd2bc3534aadb8a0e9c27e3478625574507db32 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Fri, 7 Feb 2020 20:41:10 -0700 Subject: [PATCH 03/51] enable cf and di file tests --- tests/testthat/test-continuous-flow.R | 2 +- tests/testthat/test-dual-inlet.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-continuous-flow.R b/tests/testthat/test-continuous-flow.R index 7fe9fdb6..77270090 100644 --- a/tests/testthat/test-continuous-flow.R +++ b/tests/testthat/test-continuous-flow.R @@ -22,7 +22,7 @@ test_that("test that dxf files can be read", { # test specific files # FIXME: re-enable for commits - skip("Currently not testing all continuous flow data files.") + #skip("Currently not testing all continuous flow data files.") # FIXME: run as one batch to make use of parallel processing iso_turn_reader_caching_off() diff --git a/tests/testthat/test-dual-inlet.R b/tests/testthat/test-dual-inlet.R index e4bd2149..35627937 100644 --- a/tests/testthat/test-dual-inlet.R +++ b/tests/testthat/test-dual-inlet.R @@ -32,7 +32,7 @@ test_that("test that did files can be read", { # test specific files # FIXME: re-enable for commits - skip("Currently not testing all dual inlet data files.") + #skip("Currently not testing all dual inlet data files.") # FIXME: run as one batch to make use of parallel processing iso_turn_reader_caching_off() From 6763edd96def69804ac7069eb9b5c366cca63cb7 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sat, 8 Feb 2020 00:26:46 -0700 Subject: [PATCH 04/51] add new test files --- .../testthat/test_data/dxf_example_CNS_01.dxf | Bin 0 -> 439388 bytes tests/testthat/test_data/dxf_example_HO_02.dxf | Bin 0 -> 330933 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/testthat/test_data/dxf_example_CNS_01.dxf create mode 100644 tests/testthat/test_data/dxf_example_HO_02.dxf diff --git a/tests/testthat/test_data/dxf_example_CNS_01.dxf b/tests/testthat/test_data/dxf_example_CNS_01.dxf new file mode 100644 index 0000000000000000000000000000000000000000..b3c441200555a94a1b7961342b9da315b5b1ee81 GIT binary patch literal 439388 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 literal 0 HcmV?d00001 diff --git a/tests/testthat/test_data/dxf_example_HO_02.dxf b/tests/testthat/test_data/dxf_example_HO_02.dxf new file mode 100644 index 0000000000000000000000000000000000000000..c749d6678c79d70e7d3366bab0a8694672e66032 GIT binary patch literal 330933 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

lRHOGxBQ*DS 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 Date: Sat, 8 Feb 2020 00:31:25 -0700 Subject: [PATCH 05/51] make re_direct more robust closes #90 --- R/isoread_cf.R | 8 +- R/isoread_dxf.R | 108 ++++++++++++++------------ R/isoread_isodat.R | 55 ++++++++----- R/utils_binary_files.R | 3 +- tests/testthat/test-continuous-flow.R | 8 ++ 5 files changed, 111 insertions(+), 71 deletions(-) diff --git a/R/isoread_cf.R b/R/isoread_cf.R index b4f6d2bb..c2b42666 100644 --- a/R/isoread_cf.R +++ b/R/isoread_cf.R @@ -64,12 +64,16 @@ extract_cf_raw_voltage_data <- function(ds) { gas_config <- ds$binary$data$gas # data start - data_start_re <- re_combine(re_block("stx"), re_block("fef-0"), re_block("stx"), re_direct(".{4}", size = 4)) + data_start_re <- re_combine( + re_block("stx"), re_block("fef-0"), re_block("stx"), + re_direct(".{4}", size = 4, label = ".{4}")) ds$binary <- ds$binary %>% move_to_next_C_block("CBinary") %>% move_to_next_pattern(data_start_re, max_gap = 0) data_start <- ds$binary$pos # find all masses at end of data - data_end_re <- re_combine(re_direct(".{2}", size = 2), re_block("stx"), re_block("fef-0"), re_block("stx"), re_null(4)) + data_end_re <- re_combine( + re_direct(".{2}", size = 2, label = ".{2}"), re_block("stx"), + re_block("fef-0"), re_block("stx"), re_null(4)) mass_re <- re_combine(re_block("fef-x"), re_text("Mass ")) mass_positions <- ds$binary %>% move_to_next_pattern(data_end_re) %>% find_next_patterns(mass_re) diff --git a/R/isoread_dxf.R b/R/isoread_dxf.R index bd7f4a04..dc04f2c4 100644 --- a/R/isoread_dxf.R +++ b/R/isoread_dxf.R @@ -103,94 +103,104 @@ extract_dxf_raw_voltage_data <- function(ds) { ds$binary <- ds$binary %>% move_to_C_block_range("CPeakFindParameter", "CResultArray") smoothing_positions <- find_next_patterns(ds$binary, re_text("Smoothing")) - peak_center_positions <- find_next_patterns(ds$binary, re_text("Peak Center")) - gas_name_end_re <- re_combine(re_null(4), re_direct("[\x01-\xff]")) + gas_name_end_re <- re_combine(re_null(4), re_direct("[\x01-\xff]", label = "x01-xff")) gas_name_re <- re_combine(re_block("fef-x"), re_block("text0"), gas_name_end_re) for (pos in smoothing_positions) { - gas_names <- ds$binary %>% - move_to_pos(pos) %>% - { cap_at_pos(., find_next_pattern(., re_text("Peak Center"))) } %>% - move_to_next_pattern(gas_name_re, move_to_end = FALSE) %>% + ds$binary <- ds$binary %>% + move_to_pos(pos, reset_cap = TRUE) %>% + { cap_at_pos(., find_next_pattern(., re_text("Peak Center"))) } %>% + move_to_next_pattern(gas_name_re, move_to_end = FALSE) %>% skip_pos(4) %>% # skip the fef-x at the beginning - capture_data("gas_name1", "text", gas_name_end_re, data_bytes_max = 50) %>% - move_to_next_pattern(gas_name_re, move_to_end = FALSE) %>% - skip_pos(4) %>% # skip the fef-x at the beginning - capture_data("gas_name2", "text", gas_name_end_re, data_bytes_max = 50) %>% - { .$data[c("gas_name1", "gas_name2")] } + capture_data("gas_name1", "text", gas_name_end_re, data_bytes_max = 50) + gas_name1 <- ds$binary$data$gas_name1 - # update config with alternative name - if (gas_names$gas_name2 != "" && gas_names$gas_name1 != gas_names$gas_name2 && - any(config_idx <- gas_names$gas_name1 == names(configs))) { - if (default(debug)) - glue::glue( - "renaming config '{gas_names$gas_name1}' to '{gas_names$gas_name2}' ", - "(non-standard config name)") %>% - log_message() - names(configs)[config_idx] <- gas_names$gas_name2 + # gas name 2 + next_gas_name <- find_next_pattern(ds$binary, gas_name_re) + if (!is.null(next_gas_name) && next_gas_name < ds$binary$max_pos) { + ds$binary <- ds$binary %>% + move_to_next_pattern(gas_name_re, move_to_end = FALSE) %>% + skip_pos(4) %>% # skip the fef-x at the beginning + capture_data("gas_name2", "text", gas_name_end_re, data_bytes_max = 50) + gas_name2 <- ds$binary$data$gas_name2 + + # update config with alternative name + if (gas_name2 != "" && gas_name1 != gas_name2 && + any(config_idx <- gas_name1 == names(configs))) { + if (default(debug)) + glue::glue( + "renaming config '{gas_name1}' to '{gas_name2}' ", + "(non-standard config name)") %>% + log_message() + names(configs)[config_idx] <- gas_name2 + } } } - + # move to beginning of original data to get voltages - ds$binary <- ds$binary %>% - set_binary_file_error_prefix("cannot recover raw voltages") %>% - move_to_C_block_range("CAllMoleculeWeights", "CMethod") %>% - move_to_next_C_block("CStringArray") %>% + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot recover raw voltages") %>% + move_to_C_block_range("CAllMoleculeWeights", "CMethod") %>% + move_to_next_C_block("CStringArray") %>% move_to_next_pattern(re_text("OrigDataBlock"), re_null(4), re_block("stx")) - + # find all data sets - data_start_re <- re_combine(re_block("fef-0"), re_null(4), re_block("x-000"), re_block("x-000"), re_direct("..", size = 2), re_block("x-000")) - data_end_re <- re_combine(re_direct(".{4}"), re_null(4), re_block("fef-0"), re_block("stx")) + data_start_re <- re_combine( + re_block("fef-0"), re_null(4), re_block("x-000"), re_block("x-000"), + re_direct("..", size = 2, label = ".."), re_block("x-000")) + data_end_re <- re_combine( + re_direct(".{4}", label = ".{4}"), re_null(4), + re_block("fef-0"), re_block("stx")) gas_config_re <- re_combine(re_block("fef-x"), re_block("text"), re_block("fef-0")) voltages <- data_frame() positions <- find_next_patterns(ds$binary, data_start_re) - + for (pos in positions) { # move to beginning of data ds$binary <- ds$binary %>% move_to_pos(pos + data_start_re$size + 4L) # 4 byte gap before data start_pos <- ds$binary$pos - + # find gas configuration name - gas_data_block_end <- ds$binary %>% - move_to_next_pattern(data_end_re) %>% - move_to_next_pattern(gas_config_re, move_to_end = FALSE, max_gap = 20) %>% + gas_data_block_end <- ds$binary %>% + move_to_next_pattern(data_end_re) %>% + move_to_next_pattern(gas_config_re, move_to_end = FALSE, max_gap = 20) %>% skip_pos(4) %>% # skip the fef-x at the beginning - capture_data("gas", "text", re_block("fef-0"), data_bytes_max = 50) %>% + capture_data("gas", "text", re_block("fef-0"), data_bytes_max = 50) %>% { list(gas = .$data$gas, pos = .$pos) } gas_config <- gas_data_block_end$gas - + # debug message - if (default(debug)) - glue::glue("processing data for '{gas_config}' ({start_pos}-{gas_data_block_end$pos})") %>% + if (default(debug)) + glue::glue("processing data for '{gas_config}' ({start_pos}-{gas_data_block_end$pos})") %>% log_message() - + # find gas configuration - if (!gas_config %in% names(configs)) + if (!gas_config %in% names(configs)) glue::glue("could not find gas configuration for gas '{gas_config}', ", - "available: '{paste(names(configs), collapse = \"', '\")}'") %>% + "available: '{paste(names(configs), collapse = \"', '\")}'") %>% stop(call. = FALSE) - + # find gas configuration masses masses <- configs[[gas_config]]$masses if (is.null(masses)) stop("could not identify measured ions for gas '", gas_config, "'", call. = FALSE) masses_columns <- str_c("v", masses, ".mV") - + # save voltage data - ds$binary <- ds$binary %>% + ds$binary <- ds$binary %>% capture_data("voltages", c("float", rep("double", length(masses))), data_end_re) - voltages <- bind_rows(voltages, - ds$binary$data$voltages %>% + voltages <- bind_rows(voltages, + ds$binary$data$voltages %>% as_data_frame() %>% setNames(c("time.s", masses_columns))) } - + # check for data if (nrow(voltages) == 0) stop("could not find raw voltage data", call. = FALSE) - + # add time point column tp <- time.s <- NULL # global vars ds$raw_data <- - voltages %>% arrange(time.s) %>% - mutate(tp = 1:n()) %>% + voltages %>% arrange(time.s) %>% + mutate(tp = 1:n()) %>% select(tp, time.s, everything()) return(ds) diff --git a/R/isoread_isodat.R b/R/isoread_isodat.R index 83f471a6..70f3f215 100644 --- a/R/isoread_isodat.R +++ b/R/isoread_isodat.R @@ -33,7 +33,7 @@ extract_isodat_resistors <- function(ds) { R_pre_re <- re_combine(re_or(re_text("/"), re_text("-"), size = 2), re_block("fef-0"), re_block("fef-0"), re_null(4), re_block("x-000")) R_post_re <- re_combine(re_block("x-000")) - positions <- find_next_patterns(ds$binary, R_pre_re, re_direct(".{20}"), R_post_re) + positions <- find_next_patterns(ds$binary, R_pre_re, re_direct(".{20}", label = ".{20}"), R_post_re) resistors <- list() for (pos in positions) { ds$binary <- ds$binary %>% @@ -92,7 +92,7 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { # instrument reference name reg exps instrument_pre1 <- re_combine(re_block("etx"), re_or(re_text("/"), re_text(","), re_text("-")), re_block("fef-0"), re_block("fef-x")) ### instrument_pre2 <- re_combine(re_null(4), re_block("stx"), re_block("nl"), re_text("Instrument")) - instrument_post2 <- re_combine(re_null(4), re_direct("[^\\x00]{2}"), re_block("etx")) + instrument_post2 <- re_combine(re_null(4), re_direct("[^\\x00]{2}", label = "[^00]{2}"), re_block("etx")) # capture reference names capture_ref_names <- function(pos) { @@ -146,7 +146,8 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { move_to_next_pattern(re_block("x-000"), re_block("x-000")) %>% capture_n_data("delta_value", "double", 1) %>% move_to_next_pattern(re_block("stx"), re_block("fef-x")) %>% - capture_data("reference", "text", re_null(12), re_direct("([^\\x00]{2})?"), re_block("x-000")) + capture_data("reference", "text", re_null(12), + re_direct("([^\\x00]{2})?", label = "[^00]{2}"), re_block("x-000")) # return as data frame as_data_frame( @@ -227,7 +228,10 @@ extract_isodat_sequence_line_info <- function(ds) { seq_line_info <- list() # note: fef-x block seems to be used in .dxf, nl in .did - re_end_of_info <- re_combine(re_null(4), re_or(re_combine(re_direct(".."), re_block("etx")), re_block("C-block"))) + re_end_of_info <- re_combine( + re_null(4), + re_or(re_combine(re_direct("..", label = ".."), + re_block("etx")), re_block("C-block"))) while(!is.null(find_next_pattern(ds$binary, re_end_of_info))) { ds$binary <- ds$binary %>% move_to_next_pattern(re_text("/"), re_block("fef-x")) %>% @@ -251,30 +255,36 @@ extract_isodat_old_sequence_line_info <- function(ds) { ds$binary <- ds$binary %>% set_binary_file_error_prefix("cannot process sequence line info") %>% move_to_C_block("CSequenceLineInformationGridStorage") %>% - move_to_next_pattern(re_direct("\xff{12}")) + move_to_next_pattern(re_direct("\xff{12}", label = "ff{12}")) # block delimiter cap_pos <- find_next_pattern( - ds$binary, re_direct("\x86{3}\\x00\x96{3}\\x00\xCB{3}\\x00\xB2{3}\\x00\xD7{3}\\x00\xDD{3}\\x00")) + ds$binary, + re_direct("\x86{3}\\x00\x96{3}\\x00\xCB{3}\\x00\xB2{3}\\x00\xD7{3}\\x00\xDD{3}\\x00", + label = "86{3}0096{3}00cb{3}00b2{3}00d7{3}00dd{3}00") + ) if (!is.null(cap_pos)) { ds$binary <- ds$binary %>% cap_at_pos(cap_pos) } else op_error(ds$binary, "cannot find binary delimiter for end of Sequence Information") # first line marker - line_re <- re_combine(re_block("x-000"), re_direct(".{2,8}"), re_block("fef-x"), re_text("Line")) + line_re <- re_combine( + re_block("x-000"), re_direct(".{2,8}", label = ".{2,8}"), + re_block("fef-x"), re_text("Line")) ds$binary <- ds$binary %>% move_to_next_pattern(line_re, move_to_end = FALSE) %>% capture_n_data("info_marker", "raw", 4) # regular expressions re_entry_start <- re_control(ds$binary$data$info_marker) - label_pre_re <- re_combine(re_direct(".{2,8}", size = 8), re_block("fef-x")) + label_pre_re <- re_combine(re_direct(".{2,8}", size = 8, label = ".{2,8}"), re_block("fef-x")) # NOTE: all of these seem to be valid end blocks for text segements in this part of the file, any way to make this simpler? - label_post_re <- re_or(re_combine(re_null(7), re_direct("\xff\\x00{3}")), - re_combine(re_block("x-000"), re_block("fef-x")), - re_combine(re_null(4), re_block("fef-x")), - re_combine(re_null(4), re_block("x-000")), - re_combine(re_null(4), re_direct("\xff{3}\\x00", size = 4))) + label_post_re <- re_or( + re_combine(re_null(7), re_direct("\xff\\x00{3}", label = "ff00{3}")), + re_combine(re_block("x-000"), re_block("fef-x")), + re_combine(re_null(4), re_block("fef-x")), + re_combine(re_null(4), re_block("x-000")), + re_combine(re_null(4), re_direct("\xff{3}\\x00", size = 4, label = "ff{3}00"))) # extract information positions <- find_next_patterns(ds$binary, re_entry_start) @@ -407,8 +417,11 @@ extract_isodat_continuous_flow_vendor_data_table <- function(ds, cap_at_fun = NU ### basic peak info # find basic peak information (Rts, amplitude, bg) - this information is stored separatedly from the rest of the table - rt_pre_re <- re_combine(re_null(18), re_direct("(\\x00|[\x01-\x1f])\\x00{3}", size = 4), re_block("x-000")) - rt_re <- re_combine(rt_pre_re, re_direct("..\\x00{2}"), re_block("x-000")) + rt_pre_re <- re_combine( + re_null(18), + re_direct("(\\x00|[\x01-\x1f])\\x00{3}", size = 4, label = "00|[01-1f]00{3}"), + re_block("x-000")) + rt_re <- re_combine(rt_pre_re, re_direct("..\\x00{2}", label = "..00{2}"), re_block("x-000")) positions <- find_next_patterns(ds$binary, rt_re) rts <- list() for (pos in positions) { @@ -589,8 +602,10 @@ extract_isodat_main_vendor_data_table <- function(ds, C_block, cap_at_fun = NULL # capture data if (columns[[col]]$type == "text") { ds$binary <- - ds$binary %>% move_to_next_pattern(re_block("x-000"), re_direct("\\x00{4,6}"), re_block("x-000"), re_block("x-000")) %>% - capture_data("value", "text", re_null(2), re_direct(".."), re_block("etx")) + ds$binary %>% move_to_next_pattern( + re_block("x-000"), re_direct("\\x00{4,6}", label = "00{4,6}"), + re_block("x-000"), re_block("x-000")) %>% + capture_data("value", "text", re_null(2), re_direct("..", label = ".."), re_block("etx")) } else { ds$binary <- ds$binary %>% move_to_next_pattern(re_block("x-000"), re_block("x-000")) %>% @@ -757,8 +772,10 @@ extract_isodat_main_vendor_data_table_cell_values <- function(ds, cells) { bin <- ds$binary %>% move_to_pos(pos) if (type == "text") { bin <- - bin %>% move_to_next_pattern(re_block("x-000"), re_direct("\\x00{4,6}"), re_block("x-000"), re_block("x-000")) %>% - capture_data("value", "text", re_null(2), re_direct(".."), re_block("etx")) + bin %>% move_to_next_pattern( + re_block("x-000"), re_direct("\\x00{4,6}", label = "00{4,6}"), + re_block("x-000"), re_block("x-000")) %>% + capture_data("value", "text", re_null(2), re_direct("..", label = ".."), re_block("etx")) } else { bin <- bin %>% move_to_next_pattern(re_block("x-000"), re_block("x-000")) %>% diff --git a/R/utils_binary_files.R b/R/utils_binary_files.R index f84cd1fb..008f9311 100644 --- a/R/utils_binary_files.R +++ b/R/utils_binary_files.R @@ -239,7 +239,8 @@ re_text <- function(text) { } # plain regexp (default size is an estimate) -re_direct <- function(regexp, label = regexp, size = length(charToRaw(regexp))) { +# @param label changing to proper name to avoid character errors +re_direct <- function(regexp, label = "re-direct", size = length(charToRaw(regexp))) { structure( list( label = sprintf("[%s]", label), diff --git a/tests/testthat/test-continuous-flow.R b/tests/testthat/test-continuous-flow.R index 77270090..fae90ea8 100644 --- a/tests/testthat/test-continuous-flow.R +++ b/tests/testthat/test-continuous-flow.R @@ -54,6 +54,14 @@ test_that("test that dxf files can be read", { expect_is(dxf <- iso_read_continuous_flow(file), "continuous_flow") expect_equal(nrow(problems(dxf)), 0) + expect_true(file.exists(file <- file.path(test_folder, "dxf_example_HO_02.dxf"))) + expect_is(dxf <- iso_read_continuous_flow(file), "continuous_flow") + expect_equal(nrow(problems(dxf)), 0) + + expect_true(file.exists(file <- file.path(test_folder, "dxf_example_CNS_01.dxf"))) + expect_is(dxf <- iso_read_continuous_flow(file), "continuous_flow") + expect_equal(nrow(problems(dxf)), 0) + expect_true(file.exists(file <- file.path(test_folder, "dxf_example_N2_01.dxf"))) expect_is(dxf <- iso_read_continuous_flow(file), "continuous_flow") expect_equal(nrow(problems(dxf)), 0) From 050dc789231034e72cf6eecc4f47e2412a1d06c1 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sat, 8 Feb 2020 13:51:07 -0700 Subject: [PATCH 06/51] upgrade to tidyselect 1.0.0 syntax in file info operations --- R/file_info_operations.R | 112 ++++++++++----------- tests/testthat/test-file-info-operations.R | 6 +- 2 files changed, 54 insertions(+), 64 deletions(-) diff --git a/R/file_info_operations.R b/R/file_info_operations.R index 55333ff3..e50535cf 100644 --- a/R/file_info_operations.R +++ b/R/file_info_operations.R @@ -2,53 +2,11 @@ # rename & select utils ======== -# internal function for rename and select -select_rename_isofile <- function(isofile, func, quos) { - - # global vars - to <- changed <- NULL - - old_vars <- names(isofile$file_info) - new_vars <- func(old_vars, !!!quos, .strict = FALSE) - # make sure file_id is always included - if (!"file_id" %in% new_vars) - new_vars <- c(c(file_id = "file_id"), new_vars) - - # change vars data frame - vars <- tibble( - file = isofile$file_info$file_id, - from = as.character(new_vars), - to = names(new_vars), - changed = !to %in% old_vars - ) - - # check on file_id rename - check_vars <- "file_id" +# check that specific columns have not been renamed +check_forbidden_renames <- function(vars, check_vars) { if (any(prob <- check_vars %in% filter(vars, changed)$from)) { glue::glue("renaming the '{paste(check_vars[prob], collapse = \"', '\")}' column ", - "may lead to unpredictable behaviour and is therefore not allowed, sorry") %>% - stop(call. = FALSE) - } - isofile$file_info <- dplyr::select(isofile$file_info, new_vars) - return(list(isofile = isofile, vars = vars)) -} - -# internal function to check for rename/select duplicates -check_names_changes <- function(vars) { - - # global vars - to <- from <- NULL - - reps <- vars %>% group_by(file, to) %>% - summarize(n = n(), from = paste(from, collapse = "', '")) %>% - ungroup() %>% - filter(n > 1) - if (nrow(reps)> 0) { - labels <- reps %>% select(to, from) %>% unique() %>% - mutate(label = paste0(" - '", to, "' <= '", from, "'")) %>% - { paste(.$label, collapse = "\n") } - glue::glue("the following column(s) would be assigned to the same name in at ", - "least 1 file leading to an unresolvable naming conflict:\n{labels}") %>% + "may lead to unpredictable behaviour and is therefore not allowed, sorry") %>% stop(call. = FALSE) } } @@ -85,16 +43,33 @@ iso_select_file_info.iso_file_list <- function(iso_files, ..., quiet = default(q changed <- to <- from <- NULL # variables for all files - select_quos <- quos(...) - - # select - isofiles_select <- map(iso_files, select_rename_isofile, tidyselect::vars_select, select_quos) + select_expr <- rlang::expr(c(...)) + + # run select + isofiles_select <- map(iso_files, function(isofile) { + # select positions (always include file_id) + file_id_pos <- tidyselect::eval_select(rlang::expr(file_id), data = isofile$file_info) + pos <- tidyselect::eval_select(select_expr, data = isofile$file_info, strict = FALSE) + if (!file_id_pos %in% pos) pos <- c(file_id_pos, pos) + # selected variables + vars <- tibble( + file_id = isofile$file_info$file_id, + from = names(isofile$file_info)[pos], + to = names(pos), + changed = from != to + ) + # make selection + isofile$file_info <- rlang::set_names(isofile$file_info[pos], names(pos)) + #return both + return(list(isofile = isofile, vars = vars)) + } + ) - # info summary - all_vars <- map2(names(isofiles_select), isofiles_select, ~mutate(.y$vars, file_id = .x)) %>% bind_rows() + # variable summary + all_vars <- map(isofiles_select, "vars") %>% bind_rows() - # check for duplicates - check_names_changes(all_vars) + # safety check on file ID rename + check_forbidden_renames(all_vars, "file_id") # summary information if (!quiet) { @@ -165,16 +140,31 @@ iso_rename_file_info.iso_file_list <- function(iso_files, ..., quiet = default(q changed <- to <- from <- NULL # variables for all files - rename_quos <- quos(...) - - # rename - isofiles_rename <- map(iso_files, select_rename_isofile, tidyselect::vars_rename, rename_quos) + rename_expr <- rlang::expr(c(...)) + + # run select + isofiles_rename <- map(iso_files, function(isofile) { + # rename positions + pos <- tidyselect::eval_rename(rename_expr, data = isofile$file_info, strict = FALSE) + # selected variables + vars <- tibble( + file_id = isofile$file_info$file_id, + from = names(isofile$file_info)[pos], + to = names(pos), + changed = from != to + ) + # make rename + names(isofile$file_info)[pos] <- names(pos) + #return both + return(list(isofile = isofile, vars = vars)) + } + ) - # info summary - all_vars <- map2(names(isofiles_rename), isofiles_rename, ~mutate(.y$vars, file_id = .x)) %>% bind_rows() + # variable summary + all_vars <- map(isofiles_rename, "vars") %>% bind_rows() - # check for duplicates - check_names_changes(all_vars) + # safety check on file ID rename + check_forbidden_renames(all_vars, "file_id") # summary information if (!quiet) { diff --git a/tests/testthat/test-file-info-operations.R b/tests/testthat/test-file-info-operations.R index 2a69b339..79ff7507 100644 --- a/tests/testthat/test-file-info-operations.R +++ b/tests/testthat/test-file-info-operations.R @@ -19,7 +19,7 @@ test_that("Test that selecting/renaming file info works", { expect_error(iso_select_file_info(42), "not defined") expect_error(iso_select_file_info(iso_file1, new = file_id), "renaming.*not allowed") expect_error(iso_select_file_info(iso_files, new = file_id), "renaming.*not allowed") - expect_error(iso_select_file_info(iso_files, y = new_info, y = new_info2), "unresolvable naming conflict") + expect_error(iso_select_file_info(iso_files, y = new_info, y = new_info2), class = "vctrs_error_names_must_be_unique", "must be unique") # select info message expect_message(iso_select_file_info(iso_file1), "keeping 1") # always file_info @@ -55,8 +55,8 @@ test_that("Test that selecting/renaming file info works", { expect_error(iso_rename_file_info(42), "not defined") expect_error(iso_rename_file_info(iso_file1, new = file_id), "renaming.*not allowed") expect_error(iso_rename_file_info(iso_files, new = file_id), "renaming.*not allowed") - expect_error(iso_rename_file_info(iso_files, new_info = new_info2), "unresolvable naming conflict") - expect_error(iso_rename_file_info(iso_files, y = new_info, y = new_info2), "unresolvable naming conflict") + expect_error(iso_rename_file_info(iso_files, new_info = new_info2), class = "vctrs_error_names_must_be_unique", "must be unique") + expect_error(iso_rename_file_info(iso_files, y = new_info, y = new_info2), class = "vctrs_error_names_must_be_unique", "must be unique") # rename info message expect_message(iso_rename_file_info(iso_file1), "renaming 0") From 8f96348ff86b9319b79c1c2b51205f6ea45500b7 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sat, 8 Feb 2020 21:15:02 -0700 Subject: [PATCH 07/51] switch from vars_select to eval_select for compatibility with tidyselect >= 1.0.0 #91 --- DESCRIPTION | 8 ++++---- NAMESPACE | 1 - R/file_info_operations.R | 18 ++++++++++++------ R/nse.R | 15 ++++++++++++--- R/package.R | 2 +- tests/testthat/test-aggregate-data.R | 2 +- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 42da514b..553912e0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: isoreader Title: Read IRMS data files Description: R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry. -Version: 1.0.13 +Version: 1.0.14 Authors@R: person("Sebastian", "Kopf", email = "sebastian.kopf@colorado.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2044-0201")) URL: https://github.com/isoverse/isoreader BugReports: https://github.com/isoverse/isoreader/issues @@ -11,9 +11,9 @@ Imports: methods, R.utils, magrittr, - rlang (>= 0.1.4), + rlang (>= 0.4.4), glue, - tidyselect (>= 0.2.3), + tidyselect (>= 1.0.0), vctrs, tibble, dplyr (>= 0.7.4), @@ -41,4 +41,4 @@ License: GPL (>= 2) | file LICENSE Encoding: UTF-8 LazyData: true VignetteBuilder: knitr -RoxygenNote: 6.1.1 +RoxygenNote: 7.0.2 diff --git a/NAMESPACE b/NAMESPACE index 5ddeda2c..b91a10a5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -268,7 +268,6 @@ importFrom(tidyselect,ends_with) importFrom(tidyselect,everything) importFrom(tidyselect,matches) importFrom(tidyselect,starts_with) -importFrom(tidyselect,vars_select) importFrom(utils,head) importFrom(utils,modifyList) importFrom(utils,packageVersion) diff --git a/R/file_info_operations.R b/R/file_info_operations.R index e50535cf..f45dc021 100644 --- a/R/file_info_operations.R +++ b/R/file_info_operations.R @@ -367,12 +367,18 @@ iso_parse_file_info.iso_file_list <- function(iso_files, number = c(), double = # determine variables vars <- list( - number = tidyselect::vars_select(names(file_info), !!enquo(number)), - double = tidyselect::vars_select(names(file_info), !!enquo(double)), - integer = tidyselect::vars_select(names(file_info), !!enquo(integer)), - logical = tidyselect::vars_select(names(file_info), !!enquo(logical)), - datetime = tidyselect::vars_select(names(file_info), !!enquo(datetime)), - text = tidyselect::vars_select(names(file_info), !!enquo(text)) + number = + names(file_info)[tidyselect::eval_select(rlang::enexpr(number), file_info)], + double = + names(file_info)[tidyselect::eval_select(rlang::enexpr(double), file_info)], + integer = + names(file_info)[tidyselect::eval_select(rlang::enexpr(integer), file_info)], + logical = + names(file_info)[tidyselect::eval_select(rlang::enexpr(logical), file_info)], + datetime = + names(file_info)[tidyselect::eval_select(rlang::enexpr(datetime), file_info)], + text = + names(file_info)[tidyselect::eval_select(rlang::enexpr(text), file_info)] ) %>% tibble::enframe(name = "parse", value = "column") %>% tidyr::unnest(column) %>% diff --git a/R/nse.R b/R/nse.R index bfdcb4e6..284f6506 100644 --- a/R/nse.R +++ b/R/nse.R @@ -64,14 +64,23 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ glue("parameter {df_name} is not a data frame") %>% stop(call. = FALSE) # use a safe version of vars_select to get all the column names - safe_vars_select <- safely(vars_select) + # note: uses tidyselect:eval_select because vars_select is in questioning stage + safe_vars_select <- function(col_quo, ...) { + safe_result <- safely(tidyselect::eval_select)(rlang::expr(c(!!col_quo)), df, ...) + if (is.null(safe_result$error)) { + safe_result$result <- rlang::set_names(names(df)[safe_result$result], names(safe_result$result)) + } + return(safe_result) + } + + # get all quos lquos <- list(...) cols_quos <- quos(!!!lquos) %>% # make sure to evaluate calls to default resolve_defaults() %>% # make sure that the expressions are locally evaluated map(~quo(!!rlang::quo_get_expr(.x))) - cols_results <- map(cols_quos, ~safe_vars_select(names(df), !!.x)) + cols_results <- map(cols_quos, safe_vars_select) ok <- map_lgl(cols_results, ~is.null(.x$error)) # summarize if there were any errors @@ -94,7 +103,7 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ } else { # just a warning and find the columns omitting those missing warning(err_msg, immediate. = TRUE, call. = FALSE) - cols_results <- map(cols_quos, ~safe_vars_select(names(df), !!.x, .strict = FALSE)) + cols_results <- map(cols_quos, safe_vars_select, strict = FALSE) } } diff --git a/R/package.R b/R/package.R index d1bd4cfb..09180479 100644 --- a/R/package.R +++ b/R/package.R @@ -2,7 +2,7 @@ "_PACKAGE" #' @importFrom rlang enquo quo quos UQ !! !!! := get_expr quo_squash quo_text quo_is_null quo_is_symbol is_quosure is_empty is_integerish eval_tidy sym new_formula f_lhs f_rhs -#' @importFrom tidyselect everything starts_with ends_with matches vars_select +#' @importFrom tidyselect everything starts_with ends_with matches #' @importFrom tibble tibble is_tibble #' @importFrom dplyr vars n select rename arrange desc mutate mutate_at mutate_if filter distinct as_data_frame left_join right_join full_join data_frame bind_rows bind_cols group_by ungroup tally summarize do case_when #' @importFrom tidyr gather spread nest unnest extract diff --git a/tests/testthat/test-aggregate-data.R b/tests/testthat/test-aggregate-data.R index b9d39883..ae418e8a 100644 --- a/tests/testthat/test-aggregate-data.R +++ b/tests/testthat/test-aggregate-data.R @@ -145,7 +145,7 @@ test_that("test that aggregating file info works", { expect_equal(names(iso_get_file_info(iso_file1, select = c(x = file_datetime, y = only_a))), c("file_id", "x", "y")) expect_equal(names(iso_get_file_info(iso_file1, select = starts_with("file"))), c("file_id", "file_root", "file_path", "file_subpath", "file_datetime")) expect_error(iso_get_file_info(iso_file1, select = c(x = file_id)), "renaming.*file_id.*may lead to unpredictable") - expect_equal(names(iso_get_file_info(iso_file1, select = c(x = starts_with("file"), file_id))), c("file_id", "x2", "x3", "x4", "x5")) + expect_equal(names(iso_get_file_info(iso_file1, select = c(starts_with("file")))), c("file_id", "file_root", "file_path", "file_subpath", "file_datetime")) # note: not sure how to implement but this should probably throw a warning expect_equal(names(iso_get_file_info(iso_file2, select = c("file_datetime", "only_a"))), c("file_id", "file_datetime")) }) From a89c2a62b4cf877907a2439f6a8c7c7627fcd221 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sat, 8 Feb 2020 21:15:20 -0700 Subject: [PATCH 08/51] update to roxygen 7.0 --- man/extract_data.Rd | 7 +++-- man/extract_substring.Rd | 14 ++++++--- man/extract_word.Rd | 19 ++++++++---- man/file_readers.Rd | 30 +++++++++++++------ man/iso_add_file_info.Rd | 19 ++++++------ man/iso_caching.Rd | 9 +++--- man/iso_double_with_units.Rd | 11 +++---- man/iso_expand_paths.Rd | 7 +++-- man/iso_export_to_excel.Rd | 21 ++++++++++---- man/iso_export_to_feather.Rd | 22 +++++++++----- man/iso_export_to_rda.Rd | 7 +++-- man/iso_filter_files.Rd | 11 +++---- man/iso_filter_files_with_problems.Rd | 18 +++++++----- man/iso_find_absolute_path_roots.Rd | 7 +++-- man/iso_get_bgrd_data.Rd | 22 ++++++++------ man/iso_get_data.Rd | 27 ++++++++++------- man/iso_get_default_reader_parameters.Rd | 9 +++--- man/iso_get_file_info.Rd | 16 +++++------ man/iso_get_problems.Rd | 9 +++--- man/iso_get_problems_summary.Rd | 9 +++--- man/iso_get_raw_data.Rd | 35 ++++++++++++++--------- man/iso_get_resistors_info.Rd | 20 +++++++------ man/iso_get_standards_info.Rd | 21 ++++++++------ man/iso_get_supported_file_types.Rd | 3 +- man/iso_get_units.Rd | 11 +++---- man/iso_get_vendor_data_table.Rd | 24 ++++++++++------ man/iso_has_problems.Rd | 9 +++--- man/iso_info_messages.Rd | 9 +++--- man/iso_is_double_with_units.Rd | 11 +++---- man/iso_make_units_explicit.Rd | 11 +++---- man/iso_make_units_implicit.Rd | 11 +++---- man/iso_mutate_file_info.Rd | 11 +++---- man/iso_parse_file_info.Rd | 24 ++++++++++------ man/iso_problem_functions.Rd | 9 +++--- man/iso_read_continuous_flow.Rd | 19 ++++++++---- man/iso_read_dual_inlet.Rd | 20 +++++++++---- man/iso_read_files.Rd | 21 ++++++++++---- man/iso_rename_file_info.Rd | 11 +++---- man/iso_reread_files.Rd | 18 ++++++++---- man/iso_root_paths.Rd | 7 +++-- man/iso_save.Rd | 7 +++-- man/iso_select_file_info.Rd | 11 +++---- man/iso_set_default_read_parameters.Rd | 20 ++++++++----- man/iso_shorten_relative_paths.Rd | 7 +++-- man/iso_show_default_reader_parameters.Rd | 9 +++--- man/iso_strip_units.Rd | 11 +++---- man/isoreader-package.Rd | 2 +- man/print.binary_structure_map.Rd | 9 ++++-- man/read_iso_file.Rd | 18 ++++++++++-- man/reexports.Rd | 4 +-- 50 files changed, 433 insertions(+), 264 deletions(-) diff --git a/man/extract_data.Rd b/man/extract_data.Rd index 2bd96a45..8f0f765e 100644 --- a/man/extract_data.Rd +++ b/man/extract_data.Rd @@ -14,7 +14,7 @@ For simultaneous extraction of pure text data into multiple columns, please see \item \code{\link{extract_substring}} is a generic convience function to extract parts of textual data (based on regular expression matches). Can be used in combination with the parsing functions to turn extracted substrings into numerical or logical data. -\item \code{\link{extract_word}} is a more specific convenience function to extract the 1st/2nd/3rd word from textual data. +\item \code{\link{extract_word}} is a more specific convenience function to extract the 1st/2nd/3rd word from textual data. \item \code{\link[readr]{parse_number}} is a convenience function to extract a number even if it is surrouded by text (re-exported from the \link{readr} package). @@ -31,7 +31,8 @@ use \code{\link[readr]{parse_number}} instead if this is not the case (re-export } } \seealso{ -Other data extraction functions: \code{\link{extract_substring}}, - \code{\link{extract_word}} +Other data extraction functions: +\code{\link{extract_substring}()}, +\code{\link{extract_word}()} } \concept{data extraction functions} diff --git a/man/extract_substring.Rd b/man/extract_substring.Rd index b1bf1f3b..6469c18c 100644 --- a/man/extract_substring.Rd +++ b/man/extract_substring.Rd @@ -4,8 +4,13 @@ \alias{extract_substring} \title{Extract a substring from text} \usage{ -extract_substring(string, pattern, capture_n = 1, capture_bracket = 0, - missing = NA_character_) +extract_substring( + string, + pattern, + capture_n = 1, + capture_bracket = 0, + missing = NA_character_ +) } \arguments{ \item{string}{string to extract} @@ -27,7 +32,8 @@ This is a convenience function to capture substrings from textual data. Uses \code{\link[stringr]{str_match_all}} internally but instead of returning everything, always returns only one single part of the match, depending on parameters \code{capture_n} and \code{capture_group}. } \seealso{ -Other data extraction functions: \code{\link{extract_data}}, - \code{\link{extract_word}} +Other data extraction functions: +\code{\link{extract_data}}, +\code{\link{extract_word}()} } \concept{data extraction functions} diff --git a/man/extract_word.Rd b/man/extract_word.Rd index 6fb473cc..6ebf931a 100644 --- a/man/extract_word.Rd +++ b/man/extract_word.Rd @@ -4,10 +4,16 @@ \alias{extract_word} \title{Extract words from text} \usage{ -extract_word(string, capture_n = 1, include_numbers = TRUE, - include_underscore = FALSE, include_dash = FALSE, - include_space = FALSE, include_colon = FALSE, - missing = NA_character_) +extract_word( + string, + capture_n = 1, + include_numbers = TRUE, + include_underscore = FALSE, + include_dash = FALSE, + include_space = FALSE, + include_colon = FALSE, + missing = NA_character_ +) } \arguments{ \item{string}{string to extract} @@ -38,7 +44,8 @@ x_num <- parse_number(x_text) # 16.2 7.0 } \seealso{ -Other data extraction functions: \code{\link{extract_data}}, - \code{\link{extract_substring}} +Other data extraction functions: +\code{\link{extract_data}}, +\code{\link{extract_substring}()} } \concept{data extraction functions} diff --git a/man/file_readers.Rd b/man/file_readers.Rd index 8808338d..9a9629da 100644 --- a/man/file_readers.Rd +++ b/man/file_readers.Rd @@ -5,13 +5,23 @@ \alias{iso_register_continuous_flow_file_reader} \title{Register file readers} \usage{ -iso_register_dual_inlet_file_reader(extension, func, - description = NA_character_, cacheable = TRUE, overwrite = FALSE, - env = find_func(func)) - -iso_register_continuous_flow_file_reader(extension, func, - description = NA_character_, cacheable = TRUE, overwrite = FALSE, - env = find_func(func)) +iso_register_dual_inlet_file_reader( + extension, + func, + description = NA_character_, + cacheable = TRUE, + overwrite = FALSE, + env = find_func(func) +) + +iso_register_continuous_flow_file_reader( + extension, + func, + description = NA_character_, + cacheable = TRUE, + overwrite = FALSE, + env = find_func(func) +) } \arguments{ \item{extension}{the file extension (e.g. \code{.dxf}) of the data file. Must be unique otherwise different files can not automatically be matched with the appropriate file reader based on their extension.} @@ -35,8 +45,10 @@ Register file extensions and reader functions for different data files. Isoreade \code{iso_register_continuous_flow_file_reader}: use this function to register file readers for continuous flow files. } \seealso{ -Other file_types: \code{\link{iso_get_supported_file_types}} +Other file_types: +\code{\link{iso_get_supported_file_types}()} -Other file_types: \code{\link{iso_get_supported_file_types}} +Other file_types: +\code{\link{iso_get_supported_file_types}()} } \concept{file_types} diff --git a/man/iso_add_file_info.Rd b/man/iso_add_file_info.Rd index e88cb1ff..15f408e1 100644 --- a/man/iso_add_file_info.Rd +++ b/man/iso_add_file_info.Rd @@ -6,11 +6,9 @@ \alias{iso_add_file_info} \title{Add additional file information} \usage{ -\method{iso_add_file_info}{iso_file_list}(iso_files, new_file_info, ..., - quiet = default(quiet)) +\method{iso_add_file_info}{iso_file_list}(iso_files, new_file_info, ..., quiet = default(quiet)) -\method{iso_add_file_info}{data.frame}(df, new_file_info, ..., - quiet = default(quiet)) +\method{iso_add_file_info}{data.frame}(df, new_file_info, ..., quiet = default(quiet)) iso_add_file_info(...) } @@ -32,15 +30,16 @@ the original iso files or data frame with the new file info added in. This function makes it easy to add additional file info (\code{\link{iso_get_file_info}}) to isofile objects and data frames by a single \code{\link[dplyr]{left_join}} or multiple sequential \code{\link[dplyr]{left_join}} operations. The function provides a detailed summary of the information that was added unless \code{quiet = TRUE}. Note that one-to-many joins are not permitted (and will fail with an informative error) since this would lead to likely unintended data duplication in the isofiles. However, one-to-one and many-to-one joins are fully supported and should cover all needed use cases for this function. Also note that for each join, only the \code{new_file_info} rows that have defined non-NA, non-empty ("") values in all \code{join_by} columns will be considered for the join and that only \code{new_file_info} columns that do NOT already exist in ANY file information will be added. For changing the values of existing file information, please use \code{\link{iso_mutate_file_info}} instead. } \details{ -Single \code{\link[dplyr]{left_join}}: this is the most common use of this function and basically a simple left join operation (with some additional safety checks). Specify a single \code{join_by} in the \code{...}, such as e.g. \code{c("file_id")} to add additional file information joining by the \code{file_id} column. +Single \code{\link[dplyr]{left_join}}: this is the most common use of this function and basically a simple left join operation (with some additional safety checks). Specify a single \code{join_by} in the \code{...}, such as e.g. \code{c("file_id")} to add additional file information joining by the \code{file_id} column. Multiple sequential \code{\link[dplyr]{left_join}}: this use case is for applying a set of increasingly more specific \code{join_by} rules. For example, \code{... = c("Identifier 1", "Identifier 2"), c("file_id")} would serve to first add one set of new file information for all isofiles based on their \code{Identifier 1} and \code{Identifier 2} columns and then overwrite the new information with more specific details for a subset of isofiles based on their \code{file_id} column, all based on a single overview \code{new_file_info} data frame. Basically, each set of \code{join_by} conditions specified in \code{...} must describe a valid \code{\link[dplyr]{left_join}} \code{join_by} parameter to merge the \code{new_file_info} with the existing file info. Each set of \code{new_file_info} data can overwrite the previous \code{join_by} matches such that the last set of \code{join_by} column(s) provided in \code{...} will overwrite all previous matches for which it applies, even if they have already been a match for a previous column. } \seealso{ -Other file_info operations: \code{\link{iso_filter_files}}, - \code{\link{iso_mutate_file_info}}, - \code{\link{iso_parse_file_info}}, - \code{\link{iso_rename_file_info}}, - \code{\link{iso_select_file_info}} +Other file_info operations: +\code{\link{iso_filter_files}()}, +\code{\link{iso_mutate_file_info}()}, +\code{\link{iso_parse_file_info}()}, +\code{\link{iso_rename_file_info}()}, +\code{\link{iso_select_file_info}()} } \concept{file_info operations} diff --git a/man/iso_caching.Rd b/man/iso_caching.Rd index b120f870..4bec9957 100644 --- a/man/iso_caching.Rd +++ b/man/iso_caching.Rd @@ -17,9 +17,10 @@ iso_turn_reader_caching_off(data = NULL) These functions turn caching of data files (and reading from cache) on/off in all subsequent isoread calls by changing the global settings for the \code{cache} parameter. Can be called stand alone or within a pipeline. } \seealso{ -Other settings functions: \code{\link{iso_get_default_reader_parameters}}, - \code{\link{iso_info_messages}}, - \code{\link{iso_set_default_read_parameters}}, - \code{\link{iso_show_default_reader_parameters}} +Other settings functions: +\code{\link{iso_get_default_reader_parameters}()}, +\code{\link{iso_info_messages}}, +\code{\link{iso_set_default_read_parameters}()}, +\code{\link{iso_show_default_reader_parameters}()} } \concept{settings functions} diff --git a/man/iso_double_with_units.Rd b/man/iso_double_with_units.Rd index 9ada05bd..78ea8476 100644 --- a/man/iso_double_with_units.Rd +++ b/man/iso_double_with_units.Rd @@ -15,10 +15,11 @@ iso_double_with_units(x = double(), units = "undefined units") This function generates a number with units that work well within data frames and tibbles and implement safety checks on numerical operations with numbers that have different units. To retrieve the numerical value without units, use \code{\link{iso_strip_units}} (works for single variables and data frames/tibbles) or simply \code{as.numeric} (for single variables). To retrieve the unit use \code{\link{iso_get_units}}. Note that to correctly combine data frames / tibbles that have values with units in them, use \link[vctrs]{vec_rbind} instead of \link{rbind} or \link[dplyr]{bind_rows}. \link[vctrs]{vec_rbind} will combine columns that have values with units if they have the same unit and otherwise convert to a simple number with a warning. The other functions will either fail or reduce the unit values to plain numbers with a cryptic warning message about not preserving attributes. } \seealso{ -Other functions for values with units: \code{\link{iso_get_units}}, - \code{\link{iso_is_double_with_units}}, - \code{\link{iso_make_units_explicit}}, - \code{\link{iso_make_units_implicit}}, - \code{\link{iso_strip_units}} +Other functions for values with units: +\code{\link{iso_get_units}()}, +\code{\link{iso_is_double_with_units}()}, +\code{\link{iso_make_units_explicit}()}, +\code{\link{iso_make_units_implicit}()}, +\code{\link{iso_strip_units}()} } \concept{functions for values with units} diff --git a/man/iso_expand_paths.Rd b/man/iso_expand_paths.Rd index f9f0843c..82814649 100644 --- a/man/iso_expand_paths.Rd +++ b/man/iso_expand_paths.Rd @@ -20,8 +20,9 @@ data frame with columns \code{root} (\code{root} as provided) and \code{path} of Helper function to expand the provided paths to find data files in folders and subfolders that match any of the specified extensions. Filepaths will be kept as is, only folders will be expanded. Note that this function is rarely called directly. It is used automatically by \code{\link{iso_read_dual_inlet}} and \code{\link{iso_read_continuous_flow}} to identify fiels of interest based on the file paths provided. } \seealso{ -Other file system functions: \code{\link{iso_find_absolute_path_roots}}, - \code{\link{iso_root_paths}}, - \code{\link{iso_shorten_relative_paths}} +Other file system functions: +\code{\link{iso_find_absolute_path_roots}()}, +\code{\link{iso_root_paths}()}, +\code{\link{iso_shorten_relative_paths}()} } \concept{file system functions} diff --git a/man/iso_export_to_excel.Rd b/man/iso_export_to_excel.Rd index 4a19a3c9..b7eb23b2 100644 --- a/man/iso_export_to_excel.Rd +++ b/man/iso_export_to_excel.Rd @@ -4,10 +4,17 @@ \alias{iso_export_to_excel} \title{Export data to Excel} \usage{ -iso_export_to_excel(iso_files, filepath, include_raw_data = TRUE, - include_file_info = TRUE, include_method_info = TRUE, - include_vendor_data_table = TRUE, include_problems = TRUE, - with_explicit_units = FALSE, quiet = default(quiet)) +iso_export_to_excel( + iso_files, + filepath, + include_raw_data = TRUE, + include_file_info = TRUE, + include_method_info = TRUE, + include_vendor_data_table = TRUE, + include_problems = TRUE, + with_explicit_units = FALSE, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -35,7 +42,9 @@ returns the iso_files object invisibly for use in pipelines This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file but they are only exported if the corresponding \code{include_} parameter is set to \code{TRUE} and only for data types for which this type of data is available and was read (see \code{\link{iso_read_dual_inlet}}, \code{\link{iso_read_continuous_flow}} for details on read parameters). Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export. } \seealso{ -Other export functions: \code{\link{iso_export_to_feather}}, - \code{\link{iso_export_to_rda}}, \code{\link{iso_save}} +Other export functions: +\code{\link{iso_export_to_feather}()}, +\code{\link{iso_export_to_rda}()}, +\code{\link{iso_save}()} } \concept{export functions} diff --git a/man/iso_export_to_feather.Rd b/man/iso_export_to_feather.Rd index 16dfadeb..29de808c 100644 --- a/man/iso_export_to_feather.Rd +++ b/man/iso_export_to_feather.Rd @@ -4,11 +4,17 @@ \alias{iso_export_to_feather} \title{Export to feather} \usage{ -iso_export_to_feather(iso_files, filepath_prefix, - include_raw_data = TRUE, include_file_info = TRUE, - include_method_info = TRUE, include_vendor_data_table = TRUE, - include_problems = TRUE, with_explicit_units = FALSE, - quiet = default(quiet)) +iso_export_to_feather( + iso_files, + filepath_prefix, + include_raw_data = TRUE, + include_file_info = TRUE, + include_method_info = TRUE, + include_vendor_data_table = TRUE, + include_problems = TRUE, + with_explicit_units = FALSE, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -36,7 +42,9 @@ returns the iso_files object invisibly for use in pipelines This function exports the passed in iso_files to the Python and R shared feather file format. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate feather files that are saved with the provided \code{filepath_prefix} as prefix. All are only exported if the corresponding \code{include_} parameter is set to \code{TRUE} and only for data types for which this type of data is available and was read (see \code{\link{iso_read_dual_inlet}}, \code{\link{iso_read_continuous_flow}} for details on read parameters). Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in feather output. } \seealso{ -Other export functions: \code{\link{iso_export_to_excel}}, - \code{\link{iso_export_to_rda}}, \code{\link{iso_save}} +Other export functions: +\code{\link{iso_export_to_excel}()}, +\code{\link{iso_export_to_rda}()}, +\code{\link{iso_save}()} } \concept{export functions} diff --git a/man/iso_export_to_rda.Rd b/man/iso_export_to_rda.Rd index 56a16281..2bf29263 100644 --- a/man/iso_export_to_rda.Rd +++ b/man/iso_export_to_rda.Rd @@ -20,8 +20,9 @@ returns the iso_files object invisibly for use in pipelines This function is deprecated. Please use \code{\link{iso_save}} instead to save collections of isofiles. } \seealso{ -Other export functions: \code{\link{iso_export_to_excel}}, - \code{\link{iso_export_to_feather}}, - \code{\link{iso_save}} +Other export functions: +\code{\link{iso_export_to_excel}()}, +\code{\link{iso_export_to_feather}()}, +\code{\link{iso_save}()} } \concept{export functions} diff --git a/man/iso_filter_files.Rd b/man/iso_filter_files.Rd index ada5443f..83781bec 100644 --- a/man/iso_filter_files.Rd +++ b/man/iso_filter_files.Rd @@ -17,10 +17,11 @@ iso_filter_files(iso_files, ..., quiet = default(quiet)) Filter for specific isofiles using file info columns (\code{\link{iso_get_file_info}}). Works just like dplyr's \link[dplyr]{filter} except that it provides the user with some information on what has been filtered. Returns \code{NULL} if none of the isofiles' file info matches the filter criteria. You can also use \link[dplyr]{filter} directly to filter collections of \code{iso_file} objects. } \seealso{ -Other file_info operations: \code{\link{iso_add_file_info.iso_file_list}}, - \code{\link{iso_mutate_file_info}}, - \code{\link{iso_parse_file_info}}, - \code{\link{iso_rename_file_info}}, - \code{\link{iso_select_file_info}} +Other file_info operations: +\code{\link{iso_add_file_info.iso_file_list}()}, +\code{\link{iso_mutate_file_info}()}, +\code{\link{iso_parse_file_info}()}, +\code{\link{iso_rename_file_info}()}, +\code{\link{iso_select_file_info}()} } \concept{file_info operations} diff --git a/man/iso_filter_files_with_problems.Rd b/man/iso_filter_files_with_problems.Rd index 07f0bcc1..626f4c50 100644 --- a/man/iso_filter_files_with_problems.Rd +++ b/man/iso_filter_files_with_problems.Rd @@ -4,9 +4,12 @@ \alias{iso_filter_files_with_problems} \title{Filter out problematic files} \usage{ -iso_filter_files_with_problems(iso_files, - remove_files_with_errors = TRUE, remove_files_with_warnings = FALSE, - quiet = default(quiet)) +iso_filter_files_with_problems( + iso_files, + remove_files_with_errors = TRUE, + remove_files_with_warnings = FALSE, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -21,9 +24,10 @@ iso_filter_files_with_problems(iso_files, Use this function to filter out files that have encountered problems, either errors, warnings or both and returns the remaining iso_files. For additional functions available to check for and deal with problems, see the \link{iso_problem_functions}. } \seealso{ -Other problem functions: \code{\link{iso_get_problems_summary}}, - \code{\link{iso_get_problems}}, - \code{\link{iso_has_problems}}, - \code{\link{iso_problem_functions}} +Other problem functions: +\code{\link{iso_get_problems_summary}()}, +\code{\link{iso_get_problems}()}, +\code{\link{iso_has_problems}()}, +\code{\link{iso_problem_functions}} } \concept{problem functions} diff --git a/man/iso_find_absolute_path_roots.Rd b/man/iso_find_absolute_path_roots.Rd index 0af5efa4..2b209382 100644 --- a/man/iso_find_absolute_path_roots.Rd +++ b/man/iso_find_absolute_path_roots.Rd @@ -20,8 +20,9 @@ a data frame with the root directories and paths relative to the root - order of Helper function to find the roots of absolute paths. Tries to put absolute paths into the context of the relative root. For those that this is not possible (because they are not in fact a sub-path of the relative roots), identifies the greatest common denominator for absolute paths as their root. Does not change relative paths but does check wheter they do exist if \code{check_existence = TRUE} (the default). To modify relative paths, use \link{iso_shorten_relative_paths} prior to calling this function. } \seealso{ -Other file system functions: \code{\link{iso_expand_paths}}, - \code{\link{iso_root_paths}}, - \code{\link{iso_shorten_relative_paths}} +Other file system functions: +\code{\link{iso_expand_paths}()}, +\code{\link{iso_root_paths}()}, +\code{\link{iso_shorten_relative_paths}()} } \concept{file system functions} diff --git a/man/iso_get_bgrd_data.Rd b/man/iso_get_bgrd_data.Rd index 4350a41b..8e574b33 100644 --- a/man/iso_get_bgrd_data.Rd +++ b/man/iso_get_bgrd_data.Rd @@ -4,8 +4,13 @@ \alias{iso_get_bgrd_data} \title{Aggregate background data} \usage{ -iso_get_bgrd_data(iso_files, select = everything(), gather = FALSE, - include_file_info = NULL, quiet = default(quiet)) +iso_get_bgrd_data( + iso_files, + select = everything(), + gather = FALSE, + include_file_info = NULL, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -22,11 +27,12 @@ iso_get_bgrd_data(iso_files, select = everything(), gather = FALSE, Aggregate the background data from the provided iso_files. Can aggregate either in a wide table (for easy overview) or a gathered long table (for plotting and further data processing). The background data is only available if the iso_files were read with parameter \code{read_raw_data=TRUE}. } \seealso{ -Other data retrieval functions: \code{\link{iso_get_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_raw_data}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_standards_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_data.Rd b/man/iso_get_data.Rd index 53f86124..b456dfeb 100644 --- a/man/iso_get_data.Rd +++ b/man/iso_get_data.Rd @@ -4,11 +4,17 @@ \alias{iso_get_data} \title{Aggregate all isofiles data} \usage{ -iso_get_data(iso_files, include_file_info = everything(), +iso_get_data( + iso_files, + include_file_info = everything(), include_raw_data = everything(), - include_vendor_data_table = everything(), gather = FALSE, - with_explicit_units = with_units, with_units = FALSE, - with_ratios = FALSE, quiet = default(quiet)) + include_vendor_data_table = everything(), + gather = FALSE, + with_explicit_units = with_units, + with_units = FALSE, + with_ratios = FALSE, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -36,11 +42,12 @@ data_frame with file_ids, file_types and nested data frames for each data type ( This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data \code{\link{iso_get_raw_data}}, \code{\link{iso_get_file_info}}, \code{\link{iso_get_vendor_data_table}}, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To \code{\link[tidyr]{unnest}} any of the specific data types (e.g. \code{raw_data}), make sure to filter first for the files that have this data type available (e.g. \code{filter(has_raw_data)}). } \seealso{ -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_raw_data}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_standards_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_default_reader_parameters.Rd b/man/iso_get_default_reader_parameters.Rd index 4eec9465..45c43441 100644 --- a/man/iso_get_default_reader_parameters.Rd +++ b/man/iso_get_default_reader_parameters.Rd @@ -13,9 +13,10 @@ To set messaging and caching parameters see \code{\link{iso_info_messages}} and For a piping compatible version of this function, see \link{iso_show_default_reader_parameters}. } \seealso{ -Other settings functions: \code{\link{iso_caching}}, - \code{\link{iso_info_messages}}, - \code{\link{iso_set_default_read_parameters}}, - \code{\link{iso_show_default_reader_parameters}} +Other settings functions: +\code{\link{iso_caching}}, +\code{\link{iso_info_messages}}, +\code{\link{iso_set_default_read_parameters}()}, +\code{\link{iso_show_default_reader_parameters}()} } \concept{settings functions} diff --git a/man/iso_get_file_info.Rd b/man/iso_get_file_info.Rd index eb004b6f..c21b772b 100644 --- a/man/iso_get_file_info.Rd +++ b/man/iso_get_file_info.Rd @@ -4,8 +4,7 @@ \alias{iso_get_file_info} \title{Aggregate file info} \usage{ -iso_get_file_info(iso_files, select = everything(), - quiet = default(quiet)) +iso_get_file_info(iso_files, select = everything(), quiet = default(quiet)) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -21,11 +20,12 @@ Combine file information from multiple iso_files. By default all information is File info entries with multiple values remain nested multi-value (=list) columns and can be unnested using \link[tidyr]{unnest}. } \seealso{ -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_data}}, - \code{\link{iso_get_raw_data}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_standards_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_data}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_problems.Rd b/man/iso_get_problems.Rd index 93a338cb..666a15f1 100644 --- a/man/iso_get_problems.Rd +++ b/man/iso_get_problems.Rd @@ -13,9 +13,10 @@ iso_get_problems(iso_files) This is identical to the readr \code{\link[readr]{problems}} function. } \seealso{ -Other problem functions: \code{\link{iso_filter_files_with_problems}}, - \code{\link{iso_get_problems_summary}}, - \code{\link{iso_has_problems}}, - \code{\link{iso_problem_functions}} +Other problem functions: +\code{\link{iso_filter_files_with_problems}()}, +\code{\link{iso_get_problems_summary}()}, +\code{\link{iso_has_problems}()}, +\code{\link{iso_problem_functions}} } \concept{problem functions} diff --git a/man/iso_get_problems_summary.Rd b/man/iso_get_problems_summary.Rd index b5f8e828..3c96e0c5 100644 --- a/man/iso_get_problems_summary.Rd +++ b/man/iso_get_problems_summary.Rd @@ -18,9 +18,10 @@ data frame with file_id and number of encountered errors and warnings Returns a data frame listing how many errors and warnings were encountered for each file. For details on each error/warning, see \link[readr]{problems} and the \link{iso_problem_functions}. } \seealso{ -Other problem functions: \code{\link{iso_filter_files_with_problems}}, - \code{\link{iso_get_problems}}, - \code{\link{iso_has_problems}}, - \code{\link{iso_problem_functions}} +Other problem functions: +\code{\link{iso_filter_files_with_problems}()}, +\code{\link{iso_get_problems}()}, +\code{\link{iso_has_problems}()}, +\code{\link{iso_problem_functions}} } \concept{problem functions} diff --git a/man/iso_get_raw_data.Rd b/man/iso_get_raw_data.Rd index c5bc70c8..80e55715 100644 --- a/man/iso_get_raw_data.Rd +++ b/man/iso_get_raw_data.Rd @@ -4,8 +4,13 @@ \alias{iso_get_raw_data} \title{Aggregate raw data} \usage{ -iso_get_raw_data(iso_files, select = everything(), gather = FALSE, - include_file_info = NULL, quiet = default(quiet)) +iso_get_raw_data( + iso_files, + select = everything(), + gather = FALSE, + include_file_info = NULL, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -22,18 +27,20 @@ iso_get_raw_data(iso_files, select = everything(), gather = FALSE, Aggregate the raw ion data from the provided iso_files. Can aggregate either in a wide table (for easy overview) or a gathered long table (for plotting and further data processing). The raw data is only available if the iso_files were read with parameter \code{read_raw_data=TRUE}. } \seealso{ -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_standards_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_vendor_data_table}()} -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_standards_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_resistors_info.Rd b/man/iso_get_resistors_info.Rd index a4c7376b..2c3e7f5d 100644 --- a/man/iso_get_resistors_info.Rd +++ b/man/iso_get_resistors_info.Rd @@ -4,8 +4,11 @@ \alias{iso_get_resistors_info} \title{Aggregate resistors from methods info} \usage{ -iso_get_resistors_info(iso_files, include_file_info = NULL, - quiet = default(quiet)) +iso_get_resistors_info( + iso_files, + include_file_info = NULL, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -18,11 +21,12 @@ iso_get_resistors_info(iso_files, include_file_info = NULL, Aggregates the resistor information recovered from the provided iso_files. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE} and only linked to specific masses if the iso_files were additionally read with parametr \code{read_raw_data=TRUE}. } \seealso{ -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_raw_data}}, - \code{\link{iso_get_standards_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_standards_info.Rd b/man/iso_get_standards_info.Rd index bf1cb1b7..539f2e8c 100644 --- a/man/iso_get_standards_info.Rd +++ b/man/iso_get_standards_info.Rd @@ -4,8 +4,12 @@ \alias{iso_get_standards_info} \title{Aggregate standards from methods info} \usage{ -iso_get_standards_info(iso_files, with_ratios = FALSE, - include_file_info = NULL, quiet = default(quiet)) +iso_get_standards_info( + iso_files, + with_ratios = FALSE, + include_file_info = NULL, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -20,11 +24,12 @@ iso_get_standards_info(iso_files, with_ratios = FALSE, Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter \code{with_ratios} to exclude/include the ratios. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE}. } \seealso{ -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_raw_data}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_vendor_data_table}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_supported_file_types.Rd b/man/iso_get_supported_file_types.Rd index 9229ce79..cb5afb2e 100644 --- a/man/iso_get_supported_file_types.Rd +++ b/man/iso_get_supported_file_types.Rd @@ -10,6 +10,7 @@ iso_get_supported_file_types() Get an overview of all the file types currently supported by the isoreader package. To register additional file readers, use the \code{\link{iso_register_dual_inlet_file_reader}} and \code{\link{iso_register_continuous_flow_file_reader}} functions. } \seealso{ -Other file_types: \code{\link{iso_register_dual_inlet_file_reader}} +Other file_types: +\code{\link{iso_register_dual_inlet_file_reader}()} } \concept{file_types} diff --git a/man/iso_get_units.Rd b/man/iso_get_units.Rd index 69045485..10ef7768 100644 --- a/man/iso_get_units.Rd +++ b/man/iso_get_units.Rd @@ -13,10 +13,11 @@ iso_get_units(x) This function returns the units of a numerical value generated by \code{\link{iso_double_with_units}}. It returns \code{NA}) for unitless variables. Returns a column-named vector of units if \code{x} is a data frame / tibble. Returns the direct units of \code{x} in all other cases. } \seealso{ -Other functions for values with units: \code{\link{iso_double_with_units}}, - \code{\link{iso_is_double_with_units}}, - \code{\link{iso_make_units_explicit}}, - \code{\link{iso_make_units_implicit}}, - \code{\link{iso_strip_units}} +Other functions for values with units: +\code{\link{iso_double_with_units}}, +\code{\link{iso_is_double_with_units}()}, +\code{\link{iso_make_units_explicit}()}, +\code{\link{iso_make_units_implicit}()}, +\code{\link{iso_strip_units}()} } \concept{functions for values with units} diff --git a/man/iso_get_vendor_data_table.Rd b/man/iso_get_vendor_data_table.Rd index eb6e21ed..71476790 100644 --- a/man/iso_get_vendor_data_table.Rd +++ b/man/iso_get_vendor_data_table.Rd @@ -4,9 +4,14 @@ \alias{iso_get_vendor_data_table} \title{Aggregate vendor computed table data} \usage{ -iso_get_vendor_data_table(iso_files, with_units = FALSE, - select = everything(), include_file_info = NULL, - with_explicit_units = with_units, quiet = default(quiet)) +iso_get_vendor_data_table( + iso_files, + with_units = FALSE, + select = everything(), + include_file_info = NULL, + with_explicit_units = with_units, + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -25,11 +30,12 @@ iso_get_vendor_data_table(iso_files, with_units = FALSE, Aggregate data from the vendor-computed data table. This information is only available if the iso_files were read with parameter \code{read_vendor_data_table=TRUE}. } \seealso{ -Other data retrieval functions: \code{\link{iso_get_bgrd_data}}, - \code{\link{iso_get_data}}, - \code{\link{iso_get_file_info}}, - \code{\link{iso_get_raw_data}}, - \code{\link{iso_get_resistors_info}}, - \code{\link{iso_get_standards_info}} +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors_info}()}, +\code{\link{iso_get_standards_info}()} } \concept{data retrieval functions} diff --git a/man/iso_has_problems.Rd b/man/iso_has_problems.Rd index 67ede2f6..b8b13e63 100644 --- a/man/iso_has_problems.Rd +++ b/man/iso_has_problems.Rd @@ -16,9 +16,10 @@ boolean Check for parsing problems } \seealso{ -Other problem functions: \code{\link{iso_filter_files_with_problems}}, - \code{\link{iso_get_problems_summary}}, - \code{\link{iso_get_problems}}, - \code{\link{iso_problem_functions}} +Other problem functions: +\code{\link{iso_filter_files_with_problems}()}, +\code{\link{iso_get_problems_summary}()}, +\code{\link{iso_get_problems}()}, +\code{\link{iso_problem_functions}} } \concept{problem functions} diff --git a/man/iso_info_messages.Rd b/man/iso_info_messages.Rd index 6cfc1295..771a3418 100644 --- a/man/iso_info_messages.Rd +++ b/man/iso_info_messages.Rd @@ -17,9 +17,10 @@ iso_turn_info_messages_off(data = NULL) These functions turn information messages on/off in all subsequent function calls by changing the global settings for the \code{quiet} parameter of most isoreader functions. These functions can be called stand alone or within a pipeline to turn messages on/off at a certain point during the pipeline. } \seealso{ -Other settings functions: \code{\link{iso_caching}}, - \code{\link{iso_get_default_reader_parameters}}, - \code{\link{iso_set_default_read_parameters}}, - \code{\link{iso_show_default_reader_parameters}} +Other settings functions: +\code{\link{iso_caching}}, +\code{\link{iso_get_default_reader_parameters}()}, +\code{\link{iso_set_default_read_parameters}()}, +\code{\link{iso_show_default_reader_parameters}()} } \concept{settings functions} diff --git a/man/iso_is_double_with_units.Rd b/man/iso_is_double_with_units.Rd index 583ff66d..1331d338 100644 --- a/man/iso_is_double_with_units.Rd +++ b/man/iso_is_double_with_units.Rd @@ -13,10 +13,11 @@ iso_is_double_with_units(x) Check if a variable is a double with units. That is if it has been generated by \code{\link{iso_double_with_units}}. } \seealso{ -Other functions for values with units: \code{\link{iso_double_with_units}}, - \code{\link{iso_get_units}}, - \code{\link{iso_make_units_explicit}}, - \code{\link{iso_make_units_implicit}}, - \code{\link{iso_strip_units}} +Other functions for values with units: +\code{\link{iso_double_with_units}}, +\code{\link{iso_get_units}()}, +\code{\link{iso_make_units_explicit}()}, +\code{\link{iso_make_units_implicit}()}, +\code{\link{iso_strip_units}()} } \concept{functions for values with units} diff --git a/man/iso_make_units_explicit.Rd b/man/iso_make_units_explicit.Rd index c932463f..54239c1b 100644 --- a/man/iso_make_units_explicit.Rd +++ b/man/iso_make_units_explicit.Rd @@ -28,10 +28,11 @@ iso_make_units_explicit(df) iso_make_units_explicit(df, prefix = ".", suffix = "") } \seealso{ -Other functions for values with units: \code{\link{iso_double_with_units}}, - \code{\link{iso_get_units}}, - \code{\link{iso_is_double_with_units}}, - \code{\link{iso_make_units_implicit}}, - \code{\link{iso_strip_units}} +Other functions for values with units: +\code{\link{iso_double_with_units}}, +\code{\link{iso_get_units}()}, +\code{\link{iso_is_double_with_units}()}, +\code{\link{iso_make_units_implicit}()}, +\code{\link{iso_strip_units}()} } \concept{functions for values with units} diff --git a/man/iso_make_units_implicit.Rd b/man/iso_make_units_implicit.Rd index fc69021a..d794b91b 100644 --- a/man/iso_make_units_implicit.Rd +++ b/man/iso_make_units_implicit.Rd @@ -29,10 +29,11 @@ df <- tibble(peak = 1:5, height.V = 1:5) iso_make_units_implicit(df, prefix = ".", suffix = "") } \seealso{ -Other functions for values with units: \code{\link{iso_double_with_units}}, - \code{\link{iso_get_units}}, - \code{\link{iso_is_double_with_units}}, - \code{\link{iso_make_units_explicit}}, - \code{\link{iso_strip_units}} +Other functions for values with units: +\code{\link{iso_double_with_units}}, +\code{\link{iso_get_units}()}, +\code{\link{iso_is_double_with_units}()}, +\code{\link{iso_make_units_explicit}()}, +\code{\link{iso_strip_units}()} } \concept{functions for values with units} diff --git a/man/iso_mutate_file_info.Rd b/man/iso_mutate_file_info.Rd index b5eb9377..d68d2ff9 100644 --- a/man/iso_mutate_file_info.Rd +++ b/man/iso_mutate_file_info.Rd @@ -17,10 +17,11 @@ iso_mutate_file_info(iso_files, ..., quiet = default(quiet)) Mutate the file info (\code{\link{iso_get_file_info}}) within isofile objects by changing existing columns or introducing new ones. Works just like dplyr's \link[dplyr]{mutate}. You can also use \link[dplyr]{mutate} directly but it will not provide summary information on the operation. Note that this will create missing columns that exist in some but not all of the passed in isofile objects in all isofile objects (filling them with NAs) the same way that \code{\link{iso_get_file_info}} does. } \seealso{ -Other file_info operations: \code{\link{iso_add_file_info.iso_file_list}}, - \code{\link{iso_filter_files}}, - \code{\link{iso_parse_file_info}}, - \code{\link{iso_rename_file_info}}, - \code{\link{iso_select_file_info}} +Other file_info operations: +\code{\link{iso_add_file_info.iso_file_list}()}, +\code{\link{iso_filter_files}()}, +\code{\link{iso_parse_file_info}()}, +\code{\link{iso_rename_file_info}()}, +\code{\link{iso_select_file_info}()} } \concept{file_info operations} diff --git a/man/iso_parse_file_info.Rd b/man/iso_parse_file_info.Rd index 77808f0d..26555d4b 100644 --- a/man/iso_parse_file_info.Rd +++ b/man/iso_parse_file_info.Rd @@ -4,9 +4,16 @@ \alias{iso_parse_file_info} \title{Parse file info} \usage{ -iso_parse_file_info(iso_files, number = c(), double = c(), - integer = c(), logical = c(), datetime = c(), text = c(), - quiet = default(quiet)) +iso_parse_file_info( + iso_files, + number = c(), + double = c(), + integer = c(), + logical = c(), + datetime = c(), + text = c(), + quiet = default(quiet) +) } \arguments{ \item{iso_files}{collection of iso_file objects} @@ -29,10 +36,11 @@ iso_parse_file_info(iso_files, number = c(), double = c(), Convenience function to batch parse file info (\code{\link{iso_get_file_info}}) columns in isofile objects for the most common parsing calls. Uses the \code{parse_} functions exported from \link{readr} and described in \link{extract_data}. Note that for less common parsing calls or calls that require additional parameters to the parsing function, it is better to parse columns one-by-one using \code{\link{iso_mutate_file_info}} instead. } \seealso{ -Other file_info operations: \code{\link{iso_add_file_info.iso_file_list}}, - \code{\link{iso_filter_files}}, - \code{\link{iso_mutate_file_info}}, - \code{\link{iso_rename_file_info}}, - \code{\link{iso_select_file_info}} +Other file_info operations: +\code{\link{iso_add_file_info.iso_file_list}()}, +\code{\link{iso_filter_files}()}, +\code{\link{iso_mutate_file_info}()}, +\code{\link{iso_rename_file_info}()}, +\code{\link{iso_select_file_info}()} } \concept{file_info operations} diff --git a/man/iso_problem_functions.Rd b/man/iso_problem_functions.Rd index e36c8f59..410f730c 100644 --- a/man/iso_problem_functions.Rd +++ b/man/iso_problem_functions.Rd @@ -20,9 +20,10 @@ The following functions to check for and deal with problems are available. } } \seealso{ -Other problem functions: \code{\link{iso_filter_files_with_problems}}, - \code{\link{iso_get_problems_summary}}, - \code{\link{iso_get_problems}}, - \code{\link{iso_has_problems}} +Other problem functions: +\code{\link{iso_filter_files_with_problems}()}, +\code{\link{iso_get_problems_summary}()}, +\code{\link{iso_get_problems}()}, +\code{\link{iso_has_problems}()} } \concept{problem functions} diff --git a/man/iso_read_continuous_flow.Rd b/man/iso_read_continuous_flow.Rd index f464db50..5d2014c5 100644 --- a/man/iso_read_continuous_flow.Rd +++ b/man/iso_read_continuous_flow.Rd @@ -4,15 +4,21 @@ \alias{iso_read_continuous_flow} \title{Load continuous flow data} \usage{ -iso_read_continuous_flow(..., root = ".", +iso_read_continuous_flow( + ..., + root = ".", read_raw_data = default(read_raw_data), read_file_info = default(read_file_info), read_method_info = default(read_method_info), read_vendor_data_table = default(read_vendor_data_table), - discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, cache = default(cache), - cache_files_with_errors = TRUE, read_cache = default(cache), - quiet = default(quiet)) + discard_duplicates = TRUE, + parallel = FALSE, + parallel_plan = future::multiprocess, + cache = default(cache), + cache_files_with_errors = TRUE, + read_cache = default(cache), + quiet = default(quiet) +) } \arguments{ \item{...}{one or multiple file/folder paths. All files must have a supported file extension. All folders are expanded and searched for files with supported file extensions (which are then included in the read).} @@ -45,6 +51,7 @@ iso_read_continuous_flow(..., root = ".", Load continuous flow data } \seealso{ -Other isoread functions for different types of IRMS data: \code{\link{iso_read_dual_inlet}} +Other isoread functions for different types of IRMS data: +\code{\link{iso_read_dual_inlet}()} } \concept{isoread functions for different types of IRMS data} diff --git a/man/iso_read_dual_inlet.Rd b/man/iso_read_dual_inlet.Rd index 729a1bc4..50c47feb 100644 --- a/man/iso_read_dual_inlet.Rd +++ b/man/iso_read_dual_inlet.Rd @@ -4,15 +4,22 @@ \alias{iso_read_dual_inlet} \title{Load dual inlet data} \usage{ -iso_read_dual_inlet(..., root = ".", +iso_read_dual_inlet( + ..., + root = ".", read_raw_data = default(read_raw_data), read_file_info = default(read_file_info), read_method_info = default(read_method_info), read_vendor_data_table = default(read_vendor_data_table), - nu_masses = c(), discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, cache = default(cache), - cache_files_with_errors = TRUE, read_cache = default(cache), - quiet = default(quiet)) + nu_masses = c(), + discard_duplicates = TRUE, + parallel = FALSE, + parallel_plan = future::multiprocess, + cache = default(cache), + cache_files_with_errors = TRUE, + read_cache = default(cache), + quiet = default(quiet) +) } \arguments{ \item{...}{one or multiple file/folder paths. All files must have a supported file extension. All folders are expanded and searched for files with supported file extensions (which are then included in the read).} @@ -47,6 +54,7 @@ iso_read_dual_inlet(..., root = ".", Load dual inlet data } \seealso{ -Other isoread functions for different types of IRMS data: \code{\link{iso_read_continuous_flow}} +Other isoread functions for different types of IRMS data: +\code{\link{iso_read_continuous_flow}()} } \concept{isoread functions for different types of IRMS data} diff --git a/man/iso_read_files.Rd b/man/iso_read_files.Rd index 6dd8dd15..dcb0367b 100644 --- a/man/iso_read_files.Rd +++ b/man/iso_read_files.Rd @@ -4,12 +4,21 @@ \alias{iso_read_files} \title{Core function to read isotope data files} \usage{ -iso_read_files(paths, root, supported_extensions, data_structure, - read_options = c(), reader_options = list(), - discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, cache = default(cache), - cache_files_with_errors = TRUE, read_cache = default(cache), - quiet = default(quiet)) +iso_read_files( + paths, + root, + supported_extensions, + data_structure, + read_options = c(), + reader_options = list(), + discard_duplicates = TRUE, + parallel = FALSE, + parallel_plan = future::multiprocess, + cache = default(cache), + cache_files_with_errors = TRUE, + read_cache = default(cache), + quiet = default(quiet) +) } \arguments{ \item{paths}{one or multiple file/folder paths. All files must have a supported file extension. All folders are expanded and searched for files with supported file extensions (which are then included in the read). Paths can be absolute paths or relative to the provided file \code{root} (which is the current working directory by default). For absolute paths, a common root directory will be guessed using \link{iso_find_absolute_path_roots}. The root portion of paths will never be displayed in info messages.} diff --git a/man/iso_rename_file_info.Rd b/man/iso_rename_file_info.Rd index e4ed0ed0..7d9ad65d 100644 --- a/man/iso_rename_file_info.Rd +++ b/man/iso_rename_file_info.Rd @@ -17,10 +17,11 @@ iso_rename_file_info(iso_files, ..., quiet = default(quiet)) Rename individual file info columns (\code{\link{iso_get_file_info}}) within isofile objects. Works just like dplyr's \link[dplyr]{rename} except that it can rename different columns into the same name in different iso_files depending on what exists in each file. This is very useful when working with data from multiple instruments that may have the same information (e.g. sample name) stored in different columns. You can also use \link[dplyr]{rename} directly but it will not provide summary information on the operation. To select specific columns to keep (discarding all others), use \link{iso_select_file_info} instead. } \seealso{ -Other file_info operations: \code{\link{iso_add_file_info.iso_file_list}}, - \code{\link{iso_filter_files}}, - \code{\link{iso_mutate_file_info}}, - \code{\link{iso_parse_file_info}}, - \code{\link{iso_select_file_info}} +Other file_info operations: +\code{\link{iso_add_file_info.iso_file_list}()}, +\code{\link{iso_filter_files}()}, +\code{\link{iso_mutate_file_info}()}, +\code{\link{iso_parse_file_info}()}, +\code{\link{iso_select_file_info}()} } \concept{file_info operations} diff --git a/man/iso_reread_files.Rd b/man/iso_reread_files.Rd index f1788fbd..a359bea9 100644 --- a/man/iso_reread_files.Rd +++ b/man/iso_reread_files.Rd @@ -6,11 +6,19 @@ \alias{iso_reread_archive} \title{Re-read iso_files} \usage{ -iso_reread_files(iso_files, ..., stop_if_missing = FALSE, - quiet = default(quiet)) - -iso_reread_storage(rds_filepaths, ..., stop_if_missing = FALSE, - quiet = default(quiet)) +iso_reread_files( + iso_files, + ..., + stop_if_missing = FALSE, + quiet = default(quiet) +) + +iso_reread_storage( + rds_filepaths, + ..., + stop_if_missing = FALSE, + quiet = default(quiet) +) iso_reread_archive(...) } diff --git a/man/iso_root_paths.Rd b/man/iso_root_paths.Rd index 6dbdd931..268146a7 100644 --- a/man/iso_root_paths.Rd +++ b/man/iso_root_paths.Rd @@ -20,8 +20,9 @@ a data frame with the root directories and paths relative to the root - order of Function to root both relative and absolute paths to a root directory (or directories) commonly relative to current working directory. Determines the best way to shorten relative paths and put absolute paths in a relative context (if possible) using \link{iso_shorten_relative_paths} and \link{iso_find_absolute_path_roots}, respectively. } \seealso{ -Other file system functions: \code{\link{iso_expand_paths}}, - \code{\link{iso_find_absolute_path_roots}}, - \code{\link{iso_shorten_relative_paths}} +Other file system functions: +\code{\link{iso_expand_paths}()}, +\code{\link{iso_find_absolute_path_roots}()}, +\code{\link{iso_shorten_relative_paths}()} } \concept{file system functions} diff --git a/man/iso_save.Rd b/man/iso_save.Rd index aab31569..c34e80f5 100644 --- a/man/iso_save.Rd +++ b/man/iso_save.Rd @@ -20,8 +20,9 @@ returns the iso_files object invisibly for use in pipelines This function saves the passed in iso_files to an R Data Storage (.rds) file, which is an efficient compressed data storage format. Data exported this way can be easily read back into isoreader using the standard \code{\link{iso_read_continuous_flow}} and \code{\link{iso_read_dual_inlet}} functions. } \seealso{ -Other export functions: \code{\link{iso_export_to_excel}}, - \code{\link{iso_export_to_feather}}, - \code{\link{iso_export_to_rda}} +Other export functions: +\code{\link{iso_export_to_excel}()}, +\code{\link{iso_export_to_feather}()}, +\code{\link{iso_export_to_rda}()} } \concept{export functions} diff --git a/man/iso_select_file_info.Rd b/man/iso_select_file_info.Rd index 9177a44b..03551a4c 100644 --- a/man/iso_select_file_info.Rd +++ b/man/iso_select_file_info.Rd @@ -17,10 +17,11 @@ iso_select_file_info(iso_files, ..., quiet = default(quiet)) Select which file info columns (\code{\link{iso_get_file_info}}) to keep within isofile objects. Works just like dplyr's \link[dplyr]{select} except that it can select different columns in different iso_files depending on what exists in each file (also works for on-the-fly renaming of columns). This is very useful when working with data from multiple instruments that may have the same information (e.g. sample name) stored in different columns. You can also use \link[dplyr]{select} directly but it will not provide summary information on the operation. To rename columns without removing all other information, use \link{iso_rename_file_info} instead. } \seealso{ -Other file_info operations: \code{\link{iso_add_file_info.iso_file_list}}, - \code{\link{iso_filter_files}}, - \code{\link{iso_mutate_file_info}}, - \code{\link{iso_parse_file_info}}, - \code{\link{iso_rename_file_info}} +Other file_info operations: +\code{\link{iso_add_file_info.iso_file_list}()}, +\code{\link{iso_filter_files}()}, +\code{\link{iso_mutate_file_info}()}, +\code{\link{iso_parse_file_info}()}, +\code{\link{iso_rename_file_info}()} } \concept{file_info operations} diff --git a/man/iso_set_default_read_parameters.Rd b/man/iso_set_default_read_parameters.Rd index e284c247..3953354e 100644 --- a/man/iso_set_default_read_parameters.Rd +++ b/man/iso_set_default_read_parameters.Rd @@ -4,9 +4,14 @@ \alias{iso_set_default_read_parameters} \title{Set default read options} \usage{ -iso_set_default_read_parameters(data = NULL, read_raw_data, - read_file_info, read_method_info, read_vendor_data_table, - quiet = default(quiet)) +iso_set_default_read_parameters( + data = NULL, + read_raw_data, + read_file_info, + read_method_info, + read_vendor_data_table, + quiet = default(quiet) +) } \arguments{ \item{data}{a data frame - returned invisibly as is if provided (e.g. in the middle of a pipeline)} @@ -25,9 +30,10 @@ iso_set_default_read_parameters(data = NULL, read_raw_data, Set default read options } \seealso{ -Other settings functions: \code{\link{iso_caching}}, - \code{\link{iso_get_default_reader_parameters}}, - \code{\link{iso_info_messages}}, - \code{\link{iso_show_default_reader_parameters}} +Other settings functions: +\code{\link{iso_caching}}, +\code{\link{iso_get_default_reader_parameters}()}, +\code{\link{iso_info_messages}}, +\code{\link{iso_show_default_reader_parameters}()} } \concept{settings functions} diff --git a/man/iso_shorten_relative_paths.Rd b/man/iso_shorten_relative_paths.Rd index a68ea542..53360a30 100644 --- a/man/iso_shorten_relative_paths.Rd +++ b/man/iso_shorten_relative_paths.Rd @@ -24,8 +24,9 @@ iso_shorten_relative_paths(file.path("A", "C", "D"), file.path("A", "B")) # root iso_shorten_relative_paths(file.path("A", "B", "C"), "B") # root = ".", path stays "A/B/C" } \seealso{ -Other file system functions: \code{\link{iso_expand_paths}}, - \code{\link{iso_find_absolute_path_roots}}, - \code{\link{iso_root_paths}} +Other file system functions: +\code{\link{iso_expand_paths}()}, +\code{\link{iso_find_absolute_path_roots}()}, +\code{\link{iso_root_paths}()} } \concept{file system functions} diff --git a/man/iso_show_default_reader_parameters.Rd b/man/iso_show_default_reader_parameters.Rd index 15f0c4b3..bb584589 100644 --- a/man/iso_show_default_reader_parameters.Rd +++ b/man/iso_show_default_reader_parameters.Rd @@ -18,9 +18,10 @@ Note that if the output is in RMarkdown chunks, the chunk option must have \code Shows a table with the default function parameters for this package. } \seealso{ -Other settings functions: \code{\link{iso_caching}}, - \code{\link{iso_get_default_reader_parameters}}, - \code{\link{iso_info_messages}}, - \code{\link{iso_set_default_read_parameters}} +Other settings functions: +\code{\link{iso_caching}}, +\code{\link{iso_get_default_reader_parameters}()}, +\code{\link{iso_info_messages}}, +\code{\link{iso_set_default_read_parameters}()} } \concept{settings functions} diff --git a/man/iso_strip_units.Rd b/man/iso_strip_units.Rd index e2da8848..1a54ed83 100644 --- a/man/iso_strip_units.Rd +++ b/man/iso_strip_units.Rd @@ -13,10 +13,11 @@ iso_strip_units(x) This function converts numbers with units back into unitless numbers both for single variables and data frames / tibbles. For single variables, this is equivalent to the \code{as.numeric} function. } \seealso{ -Other functions for values with units: \code{\link{iso_double_with_units}}, - \code{\link{iso_get_units}}, - \code{\link{iso_is_double_with_units}}, - \code{\link{iso_make_units_explicit}}, - \code{\link{iso_make_units_implicit}} +Other functions for values with units: +\code{\link{iso_double_with_units}}, +\code{\link{iso_get_units}()}, +\code{\link{iso_is_double_with_units}()}, +\code{\link{iso_make_units_explicit}()}, +\code{\link{iso_make_units_implicit}()} } \concept{functions for values with units} diff --git a/man/isoreader-package.Rd b/man/isoreader-package.Rd index 5d8c6234..ad59607c 100644 --- a/man/isoreader-package.Rd +++ b/man/isoreader-package.Rd @@ -17,7 +17,7 @@ Useful links: } \author{ -\strong{Maintainer}: Sebastian Kopf \email{sebastian.kopf@colorado.edu} (0000-0002-2044-0201) +\strong{Maintainer}: Sebastian Kopf \email{sebastian.kopf@colorado.edu} (\href{https://orcid.org/0000-0002-2044-0201}{ORCID}) } \keyword{internal} diff --git a/man/print.binary_structure_map.Rd b/man/print.binary_structure_map.Rd index 2c04a904..0ccc76d0 100644 --- a/man/print.binary_structure_map.Rd +++ b/man/print.binary_structure_map.Rd @@ -4,8 +4,13 @@ \alias{print.binary_structure_map} \title{Print binary structure map} \usage{ -\method{print}{binary_structure_map}(x, ..., data_as_raw = FALSE, - line_break_blocks = c("cblock", "stx", "etx"), pos_info = TRUE) +\method{print}{binary_structure_map}( + x, + ..., + data_as_raw = FALSE, + line_break_blocks = c("cblock", "stx", "etx"), + pos_info = TRUE +) } \arguments{ \item{x}{object to show.} diff --git a/man/read_iso_file.Rd b/man/read_iso_file.Rd index 45e13fe8..61bf6e7b 100644 --- a/man/read_iso_file.Rd +++ b/man/read_iso_file.Rd @@ -4,9 +4,21 @@ \alias{read_iso_file} \title{Read individual iso file} \usage{ -read_iso_file(ds, root, path, file_n, files_n, read_from_cache, - write_to_cache, write_to_cache_if_errors, cachepath, ext, reader_fun, - reader_options, reader_fun_env) +read_iso_file( + ds, + root, + path, + file_n, + files_n, + read_from_cache, + write_to_cache, + write_to_cache_if_errors, + cachepath, + ext, + reader_fun, + reader_options, + reader_fun_env +) } \arguments{ \item{ds}{the basic data structure for the type of iso_file} diff --git a/man/reexports.Rd b/man/reexports.Rd index 050abf48..a375ef89 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -28,8 +28,8 @@ below to see their documentation. \item{magrittr}{\code{\link[magrittr]{\%>\%}}} - \item{readr}{\code{\link[readr]{parse_number}}, \code{\link[readr]{parse_logical}}, \code{\link[readr]{parse_integer}}, \code{\link[readr]{parse_double}}, \code{\link[readr]{parse_datetime}}, \code{\link[readr]{problems}}, \code{\link[readr]{stop_for_problems}}} + \item{readr}{\code{\link[readr]{parse_datetime}}, \code{\link[readr]{parse_double}}, \code{\link[readr]{parse_integer}}, \code{\link[readr]{parse_logical}}, \code{\link[readr]{parse_number}}, \code{\link[readr]{problems}}, \code{\link[readr]{stop_for_problems}}} - \item{tidyselect}{\code{\link[tidyselect]{everything}}, \code{\link[tidyselect]{starts_with}}, \code{\link[tidyselect]{ends_with}}, \code{\link[tidyselect]{matches}}} + \item{tidyselect}{\code{\link[tidyselect]{ends_with}}, \code{\link[tidyselect]{everything}}, \code{\link[tidyselect]{matches}}, \code{\link[tidyselect]{starts_with}}} }} From 230a87ae00a68181599936f8b24d1750e210eeb6 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sun, 9 Feb 2020 15:07:40 -0700 Subject: [PATCH 09/51] fix problem with unnest by removing NA logical columns first to circumvent https://github.com/r-lib/vctrs/issues/800 closes #91 --- DESCRIPTION | 2 +- R/file_info_operations.R | 7 ++++++- tests/testthat/test-file-info-operations.R | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 553912e0..130adda0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: isoreader Title: Read IRMS data files Description: R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry. -Version: 1.0.14 +Version: 1.0.15 Authors@R: person("Sebastian", "Kopf", email = "sebastian.kopf@colorado.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2044-0201")) URL: https://github.com/isoverse/isoreader BugReports: https://github.com/isoverse/isoreader/issues diff --git a/R/file_info_operations.R b/R/file_info_operations.R index f45dc021..f06ec688 100644 --- a/R/file_info_operations.R +++ b/R/file_info_operations.R @@ -589,7 +589,12 @@ iso_add_file_info.data.frame <- function(df, new_file_info, ..., quiet = default ) # select data based on priority - final_data <- joined_data %>% select(..priority, data) %>% unnest(data) %>% + final_data <- + joined_data %>% + select(..priority, data) %>% + # avoid problems with the temp columns during unnest + mutate(data = map(data, ~select(.x, -starts_with("..ni_temp_")))) %>% + unnest(data) %>% select(-starts_with("..ni_temp_")) %>% group_by(..df_id) %>% filter(..priority == max(..priority)) %>% diff --git a/tests/testthat/test-file-info-operations.R b/tests/testthat/test-file-info-operations.R index 79ff7507..f8df57bf 100644 --- a/tests/testthat/test-file-info-operations.R +++ b/tests/testthat/test-file-info-operations.R @@ -307,6 +307,14 @@ test_that("Test that file info addition works", { "'file_id'\\+'y' join.*0/1 new info.*matched 0" ) + # sequential join including a logical column join + iso_add_file_info( + mutate(file_info, y = rep(c(TRUE, FALSE), each = 3)), + mutate(new_info, y = c(TRUE, FALSE, NA, NA, NA)), + by1 = "y", + by2 = "file_id" + ) + # test with isofiles (not just in data frame) template <- make_cf_data_structure("NA") template$read_options$file_info <- TRUE From 74fbf25d94ed0d3546735fbb00a1e43290139c38 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sun, 9 Feb 2020 15:20:07 -0700 Subject: [PATCH 10/51] include software in reader file types --- R/isoread.R | 15 ++++++++------- R/zzz.R | 20 ++++++++++---------- README.Rmd | 2 +- README.md | 26 +++++++++++++------------- tests/testthat/test-utils.R | 2 +- 5 files changed, 33 insertions(+), 32 deletions(-) diff --git a/R/isoread.R b/R/isoread.R index 070bdf6c..d2358e5e 100644 --- a/R/isoread.R +++ b/R/isoread.R @@ -10,25 +10,26 @@ #' @param extension the file extension (e.g. \code{.dxf}) of the data file. Must be unique otherwise different files can not automatically be matched with the appropriate file reader based on their extension. #' @param func the name of the function that should be used a filter reader. All file reader functions must accept a data structure argument as the first argument and return the same data structure with added data. #' @param description what is this file type about? +#' @param software what is the software program that creates this filetype? #' @param cacheable whether this file type is cacheable. If \code{TRUE} (the default), user requests to cache the file will be honored. If \code{FALSE}, this file type will never be cached no matter what the user requests. #' @param overwrite whether to overwrite an existing file reader for the same extension #' @param env the environment where to find the function, by default this will be determined automatically and will throw an error if there is any ambiguity (e.g. the same function name in multiple packages) in which case it should be set manually #' @family file_types #' @export iso_register_dual_inlet_file_reader <- function( - extension, func, description = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { - register_file_reader("dual inlet", "iso_read_dual_inlet", extension, func, description, cacheable, overwrite, env) + extension, func, description = NA_character_, software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { + register_file_reader("dual inlet", "iso_read_dual_inlet", extension, func, description, software, cacheable, overwrite, env) } #' @details \code{iso_register_continuous_flow_file_reader}: use this function to register file readers for continuous flow files. #' @rdname file_readers #' @family file_types iso_register_continuous_flow_file_reader <- function( - extension, func, description = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { - register_file_reader("continuous flow", "iso_read_continuous_flow", extension, func, description, cacheable, overwrite, env) + extension, func, description = NA_character_, software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { + register_file_reader("continuous flow", "iso_read_continuous_flow", extension, func, description, software, cacheable, overwrite, env) } -register_file_reader <- function(type, call, extension, func, description, cacheable, overwrite, env) { +register_file_reader <- function(type, call, extension, func, description, software, cacheable, overwrite, env) { if (!is.character(func)) stop("please provide the function name rather than the function itself to register it", @@ -50,7 +51,7 @@ register_file_reader <- function(type, call, extension, func, description, cache dplyr::data_frame( type = type, call = call, extension = extension, func = func, cacheable = cacheable, description = description, - env = env + software = software, env = env ) if (!is.null(frs) && extension %in% frs$extension) { @@ -89,7 +90,7 @@ find_func <- function(func) { #' @family file_types #' @export iso_get_supported_file_types <- function() { - dplyr::select(default("file_readers"), "extension", "description", "type", "call") + dplyr::select(default("file_readers"), "extension", "software", "description", "type", "call") } get_supported_di_files <- function() { diff --git a/R/zzz.R b/R/zzz.R index c6bf61de..dfe09303 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -26,16 +26,16 @@ initialize_options <- function() { options(temp_options) # register file readers - iso_register_dual_inlet_file_reader(".did", "iso_read_did", "Isodat Dual Inlet file format (newer)", env = "isoreader") - iso_register_dual_inlet_file_reader(".caf", "iso_read_caf", "Isodat Dual Inlet file format (older)", env = "isoreader") - iso_register_dual_inlet_file_reader(".txt", "iso_read_nu", "Nu Dual Inlet file format", env = "isoreader") - iso_register_dual_inlet_file_reader(".di.rda", "iso_read_rda", "Isoreader R Data Archive (deprecated)", cacheable = FALSE, env = "isoreader") - iso_register_dual_inlet_file_reader(".di.rds", "iso_read_rds", "Isoreader R Data Storage", cacheable = FALSE, env = "isoreader") - iso_register_continuous_flow_file_reader(".cf", "iso_read_cf", "Isodat Continuous Flow file format (older)", env = "isoreader") - iso_register_continuous_flow_file_reader(".dxf", "iso_read_dxf", "Isodat Continuous Flow file format (newer)", env = "isoreader") - iso_register_continuous_flow_file_reader(".iarc", "iso_read_flow_iarc", "IonOS Continous Flow data archieve", env = "isoreader") - iso_register_continuous_flow_file_reader(".cf.rda", "iso_read_rda", "Isoreader R Data Archive (deprecated)", cacheable = FALSE, env = "isoreader") - iso_register_continuous_flow_file_reader(".cf.rds", "iso_read_rds", "Isoreader R Data Storage", cacheable = FALSE, env = "isoreader") + iso_register_dual_inlet_file_reader(".did", "iso_read_did", "Dual Inlet file format (newer)", "Isodat", env = "isoreader") + iso_register_dual_inlet_file_reader(".caf", "iso_read_caf", "Dual Inlet file format (older)", "Isodat", env = "isoreader") + iso_register_dual_inlet_file_reader(".txt", "iso_read_nu", "Dual Inlet file format", "Nu", env = "isoreader") + iso_register_dual_inlet_file_reader(".di.rda", "iso_read_rda", "R Data Archive (deprecated)", "isoreader", cacheable = FALSE, env = "isoreader") + iso_register_dual_inlet_file_reader(".di.rds", "iso_read_rds", "R Data Storage", "isoreader", cacheable = FALSE, env = "isoreader") + iso_register_continuous_flow_file_reader(".cf", "iso_read_cf", "Continuous Flow file format (older)", "Isodat", env = "isoreader") + iso_register_continuous_flow_file_reader(".dxf", "iso_read_dxf", "Continuous Flow file format (newer)", "Isodat", env = "isoreader") + iso_register_continuous_flow_file_reader(".iarc", "iso_read_flow_iarc", "Continous Flow data archieve", "ionOS", env = "isoreader") + iso_register_continuous_flow_file_reader(".cf.rda", "iso_read_rda", "R Data Archive (deprecated)", "isoreader", cacheable = FALSE, env = "isoreader") + iso_register_continuous_flow_file_reader(".cf.rds", "iso_read_rds", "R Data Storage", "isoreader", cacheable = FALSE, env = "isoreader") invisible(options()[names(default_options)]) } diff --git a/README.Rmd b/README.Rmd index dfad80f7..8162f445 100644 --- a/README.Rmd +++ b/README.Rmd @@ -52,7 +52,7 @@ Currently supported file types: ```{r, echo=FALSE} library(isoreader) iso_get_supported_file_types() %>% - dplyr::select(extension, format = description, type) %>% + dplyr::select(extension, software, description, type) %>% knitr::kable() ``` diff --git a/README.md b/README.md index 79f58c9d..f68719fd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # isoreader [![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/isoreader)](https://cran.r-project.org/package=isoreader) -[![Git\_Hub\_Version](https://img.shields.io/badge/GitHub-1.0.11-orange.svg?style=flat-square)](https://github.com/isoverse/isoreader/commits) +[![Git\_Hub\_Version](https://img.shields.io/badge/GitHub-1.0.15-orange.svg?style=flat-square)](https://github.com/isoverse/isoreader/commits) [![Documentation](https://img.shields.io/badge/docs-online-green.svg)](http://isoreader.isoverse.org/) [![Build Status](https://travis-ci.org/isoverse/isoreader.svg?branch=master)](https://travis-ci.org/isoverse/isoreader) @@ -72,18 +72,18 @@ Currently supported file types: #> #> filter -| extension | format | type | -| :-------- | :----------------------------------------- | :-------------- | -| .did | Isodat Dual Inlet file format (newer) | dual inlet | -| .caf | Isodat Dual Inlet file format (older) | dual inlet | -| .txt | Nu Dual Inlet file format | dual inlet | -| .di.rda | Isoreader R Data Archive (deprecated) | dual inlet | -| .di.rds | Isoreader R Data Storage | dual inlet | -| .cf | Isodat Continuous Flow file format (older) | continuous flow | -| .dxf | Isodat Continuous Flow file format (newer) | continuous flow | -| .iarc | IonOS Continous Flow data archieve | continuous flow | -| .cf.rda | Isoreader R Data Archive (deprecated) | continuous flow | -| .cf.rds | Isoreader R Data Storage | continuous flow | +| extension | software | description | type | +| :-------- | :-------- | :---------------------------------- | :-------------- | +| .did | Isodat | Dual Inlet file format (newer) | dual inlet | +| .caf | Isodat | Dual Inlet file format (older) | dual inlet | +| .txt | Nu | Dual Inlet file format | dual inlet | +| .di.rda | isoreader | R Data Archive (deprecated) | dual inlet | +| .di.rds | isoreader | R Data Storage | dual inlet | +| .cf | Isodat | Continuous Flow file format (older) | continuous flow | +| .dxf | Isodat | Continuous Flow file format (newer) | continuous flow | +| .iarc | ionOS | Continous Flow data archieve | continuous flow | +| .cf.rda | isoreader | R Data Archive (deprecated) | continuous flow | +| .cf.rds | isoreader | R Data Storage | continuous flow | - for a full reference of all available functions, see the **[Function Reference](http://isoreader.isoverse.org/reference/)** diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 4a308958..fbc6eb95 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -307,7 +307,7 @@ test_that("test that get support file types are listed", { expect_true(is.data.frame(iso_get_supported_file_types())) expect_equal( iso_get_supported_file_types() %>% names(), - c("extension", "description", "type", "call") + c("extension", "software", "description", "type", "call") ) }) From e006a8acb2b17aa8ee4f96246be4c87a45eef23d Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sun, 9 Feb 2020 16:49:17 -0700 Subject: [PATCH 11/51] make tp in iarc files an integer (as it always is) --- R/isoread_flow_iarc.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/isoread_flow_iarc.R b/R/isoread_flow_iarc.R index cd14cd8b..f7075877 100644 --- a/R/isoread_flow_iarc.R +++ b/R/isoread_flow_iarc.R @@ -225,7 +225,7 @@ read_irms_data_file <- function(iso_file, filepath, gas_config, run_time.s, data dt <- run_time.s / nrow(irms_data) irms_data <- irms_data %>% rename(tp = Scan) %>% - mutate(time.s = dt * tp) %>% + mutate(tp = as.integer(tp), time.s = dt * tp) %>% select(tp, time.s, everything()) # store mass data From 2ab732399379597e350cf55219a5e833b17faee7 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sun, 9 Feb 2020 16:49:46 -0700 Subject: [PATCH 12/51] improve formatting on excel exports closes #92 --- R/export.R | 93 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 14 deletions(-) diff --git a/R/export.R b/R/export.R index 2dd12a3f..627232e6 100644 --- a/R/export.R +++ b/R/export.R @@ -50,41 +50,106 @@ iso_export_to_excel <- function(iso_files, filepath, # make excel workbook wb <- createWorkbook() - hs <- createStyle(textDecoration = "bold") if (include_raw_data) { - addWorksheet(wb, "raw data") raw_data <- iso_get_raw_data(export_iso_files, quiet = TRUE) - if (ncol(raw_data) > 0) writeData(wb, "raw data", raw_data, headerStyle = hs) + add_excel_sheet(wb, "raw data", raw_data) } if (include_file_info) { - addWorksheet(wb, "file info") # note: this takes care of nested vectors, they get concatenated with ', ' - writeData(wb, "file info", - iso_get_file_info(export_iso_files, quiet = TRUE) %>% collapse_list_columns(), - headerStyle = hs) + file_info <- iso_get_file_info(export_iso_files, quiet = TRUE) %>% collapse_list_columns() + add_excel_sheet(wb, "file info", file_info) } if (include_method_info) { - addWorksheet(wb, "method info") standards <- iso_get_standards_info(export_iso_files, quiet = TRUE) resistors <- iso_get_resistors_info (export_iso_files, quiet = TRUE) - if (ncol(standards) > 0) writeData(wb, "method info", standards, headerStyle = hs) - if (ncol(resistors) > 0) writeData(wb, "method info", resistors, startRow = nrow(standards) + 3, headerStyle = hs) + add_excel_sheet(wb, "method info", standards, resistors) } if (include_vendor_data_table) { - addWorksheet(wb, "vendor data table") vendor_data <- iso_get_vendor_data_table(export_iso_files, with_explicit_units = with_explicit_units, quiet = TRUE) %>% iso_strip_units() - if (ncol(vendor_data) > 0) writeData(wb, "vendor data table", vendor_data, headerStyle = hs) + add_excel_sheet(wb, "vendor data table", vendor_data) } if (include_problems) { - addWorksheet(wb, "problems") - writeData(wb, "problems", problems(iso_files), headerStyle = hs) + add_excel_sheet(wb, "problems", problems(iso_files)) } saveWorkbook(wb, filepath, overwrite = TRUE) return(invisible(iso_files)) } +# add an excel sheet to a workbook +# @param ... the data frames +# @param dbl_digits how many digits to export for dbls +# @param col_max_width maximum column width +add_excel_sheet <- function(wb, sheet_name, ..., dbl_digits = 2, col_max_width = 75) { + + # sheet + addWorksheet(wb, sheet_name) + hs <- createStyle(textDecoration = "bold") # header style + + # data + sheet_data_sets <- list(...) + start_row <- 1L + for (sheet_data in sheet_data_sets) { + if (ncol(sheet_data) > 0) { + writeData(wb, sheet_name, sheet_data, startRow = start_row, headerStyle = hs) + int_cols <- which(purrr::map_lgl(sheet_data, is.integer)) + dbl_cols <- setdiff(which(purrr::map_lgl(sheet_data, is.numeric)), int_cols) + if (dbl_digits < 1) { + int_cols <- c(int_cols, dbl_cols) + dbl_cols <- integer() + } + # integer column formatting + if (length(int_cols) > 0) { + openxlsx::addStyle( + wb, sheet_name, style = createStyle(numFmt = "0"), + rows = (start_row + 1L):(start_row + 1L + nrow(sheet_data)), + cols = int_cols, gridExpand = TRUE) + } + # double column formatting + if (length(dbl_cols) > 0) { + dbl_format <- paste0("0.", paste(rep("0", dbl_digits), collapse = "")) + openxlsx::addStyle( + wb, sheet_name, style = createStyle(numFmt = dbl_format), + rows = (start_row + 1L):(start_row + 1L + nrow(sheet_data)), + cols = dbl_cols, gridExpand = TRUE) + } + # new start row + start_row <- start_row + nrow(sheet_data) + 2L + } + } + + # calculate header widths + header_widths <- + sheet_data_sets %>% + # account for bold width + purrr::map(~nchar(names(.x))) + max_n_cols <- purrr::map_int(header_widths, length) %>% max() + + # calculate data widths + if (max_n_cols > 0) { + calculate_data_width <- function(x) { + if (is.integer(x)) x <- sprintf("%d", x) + else if (is.numeric(x)) x <- sprintf(paste0("%.", dbl_digits, "f"), x) + else x <- as.character(x) + return(max(c(0, nchar(x)), na.rm = TRUE)) + } + data_widths <- + sheet_data_sets %>% + purrr::map( + ~dplyr::summarise_all(.x, list(calculate_data_width)) %>% + unlist(use.names = FALSE) + ) + max_widths <- purrr::map2(header_widths, data_widths , ~{ + widths <- if (is.null(.y)) .x else pmax(.x, .y, 0) + widths <- pmin(col_max_width, widths) + c(widths, rep(0L, times = max_n_cols - length(widths))) + }) + col_widths <- do.call(pmax, args = max_widths) + openxlsx::setColWidths(wb, sheet_name, cols = 1:length(col_widths), widths = col_widths) + } + +} #' Export to feather #' From 810f6878f53c98fd718c98ef06497353eba2805a Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Sun, 9 Feb 2020 17:50:39 -0700 Subject: [PATCH 13/51] initial structure for scan files addition --- R/aggregate_data.R | 50 +++++++++++++++++++-------- R/isodata_structures.R | 33 +++++++++++++++--- R/isoread.R | 47 +++++++++++++++++++++++++ R/isoread_scn.R | 44 +++++++++++++++++++++++ R/zzz.R | 1 + tests/testthat/test-aggregate-data.R | 6 ++-- tests/testthat/test-data-structures.R | 39 +++++++++++++++++++-- tests/testthat/test-scan.R | 32 +++++++++++++++++ 8 files changed, 228 insertions(+), 24 deletions(-) create mode 100644 R/isoread_scn.R create mode 100644 tests/testthat/test-scan.R diff --git a/R/aggregate_data.R b/R/aggregate_data.R index 4449d08e..9d798dc6 100644 --- a/R/aggregate_data.R +++ b/R/aggregate_data.R @@ -42,7 +42,12 @@ iso_get_data_summary <- function(iso_files, quiet = default(quiet)) { left_join(get_raw_data_info(iso_files), by = "file_id") %>% left_join(get_file_info_info(iso_files), by = "file_id") %>% left_join(get_method_info_info(iso_files), by = "file_id") %>% - left_join(get_vendor_data_table_info(iso_files), by = "file_id") %>% + { + # scan files don't have vendor data table + if (!iso_is_scan(iso_files)) + left_join(., get_vendor_data_table_info(iso_files), by = "file_id") + else . + } %>% mutate(file_path = ifelse(!is.na(file_subpath), glue("{file_path_}|{file_subpath}"), file_path_)) %>% select(-file_path_, -file_subpath) } @@ -72,18 +77,24 @@ get_raw_data_info <- function(iso_files) { str_replace_all("[^0-9,]", "") ) - if (iso_is_continuous_flow(iso_files[[1]])) { + if (iso_is_continuous_flow(iso_files)) { raw_data_sum <- raw_data_sum %>% mutate( n_tps = map_int(iso_files, ~nrow(.x$raw_data)), label = ifelse(read_raw_data, glue("{n_tps} time points, {n_ions} ions ({ions})"), "raw data not read") ) - } else if (iso_is_dual_inlet(iso_files[[1]])) { + } else if (iso_is_dual_inlet(iso_files)) { raw_data_sum <- raw_data_sum %>% mutate( n_cycles = map_int(iso_files, ~as.integer(floor(nrow(.x$raw_data)/2))), label = ifelse(read_raw_data, glue("{n_cycles} cycles, {n_ions} ions ({ions})"), "raw data not read") ) + } else if (iso_is_scan(iso_files)) { + raw_data_sum <- raw_data_sum %>% + mutate( + n_tps = map_int(iso_files, ~nrow(.x$raw_data)), + label = ifelse(read_raw_data, glue("{n_tps} measurements, {n_ions} ions ({ions})"), "raw data not read") + ) } else if (iso_is_file(iso_files[[1]])) { # can only get here using make_iso_file_data_structure stop("make_iso_file_data_structure should never be called directly", call. = FALSE) @@ -223,13 +234,15 @@ iso_get_data <- function( else raw_data <- tibble(file_id = character(0), raw_data = list(NULL)) - # vendor data table - include_vendor_data_table_quo <- enquo(include_vendor_data_table) - dt <- iso_get_vendor_data_table(iso_files, with_explicit_units = with_explicit_units, select = !!include_vendor_data_table_quo, quiet = TRUE) - if (ncol(dt) > 1) - dt <- nest(dt, vendor_data_table = c(-file_id)) - else - dt <- tibble(file_id = character(0), vendor_data_table = list(NULL)) + # vendor data table (only cflow and dual inlet) + if (add_dt <- iso_is_continuous_flow(iso_files) || iso_is_dual_inlet(iso_files)) { + include_vendor_data_table_quo <- enquo(include_vendor_data_table) + dt <- iso_get_vendor_data_table(iso_files, with_explicit_units = with_explicit_units, select = !!include_vendor_data_table_quo, quiet = TRUE) + if (ncol(dt) > 1) + dt <- nest(dt, vendor_data_table = c(-file_id)) + else + dt <- tibble(file_id = character(0), vendor_data_table = list(NULL)) + } # methods_data - standards standards <- iso_get_standards_info(iso_files, with_ratios = with_ratios, quiet = TRUE) @@ -249,14 +262,16 @@ iso_get_data <- function( file_class %>% left_join(file_info, by = "file_id") %>% left_join(raw_data, by = "file_id") %>% - left_join(dt, by = "file_id") %>% + { if (add_dt) left_join(., dt, by = "file_id") else . } %>% left_join(standards, by = "file_id") %>% left_join(resistors, by = "file_id") %>% # info about what's missing mutate( has_file_info = !map_lgl(file_info, is.null), - has_raw_data = !map_lgl(raw_data, is.null), - has_vendor_data_table = !map_lgl(vendor_data_table, is.null), + has_raw_data = !map_lgl(raw_data, is.null) + ) %>% + { if (add_dt) mutate(., has_vendor_data_table = !map_lgl(vendor_data_table, is.null)) else . } %>% + mutate( has_standards = !map_lgl(standards, is.null), has_resistors = !map_lgl(resistors, is.null) ) @@ -578,8 +593,15 @@ iso_get_vendor_data_table <- function( # globals dt <- has_units <- NULL - iso_files <- iso_as_file_list(iso_files) + + # safety checks + if (iso_is_scan(iso_files)) + stop("scan files don't have vendor data tables", call. = FALSE) + else if (!iso_is_continuous_flow(iso_files) && !iso_is_dual_inlet(iso_files)) + stop("only dual inlet and continuous flow files can have vendor data tables", call. = FALSE) + + # process include_file_info_quo <- enquo(include_file_info) if (!quiet) { sprintf("Info: aggregating vendor data table%s from %d data file(s)%s", diff --git a/R/isodata_structures.R b/R/isodata_structures.R index 770c2a62..4816349f 100644 --- a/R/isodata_structures.R +++ b/R/isodata_structures.R @@ -8,8 +8,7 @@ make_iso_file_data_structure <- function(file_id = NA_character_) { read_options = list( # records read options+defaults file_info = FALSE, # whether file info was read method_info = FALSE, # whether method info was read - raw_data = FALSE, # whether mass data was read (Note: maybe not top-level b/c of scans?) - vendor_data_table = FALSE # whether vendor data table was read + raw_data = FALSE # whether mass data was read ), file_info = dplyr::tibble( file_id = file_id, # unique identifer @@ -19,8 +18,7 @@ make_iso_file_data_structure <- function(file_id = NA_character_) { file_datetime = NA_integer_ # the run date and time of the file ), method_info = list(), # all methods information - raw_data = dplyr::tibble(), # all mass data (Note: maybe not top-level b/c of scans?) - vendor_data_table = dplyr::tibble() # vendor computed data table (no units) + raw_data = dplyr::tibble() # all mass data ), class = c("iso_file") ) %>% @@ -31,7 +29,11 @@ make_iso_file_data_structure <- function(file_id = NA_character_) { # basic dual inlet data structure make_di_data_structure <- function(file_id = NA_character_) { struct <- make_iso_file_data_structure(file_id = file_id) - struct$bgrd_data <- data_frame() # store background data + # vendor data table + struct$read_options$vendor_data_table <- FALSE + struct$vendor_data_table <- dplyr::tibble() + # background + struct$bgrd_data <- dplyr::tibble() class(struct) <- c("dual_inlet", class(struct)) return(struct) } @@ -39,10 +41,19 @@ make_di_data_structure <- function(file_id = NA_character_) { # basic continuous flow data structure make_cf_data_structure <- function(file_id = NA_character_) { struct <- make_iso_file_data_structure(file_id = file_id) + # vendor data table + struct$read_options$vendor_data_table <- FALSE + struct$vendor_data_table <- dplyr::tibble() class(struct) <- c("continuous_flow", class(struct)) return(struct) } +# basic scan data structure +make_scan_data_structure <- function(file_id = NA_character_) { + struct <- make_iso_file_data_structure(file_id = file_id) + class(struct) <- c("scan", class(struct)) + return(struct) +} # Class testing ==== @@ -86,6 +97,12 @@ iso_is_continuous_flow <- function(x) { methods::is(x, "continuous_flow") || methods::is(x, "continuous_flow_list") } +#' @description \code{iso_is_scan} tests if an iso_file or iso_file list consists exclusively of scan file objects +#' @rdname iso_data_structure +#' @export +iso_is_scan <- function(x) { + methods::is(x, "scan") || methods::is(x, "scan_list") +} # Iso file list ---- @@ -252,6 +269,12 @@ print.continuous_flow <- function(x, ..., show_problems = TRUE) { NextMethod("print", x, ..., show_problems = show_problems) } +#' @rdname iso_printing +#' @export +print.scan <- function(x, ..., show_problems = TRUE) { + NextMethod("print", x, ..., show_problems = show_problems) +} + # Update structures ===== diff --git a/R/isoread.R b/R/isoread.R index d2358e5e..5a8f693a 100644 --- a/R/isoread.R +++ b/R/isoread.R @@ -29,6 +29,14 @@ iso_register_continuous_flow_file_reader <- function( register_file_reader("continuous flow", "iso_read_continuous_flow", extension, func, description, software, cacheable, overwrite, env) } +#' @details \code{iso_register_scan_file_reader}: use this function to register file readers for scan files. +#' @rdname file_readers +#' @family file_types +iso_register_scan_file_reader <- function( + extension, func, description = NA_character_, software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { + register_file_reader("scan", "iso_read_scan", extension, func, description, software, cacheable, overwrite, env) +} + register_file_reader <- function(type, call, extension, func, description, software, cacheable, overwrite, env) { if (!is.character(func)) @@ -101,6 +109,10 @@ get_supported_cf_files <- function() { dplyr::filter(default("file_readers"), !!sym("type") == "continuous flow") } +get_supported_scan_files <- function() { + dplyr::filter(default("file_readers"), !!sym("type") == "scan") +} + # file reading =========== #' Read isotope data file @@ -194,6 +206,41 @@ iso_read_continuous_flow <- function( ) } +#' Load scan data +#' +#' @inheritParams iso_read_dual_inlet +#' @family isoread functions for different types of IRMS data +#' @export +iso_read_scan <- function( + ..., + root = ".", + read_raw_data = default(read_raw_data), read_file_info = default(read_file_info), read_method_info = default(read_method_info), + discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multiprocess, + cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), quiet = default(quiet)) { + + # process data + iso_read_files( + unlist(list(...), use.names = FALSE), + root = root, + supported_extensions = get_supported_scan_files(), + data_structure = make_scan_data_structure(), + read_options = c( + read_raw_data = read_raw_data, + read_file_info = read_file_info, + read_method_info = read_method_info + ), + reader_options = list(), + discard_duplicates = discard_duplicates, + parallel = parallel, + parallel_plan = parallel_plan, + cache = cache, + cache_files_with_errors = cache_files_with_errors, + read_cache = read_cache, + quiet = quiet + ) +} + + #' Core function to read isotope data files #' #' This function takes care of extracting basic information about iso_files, dealing with problems and making sure only valid fire formats are processed. diff --git a/R/isoread_scn.R b/R/isoread_scn.R new file mode 100644 index 00000000..7cf99b8f --- /dev/null +++ b/R/isoread_scn.R @@ -0,0 +1,44 @@ +# read isodat .scn file +# @param ds the data structure to fill +# @param custom reader options - none needed +iso_read_scn <- function(ds, options = list()) { + + # safety checks + if(!iso_is_scan(ds)) + stop("data structure must be a 'scan' iso_file", call. = FALSE) + + # read binary file + ds$binary <- get_ds_file_path(ds) %>% read_binary_file() + + # # process file info + # if(ds$read_options$file_info) { + # ds <- exec_func_with_error_catch(extract_isodat_sequence_line_info, ds) + # ds <- exec_func_with_error_catch(extract_isodat_measurement_info, ds) + # ds <- exec_func_with_error_catch(extract_isodat_datetime, ds) + # ds <- exec_func_with_error_catch(extract_H3_factor_info, ds) + # ds <- exec_func_with_error_catch(extract_MS_integration_time_info, ds) + # } + # + # # process raw data + # if (ds$read_option$raw_data) { + # ds <- exec_func_with_error_catch(extract_dxf_raw_voltage_data, ds) + # } + # + # # process method info + # if (ds$read_options$method_info) { + # ds <- exec_func_with_error_catch( + # extract_isodat_reference_values, ds, + # cap_at_fun = function(bin) cap_at_next_C_block(bin, "CResultArray")) + # ds <- exec_func_with_error_catch(extract_isodat_resistors, ds) + # } + # + # # process pre-evaluated data table + # if (ds$read_options$vendor_data_table) { + # ds <- exec_func_with_error_catch( + # extract_isodat_continuous_flow_vendor_data_table, ds, + # cap_at_fun = function(bin) cap_at_pos(bin, find_next_pattern(bin, re_text("DetectorDataBlock")))) + # } + # + return(ds) + +} diff --git a/R/zzz.R b/R/zzz.R index dfe09303..bdf92a74 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -36,6 +36,7 @@ initialize_options <- function() { iso_register_continuous_flow_file_reader(".iarc", "iso_read_flow_iarc", "Continous Flow data archieve", "ionOS", env = "isoreader") iso_register_continuous_flow_file_reader(".cf.rda", "iso_read_rda", "R Data Archive (deprecated)", "isoreader", cacheable = FALSE, env = "isoreader") iso_register_continuous_flow_file_reader(".cf.rds", "iso_read_rds", "R Data Storage", "isoreader", cacheable = FALSE, env = "isoreader") + iso_register_scan_file_reader(".scn", "iso_read_scn", "Scan file format", "Isodat", env = "isoreader") invisible(options()[names(default_options)]) } diff --git a/tests/testthat/test-aggregate-data.R b/tests/testthat/test-aggregate-data.R index ae418e8a..432aa97e 100644 --- a/tests/testthat/test-aggregate-data.R +++ b/tests/testthat/test-aggregate-data.R @@ -275,7 +275,9 @@ test_that("test that aggregating of resistors works", { test_that("test that aggregating of vendor data table works", { - iso_file <- make_iso_file_data_structure("NA") + expect_error(iso_get_vendor_data_table(make_iso_file_data_structure("NA")), "only dual inlet and continuous flow files") + expect_error(iso_get_vendor_data_table(make_scan_data_structure("NA")), "scan files don't have") + iso_file <- make_cf_data_structure("NA") expect_warning(iso_get_vendor_data_table(iso_file), "read without extracting the vendor data table") # test data @@ -331,7 +333,7 @@ test_that("test that aggregating of vendor data table works", { # make sure that files that have no raw data do not get added back in by including file info expect_equal( suppressWarnings(iso_get_vendor_data_table( - c(make_iso_file_data_structure("NA"), iso_file1, iso_file2), + c(make_cf_data_structure("NA"), iso_file1, iso_file2), include_file_info = c("test_info")))$test_info %>% unique(), c("x", "y") ) diff --git a/tests/testthat/test-data-structures.R b/tests/testthat/test-data-structures.R index 3b4bbcb7..3ab0ac0b 100644 --- a/tests/testthat/test-data-structures.R +++ b/tests/testthat/test-data-structures.R @@ -3,8 +3,8 @@ context("Data Structures") # basic iso_file data structure is correct ==== test_that("test that basic iso_file data structure is correct", { expect_is(iso_file <- make_iso_file_data_structure("NA"), "iso_file") - expect_equal(names(iso_file), c("version", "read_options", "file_info", "method_info", "raw_data", "vendor_data_table")) - expect_equal(names(iso_file$read_options), c("file_info", "method_info", "raw_data", "vendor_data_table")) + expect_equal(names(iso_file), c("version", "read_options", "file_info", "method_info", "raw_data")) + expect_equal(names(iso_file$read_options), c("file_info", "method_info", "raw_data")) expect_equal(iso_file$version, packageVersion("isoreader")) expect_false(iso_is_file(42)) expect_false(iso_is_file(iso_as_file_list())) @@ -16,6 +16,8 @@ test_that("test that basic iso_file data structure is correct", { # dual inlet data structure is correct ==== test_that("test that dual inlet data structure is correct", { expect_is(di <- make_di_data_structure("NA"), "iso_file") + expect_equal(names(di), c("version", "read_options", "file_info", "method_info", "raw_data", "vendor_data_table", "bgrd_data")) + expect_equal(names(di$read_options), c("file_info", "method_info", "raw_data", "vendor_data_table")) expect_is(di, "dual_inlet") expect_false(iso_is_dual_inlet(42)) expect_false(iso_is_continuous_flow(di)) @@ -32,6 +34,8 @@ test_that("test that dual inlet data structure is correct", { # continuous data structure is correct ==== test_that("test that continuous data structure is correct", { expect_is(cf <- make_cf_data_structure("NA"), "iso_file") + expect_equal(names(cf), c("version", "read_options", "file_info", "method_info", "raw_data", "vendor_data_table")) + expect_equal(names(cf$read_options), c("file_info", "method_info", "raw_data", "vendor_data_table")) expect_is(cf, "continuous_flow") expect_false(iso_is_continuous_flow(42)) expect_false(iso_is_dual_inlet(cf)) @@ -45,6 +49,25 @@ test_that("test that continuous data structure is correct", { # FIXME: expand }) +# scan data strucutre is correct ==== +test_that("test that continuous data structure is correct", { + expect_is(scn <- make_scan_data_structure("NA"), "iso_file") + expect_equal(names(scn), c("version", "read_options", "file_info", "method_info", "raw_data")) + expect_equal(names(scn$read_options), c("file_info", "method_info", "raw_data")) + expect_is(scn, "scan") + expect_false(iso_is_scan(42)) + expect_false(iso_is_dual_inlet(scn)) + expect_true(iso_is_scan(scn)) + expect_true(is.null(scn$vendor_data_table)) + expect_true({ + scn1 <- scn2 <- scn + scn1$file_info$file_id <- "A" + scn2$file_info$file_id <- "B" + iso_is_scan(c(scn1, scn2)) + }) + # FIXME: expand +}) + # printing of data structure works ==== test_that("test that data structure can be printed", { cf <- make_cf_data_structure("NA") @@ -55,6 +78,10 @@ test_that("test that data structure can be printed", { expect_output(print(di), "raw data not read") di$read_options$raw_data <- TRUE expect_output(print(di), "0 ions") + scn <- make_scan_data_structure("NA") + expect_output(print(scn), "raw data not read") + scn$read_options$raw_data <- TRUE + expect_output(print(scn), "0 ions") }) # iso_file list checks work ==== @@ -71,11 +98,11 @@ test_that("test that iso_file list checks work", { expect_equal(iso_as_file_list() %>% iso_get_data_summary() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_raw_data() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_file_info() %>% nrow(), 0) - expect_equal(iso_as_file_list() %>% iso_get_vendor_data_table() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_resistors_info() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_standards_info() %>% nrow(), 0) # expected errors + expect_error(iso_as_file_list() %>% iso_get_vendor_data_table(), "only dual inlet.*continuous flow") expect_error(iso_as_file_list(1, error = "test"), "encountered incompatible data type") expect_false(iso_is_file_list(42)) expect_false(iso_is_file_list(make_iso_file_data_structure("NA"))) @@ -109,6 +136,12 @@ test_that("can set file path for data structures", { # can update read options ==== test_that("test that can update read options", { expect_is(iso_file <- make_iso_file_data_structure("NA"), "iso_file") + expect_equal(iso_file$read_options, list(file_info = FALSE, method_info = FALSE, raw_data = FALSE)) + expect_equal(update_read_options(iso_file, c(not_an_option = FALSE))$read_options, + list(file_info = FALSE, method_info = FALSE, raw_data = FALSE)) + expect_equal(update_read_options(iso_file, c(read_file_info = TRUE, raw_data = TRUE))$read_options, + list(file_info = TRUE, method_info = FALSE, raw_data = TRUE)) + expect_is(iso_file <- make_di_data_structure("NA"), "dual_inlet") expect_equal(iso_file$read_options, list(file_info = FALSE, method_info = FALSE, raw_data = FALSE, vendor_data_table = FALSE)) expect_equal(update_read_options(iso_file, c(not_an_option = FALSE))$read_options, list(file_info = FALSE, method_info = FALSE, raw_data = FALSE, vendor_data_table = FALSE)) diff --git a/tests/testthat/test-scan.R b/tests/testthat/test-scan.R new file mode 100644 index 00000000..c34777ce --- /dev/null +++ b/tests/testthat/test-scan.R @@ -0,0 +1,32 @@ +context("Scan") + +test_that("test that supported scan files are correct", { + initialize_options() + expect_is(exts <- get_supported_scan_files(), "data.frame") + expect_equal(exts$extension, c(".scn")) + expect_true(all(exts$func %>% sapply(class) == "character")) + expect_true(all(exts$func %>% map_lgl(exists, mode = "function", where = asNamespace("isoreader")))) +}) + +test_that("test that parameter checks are performed", { + + # flow iarc + expect_error(iso_read_scn (make_di_data_structure("NA")), + "data structure must be a \\'scan\\' iso_file") + + +}) + + +test_that("test that scn files can be read", { + # test specific files + + # FIXME: re-enable for commits + skip("Currently not testing all scan data files.") + # FIXME: run as one batch to make use of parallel processing + + iso_turn_reader_caching_off() + + +}) + From 4899e095fecc27229c9cc847fdba08711101a1ab Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 01:04:09 -0700 Subject: [PATCH 14/51] add example scan files --- inst/extdata/high_voltage_scan_example.scn | Bin 0 -> 36411 bytes inst/extdata/magnet_scan_example.scn | Bin 0 -> 57551 bytes inst/extdata/time_scan_example.scn | Bin 0 -> 140550 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 inst/extdata/high_voltage_scan_example.scn create mode 100644 inst/extdata/magnet_scan_example.scn create mode 100644 inst/extdata/time_scan_example.scn diff --git a/inst/extdata/high_voltage_scan_example.scn b/inst/extdata/high_voltage_scan_example.scn new file mode 100644 index 0000000000000000000000000000000000000000..e4f63647588ce5e58c0b1a257f5797bbd9059c77 GIT binary patch literal 36411 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 literal 0 HcmV?d00001 diff --git a/inst/extdata/magnet_scan_example.scn b/inst/extdata/magnet_scan_example.scn new file mode 100644 index 0000000000000000000000000000000000000000..6a0557225bca6ce98e59be344b18edcfc703a296 GIT binary patch literal 57551 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 Date: Mon, 10 Feb 2020 01:06:48 -0700 Subject: [PATCH 15/51] rename example files --- ...scan_example.scn => full_scan_example.scn} | Bin ...xample.scn => peak_shape_scan_example.scn} | Bin vignettes/scan.Rmd | 317 ++++++++++++++++++ 3 files changed, 317 insertions(+) rename inst/extdata/{magnet_scan_example.scn => full_scan_example.scn} (100%) rename inst/extdata/{high_voltage_scan_example.scn => peak_shape_scan_example.scn} (100%) create mode 100644 vignettes/scan.Rmd diff --git a/inst/extdata/magnet_scan_example.scn b/inst/extdata/full_scan_example.scn similarity index 100% rename from inst/extdata/magnet_scan_example.scn rename to inst/extdata/full_scan_example.scn diff --git a/inst/extdata/high_voltage_scan_example.scn b/inst/extdata/peak_shape_scan_example.scn similarity index 100% rename from inst/extdata/high_voltage_scan_example.scn rename to inst/extdata/peak_shape_scan_example.scn diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd new file mode 100644 index 00000000..c313b993 --- /dev/null +++ b/vignettes/scan.Rmd @@ -0,0 +1,317 @@ +--- +title: "Dual Inlet Examples" +date: "`r Sys.Date()`" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Dual Inlet Examples} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + + +```{r setup, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +# Introduction + +Isoreader supports several dual inlet IRMS data formats. This vignette shows some of the functionality for dual inlet data files. For additional information on operations more generally (caching, combining read files, data export, etc.), please consult the [operations vignette](http://isoreader.isoverse.org/articles/operations.html). For details on downstream data processing and visualization, see the [isoprocessor package](https://isoprocessor.isoverse.org). + +Note: this vignette is still a work in progress. + + +```{r, message=FALSE} +# load isoreader package +library(isoreader) +``` + + +# Reading files + +Reading dual inlet files is as simple as passing one or multiple file or folder paths to the `iso_read_dual_inlet()` function. If folders are provided, any files that have a recognized continuous flow file extensions within those folders will be processed (e.g. all `.did` and `.caf`). Here we read several files that are bundled with the package as examples (and whose paths can be retrieved using the `iso_get_reader_example()` function). + +```{r, message=FALSE} +# all available examples +iso_get_reader_examples() %>% rmarkdown::paged_table() +``` + +```{r} +# read dual inlet examples +iso_files <- + iso_read_dual_inlet( + iso_get_reader_example("dual_inlet_example.did"), + iso_get_reader_example("dual_inlet_example2.did"), + iso_get_reader_example("dual_inlet_example.caf"), + iso_get_reader_example("dual_inlet_nu_example.txt"), + nu_masses = 49:44 + ) +``` + +# File summary + +The `iso_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. + +```{r} +iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() +``` + +## Problems + +In case there was any trouble with reading any of the files, the following functions provide an overview summary as well as details of all errors and warnings, respectively. The examples here contain no errors but if you run into any unexpected file read problems, please file a bug report in the [isoreader issue tracker](https://github.com/isoverse/isoreader/issues). + +```{r} +iso_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() +iso_files %>% iso_get_problems() %>% rmarkdown::paged_table() +``` + +# File Information + +Detailed file information can be aggregated for all isofiles using the `iso_get_file_info()` function which supports the full [select syntax](https://dplyr.tidyverse.org/reference/select.html) of the [dplyr](https://dplyr.tidyverse.org/) package to specify which columns are of interest (by default, all file information is retrieved). Additionally, file information from different file formats can be renamed to the same column name for easy of downstream processing. The following provides a few examples for how this can be used (the names of the interesting info columns may vary between different file formats): + +```{r} +# all file information +iso_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() +# select file information +iso_files %>% + iso_get_file_info( + select = c( + # rename sample id columns from the different file types to a new ID column + ID = `Identifier 1`, ID = `Sample Name`, + # select columns without renaming + Analysis, Method, `Peak Center`, + # select the time stamp and rename it to `Date & Time` + `Date & Time` = file_datetime, + # rename weight columns from the different file types + `Sample Weight`, `Sample Weight` = `Weight [mg]` + ) + ) %>% rmarkdown::paged_table() +``` + +## Select/Rename + +Rather than retrieving specific file info columns using the above example of `iso_get_file_info(select = ...)`, these information can also be modified across an entire collection of isofiles using the `iso_select_file_info()` and `iso_rename_file_info()` functions. For example, the above example could be similarly achieved with the following use of `iso_select_file_info()`: + +```{r} +# select + rename specific file info columns +iso_files2 <- iso_files %>% + iso_select_file_info( + ID = `Identifier 1`, ID = `Sample Name`, Analysis, Method, + `Peak Center`, `Date & Time` = file_datetime, + `Sample Weight`, `Sample Weight` = `Weight [mg]` + ) + +# fetch all file info +iso_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() +``` + +## Filter + +Any collection of isofiles can also be filtered based on the available file information using the function `iso_filter_files`. This function can operate on any column available in the file information and supports full [dplyr](https://dplyr.tidyverse.org/reference/filter.html) syntax. + +```{r} +# find files that have 'CIT' in the new ID field +iso_files2 %>% iso_filter_files(grepl("CIT", ID)) %>% + iso_get_file_info() %>% + rmarkdown::paged_table() + +# find files that were run in 2017 +iso_files2 %>% + iso_filter_files(`Date & Time` > "2017-01-01" & `Date & Time` < "2018-01-01") %>% + iso_get_file_info() %>% + rmarkdown::paged_table() +``` + +## Mutate + +The file information in any collection of isofiles can also be mutated using the function `iso_mutate_file_info`. This function can introduce new columns and operate on any existing columns available in the file information (even if it does not exist in all files) and supports full [dplyr](https://dplyr.tidyverse.org/reference/mutate.html) syntax. + +```{r} +iso_files3 <- iso_files2 %>% + iso_mutate_file_info( + # update existing column + ID = paste("ID:", ID), + # introduce new column + `Run in 2017?` = `Date & Time` > "2017-01-01" & `Date & Time` < "2018-01-01" + ) + +iso_files3 %>% + iso_get_file_info() %>% + rmarkdown::paged_table() +``` + +## Add + +Additionally, a wide range of new file information can be added in the form of a data frame with any number of columns (usually read from a comma-separated-value/csv file or an Excel/xlsx file) using the function `iso_add_file_info` and specifying which existing file information should be used to merge in the new information. It is similar to [dplyr's left_join](https://dplyr.tidyverse.org/reference/join.html) but with additional safety checks and the possibility to join the new information sequentially as illustrated below. + +```{r} +# this kind of information data frame is frequently read in from a csv or xlsx file +new_info <- + dplyr::bind_rows( + # new information based on new vs. old samples + dplyr::tribble( + ~Analysis, ~`Run in 2017?`, ~process, ~info, + NA, TRUE, "yes", "2017 runs", + NA, FALSE, "yes", "other runs" + ), + # new information for a single specific file + dplyr::tribble( + ~Analysis, ~process, ~note, + "16068", "no", "did not inject properly" + ) + ) +new_info %>% rmarkdown::paged_table() + +# adding it to the isofiles +iso_files3 %>% + iso_add_file_info(new_info, by1 = "Run in 2017?", by2 = "Analysis") %>% + iso_get_file_info(select = names(new_info)) %>% + rmarkdown::paged_table() +``` + + +## Parse + +Most file information is initially read as text to avoid cumbersome specifications during the read process and compatibility issues between different IRMS file formats. However, many file info columns are not easily processed as text. The isoreader package therefore provides several parsing and data extraction functions to facilitate processing the text-based data (some via functionality implemented by the [readr](http://readr.tidyverse.org) package). See code block below for examples. For a complete overview, see the `?extract_data` and `?iso_parse_file_info` documentation. + +```{r} +# use parsing and extraction in iso_mutate_file_info +iso_files2 %>% + iso_mutate_file_info( + # change type of Peak Center to logical + `Peak Center` = parse_logical(`Peak Center`), + # retrieve first word of Method column + Method_1st = extract_word(Method), + # retrieve second word of Method column + Method_2nd = extract_word(Method, 2), + # retrieve file extension from the file_id using regular expression + extension = extract_substring(file_id, "\\.(\\w+)$", capture_bracket = 1) + ) %>% + iso_get_file_info(select = c(extension, `Peak Center`, matches("Method"))) %>% + rmarkdown::paged_table() + +# use parsing in iso_filter_file_info +iso_files2 %>% + iso_filter_files(parse_integer(Analysis) > 1500) %>% + iso_get_file_info() %>% + rmarkdown::paged_table() + +# use iso_parse_file_info for simplified parsing of column data types +iso_files2 %>% + iso_parse_file_info( + integer = Analysis, + number = `Sample Weight`, + logical = `Peak Center` + ) %>% + iso_get_file_info() %>% + rmarkdown::paged_table() +``` + +# Resistors + +Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette): + +```{r} +iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table() +``` + +# Reference values + +As well as isotopic reference values for the different gases: + +```{r} +# reference delta values without ratio values +iso_files %>% iso_get_standards_info() %>% rmarkdown::paged_table() +# reference values with ratios +iso_files %>% iso_get_standards_info(with_ratios = TRUE) %>% rmarkdown::paged_table() +``` + +# Raw Data + +The raw data read from the IRMS files can be retrieved similarly using the `iso_get_raw_data()` function. Most data aggregation functions also allow for inclusion of file information using the `include_file_info` parameter, which functions identically to the `select` parameter of the `iso_get_file_info` function discussed earlier. + +```{r} +# get raw data with default selections (all raw data, no additional file info) +iso_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() +# get specific raw data and add some file information +iso_files %>% + iso_get_raw_data( + # select just time and the two ions + select = c(type, cycle, v28.mV, v29.mV), + # include the Analysis number fron the file info and rename it to 'run' + include_file_info = c(run = Analysis) + ) %>% + # look at first few records only + head(n=10) %>% rmarkdown::paged_table() +``` + + +# Data Processing + +The isoreader package is intended to make raw stable isotope data easily accessible. However, as with most analytical data, there is significant downstream processing required to turn these raw signal intensities into properly referenced isotopic measurement. This and similar functionality as well as data visualization is part of the [isoprocessor package](https://isoprocessor.isoverse.org) which takes isotopic data through the various corrections in a transparent, efficient and reproducible manner. + +That said, most vendor software also performs some of these calculations and it can be useful to be able to compare new data reduction procecures against those implemented in the vendor software. For this purpose, isoreader retrieves vendor computed data tables whenver possible, as illustrated below. + +## Vendor Data Table + +As with most data retrieval funtions, the `iso_get_vendor_data_table()` function also allows specific column selection (by default, all columns are selected) and easy addition of file information via the `include_file_info` parameter (by default, none is included). + +```{r} +# entire vendor data table +iso_files %>% iso_get_vendor_data_table() %>% rmarkdown::paged_table() +# get specific parts and add some file information +iso_files %>% + iso_get_vendor_data_table( + # select cycle and all carbon columns + select = c(cycle, matches("C")), + # include the Identifier 1 fron the file info and rename it to 'id' + include_file_info = c(id = `Identifier 1`) + ) %>% rmarkdown::paged_table() +``` + +## For expert users: retrieving all data + +For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame. Use the `include_file_info`, `include_raw_data`, and `include_vendor_data_table` parameters to specify which columns to include. By default, everything is included: + +```{r} +all_data <- iso_files %>% iso_get_data() +# not printed out because this data frame is very big +``` + +# Saving collections + +Saving entire collections of isofiles for retrieval at a later point is easily done using the `iso_save` function which stores collections or individual isoreader file objects in the efficient R data storage format `.rds` (if not specified, the extension `.di.rds` will be automatically appended). These saved collections can be convientiently read back using the same `iso_read_dual_inlet` command used for raw data files. + +```{r} +# export to R data archive +iso_files %>% iso_save("iso_files_export.di.rds") + +# read back the exported R data storage +iso_read_dual_inlet("iso_files_export.di.rds") +``` + + +# Data Export + +At the moment, isoreader supports export of all data to Excel and the [Feather file format](https://blog.rstudio.com/2016/03/29/feather/) (a Python/R cross-over format). Note that both export methods have similar syntax and append the appropriate file extension for each type of export file (`.di.xlsx` and `.di.feather`, respectively). + +```{r} +# export to excel +iso_files %>% iso_export_to_excel("iso_files_export") + +# data sheets available in the exported data file: +readxl::excel_sheets("iso_files_export.di.xlsx") +``` + +```{r} +# export to feather +iso_files %>% iso_export_to_feather("iso_files_export") + +# exported feather files +list.files(pattern = ".di.feather") +``` + + + From 805a32335e34cf46d4cbd150f1e6a4572d11f4cf Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 01:25:03 -0700 Subject: [PATCH 16/51] add background scan example --- inst/extdata/background_scan_example.scn | Bin 0 -> 62303 bytes vignettes/scan.Rmd | 226 ++++++----------------- 2 files changed, 59 insertions(+), 167 deletions(-) create mode 100644 inst/extdata/background_scan_example.scn diff --git a/inst/extdata/background_scan_example.scn b/inst/extdata/background_scan_example.scn new file mode 100644 index 0000000000000000000000000000000000000000..4d1826c01c6aa13bc0b4ad3ac9c171a7c446e1fe GIT binary patch literal 62303 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�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 literal 0 HcmV?d00001 diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index c313b993..93a8f996 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -1,9 +1,9 @@ --- -title: "Dual Inlet Examples" +title: "Scan Examples" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Dual Inlet Examples} + %\VignetteIndexEntry{Scan Examples} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -18,7 +18,7 @@ knitr::opts_chunk$set( # Introduction -Isoreader supports several dual inlet IRMS data formats. This vignette shows some of the functionality for dual inlet data files. For additional information on operations more generally (caching, combining read files, data export, etc.), please consult the [operations vignette](http://isoreader.isoverse.org/articles/operations.html). For details on downstream data processing and visualization, see the [isoprocessor package](https://isoprocessor.isoverse.org). +Isoreader supports several dual inlet IRMS data formats. This vignette shows some of the functionality for scan data files. For additional information on operations more generally (caching, combining read files, data export, etc.), please consult the [operations vignette](http://isoreader.isoverse.org/articles/operations.html). For details on downstream data processing and visualization, see the [isoprocessor package](https://isoprocessor.isoverse.org). Note: this vignette is still a work in progress. @@ -31,7 +31,7 @@ library(isoreader) # Reading files -Reading dual inlet files is as simple as passing one or multiple file or folder paths to the `iso_read_dual_inlet()` function. If folders are provided, any files that have a recognized continuous flow file extensions within those folders will be processed (e.g. all `.did` and `.caf`). Here we read several files that are bundled with the package as examples (and whose paths can be retrieved using the `iso_get_reader_example()` function). +Reading scan files is as simple as passing one or multiple file or folder paths to the `iso_read_scan()` function. If folders are provided, any files that have a recognized scan file extensions within those folders will be processed (e.g. all `.scn`). Here we read several files that are bundled with the package as examples (and whose paths can be retrieved using the `iso_get_reader_example()` function). ```{r, message=FALSE} # all available examples @@ -41,12 +41,11 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() ```{r} # read dual inlet examples iso_files <- - iso_read_dual_inlet( - iso_get_reader_example("dual_inlet_example.did"), - iso_get_reader_example("dual_inlet_example2.did"), - iso_get_reader_example("dual_inlet_example.caf"), - iso_get_reader_example("dual_inlet_nu_example.txt"), - nu_masses = 49:44 + iso_read_scan( + iso_get_reader_example("peak_shape_scan_example.scn"), + iso_get_reader_example("full_scan_example.scn"), + iso_get_reader_example("time_scan_example.scn"), + read_cache = FALSE ) ``` @@ -69,57 +68,36 @@ iso_files %>% iso_get_problems() %>% rmarkdown::paged_table() # File Information -Detailed file information can be aggregated for all isofiles using the `iso_get_file_info()` function which supports the full [select syntax](https://dplyr.tidyverse.org/reference/select.html) of the [dplyr](https://dplyr.tidyverse.org/) package to specify which columns are of interest (by default, all file information is retrieved). Additionally, file information from different file formats can be renamed to the same column name for easy of downstream processing. The following provides a few examples for how this can be used (the names of the interesting info columns may vary between different file formats): +Detailed file information can be aggregated for all isofiles using the `iso_get_file_info()` function which supports the full [select syntax](https://dplyr.tidyverse.org/reference/select.html) of the [dplyr](https://dplyr.tidyverse.org/) package to specify which columns are of interest (by default, all file information is retrieved). ```{r} # all file information iso_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() -# select file information -iso_files %>% - iso_get_file_info( - select = c( - # rename sample id columns from the different file types to a new ID column - ID = `Identifier 1`, ID = `Sample Name`, - # select columns without renaming - Analysis, Method, `Peak Center`, - # select the time stamp and rename it to `Date & Time` - `Date & Time` = file_datetime, - # rename weight columns from the different file types - `Sample Weight`, `Sample Weight` = `Weight [mg]` - ) - ) %>% rmarkdown::paged_table() ``` ## Select/Rename -Rather than retrieving specific file info columns using the above example of `iso_get_file_info(select = ...)`, these information can also be modified across an entire collection of isofiles using the `iso_select_file_info()` and `iso_rename_file_info()` functions. For example, the above example could be similarly achieved with the following use of `iso_select_file_info()`: +File information can also be modified across an entire collection of isofiles using the `iso_select_file_info()` and `iso_rename_file_info()` functions: ```{r} # select + rename specific file info columns iso_files2 <- iso_files %>% - iso_select_file_info( - ID = `Identifier 1`, ID = `Sample Name`, Analysis, Method, - `Peak Center`, `Date & Time` = file_datetime, - `Sample Weight`, `Sample Weight` = `Weight [mg]` - ) + iso_select_file_info(-file_root) %>% + iso_rename_file_info(`Date & Time` = file_datetime) # fetch all file info iso_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` + ## Filter Any collection of isofiles can also be filtered based on the available file information using the function `iso_filter_files`. This function can operate on any column available in the file information and supports full [dplyr](https://dplyr.tidyverse.org/reference/filter.html) syntax. ```{r} # find files that have 'CIT' in the new ID field -iso_files2 %>% iso_filter_files(grepl("CIT", ID)) %>% - iso_get_file_info() %>% - rmarkdown::paged_table() - -# find files that were run in 2017 iso_files2 %>% - iso_filter_files(`Date & Time` > "2017-01-01" & `Date & Time` < "2018-01-01") %>% + iso_filter_files(type == "High Voltage") %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` @@ -131,10 +109,8 @@ The file information in any collection of isofiles can also be mutated using the ```{r} iso_files3 <- iso_files2 %>% iso_mutate_file_info( - # update existing column - ID = paste("ID:", ID), # introduce new column - `Run in 2017?` = `Date & Time` > "2017-01-01" & `Date & Time` < "2018-01-01" + `Run in 2019?` = `Date & Time` > "2019-01-01" & `Date & Time` < "2020-01-01" ) iso_files3 %>% @@ -142,95 +118,18 @@ iso_files3 %>% rmarkdown::paged_table() ``` -## Add - -Additionally, a wide range of new file information can be added in the form of a data frame with any number of columns (usually read from a comma-separated-value/csv file or an Excel/xlsx file) using the function `iso_add_file_info` and specifying which existing file information should be used to merge in the new information. It is similar to [dplyr's left_join](https://dplyr.tidyverse.org/reference/join.html) but with additional safety checks and the possibility to join the new information sequentially as illustrated below. - -```{r} -# this kind of information data frame is frequently read in from a csv or xlsx file -new_info <- - dplyr::bind_rows( - # new information based on new vs. old samples - dplyr::tribble( - ~Analysis, ~`Run in 2017?`, ~process, ~info, - NA, TRUE, "yes", "2017 runs", - NA, FALSE, "yes", "other runs" - ), - # new information for a single specific file - dplyr::tribble( - ~Analysis, ~process, ~note, - "16068", "no", "did not inject properly" - ) - ) -new_info %>% rmarkdown::paged_table() - -# adding it to the isofiles -iso_files3 %>% - iso_add_file_info(new_info, by1 = "Run in 2017?", by2 = "Analysis") %>% - iso_get_file_info(select = names(new_info)) %>% - rmarkdown::paged_table() -``` - - -## Parse - -Most file information is initially read as text to avoid cumbersome specifications during the read process and compatibility issues between different IRMS file formats. However, many file info columns are not easily processed as text. The isoreader package therefore provides several parsing and data extraction functions to facilitate processing the text-based data (some via functionality implemented by the [readr](http://readr.tidyverse.org) package). See code block below for examples. For a complete overview, see the `?extract_data` and `?iso_parse_file_info` documentation. - -```{r} -# use parsing and extraction in iso_mutate_file_info -iso_files2 %>% - iso_mutate_file_info( - # change type of Peak Center to logical - `Peak Center` = parse_logical(`Peak Center`), - # retrieve first word of Method column - Method_1st = extract_word(Method), - # retrieve second word of Method column - Method_2nd = extract_word(Method, 2), - # retrieve file extension from the file_id using regular expression - extension = extract_substring(file_id, "\\.(\\w+)$", capture_bracket = 1) - ) %>% - iso_get_file_info(select = c(extension, `Peak Center`, matches("Method"))) %>% - rmarkdown::paged_table() - -# use parsing in iso_filter_file_info -iso_files2 %>% - iso_filter_files(parse_integer(Analysis) > 1500) %>% - iso_get_file_info() %>% - rmarkdown::paged_table() - -# use iso_parse_file_info for simplified parsing of column data types -iso_files2 %>% - iso_parse_file_info( - integer = Analysis, - number = `Sample Weight`, - logical = `Peak Center` - ) %>% - iso_get_file_info() %>% - rmarkdown::paged_table() -``` - # Resistors Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette): ```{r} +# FIXME: these information are not yet extracted from scan files! iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table() ``` -# Reference values - -As well as isotopic reference values for the different gases: - -```{r} -# reference delta values without ratio values -iso_files %>% iso_get_standards_info() %>% rmarkdown::paged_table() -# reference values with ratios -iso_files %>% iso_get_standards_info(with_ratios = TRUE) %>% rmarkdown::paged_table() -``` - # Raw Data -The raw data read from the IRMS files can be retrieved similarly using the `iso_get_raw_data()` function. Most data aggregation functions also allow for inclusion of file information using the `include_file_info` parameter, which functions identically to the `select` parameter of the `iso_get_file_info` function discussed earlier. +The raw data read from the scan files can be retrieved similarly using the `iso_get_raw_data()` function. Most data aggregation functions also allow for inclusion of file information using the `include_file_info` parameter, which functions identically to the `select` parameter of the `iso_get_file_info` function discussed earlier. ```{r} # get raw data with default selections (all raw data, no additional file info) @@ -239,79 +138,72 @@ iso_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() iso_files %>% iso_get_raw_data( # select just time and the two ions - select = c(type, cycle, v28.mV, v29.mV), - # include the Analysis number fron the file info and rename it to 'run' - include_file_info = c(run = Analysis) + select = c(x, units, v44.mV, v45.mV), + # include the scan type and rename the column + include_file_info = c(`Scan Type` = type) ) %>% # look at first few records only head(n=10) %>% rmarkdown::paged_table() ``` -# Data Processing - -The isoreader package is intended to make raw stable isotope data easily accessible. However, as with most analytical data, there is significant downstream processing required to turn these raw signal intensities into properly referenced isotopic measurement. This and similar functionality as well as data visualization is part of the [isoprocessor package](https://isoprocessor.isoverse.org) which takes isotopic data through the various corrections in a transparent, efficient and reproducible manner. - -That said, most vendor software also performs some of these calculations and it can be useful to be able to compare new data reduction procecures against those implemented in the vendor software. For this purpose, isoreader retrieves vendor computed data tables whenver possible, as illustrated below. - -## Vendor Data Table +# Saving collections -As with most data retrieval funtions, the `iso_get_vendor_data_table()` function also allows specific column selection (by default, all columns are selected) and easy addition of file information via the `include_file_info` parameter (by default, none is included). +Saving entire collections of isofiles for retrieval at a later point is easily done using the `iso_save` function which stores collections or individual isoreader file objects in the efficient R data storage format `.rds` (if not specified, the extension `.scan.rds` will be automatically appended). These saved collections can be convientiently read back using the same `iso_read_scan` command used for raw data files. ```{r} -# entire vendor data table -iso_files %>% iso_get_vendor_data_table() %>% rmarkdown::paged_table() -# get specific parts and add some file information -iso_files %>% - iso_get_vendor_data_table( - # select cycle and all carbon columns - select = c(cycle, matches("C")), - # include the Identifier 1 fron the file info and rename it to 'id' - include_file_info = c(id = `Identifier 1`) - ) %>% rmarkdown::paged_table() +# FIXME: not implemented yet +# # export to R data archive +# iso_files %>% iso_save("iso_files_export.di.rds") +# +# # read back the exported R data storage +# iso_read_dual_inlet("iso_files_export.di.rds") ``` -## For expert users: retrieving all data +# Data Export -For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame. Use the `include_file_info`, `include_raw_data`, and `include_vendor_data_table` parameters to specify which columns to include. By default, everything is included: +At the moment, isoreader supports export of all data to Excel and the [Feather file format](https://blog.rstudio.com/2016/03/29/feather/) (a Python/R cross-over format). Note that both export methods have similar syntax and append the appropriate file extension for each type of export file (`.scan.xlsx` and `.scan.feather`, respectively). ```{r} -all_data <- iso_files %>% iso_get_data() -# not printed out because this data frame is very big +# FIXME: not implemented yet +# # export to excel +# iso_files %>% iso_export_to_excel("iso_files_export") +# +# # data sheets available in the exported data file: +# readxl::excel_sheets("iso_files_export.di.xlsx") ``` -# Saving collections - -Saving entire collections of isofiles for retrieval at a later point is easily done using the `iso_save` function which stores collections or individual isoreader file objects in the efficient R data storage format `.rds` (if not specified, the extension `.di.rds` will be automatically appended). These saved collections can be convientiently read back using the same `iso_read_dual_inlet` command used for raw data files. - ```{r} -# export to R data archive -iso_files %>% iso_save("iso_files_export.di.rds") - -# read back the exported R data storage -iso_read_dual_inlet("iso_files_export.di.rds") +# FIXME: not implemented yet +# # export to feather +# iso_files %>% iso_export_to_feather("iso_files_export") +# +# # exported feather files +# list.files(pattern = ".di.feather") ``` +# Visualization -# Data Export +FIXME: should go into isoprocessor vignette once the functions there exist -At the moment, isoreader supports export of all data to Excel and the [Feather file format](https://blog.rstudio.com/2016/03/29/feather/) (a Python/R cross-over format). Note that both export methods have similar syntax and append the appropriate file extension for each type of export file (`.di.xlsx` and `.di.feather`, respectively). +# visualization ```{r} -# export to excel -iso_files %>% iso_export_to_excel("iso_files_export") - -# data sheets available in the exported data file: -readxl::excel_sheets("iso_files_export.di.xlsx") +iso_files %>% + iso_get_raw_data(include_file_info = type) %>% + dplyr::mutate(panel = sprintf("%s [%s]", type, units)) %>% + tidyr::pivot_longer( + matches("v\\d+"), + names_to = "mass", + values_to = "value", + values_drop_na = TRUE + ) %>% + ggplot2::ggplot() + + ggplot2::aes(x, value, color = mass) + + ggplot2::geom_line() + + ggplot2::facet_wrap(~ panel + file_id, scales = "free") ``` -```{r} -# export to feather -iso_files %>% iso_export_to_feather("iso_files_export") - -# exported feather files -list.files(pattern = ".di.feather") -``` From f09e8ff926ef750abb26d8c2fc00eb24f2abf1b8 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 01:32:18 -0700 Subject: [PATCH 17/51] implement raw data reads for scn files #36 --- DESCRIPTION | 2 +- R/isoread_dxf.R | 2 +- R/isoread_scn.R | 204 ++++++++++++++++++++++++++++++++----- R/scans.R | 6 -- R/utils_binary_files.R | 70 +++++++++++-- R/utils_files.R | 52 ++++++++++ tests/testthat/test-scan.R | 24 ++++- 7 files changed, 316 insertions(+), 44 deletions(-) delete mode 100644 R/scans.R create mode 100644 R/utils_files.R diff --git a/DESCRIPTION b/DESCRIPTION index 130adda0..62ccd7bc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: isoreader Title: Read IRMS data files Description: R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry. -Version: 1.0.15 +Version: 1.0.16 Authors@R: person("Sebastian", "Kopf", email = "sebastian.kopf@colorado.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2044-0201")) URL: https://github.com/isoverse/isoreader BugReports: https://github.com/isoverse/isoreader/issues diff --git a/R/isoread_dxf.R b/R/isoread_dxf.R index dc04f2c4..a5d7eb01 100644 --- a/R/isoread_dxf.R +++ b/R/isoread_dxf.R @@ -190,7 +190,7 @@ extract_dxf_raw_voltage_data <- function(ds) { capture_data("voltages", c("float", rep("double", length(masses))), data_end_re) voltages <- bind_rows(voltages, ds$binary$data$voltages %>% - as_data_frame() %>% setNames(c("time.s", masses_columns))) + tibble::as_tibble() %>% setNames(c("time.s", masses_columns))) } # check for data diff --git a/R/isoread_scn.R b/R/isoread_scn.R index 7cf99b8f..1807b2a5 100644 --- a/R/isoread_scn.R +++ b/R/isoread_scn.R @@ -10,35 +10,189 @@ iso_read_scn <- function(ds, options = list()) { # read binary file ds$binary <- get_ds_file_path(ds) %>% read_binary_file() - # # process file info - # if(ds$read_options$file_info) { - # ds <- exec_func_with_error_catch(extract_isodat_sequence_line_info, ds) - # ds <- exec_func_with_error_catch(extract_isodat_measurement_info, ds) - # ds <- exec_func_with_error_catch(extract_isodat_datetime, ds) - # ds <- exec_func_with_error_catch(extract_H3_factor_info, ds) - # ds <- exec_func_with_error_catch(extract_MS_integration_time_info, ds) - # } - # - # # process raw data - # if (ds$read_option$raw_data) { - # ds <- exec_func_with_error_catch(extract_dxf_raw_voltage_data, ds) - # } - # + # get scan file type + ds <- exec_func_with_error_catch(extract_scn_file_type, ds) + + # process file info + if(ds$read_options$file_info) { + # there does not seem to be creation date stored inside the scn files + # try to pull it out from the operating system file creation info instead + ds <- exec_func_with_error_catch(extract_os_file_creation_datetime, ds) + # comment + ds <- exec_func_with_error_catch(extract_scn_file_info, ds) + } + + # process raw data + if (ds$read_option$raw_data) { + ds <- exec_func_with_error_catch(extract_scn_raw_voltage_data, ds) + } + return(ds) + + # FIXME: should be possible to get resistor information but it's not + # obvious yet how # # process method info # if (ds$read_options$method_info) { - # ds <- exec_func_with_error_catch( - # extract_isodat_reference_values, ds, - # cap_at_fun = function(bin) cap_at_next_C_block(bin, "CResultArray")) # ds <- exec_func_with_error_catch(extract_isodat_resistors, ds) # } - # - # # process pre-evaluated data table - # if (ds$read_options$vendor_data_table) { - # ds <- exec_func_with_error_catch( - # extract_isodat_continuous_flow_vendor_data_table, ds, - # cap_at_fun = function(bin) cap_at_pos(bin, find_next_pattern(bin, re_text("DetectorDataBlock")))) - # } - # + + return(ds) +} + +# extract scan file type +extract_scn_file_type <- function(ds) { + # find type (= x-axis label) + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot identify scan type") %>% + move_to_C_block_range("CPlotInfo", "CTraceInfo") + ds$binary <- ds$binary %>% + move_to_next_pattern(re_block("fef-x"), re_text("Arial"), re_block("fef-x")) %>% + capture_data("type", "text", re_block("fef-x")) + ds$file_info$type <- ds$binary$data$type + return(ds) +} + +# extract file info in scn file +extract_scn_file_info <- function(ds) { + # find comment + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot extrat comment") %>% + move_to_C_block_range("CScanStorage", "CBinary") + ds$binary <- ds$binary %>% + move_to_next_pattern(re_block("x-000"), re_block("fef-x")) + end_pos <- ds$binary %>% find_next_pattern(re_direct("\xff\xff")) + + # comment + if ((text_length <- end_pos - ds$binary$pos - 8) > 0) { + ds$file_info$comment <- ds$binary %>% + capture_n_data("comment", "text", text_length/2) %>% + { .$data$comment } + } else { + ds$file_info$comment <- NA_character_ + } + + return(ds) +} + +# extract voltage data in scn file +extract_scn_raw_voltage_data <- function(ds) { + + # data points + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot identify number of scan points") %>% + move_to_C_block_range("CScanStorage", "CBinary") + + ds$binary <- ds$binary %>% + move_to_next_pattern(re_block("x-000"), re_block("fef-x")) + end_pos <- ds$binary %>% find_next_pattern(re_direct("\xff\xff")) + + ds$binary <- ds$binary %>% + skip_pos(end_pos - ds$binary$pos - 8) %>% # skip comment + capture_n_data("n_points", "integer", 1) %>% + capture_n_data("n_traces", "integer", 1) + + # find units + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot identify scan units") %>% + move_to_C_block_range("CVisualisationData", "CIntegrationUnitScanPart") + ds$binary <- ds$binary %>% + move_to_next_pattern( + # seems to begin with this unique 88 c3 40 sequence + re_direct("\x88\xc3\x40"), re_null(12), + # but this could be sufficient too if the above turns too specific + re_block("fef-0"), re_block("x-000"), re_block("fef-x") + ) %>% + capture_data("units", "text", re_null(4), re_not_null(1)) + + # range + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot identify scan range") %>% + move_to_C_block("^CPlotRange", regexp_match = TRUE, move_to_end = FALSE) %>% + skip_pos(16) + ds$binary <- ds$binary %>% + capture_n_data("min", "float", 1) %>% + capture_n_data("max", "float", 1) + + # find masses and cups + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot identify masses/cups") %>% + move_to_C_block("^CPlotRange", regexp_match = TRUE) %>% + cap_at_next_pattern(re_text("Administrator")) + + # masses + mass_positions <- find_next_patterns(ds$binary, re_text("Mass")) + masses <- c() + cups <- c() + if (length(mass_positions) > 0) { + for (pos in mass_positions) { + ds$binary <- ds$binary %>% move_to_pos(pos) %>% + capture_data("mass", "text", re_not_null(2)) + masses <- c(masses, ds$binary$data$mass) + } + cups <- c(stringr::str_extract(masses, "C\\d")) + } else { + # cups + cup_positions <- find_next_patterns(ds$binary, re_text("Cup")) + for (pos in cup_positions) { + ds$binary <- ds$binary %>% move_to_pos(pos) %>% + capture_data("cup", "text", re_not_null(2)) + cups <- c(cups, ds$binary$data$cup) + } + masses <- rep(NA_character_, length(cups)) + } + + # configuration + config <- tibble( + cup = parse_number(cups) %>% as.integer(), + mass = parse_number(masses) %>% as.character(), + mass_column = ifelse( + !is.na(mass), + sprintf("v%s.mV", mass), + # Note: should cups be designated in some other way?? + sprintf("v%s.mV", cup) + ) + ) + + # raw data (=voltages) + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot read raw data") %>% + move_to_C_block_range("CBinary", "CPlotInfo") %>% + skip_pos(16) %>% + capture_n_data( + "voltages", c("float", rep("double", ds$binary$data$n_traces)), + ds$binary$data$n_points + ) + voltages <- ds$binary$data$voltages %>% + tibble::as_tibble() %>% + setNames(c("step", config$mass_column)) + + # calculate x values from step + convert_step_to_x <- function(step) { + type <- ds$file_info$type + if (!is.null(type) && type == "MagnetCurrent") { + # x is in steps + return(step) + } else if (!is.null(type) && type == "Clock") { + # x is in sec, step is in msec + return(step/1000) + } else { + # calculate based on max and min + return( + ds$binary$data$min + (step - min(step)) / + diff(range(step)) * (ds$binary$data$max - ds$binary$data$min) + ) + } + } + + # set raw data + ds$raw_data <- voltages %>% + dplyr::mutate( + # calculate x values + x = convert_step_to_x(step), + # set units + units = ds$binary$data$units + ) %>% + dplyr::select(step, x, units, everything()) + return(ds) } diff --git a/R/scans.R b/R/scans.R deleted file mode 100644 index 10fd6e27..00000000 --- a/R/scans.R +++ /dev/null @@ -1,6 +0,0 @@ -#' Load scan data -#' -#' @export -iso_read_scan <- function() { - stop("sorry, this functionality is not implemented yet", call. = FALSE) -} \ No newline at end of file diff --git a/R/utils_binary_files.R b/R/utils_binary_files.R index 008f9311..fd9e7985 100644 --- a/R/utils_binary_files.R +++ b/R/utils_binary_files.R @@ -94,7 +94,7 @@ cap_at_pos <- function(bfile, pos) { # @param min_pos minimum position where to find the block # @param occurence which occurence to find? (use -1 for last occurence, use NULL for all) # @FIXME: testing -fetch_C_block <- function(bfile, C_block, min_pos = 1, occurence = NULL) { +fetch_C_block <- function(bfile, C_block, min_pos = 1, occurence = NULL, regexp_match = FALSE) { # basic checks if (!is(bfile, "binary_file")) stop("need binary file object", call. = FALSE) if (nrow(bfile$C_blocks) == 0) stop("no C_blocks available", call. = FALSE) @@ -102,7 +102,10 @@ fetch_C_block <- function(bfile, C_block, min_pos = 1, occurence = NULL) { # find C_blocks block <- start <- NULL # global vars - C_blocks <- filter(bfile$C_blocks, block == C_block, start >= min_pos) + if (regexp_match) + C_blocks <- filter(bfile$C_blocks, str_detect(block, C_block), start >= min_pos) + else + C_blocks <- filter(bfile$C_blocks, block == C_block, start >= min_pos) if (nrow(C_blocks) == 0) { op_error(bfile, sprintf("block '%s' not found after position %.0f", C_block, min_pos)) } @@ -128,9 +131,9 @@ fetch_C_block <- function(bfile, C_block, min_pos = 1, occurence = NULL) { # @FIXME: testing # move_to_C_block(my_test$binary, "NO") # move_to_C_block(my_test$binary, "CData", occurence = 2) -move_to_C_block <- function(bfile, C_block, min_pos = 1, occurence = 1, move_to_end = TRUE, reset_cap = TRUE) { +move_to_C_block <- function(bfile, C_block, min_pos = 1, occurence = 1, move_to_end = TRUE, reset_cap = TRUE, regexp_match = FALSE) { # fetch right C block - cblock <- fetch_C_block(bfile, C_block, min_pos = min_pos, occurence = occurence) + cblock <- fetch_C_block(bfile, C_block, min_pos = min_pos, occurence = occurence, regexp_match = regexp_match) if (is.null(cblock)) { op_error(bfile, sprintf("cannot move to C block '%s'", C_block)) } @@ -141,14 +144,14 @@ move_to_C_block <- function(bfile, C_block, min_pos = 1, occurence = 1, move_to_ } # move to next C block (does not reet cap by default) -move_to_next_C_block <- function(bfile, C_block, reset_cap = FALSE) { - move_to_C_block(bfile, C_block, min_pos = bfile$pos, occurence = 1, reset_cap = reset_cap) +move_to_next_C_block <- function(bfile, C_block, reset_cap = FALSE, regexp_match = FALSE) { + move_to_C_block(bfile, C_block, min_pos = bfile$pos, occurence = 1, reset_cap = reset_cap, regexp_match = regexp_match) } # cap valid position at the beginning of a specific C_block -cap_at_C_block <- function(bfile, C_block, min_pos = 1, occurence = 1) { +cap_at_C_block <- function(bfile, C_block, min_pos = 1, occurence = 1, regexp_match = FALSE) { # fetch right C block - cblock <- fetch_C_block(bfile, C_block, min_pos = min_pos, occurence = occurence) + cblock <- fetch_C_block(bfile, C_block, min_pos = min_pos, occurence = occurence, regexp_match = regexp_match) if (is.null(cblock)) { op_error(bfile, sprintf("cannot cap at C block '%s'", C_block)) } @@ -156,8 +159,8 @@ cap_at_C_block <- function(bfile, C_block, min_pos = 1, occurence = 1) { } # cap at next C block -cap_at_next_C_block <- function(bfile, C_block) { - cap_at_C_block(bfile, C_block, min_pos = bfile$pos, occurence = 1) +cap_at_next_C_block <- function(bfile, C_block, regexp_match = FALSE) { + cap_at_C_block(bfile, C_block, min_pos = bfile$pos, occurence = 1, regexp_match = regexp_match) } # move to C block range @@ -207,6 +210,17 @@ re_null <- function(n) { class = "binary_regexp") } +# regular expression for anything but null characters +re_not_null <- function(n) { + structure( + list( + label = sprintf("", n), + regexp = str_c("[\x01-\xff]{", n, "}"), + size = n + ), + class = "binary_regexp") +} + # regular expression for control sequences (00-0f) re_control <- function(raw) { # can't easily assemble so it's hard copy for now @@ -331,6 +345,37 @@ move_to_next_pattern <- function(bfile, ..., max_gap = NULL, move_to_end = TRUE) generate_binary_structure_map_printout())) } +# cap at next regular expression pattern +# @param ... construct with the re... functions +# @param max_gap maximum number of bytes until the pattern +# @param move_to_end whether to move to the end of the pattern (default is yes) +cap_at_next_pattern <- function(bfile, ..., max_gap = NULL) { + # safety check + if (!is(bfile, "binary_file")) stop("need binary file object", call. = FALSE) + if(!is.null(max_gap) && !is.numeric(max_gap)) stop("max gap must be a number", call. = FALSE) + + # find pattern + pos <- find_next_pattern(bfile, ..., max_gap = max_gap) + + # cap at new position + if ( !is.null(pos) ) { + return(cap_at_pos(bfile, pos)) + } + + # encountered a problem + regexps <- re_combine(...) + gap_text <- if (!is.null(max_gap)) sprintf(" after maximal gap of %.0f bytes", max_gap) else "" + op_error( + bfile, + sprintf("could not find '%s'%s in search interval %.0f to %.0f, found '%s...'", + regexps$label, + gap_text, bfile$pos, bfile$max_pos, + bfile %>% + map_binary_structure(length = regexps$size + + (if(!is.null(max_gap)) max_gap else 50) + 10) %>% + generate_binary_structure_map_printout())) +} + # capture data block data in specified type # uses parse_raw_data and therefore can handle multiple data types # @inheritParams parse_raw_data @@ -390,6 +435,11 @@ capture_n_data <- function(bfile, id, type, n, sensible = NULL) { # find raw length if (!is(bfile, "binary_file")) stop("need binary file object", call. = FALSE) + + # data type safety check + if (length(missing <- setdiff(type, names(get_data_blocks_config()))) > 0) + stop("ecountered invalid data type(s): ", str_c(missing, collapse = ", "), call. = FALSE) + dbc <- get_data_blocks_config()[type] size <- sum(map_int(dbc, "size")) diff --git a/R/utils_files.R b/R/utils_files.R new file mode 100644 index 00000000..670c5edb --- /dev/null +++ b/R/utils_files.R @@ -0,0 +1,52 @@ +# general file utils + +# generic function to try to get file creation date from OS file info +# this does NOT always work, retrieving file creation info from inside a file +# is always preferable if this information is available +extract_os_file_creation_datetime <- function(ds) { + + # full path to file + path <- get_ds_file_path(ds) + + # platform dependent createion time + if (.Platform$OS.type == "windows") { + # original datetime (doesn't work on unix, just gives last modification date) + ds$file_info$file_datetime <- as_datetime(file.info(path)$ctime, tz = Sys.timezone()) + } else if (.Platform$OS.type == "unix") { + # Linux and OS X, try it with stat, otherwise just throw a warning and use mdate + + # m date (meaning BSD stat didn't work) + get_from_mdate <- function(ds) { + # last modification time the only info that's available + ds <- ds %>% register_warning( + paste0( + "file creation date could not be determined on this operating system (", + .Platform$OS.type, "/", Sys.info()[['sysname']], + "), recovering 'last modified date' instead." + ), + warn = TRUE + ) + ds$file_info$file_datetime <- as_datetime(file.info(path)$mtime, tz = Sys.timezone()) + return(ds) + } + + # use BSD stat + ds <- + tryCatch({ + cmd <- paste0('stat -f "%DB" "', path, '"') # use BSD stat command + ds$file_info$file_datetime <- + # retrieve birth date in seconds from start of epoch (%DB) + system(cmd, intern=TRUE, ignore.stderr = TRUE) %>% as.integer() %>% + # convert to POSIXct + as.POSIXct(origin = "1970-01-01", tz = "") %>% + # force local timezone + as_datetime(tz = Sys.timezone()) + ds + }, + error = function(e) { return(get_from_mdate(ds)) }, + warning = function(e) { return(get_from_mdate(ds)) }) + } else { + stop("don't know how to get file creation date on platform ", .Platform$OS.type) + } + return(ds) +} \ No newline at end of file diff --git a/tests/testthat/test-scan.R b/tests/testthat/test-scan.R index c34777ce..5d2bb87c 100644 --- a/tests/testthat/test-scan.R +++ b/tests/testthat/test-scan.R @@ -22,11 +22,33 @@ test_that("test that scn files can be read", { # test specific files # FIXME: re-enable for commits - skip("Currently not testing all scan data files.") + #skip("Currently not testing all scan data files.") # FIXME: run as one batch to make use of parallel processing iso_turn_reader_caching_off() + expect_true(file.exists(file <- iso_get_reader_example("peak_shape_scan_example.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(problems(scan)), 0) + + expect_true(file.exists(file <- iso_get_reader_example("background_scan_example.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(problems(scan)), 0) + + expect_true(file.exists(file <- iso_get_reader_example("full_scan_example.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(problems(scan)), 0) + + expect_true(file.exists(file <- iso_get_reader_example("time_scan_example.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(problems(scan)), 0) + + test_folder <- file.path("test_data") + + expect_true(file.exists(file <- file.path(test_folder, "scan_hv_01.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(problems(scan)), 0) + }) From 75aed0207b26573a6658eb2bb010b3d5d7dd49e0 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 01:32:26 -0700 Subject: [PATCH 18/51] additional test files --- tests/testthat/test_data/scan_hv_01.scn | Bin 0 -> 38521 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/testthat/test_data/scan_hv_01.scn diff --git a/tests/testthat/test_data/scan_hv_01.scn b/tests/testthat/test_data/scan_hv_01.scn new file mode 100644 index 0000000000000000000000000000000000000000..3a1a4daf83d42aab5d8bf735e750983a69b197a6 GIT binary patch literal 38521 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 E4-u3G4gdfE literal 0 HcmV?d00001 From db713bff27555276d61f1eb97f8b0ab621b95c89 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 01:32:32 -0700 Subject: [PATCH 19/51] draft scan vignette --- vignettes/scan.Rmd | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index 93a8f996..911a43b0 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -43,6 +43,7 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() iso_files <- iso_read_scan( iso_get_reader_example("peak_shape_scan_example.scn"), + iso_get_reader_example("background_scan_example.scn"), iso_get_reader_example("full_scan_example.scn"), iso_get_reader_example("time_scan_example.scn"), read_cache = FALSE @@ -184,11 +185,8 @@ At the moment, isoreader supports export of all data to Excel and the [Feather f # Visualization -FIXME: should go into isoprocessor vignette once the functions there exist - -# visualization - -```{r} +```{r "scans", fig.width=10, fig.height=10} +# FIXME: should go into isoprocessor vignette once the functions there exist iso_files %>% iso_get_raw_data(include_file_info = type) %>% dplyr::mutate(panel = sprintf("%s [%s]", type, units)) %>% From 1ef6aa0549db692c6e9a182bb579ec6ed06d019a Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 21:33:39 -0700 Subject: [PATCH 20/51] clean up default function --- R/nse.R | 35 ---------------------------------- R/settings.R | 15 ++++++++++++++- tests/testthat/test-nse.R | 13 ------------- tests/testthat/test-settings.R | 1 + 4 files changed, 15 insertions(+), 49 deletions(-) diff --git a/R/nse.R b/R/nse.R index 284f6506..df1ed9fa 100644 --- a/R/nse.R +++ b/R/nse.R @@ -191,38 +191,3 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ return(cols) } - -# resolve default cols in a list of quos -resolve_defaults <- function(quos) { - resolve_default <- function(x) if (rlang::quo_is_call(x) && rlang::call_name(x) == sym("default")) eval_tidy(x) else x - if (is_quosure(quos)) return(resolve_default(quos)) - else map(quos, resolve_default) -} - -# Convert quo to text accounting for plain text and symbol quos -quos_to_text <- function(lquos, check_for_validity = TRUE, variable = "variable") { - single_quo <- is_quosure(lquos) - lquos <- if(single_quo) quos(!!lquos) else quos(!!!lquos) - are_text_quos <- map_lgl(lquos, ~is.character(quo_squash(.x))) - are_symbol_quos <- map_lgl(lquos, quo_is_symbol) - - # check for validity - if (check_for_validity && !all(ok <- are_text_quos | are_symbol_quos)) { - params <- - str_c(names(lquos)[!ok] %>% { ifelse(nchar(.) > 0, str_c(., " = "), .) }, - map_chr(lquos[!ok], quo_text)) %>% - collapse("', '", last = "' and '") - if (sum(!ok) > 1) - glue("parameters '{params}' do not refer to valid {variable} names") %>% stop(call. = FALSE) - else - glue("parameter '{params}' does not refer to a valid {variable} name") %>% stop(call. = FALSE) - } - - text_quos <- - map2_chr(lquos, are_text_quos, function(lquo, is_text) - if(is_text) quo_squash(lquo) else rlang::as_label(lquo)) %>% - as.list() - if (single_quo) return(text_quos[[1]]) - else return(text_quos) -} - diff --git a/R/settings.R b/R/settings.R index 5b493eb2..0b9bcb4b 100644 --- a/R/settings.R +++ b/R/settings.R @@ -1,11 +1,24 @@ # retrieve package settings, internal function, not exported default <- function(name, allow_null = FALSE) { - name <- enquo(name) %>% quos_to_text(variable = "setting") + name_exp <- rlang::enexpr(name) + if (rlang::is_symbol(name_exp)) + name <- rlang::as_name(name_exp) + else if (is.character(name_exp)) + name <- name_exp + else + stop("don't know how to process setting expression '", rlang::as_label(name_exp), "'", call. = FALSE) value <- getOption(str_c("isoreader.", name)) if (!allow_null && is.null(value)) stop("isoreader setting '", name, "' does not exist", call. = FALSE) return(value) } +# resolve default cols in a list of quos +resolve_defaults <- function(quos) { + resolve_default <- function(x) if (rlang::quo_is_call(x) && rlang::call_name(x) == sym("default")) eval_tidy(x) else x + if (is_quosure(quos)) return(resolve_default(quos)) + else map(quos, resolve_default) +} + # set package setting, internal function, not exported set_default <- function(name, value, overwrite = TRUE) { if (overwrite || !str_c("isoreader.", name) %in% names(options())) diff --git a/tests/testthat/test-nse.R b/tests/testthat/test-nse.R index d985b6f8..0ef0638c 100644 --- a/tests/testthat/test-nse.R +++ b/tests/testthat/test-nse.R @@ -86,16 +86,3 @@ test_that("Getting column names (with # and type requirement checks) works", { expect_error(get_column_names(nest(df, data = c(-rowname)), a = quo(data), type_reqs = list(a = "character")), "not .* the correct column types") expect_equal(get_column_names(nest(df, data = c(-rowname)), a = quo(data), type_reqs = list(a = "list")), list(a = c(data = "data"))) }) - -test_that("Test that quos to text conversion works", { - - expect_error(quos_to_text(quo(a^2)), "not .* valid") - expect_error(quos_to_text(quo(NULL)), "not .* valid") - expect_error(quos_to_text(quo(~mean)), "not .* valid") - expect_error(quos_to_text(quos(a^2, NULL, mean)), "not .* valid") - expect_equal(quos_to_text(quo("a")), "a") - expect_equal(quos_to_text(quo(a)), "a") - expect_equal(quos_to_text(quos(a, "b")) %>% unlist(use.name = FALSE), c("a", "b")) - expect_equal(quos_to_text(quos(x = a)), list(x="a")) - -}) \ No newline at end of file diff --git a/tests/testthat/test-settings.R b/tests/testthat/test-settings.R index 39544f35..ae5e6aa6 100644 --- a/tests/testthat/test-settings.R +++ b/tests/testthat/test-settings.R @@ -6,6 +6,7 @@ test_that("default values can be set and retrieved", { expect_true(set_default("quiet", TRUE)) expect_true(default("quiet")) expect_true(default(quiet)) + expect_error(default(quiet^2), "don't know how to process.*expression") expect_false(set_default("quiet", FALSE)) expect_false(default("quiet")) expect_false(default(quiet)) From 876569f5fe52a50c74cf4e904d08afe0ddcc1987 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 23:03:07 -0700 Subject: [PATCH 21/51] cleanup column names NSE function --- NAMESPACE | 5 +++- R/nse.R | 50 +++++++++++++++++++--------------- R/package.R | 2 +- R/settings.R | 19 +++++++++---- tests/testthat/test-nse.R | 34 +++++++++++++++++------ tests/testthat/test-settings.R | 9 ++++++ 6 files changed, 81 insertions(+), 38 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index b91a10a5..a2416df4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -36,6 +36,7 @@ S3method(print,continuous_flow) S3method(print,dual_inlet) S3method(print,iso_file) S3method(print,iso_file_list) +S3method(print,scan) S3method(rename,iso_file) S3method(rename,iso_file_list) S3method(select,iso_file) @@ -107,6 +108,7 @@ export(iso_is_dual_inlet) export(iso_is_file) export(iso_is_file_list) export(iso_is_object) +export(iso_is_scan) export(iso_make_units_explicit) export(iso_make_units_implicit) export(iso_mutate_file_info) @@ -224,8 +226,10 @@ importFrom(rlang,"!!!") importFrom(rlang,"!!") importFrom(rlang,":=") importFrom(rlang,UQ) +importFrom(rlang,enexpr) importFrom(rlang,enquo) importFrom(rlang,eval_tidy) +importFrom(rlang,expr) importFrom(rlang,f_lhs) importFrom(rlang,f_rhs) importFrom(rlang,get_expr) @@ -237,7 +241,6 @@ importFrom(rlang,quo) importFrom(rlang,quo_is_null) importFrom(rlang,quo_is_symbol) importFrom(rlang,quo_squash) -importFrom(rlang,quo_text) importFrom(rlang,quos) importFrom(rlang,sym) importFrom(stats,embed) diff --git a/R/nse.R b/R/nse.R index df1ed9fa..6c286e17 100644 --- a/R/nse.R +++ b/R/nse.R @@ -48,7 +48,7 @@ check_expressions <- function(df, ...) { # criteria including both standard and non-standard evaluation. Throws errors if expressions cannot be evaluated or if an incorrect # number of columns are identified for a given parameter. # @param df the data frame -# @param ... named quoted variable selection criteria (anything that tidyselect::vars_select supports) +# @param ... named expressions with variable selection criteria (anything that tidyselect::eval_select supports) # @param n_reqs named list to specify how many columns are allowed/required for each selection criterion, default for all that are not specified is 1. # Allowed values are a specific number 1,2,3,4, etc. "*" for any number, "?" for 0 or 1 and "+" for at least one. # @param type_reqs named list to specify what types certain columns must be, allowed: "list" (also includes "vctrs_list_of"), "numeric", "integer", "character", "logical" @@ -58,35 +58,41 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ # df name and data frame test if (missing(df)) stop("no data frame supplied", call. = FALSE) - df_name <- enquo(df) %>% rlang::as_label() - df <- enquo(df) %>% eval_tidy() + df_name <- rlang::as_label(rlang::enexpr(df)) + df <- force(df) if (!is.data.frame(df)) - glue("parameter {df_name} is not a data frame") %>% stop(call. = FALSE) + sprintf("parameter '%s' is not a data frame", df_name) %>% stop(call. = FALSE) - # use a safe version of vars_select to get all the column names + # use a safe version of eval select to get all the column names # note: uses tidyselect:eval_select because vars_select is in questioning stage - safe_vars_select <- function(col_quo, ...) { - safe_result <- safely(tidyselect::eval_select)(rlang::expr(c(!!col_quo)), df, ...) - if (is.null(safe_result$error)) { - safe_result$result <- rlang::set_names(names(df)[safe_result$result], names(safe_result$result)) + safe_vars_select <- function(col_exp, ...) { + safe_pos <- safely(tidyselect::eval_select)(col_exp, data = df, ...) + if (is.null(safe_pos$error)) { + # assign names based on opsition indices + safe_pos$result <- rlang::set_names(names(df)[safe_pos$result], names(safe_pos$result)) + # assign any missing names with the values + names(safe_pos$result)[nchar(names(safe_pos$result)) == 0] <- + safe_pos$result[nchar(names(safe_pos$result)) == 0] } - return(safe_result) + return(safe_pos) } - # get all quos - lquos <- list(...) - cols_quos <- quos(!!!lquos) %>% + # get colum name expressions from ... + cols_exps <- list(...) %>% # make sure to evaluate calls to default resolve_defaults() %>% - # make sure that the expressions are locally evaluated - map(~quo(!!rlang::quo_get_expr(.x))) - cols_results <- map(cols_quos, safe_vars_select) + # convert quos to expressions (to ensure evaluation inside the df data frame to avoid name conflicts) + map(~{if (rlang::is_quosure(.x)) rlang::quo_get_expr(.x) else .x}) %>% + # naming + { if(is.null(names(.))) setNames(., rep("", length(.))) else . } + + cols_results <- map(cols_exps, safe_vars_select) ok <- map_lgl(cols_results, ~is.null(.x$error)) # summarize if there were any errors if (!all(ok)) { params <- - map2_chr(names(cols_quos)[!ok], cols_quos[!ok], function(var, val) { + map2_chr(names(cols_exps)[!ok], cols_exps[!ok], function(var, val) { if (nchar(var) > 0 && var != rlang::as_label(val)) str_c(var, " = ", rlang::as_label(val)) else rlang::as_label(val) }) %>% @@ -103,7 +109,7 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ } else { # just a warning and find the columns omitting those missing warning(err_msg, immediate. = TRUE, call. = FALSE) - cols_results <- map(cols_quos, safe_vars_select, strict = FALSE) + cols_results <- map(cols_exps, safe_vars_select, strict = FALSE) } } @@ -140,8 +146,8 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ if (!all(col_meets_n_reqs)) { n_errors <- sprintf("'%s%s' refers to %d column(s) instead of %s (%s)", - names(cols_quos)[!col_meets_n_reqs] %>% { ifelse(nchar(.) > 0, str_c(., " = "), .) }, - map_chr(cols_quos[!col_meets_n_reqs], quo_text), + names(cols_exps)[!col_meets_n_reqs] %>% { ifelse(nchar(.) > 0, str_c(., " = "), .) }, + map_chr(cols_exps[!col_meets_n_reqs], rlang::as_label), map_int(cols[!col_meets_n_reqs], length), n_req_types[all_n_req_types[!col_meets_n_reqs]], map_chr(all_n_reqs[!col_meets_n_reqs], as.character)) %>% @@ -178,8 +184,8 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ if (!all(col_meets_type_reqs)) { n_errors <- sprintf("'%s%s' refers to column(s) of type '%s' instead of '%s'", - names(cols_quos)[!col_meets_type_reqs] %>% { ifelse(nchar(.) > 0, str_c(., " = "), .) }, - map_chr(cols_quos[!col_meets_type_reqs], quo_text), + names(cols_exps)[!col_meets_type_reqs] %>% { ifelse(nchar(.) > 0, str_c(., " = "), .) }, + map_chr(cols_exps[!col_meets_type_reqs], rlang::as_label), map_chr(cols[!col_meets_type_reqs], ~collapse(all_df_types[.x], "/")), map_chr(all_type_reqs[!col_meets_type_reqs], ~types[.x])) %>% collapse("\n- ") diff --git a/R/package.R b/R/package.R index 09180479..420a5e1a 100644 --- a/R/package.R +++ b/R/package.R @@ -1,7 +1,7 @@ #' @keywords internal "_PACKAGE" -#' @importFrom rlang enquo quo quos UQ !! !!! := get_expr quo_squash quo_text quo_is_null quo_is_symbol is_quosure is_empty is_integerish eval_tidy sym new_formula f_lhs f_rhs +#' @importFrom rlang expr enexpr enquo quo quos UQ !! !!! := get_expr quo_squash quo_is_null quo_is_symbol is_quosure is_empty is_integerish eval_tidy sym new_formula f_lhs f_rhs #' @importFrom tidyselect everything starts_with ends_with matches #' @importFrom tibble tibble is_tibble #' @importFrom dplyr vars n select rename arrange desc mutate mutate_at mutate_if filter distinct as_data_frame left_join right_join full_join data_frame bind_rows bind_cols group_by ungroup tally summarize do case_when diff --git a/R/settings.R b/R/settings.R index 0b9bcb4b..a0062ab1 100644 --- a/R/settings.R +++ b/R/settings.R @@ -12,11 +12,20 @@ default <- function(name, allow_null = FALSE) { return(value) } -# resolve default cols in a list of quos -resolve_defaults <- function(quos) { - resolve_default <- function(x) if (rlang::quo_is_call(x) && rlang::call_name(x) == sym("default")) eval_tidy(x) else x - if (is_quosure(quos)) return(resolve_default(quos)) - else map(quos, resolve_default) +# resolve defaults in a list of quos or expressions +# @param q single quo or expression, or list of quos and/or expressions +resolve_defaults <- function(q) { + resolve_default <- function(x) { + if ( + ((rlang::is_quosure(x) && rlang::quo_is_call(x)) || (!rlang::is_quosure(x) && rlang::is_call(x))) && + rlang::call_name(x) == "default") { + return(eval_tidy(x)) + } else { + return(x) + } + } + if (is.list(q)) return(purrr::map(q, resolve_default)) + else return(resolve_default(q)) } # set package setting, internal function, not exported diff --git a/tests/testthat/test-nse.R b/tests/testthat/test-nse.R index 0ef0638c..40261d15 100644 --- a/tests/testthat/test-nse.R +++ b/tests/testthat/test-nse.R @@ -1,11 +1,5 @@ context("Standard and non-standard evaluation") -test_that("Default calls are resolved", { - # resolve defaults in a list of quos - expect_equal(resolve_defaults(quo(default(quiet))), FALSE) - expect_equal(resolve_defaults(list(quo(default(quiet)), quo(x))), list(FALSE, quo(x))) -}) - test_that("Testing expression checks", { df <- as_data_frame(mtcars) %>% tibble::rownames_to_column() @@ -26,6 +20,7 @@ test_that("Testing expression checks", { }) + test_that("Getting column names (with # and type requirement checks) works", { df <- tibble::as_tibble(mtcars) %>% tibble::rownames_to_column() @@ -40,10 +35,22 @@ test_that("Getting column names (with # and type requirement checks) works", { expect_error(get_column_names(df, x = quo(x)), "'x'.*unknown column") expect_error(get_column_names(df, x = quo(c(x))), "'x = c\\(x\\)'.*unknown column") expect_error(get_column_names(df, a = quo(x)), "'a = x'.*unknown column") + set_default("x", quo(y)) + expect_error(get_column_names(df, a = quo(default(x))), "'a = y'.*unknown column") # single column per identifier - expect_equal(get_column_names(df, a = quo(mpg), b = quo(wt)), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) - expect_equal(get_column_names(df, a = quo("mpg"), b = quo("wt")), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) + expect_equal(get_column_names(df, a = quo(mpg), b = expr(wt)), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) + expect_equal(get_column_names(df, a = quo("mpg"), b = expr("wt")), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) + + # with defaults + set_default("x", quo(mpg)); set_default("y", quo(wt)) + expect_equal(get_column_names(df, a = quo(default(x)), b = expr(default(y))), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) + set_default("x", expr(mpg)); set_default("y", expr(wt)) + expect_equal(get_column_names(df, a = quo(default(x)), b = expr(default(y))), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) + set_default("x", quo("mpg")); set_default("y", quo("wt")) + expect_equal(get_column_names(df, a = quo(default(x)), b = expr(default(y))), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) + set_default("x", "mpg"); set_default("y", "wt") + expect_equal(get_column_names(df, a = quo(default(x)), b = expr(default(y))), list(a=c(mpg = "mpg"), b = c(wt = "wt"))) # no columns per identifier expect_error(get_column_names(df, a = quo(mpg), n_reqs = list(a = "wrong")), "unknown number requirement") @@ -61,13 +68,22 @@ test_that("Getting column names (with # and type requirement checks) works", { expect_error(get_column_names(df, a = quo(c(mpg, wt)), b = quo(c(mpg, wt)), n_reqs = list(a = "+")), "not .* the correct number of columns") expect_equal(get_column_names(df, a = quo(c(mpg, wt)), b = quo(starts_with("d")), n_reqs = list(a = "+", b="*")), list(a = c(mpg = "mpg", wt = "wt"), b = c(disp = "disp", drat = "drat"))) + expect_equal(get_column_names(df, a = expr(c(mpg, wt)), b = expr(starts_with("d")), n_reqs = list(a = "+", b="*")), + list(a = c(mpg = "mpg", wt = "wt"), b = c(disp = "disp", drat = "drat"))) # named columns expect_equal(get_column_names(df, a = quo(c(x = mpg)), b = quo(wt)), list(a=c(x = "mpg"), b = c(wt = "wt"))) - expect_equal(get_column_names(df, a = quo(c(x = mpg, y = wt)), n_reqs = list(a = "*")), list(a = c(x = "mpg", y = "wt"))) + expect_equal(get_column_names(df, a = quo(c(x = mpg, y = "wt")), n_reqs = list(a = "*")), list(a = c(x = "mpg", y = "wt"))) expect_equal(get_column_names(df, a = quo(c(mpg, y = wt)), n_reqs = list(a = "*")), list(a = c(mpg = "mpg", y = "wt"))) expect_equal(get_column_names(df, a = quo(c(x = starts_with("c"))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) + # expected error from conversion to expressions inside get_column_names (may not work interactively!) + test <- "c" + expect_error(get_column_names(df, a = quo(c(x = starts_with(test))), n_reqs = list(a = "*")), "'test' not found") + expect_error(get_column_names(df, a = expr(c(x = starts_with(test))), n_reqs = list(a = "*")), "'test' not found") + expect_equal(get_column_names(df, a = quo(c(x = starts_with(!!test))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) + expect_equal(get_column_names(df, a = expr(c(x = starts_with(!!test))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) + # allow missing columns expect_warning(get_column_names(df, a = quo(x), n_reqs = list(a = "*"), cols_must_exist = FALSE), "unknown column") expect_warning(found <- get_column_names(df, a = quo(c(mpg, x)), n_reqs = list(a = "*"), cols_must_exist = FALSE), "unknown column") diff --git a/tests/testthat/test-settings.R b/tests/testthat/test-settings.R index ae5e6aa6..82335d51 100644 --- a/tests/testthat/test-settings.R +++ b/tests/testthat/test-settings.R @@ -12,6 +12,15 @@ test_that("default values can be set and retrieved", { expect_false(default(quiet)) }) +test_that("default calls are resolved", { + # resolve defaults in a list of quos + expect_equal(resolve_defaults(quo(default(quiet))), FALSE) + expect_equal(resolve_defaults(expr(default(quiet))), FALSE) + expect_equal(resolve_defaults(list( + quo(default(quiet)), expr(default(quiet)), quo(x), expr(y) + )), list(FALSE, FALSE, quo(x), expr(y))) +}) + test_that("info messages can be turned on and off", { expect_message(iso_turn_info_messages_on(), "messages turned on") expect_false(default(quiet)) From 7e4f0e6af69a4abad1ff33615e17235ba648de5f Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 23:03:14 -0700 Subject: [PATCH 22/51] update scan vignette --- vignettes/scan.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index 911a43b0..f0c65633 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -39,7 +39,7 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() ``` ```{r} -# read dual inlet examples +# read scan examples iso_files <- iso_read_scan( iso_get_reader_example("peak_shape_scan_example.scn"), @@ -185,7 +185,7 @@ At the moment, isoreader supports export of all data to Excel and the [Feather f # Visualization -```{r "scans", fig.width=10, fig.height=10} +```{r "scans", fig.width=10, fig.height=10, eval=FALSE} # FIXME: should go into isoprocessor vignette once the functions there exist iso_files %>% iso_get_raw_data(include_file_info = type) %>% From 8f7d409a23778b5d6820ef4e9e5345f8b2319625 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 23:03:23 -0700 Subject: [PATCH 23/51] add scn function documentation --- man/file_readers.Rd | 20 ++++++++++++++ man/iso_data_structure.Rd | 5 ++++ man/iso_printing.Rd | 3 +++ man/iso_read_continuous_flow.Rd | 3 ++- man/iso_read_dual_inlet.Rd | 3 ++- man/iso_read_scan.Rd | 48 +++++++++++++++++++++++++++++++-- 6 files changed, 78 insertions(+), 4 deletions(-) diff --git a/man/file_readers.Rd b/man/file_readers.Rd index 9a9629da..55eb3ec5 100644 --- a/man/file_readers.Rd +++ b/man/file_readers.Rd @@ -3,12 +3,14 @@ \name{iso_register_dual_inlet_file_reader} \alias{iso_register_dual_inlet_file_reader} \alias{iso_register_continuous_flow_file_reader} +\alias{iso_register_scan_file_reader} \title{Register file readers} \usage{ iso_register_dual_inlet_file_reader( extension, func, description = NA_character_, + software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func) @@ -18,6 +20,17 @@ iso_register_continuous_flow_file_reader( extension, func, description = NA_character_, + software = NA_character_, + cacheable = TRUE, + overwrite = FALSE, + env = find_func(func) +) + +iso_register_scan_file_reader( + extension, + func, + description = NA_character_, + software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func) @@ -30,6 +43,8 @@ iso_register_continuous_flow_file_reader( \item{description}{what is this file type about?} +\item{software}{what is the software program that creates this filetype?} + \item{cacheable}{whether this file type is cacheable. If \code{TRUE} (the default), user requests to cache the file will be honored. If \code{FALSE}, this file type will never be cached no matter what the user requests.} \item{overwrite}{whether to overwrite an existing file reader for the same extension} @@ -43,11 +58,16 @@ Register file extensions and reader functions for different data files. Isoreade \code{iso_register_dual_inlet_file_reader}: use this function to register file readers for dual inlet files. \code{iso_register_continuous_flow_file_reader}: use this function to register file readers for continuous flow files. + +\code{iso_register_scan_file_reader}: use this function to register file readers for scan files. } \seealso{ Other file_types: \code{\link{iso_get_supported_file_types}()} +Other file_types: +\code{\link{iso_get_supported_file_types}()} + Other file_types: \code{\link{iso_get_supported_file_types}()} } diff --git a/man/iso_data_structure.Rd b/man/iso_data_structure.Rd index a7a88974..6b8507d2 100644 --- a/man/iso_data_structure.Rd +++ b/man/iso_data_structure.Rd @@ -6,6 +6,7 @@ \alias{iso_is_object} \alias{iso_is_dual_inlet} \alias{iso_is_continuous_flow} +\alias{iso_is_scan} \alias{iso_as_file_list} \title{Isoreader data structure functions} \usage{ @@ -19,6 +20,8 @@ iso_is_dual_inlet(x) iso_is_continuous_flow(x) +iso_is_scan(x) + iso_as_file_list(..., discard_duplicates = TRUE) } \arguments{ @@ -39,5 +42,7 @@ iso_as_file_list(..., discard_duplicates = TRUE) \code{iso_is_continuous_flow} tests if an iso_file or iso_file list consists exclusively of continuous flow file objects +\code{iso_is_scan} tests if an iso_file or iso_file list consists exclusively of scan file objects + \code{iso_as_file_list} concatenates iso_file and iso_file list object(s) into one combined iso_file list (equivalent to calling \code{c(...)}), flattens all passed lists into one list structure, all individual objects and objects within iso_file lists have to be the same type of iso_file, issues warnings if there are duplicate file ids and summarizes all problems in the iso_file list. If duplicates are allowed (\code{discard_duplicates = FALSE}), their file IDs will append a #1, #2, #3, etc. to preserve unique file IDs (important for many data aggregation operations). } diff --git a/man/iso_printing.Rd b/man/iso_printing.Rd index 94b5f125..fab5ec56 100644 --- a/man/iso_printing.Rd +++ b/man/iso_printing.Rd @@ -5,6 +5,7 @@ \alias{print.iso_file} \alias{print.dual_inlet} \alias{print.continuous_flow} +\alias{print.scan} \title{Isofile printing} \usage{ \method{print}{iso_file_list}(x, ...) @@ -14,6 +15,8 @@ \method{print}{dual_inlet}(x, ..., show_problems = TRUE) \method{print}{continuous_flow}(x, ..., show_problems = TRUE) + +\method{print}{scan}(x, ..., show_problems = TRUE) } \arguments{ \item{x}{Object to show.} diff --git a/man/iso_read_continuous_flow.Rd b/man/iso_read_continuous_flow.Rd index 5d2014c5..a703fc7c 100644 --- a/man/iso_read_continuous_flow.Rd +++ b/man/iso_read_continuous_flow.Rd @@ -52,6 +52,7 @@ Load continuous flow data } \seealso{ Other isoread functions for different types of IRMS data: -\code{\link{iso_read_dual_inlet}()} +\code{\link{iso_read_dual_inlet}()}, +\code{\link{iso_read_scan}()} } \concept{isoread functions for different types of IRMS data} diff --git a/man/iso_read_dual_inlet.Rd b/man/iso_read_dual_inlet.Rd index 50c47feb..15e6ef3a 100644 --- a/man/iso_read_dual_inlet.Rd +++ b/man/iso_read_dual_inlet.Rd @@ -55,6 +55,7 @@ Load dual inlet data } \seealso{ Other isoread functions for different types of IRMS data: -\code{\link{iso_read_continuous_flow}()} +\code{\link{iso_read_continuous_flow}()}, +\code{\link{iso_read_scan}()} } \concept{isoread functions for different types of IRMS data} diff --git a/man/iso_read_scan.Rd b/man/iso_read_scan.Rd index 49e8791f..ebb4809b 100644 --- a/man/iso_read_scan.Rd +++ b/man/iso_read_scan.Rd @@ -1,11 +1,55 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/scans.R +% Please edit documentation in R/isoread.R \name{iso_read_scan} \alias{iso_read_scan} \title{Load scan data} \usage{ -iso_read_scan() +iso_read_scan( + ..., + root = ".", + read_raw_data = default(read_raw_data), + read_file_info = default(read_file_info), + read_method_info = default(read_method_info), + discard_duplicates = TRUE, + parallel = FALSE, + parallel_plan = future::multiprocess, + cache = default(cache), + cache_files_with_errors = TRUE, + read_cache = default(cache), + quiet = default(quiet) +) +} +\arguments{ +\item{...}{one or multiple file/folder paths. All files must have a supported file extension. All folders are expanded and searched for files with supported file extensions (which are then included in the read).} + +\item{root}{root for relative paths. Can be relative to the current working directory (e.g. \code{"data"}) or an absolute path on the file system (e.g. \code{"/Users/..."} or \code{"C:/Data/.."}). The default is the current working directory (\code{"."}). Can be supplied as a vector of same length as the provided paths if the paths have different roots.} + +\item{read_raw_data}{whether to read the raw mass/ion data from the file} + +\item{read_file_info}{whether to read auxiliary file information (file id, sequence information, etc.)} + +\item{read_method_info}{whether to read methods information (standards, processing info)} + +\item{discard_duplicates}{whether to automatically discard files with duplicate file IDs (i.e. duplicate file names). If \code{TRUE} (the default), only the first files are kept and any files with the same file ID are discarded. If \code{FALSE}, all duplicate files are kept but their file IDs are appended with suffix \code{#1}, \code{#2}, etc.} + +\item{parallel}{whether to process in parallel based on the number of available CPU cores. This may yield performance increases for files that are slow to parse such as continuous flow isodat files but usually provides little benefit for efficient data formats such as reading from R Data Archives.} + +\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multiprocess} (the default, uses multicore if supported by the operating system, otherwise multisession), \code{future::multisession} or \code{future::multicore}.} + +\item{cache}{whether to cache iso_files. Note that previously exported R Data Archives (di.rda, cf.rda) are never cached since they are already essentially in cached form.} + +\item{cache_files_with_errors}{whether to cache files that had errors during reading} + +\item{read_cache}{whether to reload from cache if a cached version exists. Note that it will only read from cache if the file was previously read with the exact same isoreader version and read options and has not been modified since.} + +\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} } \description{ Load scan data } +\seealso{ +Other isoread functions for different types of IRMS data: +\code{\link{iso_read_continuous_flow}()}, +\code{\link{iso_read_dual_inlet}()} +} +\concept{isoread functions for different types of IRMS data} From fb3e9053f2e4bb98171191ec7b1b5421aaf90bdf Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 23:50:15 -0700 Subject: [PATCH 24/51] account for unix creation date warnings in scan test files --- tests/testthat/test-scan.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-scan.R b/tests/testthat/test-scan.R index 5d2bb87c..39780915 100644 --- a/tests/testthat/test-scan.R +++ b/tests/testthat/test-scan.R @@ -29,25 +29,25 @@ test_that("test that scn files can be read", { expect_true(file.exists(file <- iso_get_reader_example("peak_shape_scan_example.scn"))) expect_is(scan <- iso_read_scan(file), "scan") - expect_equal(nrow(problems(scan)), 0) + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) expect_true(file.exists(file <- iso_get_reader_example("background_scan_example.scn"))) expect_is(scan <- iso_read_scan(file), "scan") - expect_equal(nrow(problems(scan)), 0) + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) expect_true(file.exists(file <- iso_get_reader_example("full_scan_example.scn"))) expect_is(scan <- iso_read_scan(file), "scan") - expect_equal(nrow(problems(scan)), 0) + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) expect_true(file.exists(file <- iso_get_reader_example("time_scan_example.scn"))) expect_is(scan <- iso_read_scan(file), "scan") - expect_equal(nrow(problems(scan)), 0) + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) test_folder <- file.path("test_data") expect_true(file.exists(file <- file.path(test_folder, "scan_hv_01.scn"))) expect_is(scan <- iso_read_scan(file), "scan") - expect_equal(nrow(problems(scan)), 0) + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) }) From 9315ada3e4168b00cf0e79f27209afb499ec8520 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 23:50:27 -0700 Subject: [PATCH 25/51] export file reader registration functions --- NAMESPACE | 2 ++ R/isoread.R | 2 ++ 2 files changed, 4 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index a2416df4..7d8da3df 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -120,7 +120,9 @@ export(iso_plot_raw_data) export(iso_read_continuous_flow) export(iso_read_dual_inlet) export(iso_read_scan) +export(iso_register_continuous_flow_file_reader) export(iso_register_dual_inlet_file_reader) +export(iso_register_scan_file_reader) export(iso_rename_file_info) export(iso_reread_archive) export(iso_reread_files) diff --git a/R/isoread.R b/R/isoread.R index 5a8f693a..7e92c595 100644 --- a/R/isoread.R +++ b/R/isoread.R @@ -24,6 +24,7 @@ iso_register_dual_inlet_file_reader <- function( #' @details \code{iso_register_continuous_flow_file_reader}: use this function to register file readers for continuous flow files. #' @rdname file_readers #' @family file_types +#' @export iso_register_continuous_flow_file_reader <- function( extension, func, description = NA_character_, software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { register_file_reader("continuous flow", "iso_read_continuous_flow", extension, func, description, software, cacheable, overwrite, env) @@ -32,6 +33,7 @@ iso_register_continuous_flow_file_reader <- function( #' @details \code{iso_register_scan_file_reader}: use this function to register file readers for scan files. #' @rdname file_readers #' @family file_types +#' @export iso_register_scan_file_reader <- function( extension, func, description = NA_character_, software = NA_character_, cacheable = TRUE, overwrite = FALSE, env = find_func(func)) { register_file_reader("scan", "iso_read_scan", extension, func, description, software, cacheable, overwrite, env) From 4dfe0190bdafe5f85c03c72b06431a813039636b Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Mon, 10 Feb 2020 23:50:43 -0700 Subject: [PATCH 26/51] introduce warning flag in get_column_names --- R/nse.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/nse.R b/R/nse.R index 6c286e17..6d3cb3b4 100644 --- a/R/nse.R +++ b/R/nse.R @@ -52,9 +52,9 @@ check_expressions <- function(df, ...) { # @param n_reqs named list to specify how many columns are allowed/required for each selection criterion, default for all that are not specified is 1. # Allowed values are a specific number 1,2,3,4, etc. "*" for any number, "?" for 0 or 1 and "+" for at least one. # @param type_reqs named list to specify what types certain columns must be, allowed: "list" (also includes "vctrs_list_of"), "numeric", "integer", "character", "logical" -# @param cols_must_exist - if TRUE, will throw an error if a column does not exist, otherwise just warning +# @param cols_must_exist - if TRUE, will throw an error if a column does not exist, otherwise just warning (unless warn = FALSE) # @return list of column names for each entry (may contain multiple depending on selection conditions) -get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_must_exist = TRUE) { +get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_must_exist = TRUE, warn = TRUE) { # df name and data frame test if (missing(df)) stop("no data frame supplied", call. = FALSE) @@ -108,7 +108,7 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ stop(err_msg, call. = FALSE) } else { # just a warning and find the columns omitting those missing - warning(err_msg, immediate. = TRUE, call. = FALSE) + if (warn) warning(err_msg, immediate. = TRUE, call. = FALSE) cols_results <- map(cols_exps, safe_vars_select, strict = FALSE) } } From 4d144ef083b15dcc0a73fa4e75edb45f36a642e6 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 00:09:16 -0700 Subject: [PATCH 27/51] make isoread scan read more robust and add additional test files --- R/isoread_scn.R | 14 +++++++++----- tests/testthat/test-scan.R | 7 +++++++ tests/testthat/test_data/scan_hv_02.scn | Bin 0 -> 71562 bytes tests/testthat/test_data/scan_hv_03.scn | Bin 0 -> 71562 bytes 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tests/testthat/test_data/scan_hv_02.scn create mode 100644 tests/testthat/test_data/scan_hv_03.scn diff --git a/R/isoread_scn.R b/R/isoread_scn.R index 1807b2a5..0e929021 100644 --- a/R/isoread_scn.R +++ b/R/isoread_scn.R @@ -83,7 +83,7 @@ extract_scn_raw_voltage_data <- function(ds) { ds$binary <- ds$binary %>% move_to_next_pattern(re_block("x-000"), re_block("fef-x")) - end_pos <- ds$binary %>% find_next_pattern(re_direct("\xff\xff")) + end_pos <- ds$binary %>% find_next_pattern(re_direct("\xff\xff", label = "xffxff")) ds$binary <- ds$binary %>% skip_pos(end_pos - ds$binary$pos - 8) %>% # skip comment @@ -95,11 +95,14 @@ extract_scn_raw_voltage_data <- function(ds) { set_binary_file_error_prefix("cannot identify scan units") %>% move_to_C_block_range("CVisualisationData", "CIntegrationUnitScanPart") ds$binary <- ds$binary %>% + move_to_next_pattern(re_text("Arial")) %>% move_to_next_pattern( # seems to begin with this unique 88 c3 40 sequence - re_direct("\x88\xc3\x40"), re_null(12), + re_direct("\x88\xc3\x40", label = "x88xc3x40") + ) %>% + move_to_next_pattern( # but this could be sufficient too if the above turns too specific - re_block("fef-0"), re_block("x-000"), re_block("fef-x") + re_block("x-000"), re_block("fef-x") ) %>% capture_data("units", "text", re_null(4), re_not_null(1)) @@ -116,8 +119,8 @@ extract_scn_raw_voltage_data <- function(ds) { ds$binary <- ds$binary %>% set_binary_file_error_prefix("cannot identify masses/cups") %>% move_to_C_block("^CPlotRange", regexp_match = TRUE) %>% - cap_at_next_pattern(re_text("Administrator")) - + cap_at_C_block("CIntegrationUnitGasConfPart") + # masses mass_positions <- find_next_patterns(ds$binary, re_text("Mass")) masses <- c() @@ -161,6 +164,7 @@ extract_scn_raw_voltage_data <- function(ds) { "voltages", c("float", rep("double", ds$binary$data$n_traces)), ds$binary$data$n_points ) + voltages <- ds$binary$data$voltages %>% tibble::as_tibble() %>% setNames(c("step", config$mass_column)) diff --git a/tests/testthat/test-scan.R b/tests/testthat/test-scan.R index 39780915..7a73fc2f 100644 --- a/tests/testthat/test-scan.R +++ b/tests/testthat/test-scan.R @@ -49,6 +49,13 @@ test_that("test that scn files can be read", { expect_is(scan <- iso_read_scan(file), "scan") expect_equal(nrow(filter(problems(scan), type != "warning")), 0) + expect_true(file.exists(file <- file.path(test_folder, "scan_hv_02.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) + + expect_true(file.exists(file <- file.path(test_folder, "scan_hv_03.scn"))) + expect_is(scan <- iso_read_scan(file), "scan") + expect_equal(nrow(filter(problems(scan), type != "warning")), 0) }) diff --git a/tests/testthat/test_data/scan_hv_02.scn b/tests/testthat/test_data/scan_hv_02.scn new file mode 100644 index 0000000000000000000000000000000000000000..c06ebf0185db93f84694614f698d98356c6234b4 GIT binary patch literal 71562 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 KzdXVbYxHlZQ|2T9 literal 0 HcmV?d00001 diff --git a/tests/testthat/test_data/scan_hv_03.scn b/tests/testthat/test_data/scan_hv_03.scn new file mode 100644 index 0000000000000000000000000000000000000000..9a6656b3cf36962f6121cc189fe7514eeadd3cc3 GIT binary patch literal 71562 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 R@rS*cDWBGB3ztx&{|^fTFT4N% literal 0 HcmV?d00001 From 68e4af29ba3c6356430c478787ab097f32535591 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 00:17:30 -0700 Subject: [PATCH 28/51] clearer message for creation date error --- R/utils_files.R | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/R/utils_files.R b/R/utils_files.R index 670c5edb..31cacd58 100644 --- a/R/utils_files.R +++ b/R/utils_files.R @@ -16,15 +16,13 @@ extract_os_file_creation_datetime <- function(ds) { # Linux and OS X, try it with stat, otherwise just throw a warning and use mdate # m date (meaning BSD stat didn't work) - get_from_mdate <- function(ds) { + get_creation_date <- function(ds) { # last modification time the only info that's available ds <- ds %>% register_warning( paste0( - "file creation date could not be determined on this operating system (", - .Platform$OS.type, "/", Sys.info()[['sysname']], - "), recovering 'last modified date' instead." + "file creation date cannot be accessed on this Linux system, using last modified time for file_datetime instead" ), - warn = TRUE + warn = FALSE ) ds$file_info$file_datetime <- as_datetime(file.info(path)$mtime, tz = Sys.timezone()) return(ds) @@ -43,8 +41,8 @@ extract_os_file_creation_datetime <- function(ds) { as_datetime(tz = Sys.timezone()) ds }, - error = function(e) { return(get_from_mdate(ds)) }, - warning = function(e) { return(get_from_mdate(ds)) }) + error = function(e) { return(get_creation_date(ds)) }, + warning = function(e) { return(get_creation_date(ds)) }) } else { stop("don't know how to get file creation date on platform ", .Platform$OS.type) } From 2106d3dfb5d028c3aa48bc697d996398e592bb1a Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 01:53:51 -0700 Subject: [PATCH 29/51] implement resistors on scan files #36 --- DESCRIPTION | 2 +- R/aggregate_data.R | 8 ++ R/isoread_scn.R | 157 +++++++++++++++++--------- R/utils_binary_files.R | 2 +- tests/testthat/test-aggregate-data.R | 9 +- tests/testthat/test-data-structures.R | 2 +- 6 files changed, 123 insertions(+), 57 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 62ccd7bc..c2fbea53 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: isoreader Title: Read IRMS data files Description: R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry. -Version: 1.0.16 +Version: 1.0.17 Authors@R: person("Sebastian", "Kopf", email = "sebastian.kopf@colorado.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2044-0201")) URL: https://github.com/isoverse/isoreader BugReports: https://github.com/isoverse/isoreader/issues diff --git a/R/aggregate_data.R b/R/aggregate_data.R index 9d798dc6..93415284 100644 --- a/R/aggregate_data.R +++ b/R/aggregate_data.R @@ -490,7 +490,15 @@ iso_get_bgrd_data <- function(iso_files, select = everything(), gather = FALSE, #' @family data retrieval functions #' @export iso_get_standards_info <- function(iso_files, with_ratios = FALSE, include_file_info = NULL, quiet = default(quiet)) { + iso_files <- iso_as_file_list(iso_files) + + # safety checks + if (iso_is_scan(iso_files)) + stop("scan files don't have standards information", call. = FALSE) + else if (!iso_is_continuous_flow(iso_files) && !iso_is_dual_inlet(iso_files)) + stop("only dual inlet and continuous flow files can have standards information", call. = FALSE) + include_file_info_quo <- enquo(include_file_info) if (!quiet) { sprintf("Info: aggregating standards info from %d data file(s)%s", length(iso_files), diff --git a/R/isoread_scn.R b/R/isoread_scn.R index 0e929021..d885c4d1 100644 --- a/R/isoread_scn.R +++ b/R/isoread_scn.R @@ -13,6 +13,9 @@ iso_read_scn <- function(ds, options = list()) { # get scan file type ds <- exec_func_with_error_catch(extract_scn_file_type, ds) + # get mass and cup config + ds <- exec_func_with_error_catch(extract_scn_mass_cup_info, ds) + # process file info if(ds$read_options$file_info) { # there does not seem to be creation date stored inside the scn files @@ -23,18 +26,15 @@ iso_read_scn <- function(ds, options = list()) { } # process raw data - if (ds$read_option$raw_data) { + if (ds$read_option$raw_data && !is.null(ds$binary$data$config)) { ds <- exec_func_with_error_catch(extract_scn_raw_voltage_data, ds) } - return(ds) - - # FIXME: should be possible to get resistor information but it's not - # obvious yet how - # # process method info - # if (ds$read_options$method_info) { - # ds <- exec_func_with_error_catch(extract_isodat_resistors, ds) - # } + # process method info + if (ds$read_options$method_info && !is.null(ds$binary$data$config)) { + ds <- exec_func_with_error_catch(extract_scn_resistors, ds) + } + return(ds) } @@ -73,6 +73,50 @@ extract_scn_file_info <- function(ds) { return(ds) } +# extract mass and cup info +extract_scn_mass_cup_info <- function(ds){ + # find masses and cups + ds$binary <- ds$binary %>% + set_binary_file_error_prefix("cannot identify masses/cups") %>% + move_to_C_block("^CPlotRange", regexp_match = TRUE) %>% + cap_at_next_pattern(re_text("Administrator")) + + # masses + mass_positions <- find_next_patterns(ds$binary, re_text("Mass")) + masses <- c() + cups <- c() + if (length(mass_positions) > 0) { + for (pos in mass_positions) { + ds$binary <- ds$binary %>% move_to_pos(pos) %>% + capture_data("mass", "text", re_not_null(2)) + masses <- c(masses, ds$binary$data$mass) + } + cups <- c(stringr::str_extract(masses, "C\\d+")) + } else { + # cups + cup_positions <- find_next_patterns(ds$binary, re_text("Cup")) + for (pos in cup_positions) { + ds$binary <- ds$binary %>% move_to_pos(pos) %>% + capture_data("cup", "text", re_not_null(2)) + cups <- c(cups, ds$binary$data$cup) + } + masses <- rep(NA_character_, length(cups)) + } + + ds$binary$data$config <- tibble( + cup = parse_number(cups) %>% as.integer(), + mass = parse_number(masses) %>% as.character(), + mass_column = ifelse( + !is.na(mass), + sprintf("v%s.mV", mass), + # Note: okay to designate cups in this way? + sprintf("vC%s.mV", cup) + ) + ) %>% filter(!is.na(cup)) + + return(ds) +} + # extract voltage data in scn file extract_scn_raw_voltage_data <- function(ds) { @@ -115,46 +159,6 @@ extract_scn_raw_voltage_data <- function(ds) { capture_n_data("min", "float", 1) %>% capture_n_data("max", "float", 1) - # find masses and cups - ds$binary <- ds$binary %>% - set_binary_file_error_prefix("cannot identify masses/cups") %>% - move_to_C_block("^CPlotRange", regexp_match = TRUE) %>% - cap_at_C_block("CIntegrationUnitGasConfPart") - - # masses - mass_positions <- find_next_patterns(ds$binary, re_text("Mass")) - masses <- c() - cups <- c() - if (length(mass_positions) > 0) { - for (pos in mass_positions) { - ds$binary <- ds$binary %>% move_to_pos(pos) %>% - capture_data("mass", "text", re_not_null(2)) - masses <- c(masses, ds$binary$data$mass) - } - cups <- c(stringr::str_extract(masses, "C\\d")) - } else { - # cups - cup_positions <- find_next_patterns(ds$binary, re_text("Cup")) - for (pos in cup_positions) { - ds$binary <- ds$binary %>% move_to_pos(pos) %>% - capture_data("cup", "text", re_not_null(2)) - cups <- c(cups, ds$binary$data$cup) - } - masses <- rep(NA_character_, length(cups)) - } - - # configuration - config <- tibble( - cup = parse_number(cups) %>% as.integer(), - mass = parse_number(masses) %>% as.character(), - mass_column = ifelse( - !is.na(mass), - sprintf("v%s.mV", mass), - # Note: should cups be designated in some other way?? - sprintf("v%s.mV", cup) - ) - ) - # raw data (=voltages) ds$binary <- ds$binary %>% set_binary_file_error_prefix("cannot read raw data") %>% @@ -164,10 +168,22 @@ extract_scn_raw_voltage_data <- function(ds) { "voltages", c("float", rep("double", ds$binary$data$n_traces)), ds$binary$data$n_points ) + voltages <- tibble::as_tibble(ds$binary$data$voltages) - voltages <- ds$binary$data$voltages %>% - tibble::as_tibble() %>% - setNames(c("step", config$mass_column)) + # safety check + if (ncol(voltages) - 1L != nrow(ds$binary$data$config)) { + if (default("debug")) { + print(voltages) + print(ds$binary$data$config) + } + glue::glue( + "inconsistent number of data traces ({ncol(voltages) - 1L}) ", + "and raw data masses/cups ({nrow(ds$binary$data$config)}) recovered") %>% + stop(call. = FALSE) + } + + # set column names + voltages <- setNames(voltages, c("step", ds$binary$data$config$mass_column)) # calculate x values from step convert_step_to_x <- function(step) { @@ -200,3 +216,40 @@ extract_scn_raw_voltage_data <- function(ds) { return(ds) } + + +# extract resistor information +# not the same format as other isodat files +extract_scn_resistors <- function(ds) { + + ds$binary <- ds$binary %>% + move_to_C_block_range("CCupHardwarePart", "CChannelHardwarePart") + + cup_positions <- find_next_patterns(ds$binary, re_block("fef-x"), re_text("Cup")) + cup_caps <- c(cup_positions[-1], ds$binary$max_pos) + + cups <- c() + ohms <- c() + for (i in 1:length(cup_positions)) { + ds$binary <- ds$binary %>% move_to_pos(cup_positions[i]) %>% + cap_at_pos(cup_caps[i]) %>% + skip_pos(4) %>% + capture_data("cup", "text", re_block("x-000")) %>% + move_to_next_pattern(re_null(16), re_direct("(\xff\xfe\xff\\x00)?"), re_block("x-000"), re_not_null(1)) %>% + capture_n_data("R.Ohm", "double", 1) + cups <- c(cups, ds$binary$data$cup) + ohms <- c(ohms, ds$binary$data$R.Ohm) + } + + ds$method_info$resistors <- + dplyr::tibble( + cup = parse_number(cups) %>% as.integer(), + R.ohm = ohms + ) %>% + dplyr::right_join( + select(ds$binary$data$config, cup, mass), + by = "cup" + ) + + return(ds) +} \ No newline at end of file diff --git a/R/utils_binary_files.R b/R/utils_binary_files.R index fd9e7985..ccc1f398 100644 --- a/R/utils_binary_files.R +++ b/R/utils_binary_files.R @@ -288,7 +288,7 @@ re_combine <- function(...) { list( label = str_c(map_chr(regexps, "label"), collapse = ""), # NOTE: on windows, the following command with str_c instead of paste or map_chr instead of sapply strangly leads to the regexp not getting recognized anymore in grepRaw - regexp = paste(sapply(regexps, `[[`, "regexp"), collapse = ""), + regexp = stringr::str_c(sapply(regexps, `[[`, "regexp"), collapse = ""), size = sum(map_dbl(regexps, "size")) ), class = "binary_regexp") diff --git a/tests/testthat/test-aggregate-data.R b/tests/testthat/test-aggregate-data.R index 432aa97e..40ef8805 100644 --- a/tests/testthat/test-aggregate-data.R +++ b/tests/testthat/test-aggregate-data.R @@ -201,7 +201,10 @@ test_that("test that aggregeting raw data works", { test_that("test that aggregating of methods standards works", { - iso_file <- make_iso_file_data_structure("NA") + expect_error(iso_get_standards_info(make_iso_file_data_structure("NA")), "only dual inlet and continuous flow files") + expect_error(iso_get_standards_info(make_scan_data_structure("NA")), "scan files don't have") + + iso_file <- make_di_data_structure("NA") expect_warning(iso_get_standards_info(iso_file), "read without extracting the method info") # test data @@ -226,7 +229,7 @@ test_that("test that aggregating of methods standards works", { # make sure that files that have no raw data do not get added back in by including file info expect_equal( suppressWarnings(iso_get_standards_info( - c(make_iso_file_data_structure("NA"), iso_file1, iso_file2), + c(make_di_data_structure("NA"), iso_file1, iso_file2), include_file_info = c("test_info")))$test_info %>% unique(), c("x", "y") ) @@ -277,6 +280,7 @@ test_that("test that aggregating of vendor data table works", { expect_error(iso_get_vendor_data_table(make_iso_file_data_structure("NA")), "only dual inlet and continuous flow files") expect_error(iso_get_vendor_data_table(make_scan_data_structure("NA")), "scan files don't have") + iso_file <- make_cf_data_structure("NA") expect_warning(iso_get_vendor_data_table(iso_file), "read without extracting the vendor data table") @@ -415,3 +419,4 @@ test_that("test that total data aggregation works", { expect_equal(select(out, cup, R.Ohm), bind_rows(iso_file1$method_info$resistors, iso_file2$method_info$resistors)) }) + diff --git a/tests/testthat/test-data-structures.R b/tests/testthat/test-data-structures.R index 3ab0ac0b..a10944d4 100644 --- a/tests/testthat/test-data-structures.R +++ b/tests/testthat/test-data-structures.R @@ -99,10 +99,10 @@ test_that("test that iso_file list checks work", { expect_equal(iso_as_file_list() %>% iso_get_raw_data() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_file_info() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_resistors_info() %>% nrow(), 0) - expect_equal(iso_as_file_list() %>% iso_get_standards_info() %>% nrow(), 0) # expected errors expect_error(iso_as_file_list() %>% iso_get_vendor_data_table(), "only dual inlet.*continuous flow") + expect_error(iso_as_file_list() %>% iso_get_standards_info(), "only dual inlet.*continuous flow") expect_error(iso_as_file_list(1, error = "test"), "encountered incompatible data type") expect_false(iso_is_file_list(42)) expect_false(iso_is_file_list(make_iso_file_data_structure("NA"))) From 1b90da0bc7e4712aed25de0189703668204639f1 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 01:53:58 -0700 Subject: [PATCH 30/51] update scan file vignette --- vignettes/scan.Rmd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index f0c65633..040cdbbe 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -124,7 +124,6 @@ iso_files3 %>% Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette): ```{r} -# FIXME: these information are not yet extracted from scan files! iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table() ``` @@ -191,7 +190,7 @@ iso_files %>% iso_get_raw_data(include_file_info = type) %>% dplyr::mutate(panel = sprintf("%s [%s]", type, units)) %>% tidyr::pivot_longer( - matches("v\\d+"), + matches("vC?\\d+"), names_to = "mass", values_to = "value", values_drop_na = TRUE From 285b5b2ead285d2d4891c205dd0ec9612b9154bb Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 10:14:57 -0700 Subject: [PATCH 31/51] switch multiprocess default to multisession --- R/isoread.R | 10 +++++----- man/iso_read_continuous_flow.Rd | 4 ++-- man/iso_read_dual_inlet.Rd | 4 ++-- man/iso_read_files.Rd | 4 ++-- man/iso_read_scan.Rd | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/R/isoread.R b/R/isoread.R index 7e92c595..e248b581 100644 --- a/R/isoread.R +++ b/R/isoread.R @@ -146,7 +146,7 @@ iso_read_dual_inlet <- function( read_raw_data = default(read_raw_data), read_file_info = default(read_file_info), read_method_info = default(read_method_info), read_vendor_data_table = default(read_vendor_data_table), nu_masses = c(), - discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multiprocess, + discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), quiet = default(quiet)) { # process data @@ -182,7 +182,7 @@ iso_read_continuous_flow <- function( root = ".", read_raw_data = default(read_raw_data), read_file_info = default(read_file_info), read_method_info = default(read_method_info), read_vendor_data_table = default(read_vendor_data_table), - discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multiprocess, + discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), quiet = default(quiet)) { # process data @@ -217,7 +217,7 @@ iso_read_scan <- function( ..., root = ".", read_raw_data = default(read_raw_data), read_file_info = default(read_file_info), read_method_info = default(read_method_info), - discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multiprocess, + discard_duplicates = TRUE, parallel = FALSE, parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), quiet = default(quiet)) { # process data @@ -255,7 +255,7 @@ iso_read_scan <- function( #' @param data_structure the basic data structure for the type of iso_file #' @inheritParams iso_as_file_list #' @param parallel whether to process in parallel based on the number of available CPU cores. This may yield performance increases for files that are slow to parse such as continuous flow isodat files but usually provides little benefit for efficient data formats such as reading from R Data Archives. -#' @param parallel_plan which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multiprocess} (the default, uses multicore if supported by the operating system, otherwise multisession), \code{future::multisession} or \code{future::multicore}. +#' @param parallel_plan which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multisession} for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use \code{future::multicore}. #' @param quiet whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off} #' @param cache whether to cache iso_files. Note that previously exported R Data Archives (di.rda, cf.rda) are never cached since they are already essentially in cached form. #' @param cache_files_with_errors whether to cache files that had errors during reading @@ -265,7 +265,7 @@ iso_read_scan <- function( #' @return single iso_file object (if single file) or list of iso_files (iso_file_list) iso_read_files <- function(paths, root, supported_extensions, data_structure, read_options = c(), reader_options = list(), discard_duplicates = TRUE, - parallel = FALSE, parallel_plan = future::multiprocess, + parallel = FALSE, parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), quiet = default(quiet)) { diff --git a/man/iso_read_continuous_flow.Rd b/man/iso_read_continuous_flow.Rd index a703fc7c..52902155 100644 --- a/man/iso_read_continuous_flow.Rd +++ b/man/iso_read_continuous_flow.Rd @@ -13,7 +13,7 @@ iso_read_continuous_flow( read_vendor_data_table = default(read_vendor_data_table), discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, + parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), @@ -37,7 +37,7 @@ iso_read_continuous_flow( \item{parallel}{whether to process in parallel based on the number of available CPU cores. This may yield performance increases for files that are slow to parse such as continuous flow isodat files but usually provides little benefit for efficient data formats such as reading from R Data Archives.} -\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multiprocess} (the default, uses multicore if supported by the operating system, otherwise multisession), \code{future::multisession} or \code{future::multicore}.} +\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multisession} for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use \code{future::multicore}.} \item{cache}{whether to cache iso_files. Note that previously exported R Data Archives (di.rda, cf.rda) are never cached since they are already essentially in cached form.} diff --git a/man/iso_read_dual_inlet.Rd b/man/iso_read_dual_inlet.Rd index 15e6ef3a..621b1d04 100644 --- a/man/iso_read_dual_inlet.Rd +++ b/man/iso_read_dual_inlet.Rd @@ -14,7 +14,7 @@ iso_read_dual_inlet( nu_masses = c(), discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, + parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), @@ -40,7 +40,7 @@ iso_read_dual_inlet( \item{parallel}{whether to process in parallel based on the number of available CPU cores. This may yield performance increases for files that are slow to parse such as continuous flow isodat files but usually provides little benefit for efficient data formats such as reading from R Data Archives.} -\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multiprocess} (the default, uses multicore if supported by the operating system, otherwise multisession), \code{future::multisession} or \code{future::multicore}.} +\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multisession} for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use \code{future::multicore}.} \item{cache}{whether to cache iso_files. Note that previously exported R Data Archives (di.rda, cf.rda) are never cached since they are already essentially in cached form.} diff --git a/man/iso_read_files.Rd b/man/iso_read_files.Rd index dcb0367b..a6592647 100644 --- a/man/iso_read_files.Rd +++ b/man/iso_read_files.Rd @@ -13,7 +13,7 @@ iso_read_files( reader_options = list(), discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, + parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), @@ -37,7 +37,7 @@ iso_read_files( \item{parallel}{whether to process in parallel based on the number of available CPU cores. This may yield performance increases for files that are slow to parse such as continuous flow isodat files but usually provides little benefit for efficient data formats such as reading from R Data Archives.} -\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multiprocess} (the default, uses multicore if supported by the operating system, otherwise multisession), \code{future::multisession} or \code{future::multicore}.} +\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multisession} for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use \code{future::multicore}.} \item{cache}{whether to cache iso_files. Note that previously exported R Data Archives (di.rda, cf.rda) are never cached since they are already essentially in cached form.} diff --git a/man/iso_read_scan.Rd b/man/iso_read_scan.Rd index ebb4809b..207163c9 100644 --- a/man/iso_read_scan.Rd +++ b/man/iso_read_scan.Rd @@ -12,7 +12,7 @@ iso_read_scan( read_method_info = default(read_method_info), discard_duplicates = TRUE, parallel = FALSE, - parallel_plan = future::multiprocess, + parallel_plan = future::multisession, cache = default(cache), cache_files_with_errors = TRUE, read_cache = default(cache), @@ -34,7 +34,7 @@ iso_read_scan( \item{parallel}{whether to process in parallel based on the number of available CPU cores. This may yield performance increases for files that are slow to parse such as continuous flow isodat files but usually provides little benefit for efficient data formats such as reading from R Data Archives.} -\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multiprocess} (the default, uses multicore if supported by the operating system, otherwise multisession), \code{future::multisession} or \code{future::multicore}.} +\item{parallel_plan}{which parallel processing strategy to use, see \link[future]{plan}, typically \code{future::multisession} for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use \code{future::multicore}.} \item{cache}{whether to cache iso_files. Note that previously exported R Data Archives (di.rda, cf.rda) are never cached since they are already essentially in cached form.} From 124bab24c1102d29ce7a56c129cc682065093d83 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 10:16:17 -0700 Subject: [PATCH 32/51] simplify auto testing without example files --- Makefile | 5 +++++ tests/testthat/test-continuous-flow.R | 28 +++++++++++++++++++++++---- tests/testthat/test-dual-inlet.R | 13 +++++++------ tests/testthat/test-isoread.R | 7 ------- tests/testthat/test-scan.R | 10 ++++++---- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 5756a4d7..beac4705 100644 --- a/Makefile +++ b/Makefile @@ -11,5 +11,10 @@ vignettes: check: Rscript -e "devtools::check()" +# test package functionality without all example files auto_test: + R -q -e "rm(list = ls()); options("isoreader.run_file_tests" = FALSE); testthat::auto_test_package()" + +# test all example files +auto_test_all: R -q -e "rm(list = ls()); testthat::auto_test_package()" diff --git a/tests/testthat/test-continuous-flow.R b/tests/testthat/test-continuous-flow.R index fae90ea8..e1649305 100644 --- a/tests/testthat/test-continuous-flow.R +++ b/tests/testthat/test-continuous-flow.R @@ -19,12 +19,14 @@ test_that("test that parameter checks are performed", { test_that("test that dxf files can be read", { - # test specific files - # FIXME: re-enable for commits - #skip("Currently not testing all continuous flow data files.") - # FIXME: run as one batch to make use of parallel processing + # check if tests are enabled + run_file_tests <- getOption("isoreader.run_file_tests") + if (!is.null(run_file_tests) && identical(run_file_tests, FALSE)) { + skip("Currently not testing all continuous flow data files.") + } + # test specific files iso_turn_reader_caching_off() expect_true(file.exists(file <- iso_get_reader_example("linearity_example.dxf"))) @@ -107,5 +109,23 @@ test_that("test that dxf files can be read", { expect_equal(nrow(problems(reread_dxf)), 0) + # test multiprocess and multisession + file_paths <- + file.path(test_folder, + c("dxf_example_H_01.dxf", "dxf_example_HO_01.dxf", "dxf_example_HO_02.dxf", "dxf_example_CNS_01.dxf", "dxf_example_N2_01.dxf")) + + expect_message(files <- iso_read_continuous_flow(file_paths, parallel = TRUE, parallel_plan = future::multisession), + "preparing to read 5 data files.*setting up.*parallel processes") + expect_equal(nrow(problems(files)), 0) + + # test multiprocess and multisession + file_paths <- + file.path(test_folder, + c("dxf_example_H_01.dxf", "dxf_example_HO_01.dxf", "dxf_example_HO_02.dxf", "dxf_example_CNS_01.dxf", "dxf_example_N2_01.dxf")) + + expect_message(files <- iso_read_continuous_flow(file_paths, parallel = TRUE, parallel_plan = future::multiprocess), + "preparing to read 5 data files.*setting up.*parallel processes") + expect_equal(nrow(problems(files)), 0) + }) diff --git a/tests/testthat/test-dual-inlet.R b/tests/testthat/test-dual-inlet.R index 35627937..2796969f 100644 --- a/tests/testthat/test-dual-inlet.R +++ b/tests/testthat/test-dual-inlet.R @@ -29,15 +29,16 @@ test_that("test that nu file processor works properly", { # actual files ======== test_that("test that did files can be read", { - # test specific files - # FIXME: re-enable for commits - #skip("Currently not testing all dual inlet data files.") - # FIXME: run as one batch to make use of parallel processing - iso_turn_reader_caching_off() + # check if tests are enabled + run_file_tests <- getOption("isoreader.run_file_tests") + if (!is.null(run_file_tests) && identical(run_file_tests, FALSE)) { + skip("Currently not testing all dual inlet data files.") + } - # .did files + # test specific files + iso_turn_reader_caching_off() expect_true(file.exists(file <- iso_get_reader_example("dual_inlet_example.did"))) expect_is(did <- iso_read_dual_inlet(file), "dual_inlet") diff --git a/tests/testthat/test-isoread.R b/tests/testthat/test-isoread.R index 16aece99..5e65ed21 100644 --- a/tests/testthat/test-isoread.R +++ b/tests/testthat/test-isoread.R @@ -94,10 +94,3 @@ test_that("test that file event expressions work", { set_finish_file_event_expr({}) }) -# implement: parallel processing test - -test_that("test that parallel reading works", { - - # try both multiprocess and multisession - -}) \ No newline at end of file diff --git a/tests/testthat/test-scan.R b/tests/testthat/test-scan.R index 7a73fc2f..8e795d1c 100644 --- a/tests/testthat/test-scan.R +++ b/tests/testthat/test-scan.R @@ -19,12 +19,14 @@ test_that("test that parameter checks are performed", { test_that("test that scn files can be read", { - # test specific files - # FIXME: re-enable for commits - #skip("Currently not testing all scan data files.") - # FIXME: run as one batch to make use of parallel processing + # check if tests are enabled + run_file_tests <- getOption("isoreader.run_file_tests") + if (!is.null(run_file_tests) && identical(run_file_tests, FALSE)) { + skip("Currently not testing all scan data files.") + } + # test specific files iso_turn_reader_caching_off() expect_true(file.exists(file <- iso_get_reader_example("peak_shape_scan_example.scn"))) From ad933430a82b3eab5d1913c73a1a8772a609b6bb Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 10:16:41 -0700 Subject: [PATCH 33/51] update names space for future::multisession changes --- NAMESPACE | 3 +-- R/package.R | 9 +++++---- R/utils.R | 13 +++++-------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 7d8da3df..ede87ddc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -182,10 +182,8 @@ importFrom(dplyr,vars) importFrom(feather,write_feather) importFrom(future,availableCores) importFrom(future,future) -importFrom(future,multiprocess) importFrom(future,plan) importFrom(future,resolved) -importFrom(future,sequential) importFrom(future,value) importFrom(glue,glue) importFrom(lazyeval,as.lazy) @@ -227,6 +225,7 @@ importFrom(rhdf5,h5readAttributes) importFrom(rlang,"!!!") importFrom(rlang,"!!") importFrom(rlang,":=") +importFrom(rlang,.data) importFrom(rlang,UQ) importFrom(rlang,enexpr) importFrom(rlang,enquo) diff --git a/R/package.R b/R/package.R index 420a5e1a..944a0af9 100644 --- a/R/package.R +++ b/R/package.R @@ -8,7 +8,7 @@ #' @importFrom tidyr gather spread nest unnest extract #' @importFrom glue glue #' @importFrom purrr map map_lgl map_chr map_df map_int map_dbl map2 map2_chr map2_lgl map2_dbl map2_int safely is_empty -#' @importFrom future plan multiprocess sequential future availableCores resolved value +#' @importFrom future plan future availableCores resolved value #' @importFrom tibble tribble deframe rownames_to_column #' @importFrom lubridate interval duration as_datetime #' @importFrom stringr str_c str_detect str_to_title str_replace str_replace_all str_replace_na str_match str_match_all str_interp str_subset str_extract fixed @@ -49,9 +49,10 @@ dplyr::filter #' @export dplyr::tibble -# quiets concerns of R CMD check about . that appears in pipelines -# and some very commonly used variable names used in NSE commands -utils::globalVariables(c(".", "file_id", "mass", "quiet")) +# quiets concerns of R CMD check about . in pipelineds +# and .data in tidyverse functions +utils::globalVariables(".") +#' @importFrom rlang .data # release questions release_questions <- function() { diff --git a/R/utils.R b/R/utils.R index dd9eb520..2cd1b633 100644 --- a/R/utils.R +++ b/R/utils.R @@ -113,9 +113,6 @@ monitor_parallel_logs <- function(processes) { # process parallel logs process_parallel_logs <- function(status) { - # global vars - X1 <- X2 <- X3 <- prefix <- NULL - # logs log <- get_temp("parallel_log_file") if (!is.null(log) && file.exists(log)) { @@ -146,13 +143,13 @@ process_parallel_logs <- function(status) { status$log_n <- status$log_n + nrow(logs) logs %>% mutate( - X2 = as.character(X2), + X2 = as.character(.data$X2), prefix = case_when( - X1 == "info" ~ sprintf("Info (process %s): ", X2), - X1 == "warning" ~ sprintf("Warning (process %s): ", X2), - TRUE ~ sprintf("Process %s: ", X2) + X1 == "info" ~ sprintf("Info (process %s): ", .data$X2), + X1 == "warning" ~ sprintf("Warning (process %s): ", .data$X2), + TRUE ~ sprintf("Process %s: ", .data$X2) )) %>% - with(purrr::walk2(X3, prefix, ~log_message(.x, prefix = .y))) + { purrr::walk2(.$X3, .$prefix, ~log_message(.x, prefix = .y)) } } } From 59b1706ddea1a25f3ffcb589b1f02738ae7330d0 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 11:15:54 -0700 Subject: [PATCH 34/51] complete scn file implementation #36 --- .gitignore | 3 ++ R/export.R | 21 ++++++++++---- R/isosave.R | 2 ++ R/zzz.R | 1 + tests/testthat/test-export.R | 35 +++++++++++++++++++++++ tests/testthat/test-scan.R | 2 +- vignettes/scan.Rmd | 55 ++++++++++-------------------------- 7 files changed, 73 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 48056316..385651c0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ .Rprofile # Example code in package build process *.xlsx +*.rds +*.rda +*.feather *-Ex.R # Output files from R CMD build /*.tar.gz diff --git a/R/export.R b/R/export.R index 627232e6..90f36705 100644 --- a/R/export.R +++ b/R/export.R @@ -60,11 +60,17 @@ iso_export_to_excel <- function(iso_files, filepath, add_excel_sheet(wb, "file info", file_info) } if (include_method_info) { - standards <- iso_get_standards_info(export_iso_files, quiet = TRUE) resistors <- iso_get_resistors_info (export_iso_files, quiet = TRUE) - add_excel_sheet(wb, "method info", standards, resistors) + if (iso_is_scan(export_iso_files)) { + # scan + add_excel_sheet(wb, "method info", resistors) + } else { + # cf and dual inlet + standards <- iso_get_standards_info(export_iso_files, quiet = TRUE) + add_excel_sheet(wb, "method info", standards, resistors) + } } - if (include_vendor_data_table) { + if (include_vendor_data_table && !iso_is_scan(export_iso_files)) { vendor_data <- iso_get_vendor_data_table(export_iso_files, with_explicit_units = with_explicit_units, quiet = TRUE) %>% iso_strip_units() add_excel_sheet(wb, "vendor data table", vendor_data) @@ -188,11 +194,12 @@ iso_export_to_feather <- function(iso_files, filepath_prefix, filepaths[['file_info']]) if (include_method_info) { - write_feather(iso_get_standards_info(iso_files, quiet = TRUE), filepaths[['method_info_standards']]) + if (!iso_is_scan(iso_files)) + write_feather(iso_get_standards_info(iso_files, quiet = TRUE), filepaths[['method_info_standards']]) write_feather(iso_get_resistors_info (iso_files, quiet = TRUE), filepaths[['method_info_resistors']]) } - if (include_vendor_data_table) + if (include_vendor_data_table && !iso_is_scan(iso_files)) write_feather( iso_get_vendor_data_table(iso_files, with_explicit_units = with_explicit_units, quiet = TRUE) %>% iso_strip_units(), filepaths[['vendor_data_table']]) @@ -224,6 +231,8 @@ get_excel_export_filepath <- function(iso_files, filepath) { ext <- ".cf.xlsx" else if (iso_is_dual_inlet(iso_files)) ext <- ".di.xlsx" + else if (iso_is_scan(iso_files)) + ext <- ".scan.xlsx" else stop("Excel export of this type of iso_files not yet supported", call. = FALSE) return(get_export_filepath(filepath, ext)) @@ -235,6 +244,8 @@ get_feather_export_filepaths <- function(iso_files, filepath) { ext <- ".cf.feather" else if (iso_is_dual_inlet(iso_files)) ext <- ".di.feather" + else if (iso_is_scan(iso_files)) + ext <- ".scan.feather" else stop("Feather export of this type of iso_files not yet supported", call. = FALSE) diff --git a/R/isosave.R b/R/isosave.R index 8b288f13..be5ff553 100644 --- a/R/isosave.R +++ b/R/isosave.R @@ -34,6 +34,8 @@ get_rds_export_filepath <- function(iso_files, filepath) { ext <- ".cf.rds" else if (iso_is_dual_inlet(iso_files)) ext <- ".di.rds" + else if (iso_is_scan(iso_files)) + ext <- ".scan.rds" else stop("R data storage export of this type of iso_files not supported", call. = FALSE) return(get_export_filepath(filepath, ext)) diff --git a/R/zzz.R b/R/zzz.R index bdf92a74..db1e9156 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -37,6 +37,7 @@ initialize_options <- function() { iso_register_continuous_flow_file_reader(".cf.rda", "iso_read_rda", "R Data Archive (deprecated)", "isoreader", cacheable = FALSE, env = "isoreader") iso_register_continuous_flow_file_reader(".cf.rds", "iso_read_rds", "R Data Storage", "isoreader", cacheable = FALSE, env = "isoreader") iso_register_scan_file_reader(".scn", "iso_read_scn", "Scan file format", "Isodat", env = "isoreader") + iso_register_scan_file_reader(".scan.rds", "iso_read_rds", "R Data Storage", "isoreader", cacheable = FALSE, env = "isoreader") invisible(options()[names(default_options)]) } diff --git a/tests/testthat/test-export.R b/tests/testthat/test-export.R index 91202331..0b6676a8 100644 --- a/tests/testthat/test-export.R +++ b/tests/testthat/test-export.R @@ -2,6 +2,7 @@ context("Export functions") di_example <- iso_read_dual_inlet(iso_get_reader_example("dual_inlet_example.did")) cf_example <- iso_read_continuous_flow(iso_get_reader_example("continuous_flow_example.cf")) +scan_example <- iso_read_scan(iso_get_reader_example("peak_shape_scan_example.scn")) test_that("test that export to rda works properly", { expect_error(iso_save(42), "can only export iso files") @@ -167,6 +168,28 @@ test_that("test that export to Excel works properly", { dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) expect_true(file.remove(str_c(filepath, ".cf.xlsx"))) + # export real data files - scan + expect_message(iso_export_to_excel(scan_example, filepath, quiet = FALSE), "exporting data .* into Excel") + expect_true(file.exists(str_c(filepath, ".scan.xlsx"))) + expect_equal(iso_get_raw_data(scan_example) %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif), + read_excel(str_c(filepath, ".scan.xlsx"), "raw data") %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) + expect_equal(iso_get_file_info(scan_example) %>% collapse_list_columns() %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% + dplyr::select_if(function(x) !is.na(x)) %>% + select(-file_datetime), # never exactly identical, + read_excel(str_c(filepath, ".scan.xlsx"), "file info") %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% + dplyr::select_if(function(x) !is.na(x)) %>% + select(-file_datetime)) + expect_equal(iso_get_resistors_info(scan_example) %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif), + read_excel(str_c(filepath, ".scan.xlsx"), "method info") %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) + expect_true(file.remove(str_c(filepath, ".scan.xlsx"))) + + }) @@ -248,4 +271,16 @@ test_that("test that export to Feather works properly", { read_feather(str_c(filepath, "_vendor_data_table.cf.feather"))) expect_true(all(file.remove(list.files(dirname(filepath), pattern = "\\.cf\\.feather$", full.names = TRUE)))) + # export real data files - scan + expect_message(iso_export_to_feather(scan_example, filepath, quiet = FALSE), "exporting data .* into .scan.feather") + expect_true(file.exists(str_c(filepath, "_raw_data.scan.feather"))) + expect_true(file.exists(str_c(filepath, "_file_info.scan.feather"))) + expect_true(file.exists(str_c(filepath, "_method_info-resistors.scan.feather"))) + # note for comparisons: rounding is NOT necessary because storage is equivalent to values in R + expect_equal(iso_get_raw_data(scan_example), read_feather(str_c(filepath, "_raw_data.scan.feather"))) + expect_equal(iso_get_file_info(scan_example) %>% collapse_list_columns(), read_feather(str_c(filepath, "_file_info.scan.feather"))) + expect_equal(iso_get_resistors_info (scan_example), read_feather(str_c(filepath, "_method_info-resistors.scan.feather"))) + expect_true(all(file.remove(list.files(dirname(filepath), pattern = "\\.scan\\.feather$", full.names = TRUE)))) + + }) diff --git a/tests/testthat/test-scan.R b/tests/testthat/test-scan.R index 8e795d1c..e58a04f5 100644 --- a/tests/testthat/test-scan.R +++ b/tests/testthat/test-scan.R @@ -3,7 +3,7 @@ context("Scan") test_that("test that supported scan files are correct", { initialize_options() expect_is(exts <- get_supported_scan_files(), "data.frame") - expect_equal(exts$extension, c(".scn")) + expect_equal(exts$extension, c(".scn", ".scan.rds")) expect_true(all(exts$func %>% sapply(class) == "character")) expect_true(all(exts$func %>% map_lgl(exists, mode = "function", where = asNamespace("isoreader")))) }) diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index 040cdbbe..1933d864 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -45,8 +45,7 @@ iso_files <- iso_get_reader_example("peak_shape_scan_example.scn"), iso_get_reader_example("background_scan_example.scn"), iso_get_reader_example("full_scan_example.scn"), - iso_get_reader_example("time_scan_example.scn"), - read_cache = FALSE + iso_get_reader_example("time_scan_example.scn") ) ``` @@ -152,12 +151,11 @@ iso_files %>% Saving entire collections of isofiles for retrieval at a later point is easily done using the `iso_save` function which stores collections or individual isoreader file objects in the efficient R data storage format `.rds` (if not specified, the extension `.scan.rds` will be automatically appended). These saved collections can be convientiently read back using the same `iso_read_scan` command used for raw data files. ```{r} -# FIXME: not implemented yet -# # export to R data archive -# iso_files %>% iso_save("iso_files_export.di.rds") -# -# # read back the exported R data storage -# iso_read_dual_inlet("iso_files_export.di.rds") +# export to R data archive +iso_files %>% iso_save("iso_files_export.scan.rds") + +# read back the exported R data storage +iso_read_scan("iso_files_export.scan.rds") ``` # Data Export @@ -165,42 +163,19 @@ Saving entire collections of isofiles for retrieval at a later point is easily d At the moment, isoreader supports export of all data to Excel and the [Feather file format](https://blog.rstudio.com/2016/03/29/feather/) (a Python/R cross-over format). Note that both export methods have similar syntax and append the appropriate file extension for each type of export file (`.scan.xlsx` and `.scan.feather`, respectively). ```{r} -# FIXME: not implemented yet -# # export to excel -# iso_files %>% iso_export_to_excel("iso_files_export") -# -# # data sheets available in the exported data file: -# readxl::excel_sheets("iso_files_export.di.xlsx") -``` +# export to excel +iso_files %>% iso_export_to_excel("iso_files_export") -```{r} -# FIXME: not implemented yet -# # export to feather -# iso_files %>% iso_export_to_feather("iso_files_export") -# -# # exported feather files -# list.files(pattern = ".di.feather") +# data sheets available in the exported data file: +readxl::excel_sheets("iso_files_export.scan.xlsx") ``` -# Visualization +```{r} +# export to feather +iso_files %>% iso_export_to_feather("iso_files_export") -```{r "scans", fig.width=10, fig.height=10, eval=FALSE} -# FIXME: should go into isoprocessor vignette once the functions there exist -iso_files %>% - iso_get_raw_data(include_file_info = type) %>% - dplyr::mutate(panel = sprintf("%s [%s]", type, units)) %>% - tidyr::pivot_longer( - matches("vC?\\d+"), - names_to = "mass", - values_to = "value", - values_drop_na = TRUE - ) %>% - ggplot2::ggplot() + - ggplot2::aes(x, value, color = mass) + - ggplot2::geom_line() + - ggplot2::facet_wrap(~ panel + file_id, scales = "free") +# exported feather files +list.files(pattern = ".scan.feather") ``` - - From 6f936472147dbae171dfecca8fd2bfd3c9164d81 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 21:33:26 -0700 Subject: [PATCH 35/51] update vignette headers --- vignettes/continuous_flow.Rmd | 13 +++++- vignettes/development.Rmd | 12 +++++- vignettes/dual_inlet.Rmd | 14 +++++-- vignettes/quick_start.Rmd | 76 +++++++++++++++++++++++++++++++++++ vignettes/scan.Rmd | 16 ++++++-- 5 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 vignettes/quick_start.Rmd diff --git a/vignettes/continuous_flow.Rmd b/vignettes/continuous_flow.Rmd index 80815806..31e11b2c 100644 --- a/vignettes/continuous_flow.Rmd +++ b/vignettes/continuous_flow.Rmd @@ -1,7 +1,17 @@ --- title: "Continuous Flow Examples" date: "`r Sys.Date()`" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + html_document: + code_folding: show + df_print: paged + number_sections: yes + toc: yes + toc_depth: 3 + toc_float: yes +editor_options: + chunk_output_type: console vignette: > %\VignetteIndexEntry{Continuous Flow Examples} %\VignetteEngine{knitr::rmarkdown} @@ -24,7 +34,6 @@ Isoreader supports several continuous flow IRMS data formats. This vignette show library(isoreader) ``` - # Reading files Reading continuous flow files is as simple as passing one or multiple file or folder paths to the `iso_read_continuous_flow()` function. If folders are provided, any files that have a recognized continuous flow file extensions within those folders will be processed (e.g. all `.dxf`, `.cf` and `.iarc`). Here we read several files that are bundled with the package as examples (and whose paths can be retrieved using the `iso_get_reader_example()` function). Note that some of the files (.cf, .dxf) are individual analysis files whereas others (.iarc) are collections of several files. diff --git a/vignettes/development.Rmd b/vignettes/development.Rmd index e2902614..4046d291 100644 --- a/vignettes/development.Rmd +++ b/vignettes/development.Rmd @@ -1,7 +1,17 @@ --- title: "Development features of isoreader" date: "`r Sys.Date()`" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + html_document: + code_folding: show + df_print: paged + number_sections: yes + toc: yes + toc_depth: 3 + toc_float: yes +editor_options: + chunk_output_type: console vignette: > %\VignetteIndexEntry{Development} %\VignetteEngine{knitr::rmarkdown} diff --git a/vignettes/dual_inlet.Rmd b/vignettes/dual_inlet.Rmd index c313b993..eceb51c8 100644 --- a/vignettes/dual_inlet.Rmd +++ b/vignettes/dual_inlet.Rmd @@ -1,7 +1,17 @@ --- title: "Dual Inlet Examples" date: "`r Sys.Date()`" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + html_document: + code_folding: show + df_print: paged + number_sections: yes + toc: yes + toc_depth: 3 + toc_float: yes +editor_options: + chunk_output_type: console vignette: > %\VignetteIndexEntry{Dual Inlet Examples} %\VignetteEngine{knitr::rmarkdown} @@ -20,8 +30,6 @@ knitr::opts_chunk$set( Isoreader supports several dual inlet IRMS data formats. This vignette shows some of the functionality for dual inlet data files. For additional information on operations more generally (caching, combining read files, data export, etc.), please consult the [operations vignette](http://isoreader.isoverse.org/articles/operations.html). For details on downstream data processing and visualization, see the [isoprocessor package](https://isoprocessor.isoverse.org). -Note: this vignette is still a work in progress. - ```{r, message=FALSE} # load isoreader package diff --git a/vignettes/quick_start.Rmd b/vignettes/quick_start.Rmd new file mode 100644 index 00000000..c25dea78 --- /dev/null +++ b/vignettes/quick_start.Rmd @@ -0,0 +1,76 @@ +--- +title: "Quick Start Guide" +date: "`r Sys.Date()`" +output: + rmarkdown::html_vignette: + html_document: + code_folding: show + df_print: paged + number_sections: yes + toc: yes + toc_depth: 3 + toc_float: yes +editor_options: + chunk_output_type: console +vignette: > + %\VignetteIndexEntry{Quick Start Guide} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + + +```{r setup, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +# Introduction + +Isoreader supports various dual inlet, continuous flow, and scan file formats. This vignette shows how to get started reading these raw IRMS data files and exporting the information to Excel. For more details on isoreader functionality and each file type, please read the **Full Examples** vignettes. For more information on downstream processing with isoverse, check out the [isoprocessor](https://isoprocessor.isoverse.org) package. + +```{r, message=FALSE} +# load isoreader package +library(isoreader) +``` + +# Data files + +For demonstration purposes, this vignette simply reads all supported dual inlet, continuous flow, and scan files that are bundled with the isoreader package. + +```{r} +# all available examples +iso_get_reader_examples() %>% rmarkdown::paged_table() +``` + +# Dual Inlet Files + +```{r} +# read all available examples +di_files <- iso_read_dual_inlet(iso_get_reader_examples_folder()) + +# export to excel +iso_export_to_excel(di_files, filepath = "di_export") +``` + +# Continuous Flow Files + +```{r} +# read all available examples +cf_files <- iso_read_continuous_flow(iso_get_reader_examples_folder()) + +# export to excel +iso_export_to_excel(cf_files, filepath = "cf_export") +``` + +# Scan Files + +```{r} +# read all available examples +scan_files <- iso_read_scan(iso_get_reader_examples_folder()) + +# export to excel +iso_export_to_excel(scan_files, filepath = "scan_export") +``` + diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index 1933d864..4d4ac667 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -1,7 +1,17 @@ --- title: "Scan Examples" date: "`r Sys.Date()`" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + html_document: + code_folding: show + df_print: paged + number_sections: yes + toc: yes + toc_depth: 3 + toc_float: yes +editor_options: + chunk_output_type: console vignette: > %\VignetteIndexEntry{Scan Examples} %\VignetteEngine{knitr::rmarkdown} @@ -10,10 +20,10 @@ vignette: > ```{r setup, include = FALSE} +# global knitting options for code rendering knitr::opts_chunk$set( collapse = TRUE, - comment = "#>" -) + comment = "#>") ``` # Introduction From 1e2edcc217168c9edd710d9e4119253383b3649f Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 21:33:34 -0700 Subject: [PATCH 36/51] complete operations vignetted --- vignettes/operations.Rmd | 140 +++++++++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 14 deletions(-) diff --git a/vignettes/operations.Rmd b/vignettes/operations.Rmd index 82e9fcd1..b7419029 100644 --- a/vignettes/operations.Rmd +++ b/vignettes/operations.Rmd @@ -1,7 +1,17 @@ --- title: "Operations" date: "`r Sys.Date()`" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + html_document: + code_folding: show + df_print: paged + number_sections: yes + toc: yes + toc_depth: 3 + toc_float: yes +editor_options: + chunk_output_type: console vignette: > %\VignetteIndexEntry{Operations} %\VignetteEngine{knitr::rmarkdown} @@ -18,33 +28,93 @@ knitr::opts_chunk$set( # Introduction -Coming soon... - Isoreader provides a number of general purpose operations that work on all supported IRMS data formats such as caching of files, parallel processing and catching read errors. This vignette demonstrates some of these general operations. +```{r, message=FALSE} +# load isoreader package +library(isoreader) +``` + # Supported file types ```{r} - +# list all suported file types +iso_get_supported_file_types() %>% + dplyr::select(extension, software, description, type) %>% + knitr::kable() ``` +# Messages -# Caching +By default, isoreader is quite verbose to let the user know what is happening. However, most functions can be silenced by adding the parameter `quiet = TRUE` to the function call. This can also be done globally using `iso_turn_info_message_off()` ```{r} -#iso_cleanup_reader_cache() +# read a file in the default verbose mode +iso_get_reader_example("dual_inlet_example.did") %>% + iso_read_dual_inlet() %>% + iso_select_file_info(file_datetime, `Identifier 1`) %>% + iso_get_file_info() %>% + knitr::kable() + +# read the same file but make the read process quiet +iso_get_reader_example("dual_inlet_example.did") %>% + iso_read_dual_inlet(quiet = TRUE) %>% + iso_select_file_info(file_datetime, `Identifier 1`) %>% + iso_get_file_info() %>% + knitr::kable() + +# read the same file but turn all isoreader messages off +iso_turn_info_messages_off() +iso_get_reader_example("dual_inlet_example.did") %>% + iso_read_dual_inlet(quiet = TRUE) %>% + iso_select_file_info(file_datetime, `Identifier 1`) %>% + iso_get_file_info() %>% + knitr::kable() + +# turn message back on +iso_turn_info_messages_on() ``` +# Caching -# Parallel processing +By default, isoreader caches files as R objects to make access faster in the future. This feature can be turned off if you want to force a fresh read from the source file. Alternatively, you can clear the entire isoreader cache in your working directory to clean up previous file reads. -Whether parallel processing yields signifcant improvemens in read speeds depends on the number of available processors, file types and operating system. In theory, parallel processing always reduces computation time but in practice this is offset by various factors including the size of the data that needs to be sent back and forth between the processors, file system read/write speed, and the spin-up time for new processes. Generally speaking, parallel processing can provide significant improvements in speed with larger number of files (~10+) and more complex read operations (e.g. continuous flow vs. dual inlet). Reading from cache is so efficient that there are rarely gains from parallel processing and it is usually faster NOT to read in parallel. +```{r} +# cleanup reader cache +iso_cleanup_reader_cache(all = TRUE) +# read a new file (notice the time elapsed) +cf_file <- iso_get_reader_example("continuous_flow_example.dxf") %>% + iso_read_continuous_flow() -# Messages and default parameters +# re-read the same file much faster (it will be read from cache) +cf_file <- iso_get_reader_example("continuous_flow_example.dxf") %>% + iso_read_continuous_flow() -```{r} +# turn reader caching off +iso_turn_reader_caching_off() +# re-read the same file (it will NOT be read from cache) +cf_file <- iso_get_reader_example("continuous_flow_example.dxf") %>% + iso_read_continuous_flow() + +# turn reader caching back on +iso_turn_reader_caching_on() +``` + +# Parallel processing + +Isoreader supports parallel processing of data files based on the number of processors available in a computer simply by setting the `parallel = TRUE` flag in any file read operation. This makes it possible to read large quantities of data files much more quickly on a multi-core system (i.e. most modern laptops). However, whether parallel processing yields signifcant improvemens in read speeds depends on the number of available processors, file types and operating system. In theory, parallel processing always reduces computation time but in practice this is offset by various factors including the size of the data that needs to be sent back and forth between the processors, file system read/write speed, and the spin-up time for new processes. Generally speaking, parallel processing can provide significant improvements in speed with larger number of files (~10+) and more complex read operations (e.g. continuous flow > dual inlet > scan file). Reading from cache is so efficient that there are rarely gains from parallel processing and it is usually faster NOT to read in parallel once a set of files is already cached. + +```{r} +# read 3 files in parallel (note that this is usually not a large enough file number to be worth it) +di_files <- + iso_read_dual_inlet( + iso_get_reader_example("dual_inlet_example.did"), + iso_get_reader_example("dual_inlet_example2.did"), + iso_get_reader_example("dual_inlet_example.caf"), + parallel = TRUE + ) ``` @@ -53,25 +123,67 @@ Whether parallel processing yields signifcant improvemens in read speeds depends All isoreader objects are lists that can be combined or subset to work with only specific files or create a larger collection. ```{r} +# all 3 di_files read above +di_files -``` +# only one of the files (by index) +di_files[[2]] -# Dealing with file read problems +# only one of the files (by file_id) +di_files$dual_inlet_example2.did +# a subset of the files (by index) +di_files[c(1,3)] +# a subset of the files (by file_id) +di_files[c("dual_inlet_example.did", "dual_inlet_example.caf")] -# Removing erroneous files +# same result using iso_filter_files (more flexible + verbose output) +di_files %>% iso_filter_files( + file_id %in% c("dual_inlet_example.did", "dual_inlet_example.caf") +) -Removing files with problems: `iso_filter_files_with_problems` +# recombining subset files +c( + di_files[3], + di_files[1] +) +``` + +# Dealing with file read problems + +Isoreader is designed to catch problems during file reading without crashing the read pipeline. It keeps track of all problems encountered along the way to make it easy to see what went wrong and remove erroneous files. Most times, files that were only partly saved because of an interrupted instrument analysis will have errors. If you encounter a file that should have intact data in it but has an error in isoreader, please file a bug report and submit your file at https://github.com/isoverse/isoreader/issues ```{r} +# read two files, one of which is erroneous +iso_files <- + iso_read_continuous_flow( + iso_get_reader_example("continuous_flow_example.dxf"), + system.file("errdata", "cf_without_data.dxf", package = "isoreader") + ) +# retrieve problem summary +iso_files %>% iso_get_problems_summary() %>% knitr::kable() + +# retrieve problem details +iso_files %>% iso_get_problems() %>% knitr::kable() + +# filter out erroneous files +iso_files <- iso_files %>% iso_filter_files_with_problems() ``` # Re-reading files +If a file has changed (e.g. is edited through the vendor software) and the changes should be loaded in isoreader, it is easy to re-read an entire collection of files using the `iso_reread_files()` function. This function will read unchanged files from cache and re-read all changed files anew from their source. It will throw a warning for files that are no longer accessible at their original location. If the location for all files has changed, it can be easily adjusted by modifiny the `file_root` parameter in files. + ```{r} +# re-read the 3 dual inlet files from their original location if any have changed +di_files %>% iso_reread_files() +# update the file_root for the files before re-read (in this case to a location +# that does not hold these files and hence will lead to a warning) +di_files %>% iso_mutate_file_info(file_root = ".") %>% + iso_reread_files() ``` From 6abbb5635877f9c1025482ce9b7147b42ad8b41b Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 21:33:47 -0700 Subject: [PATCH 37/51] add test file --- tests/testthat/test-dual-inlet.R | 4 ++++ tests/testthat/test_data/did_ultra_example.did | Bin 0 -> 135808 bytes 2 files changed, 4 insertions(+) create mode 100644 tests/testthat/test_data/did_ultra_example.did diff --git a/tests/testthat/test-dual-inlet.R b/tests/testthat/test-dual-inlet.R index 2796969f..1ee42167 100644 --- a/tests/testthat/test-dual-inlet.R +++ b/tests/testthat/test-dual-inlet.R @@ -55,6 +55,10 @@ test_that("test that did files can be read", { expect_true(file.exists(file <- file.path("test_data", "did_example_unicode.did"))) expect_is(did <- iso_read_dual_inlet(file), "dual_inlet") expect_equal(nrow(problems(did)), 0) + + expect_true(file.exists(file <- file.path("test_data", "did_ultra_example.did"))) + expect_is(did <- iso_read_dual_inlet(file), "dual_inlet") + expect_equal(nrow(problems(did)), 0) # minimal files expect_true(dir.exists(files <- file.path("test_data", "minimal_files"))) diff --git a/tests/testthat/test_data/did_ultra_example.did b/tests/testthat/test_data/did_ultra_example.did new file mode 100644 index 0000000000000000000000000000000000000000..5ad5c177045d2c0a3e584e477ba066076b92a5a5 GIT binary patch literal 135808 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 H&-nlUuhPnp literal 0 HcmV?d00001 From b6504460d5bfdc4d53841477fa1d1b832b0df4b8 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 21:34:13 -0700 Subject: [PATCH 38/51] add erroneous file for demonstration purposes --- inst/errdata/cf_without_data.dxf | Bin 0 -> 119237 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 inst/errdata/cf_without_data.dxf diff --git a/inst/errdata/cf_without_data.dxf b/inst/errdata/cf_without_data.dxf new file mode 100644 index 0000000000000000000000000000000000000000..d4276b1942854bfbe7ba57fbcca9964fd76a3831 GIT binary patch literal 119237 zcmeHQ349bq*6)xILb&fk#6dv3fN+INCY4N1Oduqj0)`=(Bm>DzoPDK(KS$@U^!s?C~bbDjOMUs$9hN!4eoqb*W`R3tg2Txq71C|RX4y2zhGB`KV) zzIYpuASFw&(m-h_T{-lNmj=_ng|1v_9$kP?DA}aBl-fb}94S*8D~+dnKe`6dWsy=T ztcY?EF-pm=KZV3lNFOPWQsyJ4CdrbWV%a1U#kW!1JoviTWFDnNmHm;D5#4EsMQ^3jEP_==VG}5( zi(-`$ZcZw}6doH8l^E*yeJEFy8-$FOEQ!`KYnmg^HrGQ(4+Eg&aA9;2O~VIJRmI?` zAsC?#Rc?QR(m?7cSu9RRncZP`&MHkVa?VL8a^}tiU=Pf;qV#3FJa)oz7GZ0WsWFF_ z&%=DW;{rF}*b?|rXJb% zwv#Mr+3DH2E_+E?hOM;JT43`j7{Ij7rs_aNWE05+3xR_ZIWNzmkUsQ-3j@%z)m=ix z2Z0;}p^WY(BDqPLEcKM8a+EC|mICvjDAnWuy;-@ZxH491Z1OzV2O(wemwD!zhO2H}VP%fu<&rEjM!u}`` ztQ2dhCak6@)VeUF1&0wKHK)0Y%bIIbphO^(8g3F3UXvKeakU&yB@eeqqcQAyo)9F; z8YZuvtAoeMT6> zmH0q;wmM3gxjBdqm^M*{T7C_B3P$jj12Mx)@;@NbJ8s-@X3&6yK!o-19rJh^1tV{9 zM-glD{x?d)k1(2rJZp)OKO`L((TM5f^8<`q7y-+WueNX9u+o5~1Ft3-oOhKGMkyKL z7C5AC_yvHc1INZQWV7%2C1%bP>dk!yGo?>|vK&(pc(EW^=}cSH2v zc~t5`P|x@6+;C9A(t$^d;By?pEp!Zy?|=ZDBG3H8Oh879Q$oN;su0{0883ggfL{sY zDDS(8KJ^B|kK-7lg=55INF-Eid`7ywrs&HFK5?3 z&bqj}Sr+xh14;fMzNY)~IgaPcI(TRp|5imGu3Y2;2Oq^@zJ*>sa6U@RAYZW50s^Ne z=%K>HYJq7`So(0q8eTw}68xaywZt_rEFHKu3!EBt7BsXrcm{!_1IzXh`9Q#9<>DOi z6okUHjjx_#D4g7QGKr6{K5wEKJtT1-#@)Jm^zodLpYkFTeVYC+V8vEXg3^aA6c;dR0|Fhm^~%PlAXzQIFlk8Kcm zI&clx76M>j+xS}Lskip3g`-kt-dZ4J^sJF)E;t(w!7s_-vkP5z$4m%Yv1;5@PIC?2 z@s2x|jB&?8H|~w(dn@_gTE4fD?``FKJNe#TzITxCUNp#qL!Ce4#j^W5o{jtB+Ryg1 zvY&C^{SQa$TPmU%!qqNU{X)+un7_lCHx+BV-8RSKEK-Y0JUs{shC(4~V=i6bF*|B3 zMI)*rNhaX|U-(bAJ1BlRg}?!aQRPxOo;`2!s|YJE=RtBzC90B61d2DMgOkJLSG>uk zN^#lqM5)RZs1%52EVN-bo{R1Ok=PN|%O@R=kc=^ZsgmeN5;Q2aVC=lcag91^^rM_jTwcI7h zpra*H(ekU?5_kZtftN}cLiqT;@qtluQO^*wfV5g1Vu)FMg@eQ?hM2_= zvjpkAJ)#ime^g=xuV_rO2QtJgzU{15^w77IrzMmy#4G{&eGL&rKR!l;m=|cIGsG+) zoIK;wppP!@r6rx9{2MBq&wb8m5j|)QN(qDXi<-;-hL{DEqmcqP?v$CiR(%^{7Hnzs z6T*a9F`63@U4XPSq7G+Z?wdQ&gUj&84tAhT5^bA47A5@3IL&IuQ`+-ZW1IvKR zumOaFwT*vXmuHBsTac@Cm0xg&a0Cu>9>t~ku8DJRlnimNEe|F;8RZdG0N(kR zl}sQRNIB&s9G;bT+!f)-q;iB`c=8a?Q%Ev^pReAXV|#Rwph=-U?vO%4?{oe$83wTIV&S7no=1T~9Zp7qY=V!}J!qHszZI&vGvpKyISM2r^~{ zkt$F=!9&ALz{Pi#1|RZ=)4($bJf4L9g5XN+pdJix)dt_7IqJhWur>tIhu{Yd534nW zgF@7SvAk}nwdPLv1`n-0wn5vY@2Eekm z@dKBt-deL3rb@~ANbO^`Yi{}jZ7+1m2e60aL+!AB?(YoA$NyUL;a>xiR6Bi=bCrTz zNeBd)tJSbwMZJ!eU)`)0)qn(QpoIm)$gqqBGvYcZ6)`e3Vy4dVKGl)9!bT)p|pRLq%N; z`WR9WzXNvo+{dtt<<+6mW#>(EUEh#`pzTqwh8uT$ZH4UiYH9iCvilZ9E`i=NsMH~h zuhDaSfD>Kh1E+yu5J)@;{RJ7>J!_d-tHzXDsPM2_U>X#bK3s!JHVA&u@LJ*;7?uuP zgGeEWs=-5RgJ%#(I9`ei+b%u_y}67Qvn}M-&*2R6NV0aRFw6@DR4+V)S@~s9aze> zI9Rh5{d{XlHBpOd7<%x*Skg~Yr$Q}i`qmPknlKc6c<dWyQdh_?F;3u8(?n&vuxS<@);EWa>$CD>`x58?Jtxt1Cej2918o)EzUu0R zbY7Gt%Q`2~T4seUKQ84}t;i!bauYe0;>TVQ1XOr{31 zU;H2Uv}r3h?u~pYuJtV@Pr8co1nJ1QPuciAI5dCLtVLv?j$xqy-)Q;A*5ATob z6fvGDDw0Zm?28o3*_nIeP)0aZg+e+I3+0!q$&X|>t*nh?i7zV3a2DBeXBF8d*z5&` zWu+b(b6@}pfN;3q{ogGsf84j;jQ47qBG4IL*=2O#SwXzZWt|6Kr6w9k7fF^hN13g_ zWi7Ki9pfDKvJ`8n#p%eW!?VheO)q3|Q5v1z>?HPZ@{<-#QihaGm8>k67SIt&U>-Y# zJ0z1dj{b3WG^K2z9Y=`)RDd!a%oc&7S_-WWhposDhpTxpK{q%wabQL+nem(CwdyI} zDP#en)J!P)hw@MfJ=+vGJS2(~kW-kr*UVAXqMD5+g4m@3?KTx86tET6xQ3U&7ZI=u zF7pEXSJ7oO8uiXvB8bE^=oYATBrHG*LDNK@?tjGYf!OoH=Igc=YUV{WHq4@bG8o4Y``{1 zD8UqCp?6<6^=)*5BYNEoyUXQtsYub@Lcm@019)#Qi<5Rs$p>UAkqKXljB6Dis3_!@ z#~VS8Od2uDoU_UjZKb)M5(7L^@FdDNplG*j{J3S`kSbyoyVVX0B|lr8c+Tpj8&S>VRORN zDVZP7gxSD*9LlCrIfp&_XKLB!?@jm8gT=80EeHJ17+cGuda?lKI84B zv#dq-Qa&|OrnbIQ3Z>;hF0R!W6$Uh4eS&&1jx8e~O9g*pLm7-|nRAf-VT%MEgUtagJ0 zHtGgf*9IAOTqbx_cw@8lXXz*DJMwz9(aIL=l5%qtKIUm;vCJybY&Q)pG^CeESfWDl zy>BR%;Q9tMwu!MW2-~ZvfMY@Y`NbR(U#%*1C zh$i+f{1J%;PCGt3b+9g?@k2j7L>tIy{6{4k72eJ`1yp}poW}M4JwzMCWB(C}CU-UZ zh!*QJHqdxR?vF?`J`m{Tw81_^8~leQTCF&3h!4?*{9%bEIBf}Yyeu`r?LH^WKRugJU&BUTW z&4frSNuJe&KnZ=<>yap=oK9XkO|(15x~z`Ue49&ItmIg<*Iuj?>(_Q#8pTSti*{@k zN--FGS}7sI-^4?FJ5!VdHIy8E5Rabau9e>Pi(2H$KfFWcDl{wVFIV2Zh0IlaiPc}O zyn_gttN4`h&lPQ;ZWQJpN+EL<-va)*qJHa^EC136nX8yg*I%xDsva^|F&nMFT=`r# zWUgX5SAV(k$zsS{hoTDWvAqsOFVQ-5FA)Xd1-&a~Jd^BA3hn0@nU0?>6#$b+EMxER& z>I7Z%44D;S(JXK%@>XzAo*nJQAcfUN+MZ$|G0SwXERFZ-ErlE{N0vL~(2C;#>T-wD zcG(!d-4bOF6-UVr;}FVEWK`vq>9jleCYLKvglH6@lzx~1_b2_?7}3K=#n7`!%A|fD zvPxka002J~gA_eo&gf4nfA4jE$MXi>xBJ8GFAY(_?H2&cMZdT#XXM@>VR}i9N2M-22v9kw5Ue@dHOz8hD(NP}M zm)$Q?-}sEUM{e~9eFQxe{2@(9ke+&y+;!z5Zp+))IJmuciu;a5!e0X>Pxs~4Pi;a4LJp=!U?QA0aw{I6Xxn5-J;D7lJHVOV8?O?p} zmhNQF!av3A`8V66JV*7X!E_2k&Os zPFa25ZZ;PAeYu;hg@4)}b{77R?qS*Rm-ezJ;6HgU`xX9M_p&Vbx8BFrz`uAO`vv}$ z``8%x_t?)KhyVQj>}UADx1VLgKem!R2LHvC>?intQOQQbKlK266#lCZuru&Kdw`9C z|8)o1YWQzC$WFt*<;yGs{zWgdN8rEzW%eWdFFnN4;s2LIY!&?9J;Z*1|G>lSTKF$I z%vQqx7%q~B9=`92IrLcBQ-@D|S zXH(hK7dF4t{oyn=FZ#&)uPhqL4kw>DbNcpcS;r;)H?O%Ro%MJo|GBDLGT6Td$Lw6Y za1`6uX3wIi`$n^tUEf|1mYK;+W2W9T`rS;nqpJCpU+0ZsmB%Y@Kk>~Nc1hp2?i+qf z7F#=QS;pt>ve_GHBX^#DGMl~m+LkZv31iu%>#I+)_s6n7|Ki$LK6e~j`BB#5t2>Nm zkM3MQJ!RW?)^~Dp_M4L@u#F|ze|bA>B8w~hOW2qvC$eP+pZg_w+$46zWv2VKH@J?? z?SAT$4I8gxpWidRYj4|RR&?3Q+?dW&nCa)IKic`)6t>$kVduZ@oXV!p?bZ8^)a%*d z6=NnJZj{3gE?+b6i32(8a9Z-z7AezM>b>7RvHZzt?6K~PUMOo}WlueIbMHrHSy@Ss z6AyL&z{++M-}}T*Nz>Uw7k}~jDC$fdF<$0bNblc$zyNcbp7FqG#jh3ybwNrhmCEX@oh3?rMFHovnGKZPnuD zGuWr;($2zpGl(M;*Dv$sHEljSX3qR_*@=ODcbmI8u36Cc^*!cEr#7w`Gjgl>(#3Iq z-M4q0d716YZd3MeFx#4Z*|bm02J?Y#UH`hzwZZ)KKgRaI^QH~vhBvLhENRIGbJvRo zKGkjQ26LA4^UH1S4Q6YD^^dN2YlC@c7uWFaUu-bn)A{}D-)*qby!-xXV_)jD(VV{O z$F6V1Y&5^o?ZwAhrEWA&xaQ#%r8yhTkFf{=E&C{?()z*8_grzNM~{$-)Np3 z_5CsP&W+}~7c6Z*~v&%P`U+UQI$2QA1nJ2I4K6clJP3Hd6WwXl9wg z`{=<1=6RdVJza;I7v8to+~~3!hCKAtW^?5C{nmZ3XS4amw=V8^#fO{C?%~%=nE&Hu zbBD*iZ&1~8i@En#y-tq2a*Mg~j(6U2Teg_H+<*2S^VBWo;T@{q-#2@UdHKKRJ~?sG z7IVgB8TN12ZZZG#ua~b|wtI`YO~wsP5Zcj==2^KDzrnQNLow|vT0^Zc-c1u3Vunm6zG#`H<=E#~-H_Um#c zyvTR1f)^4b-g~~4FM3hco%ina&N*h?YH{!J5SxZxhW!6mci!z&e#TwUy}s#Cf#Lk% zbWy)l*~UD=m`51%2xA@*Y#xCxgjmn)^2o81OWzyUgRSrRMCL z4yzo^`W<>^^pRIH**mlPbhvfT7`F49R#%U5XR-H=EzcbGQZ`$1*NMVO2gkCigu*%7 z-y6r?4BNiEpU;`bw!1Que{N=Fom)I`{m=?4`(bNj&p*wc z&Q^T2v-zQzTy}Rx<qcZP%q7 zYkwbWUiKba)8rp))fluZ*k6yNsy|cPxM0DOJ_Tu*K+lQ~( zEZ~2*h23^?%(jOwdXCK`G+v#!jb&bRd~?{+=h>hEkT}w-^DJu_2ih- zzwBbI$Sr=5wn|bqR}9-D@IShTeYSbQsL6BovUk~-MSSWWcHPBM7|LHGMh{U%34cb zc%VbdAr_aInE2OE53znk=!v!#-cpKB}VfbfaIc=p0dDd-DB1HQgaq zENOe$j(^6UbyrS_f8@j)KfC{B?zTGimNV`{ufP4Rwd6Ki@eg4?(@3?#4dgYdd5pTOIPR7mt^U62fr@u{@ zaP!1B+#A=dYW?3UUUzTpygoCh^=t0vqr+S8Xz{B1p}pxRQrRnRXWyceFW+*={rsQ` zxAfUT_tBJNoq81-(Y&YM z?{VF3_g#I*g|F|r%e^DJqj_Dfk(2o@9TZn1YjLss#3%fSU_8?uuAkAFDgl}N^#ewKdbkw&cKPsf+Pc4HJ< zv~TR4qca<`mNRd9tE5*nt5}-)^(3hY%WQOssq?>@u=SM#mc8&?QwndeD`9CfRxz;M zsco~Hv#J%&6_-wG!79G~OX{w!E!oPae){Xzi(9gaypx}qB3m)`)m1&SZfeC|y=~|I z+rFmvf36r(QP`TToY;C#byaJ&{;d6>JpAS8w(Qi`T@Q^p z)RtAWpE0Q-tsP?tcU(5SvK_0sb3o(WDeYPL&r1$ZtZdIJ+BO>eYgz|p+IdyjhC?0L zH1o--vdoUGy!@J5)86XHs#eYUN8ibvSVf1-4ZS|@#8zIvf|9lZ!*`d!>A4PRxc|XJ*e?3)RVOFgbcxy~qi8SVl#$3^uE1r+JBHG$SE)()Y{I#PBgwJS$KqBYlFM%ft&NUvN=OKmKdSCt?}gz}86AskwK zY&lLP=Fc5ShP9QW!=ZK7hL*(j-Jo;nBH^DyGD%7F4-LHnQa1z^!DA@7-KWaADV122 zbDKapLk0(7q|m9SyrPo0T+ZvK1s&QXD(LM3L5KPzBzsP@0pMg5qtZshBuKwhH4_dC)nQ&A0N{=(;bFEHLW=wJ()H zk<W@;Ct!SNLvCP>#FC(7!MtCT^xqgF(oyhaP;OVSdtnm z^^3W-oV2tYKio815MiY0uZ9~Ck4Ea+av=$?d4*!s#wU`ohOF#VKTMjSjobN@1`Q83 zFtW+S0^t%MM1hPuc5PvM4W1RJl)?*BK$Q7VLXSmpaPiXm$3vLnfIS@i@T|ZHkZ&Vz zf0ah{uEbX5=ocecURwEmiSAi;NUP+aj;pGn#;Kf}(x z4ZZ$KO63lmR1lhD*JnOmVxy$!$5pL1Xt)9sWkY}7OzDEX48%9Ax6#TZo;MFCbswTj z%0(6GjH%98RTwfOjKbg>NEly1N9y@H2@oFHYymuqo60fV@MRjFsXzo6&RPHcs2a|(lqvd z447yiEG7n&O2gMLo#qH9ic&y5wv&p2f)Xs9G4;U5SRjn%)NMRh(JESNydrEh{rzy$ z9LaKs-q=wrmI}0>dt>;)>!=kQ^@zS?HqlY^B|i09>ok6OQs0~<#nYIbM16M}>dDW? zFMgoWL(?B=(O58dQuu zwgi>guWZgrwrA*RfgHHp&+wy3#BS$V{QDbBg)o)TDInlU!_(0egDljHwHMoxX477J ze4^;ci-vWWHN%t+=EKAedTJJgG))qGpn00OCvWBRLBRzGAyy~4Fi6lx5a#JlN9r8q zlaM|w%B*&W&EA` zX4@2W6lE)Juc=-!L9Rvbi~v%+?tlRtX!WbNOYEiZv!E|GS>``@*Ze9W=(QME)zr$| zE%5VDi{r@Y@|)uLd!u7Xt1$j4eWBA))b@qTkEhZ=L2uisHu#ZxXa63c=FA#2Q@87u z%RtA=FE#j4UdyRt|G4PYaB0!A*F|0EbiB~Vne|;Gr1kwbT<~;6JA%7V$Lp904W-kI zZ@A#;c%k!S=3@<|AxkcNItp&QaQX4o464g$V%Kq;IdxSdY3_GP7d#y= zbbc(!i;_x48vN*yVbzfz7j2A^Dl0DhdC!H;k1bC&mdec+{=DZxAFpGE(#kU}xL@e= z@BsW+uWL-|@4RX2qNVk7!nMYYdd(AbSFdaRugh6gK&EcA>;Jr#^FnVI($+!JkdlwJ z>fczuBe9GSQG{MEF-62mB}aEg{7(Cq-?XmP{SnKGl_viEX@ein@q1v}N;0yg-QWGD zb**z8_)T$C^JDp!zYjWI=;O@FYsmbX!H*=9Jh$)siaUNEbiB~{vFhmWgO2KXveat` zmkDEAU?&`hXm#Y#QAJ`al@6hOtv3iDb5tZ2v*oLozKvRX1<%L3zPRRbf zh5|hze~tqpsyk@B!vW7_!^r*{{s>efY~vY2PU`l(D zO4cy$SMr>=iwOMR69;dqaO|1rL1**?+RS>sN{q1_b=JXmQ{VZ@4t#*3Qg+I&80?jw z#ZR_Uc4TPoj0w66Mr^j>yI?HN;u5Dr|1Oy3sB3I)#s+c+ZIs5wU~G5BnRcKI-xRNc zS(BAPLpaoJb#+r%IZw01=kQb30uZv3P&31(WTDRmENY&tr#?OqIq=%z`cdMzKkV95 zwkq-CdkEi)h57+D`lXfGim@5Uf6Gfv0s}ZlpHxyTgp`H$U**vMXxev|L%Y4O6L=aI z!+Y#Khl@}JEgUa5oyTZ_CRFDGR!@cMXLp3yl0OeXRLxT(Cs5Y(hiUJ+W_D4HTjI+3L1{>#B-3Qm96&Yu+ ziAJuQXy7#C47NHDjgsg)PRuxiO(;ysgPcC18E3Gm4&&6zX~r3Bb>KAP3^p~VX`R8g z*gFreaoQE1uxa>G?`WL07U0}4_l!Xr%(yeo zKQqohGtNIV&ObBG%H7J0vvT=azrUliaxsL3KL1R0%#d-w86Wi_r&J%GW*l$^vVbba z0cVnA9B}sk_kgqAd}jhbo>8xEsIGWQ`w4WA8CK6!X8*ehrF5wKI!IL+>dH8!N1Q4( zhn_M?ffxq;g?S3)tPQ9%!zT~w%xA0X0g8e*-?Ji2{b#!D#a7om#hO6?rRRw8gkvHZ z^;aMeJ^}3+P?#rv>dn|8;=An+xx*JK+H_JUw8)|Ioamf2y{Lwbe||oVM}3kGVZ<|S zJ5#i$aRNd*ZBH3S>dwPG&tgY(7LgVkS1g1;KMJ=XZNr}Q+(#NrR=Md7R~YvXGx28= z<-qgccl-feuhmn11XA1i)?xDQW||Sh^D~5k38IMF_|hw zFgB}gP1T?VoeqjaY*KkW@nf}Q9vY(JA?_a*s-Et?zFB7H89mN#J!x>bJ}!D=T3(wc zKN(U->~wN?ov~YeTlw9CiWMIB@)2=KxY(}ded7HDbuV4$pg^mu)RyNpRgqohqBDWx zT`uc9bV9rZ*GR8$py(*MjSX!$JObHF7)WOv2_*VT1L^0FkQX(2YJk|{K9CL=1rnlD zdFo~_yx3a7<9kI3?m+53yzsEW+M6@I$nI2J9zA(OI5Ddm6COB4lFm%FJ7<+97dhuB z!-D-rZssG%@icTE6D&^D9J@@^sBsu=A&q;;)!VdjbPaYFs!d_gsifRNVvC0%PukQB`WdmFrqFZ$3Wy?kJfjF`zsGKDyDnvw zr9xD+Of1jDf+Bd_Rz8x{u0;K*WazQfC4!&2M1Es4^<3vxqUd*4myEb$XoXLSRHK-u zK=hz6-`rO70yY59#BUV!p^r>6O$PI)XCQ_GBcAgJ+Sr(h2@_tZ;kwLWjhPs4m&Q!Y z(^CfHqTgyJ2JSXYO<_u7qpyY8s4K(Nl=#L5Yj8(2kqlE)+4L=Gn3_^}LcRyG7=@{d zBpIfrWHRv~z%VuCnIU7QTvyv*NE_?|>orv99^{T*O}Uh4c&|}SN}zm49Kh!+ZkU=9 zAA)*`hKe&xO%V}l%V~zGsX7pilIXLVWSE+&g8?AJ)Rbx<(93Ctsi`_}nqg{6&1r_I zsk%?x#CHje3*>^CO)^YP2^wLVqL*fdsVQxmVa}^|c*hdGVQR`t6~ojNZ8L%#54#rN z(CF&N=o9bCLK=M%7UAkISHsklzab<#6{}wB)i5>XPhrE9ap z?vY!W93ovVe57mSdooLqo_dnpb>+gRPxsIL=;{3EuriSXaPUEt(;k*$OOAo0R6~Z5>tEHQqxIf216@M;hz5p4Ou|3S!X8O z9eDxlV6;^%cVrSttkO&(VKU!Q0J9DOY*0C(TUiF-JEU^(2{ILd%r8sj(RUY;*DIdIRm3|RQPYsrIhZH`>?h_qcA`@BFN+IT_@YdOcla0Z*;B!F6=agIIr`+k|ljD0J z&i{^hzRUt25$l)?4L((>XS~;RhMKpb=3U(|o;pxF=VIbsaIl9_@NB4g8*1JjhVVWZ zYTgPhsF8$dLN!!j7w%?CYTVJxnu>daVre`NT2G6R%FaISA^*u8zsIWuJ*k^40{q{l zg}W=@Pj}C5!LZ{p^r-O0W>P%(N&1ewUTw6p1-qo&95rVFf_ni}Lu?u?p%IQ7Z&AGg z;ven6aLfz%rbk1~Tku&8-Ue5w9C#5#1K%2I-b4ftL@&|6X@;729f(Fr3^niCa2lGY zZlW1#-fHDrot$QV_-B7v@In#qHzLUvwn_^dM2ygek53^i|BDK^x+MTdq4vy!hoa5K>DwS_`i zL@$VNp(zO+NhtjeL?;(grF{_RJ2cVyQKaWaqC(~>wv*Lgu7;Yoq2}$Y$W6R&sCi36 zNCjI_$Hw6>TI$$4NbD?}P?N5>$58V&)V#+)J-x?%_Bhni=kI4fL;vf&{VWsyv6bvG z_%E(xKf(WtN;Vq)sR!7j@Lzp^oq_+^18fxhuRF+A!+*;`b{hUIUuGHbFM637YTkyL zx1r{3sCgS|-iDgDq2_Ivi!tUAhm%j7Ieq)JtmBgYo7dcu&U!qP|6J8A8SLMKV|K1x zIEw9Svu9D%eWO{+u5T|0%gkh^F;j0E{ca{R<`KWmJOV2;u~MUzQ+J%b;Zk&v1Jd(t&+P388J13;DB@rWUTJ%LKOWM9_VWWFT zvJHub3aX?s5oVkc|NB!xHQHEMWY7HT*pu^)IuATJnLYaHuZLT|JB96E|Iu@;x?ayd z{pYGT-=2}f)|$E$Kc6#=ZFglR|J=;VI=6V>`k@t8_QTf5o`0G>ovrw4XY)fbx$N$Y z%BB6j%4Owij*Qr{A&>p_&NF|@nQLSBr*4THYRP9~4#XvnY+b;Ti`u^N;3oyFXYH;4ze()r#0n>GM;SbDKap8*1L#l*u9mU9VC%)iWK%tM*Z7Zm4-1YTnwa+{816 znm6@pih_DJ=`wqDNR{?;^ykA%lxOyhP~;k(eqt*86m4ES`#Y?zsV}0otOO2U@0))* zh?%RaznZW3qsOkU4wJe|mW)|NW%evvzRhKGX$zr6wRg3WP^Ce2cgm)wb^{gOM)Sa2-iM;_*!^na$7tvC z-JHd$&PIXOk{(Vj@U(|8u8A6+Me*k#O3kn)UYigWo*!&mxN5}ZM|ZTrZbHmdVKW14 zl+X8$`B#;HK0GZQ3_pNU35yTYdF9%va_4F1)L6t$rV_%=1}LZv)k0yy#)QHspi_Z% z?B<}WjVw`^q|wq`!XeL(f-1@-jwUM==@fG;rNI7K0e6Ztikj#Y((oNhJLm?`fm4Gh zP73X;HIaV;`I|`U6GK)kOyrkJ|D{AT;9o}17$RoUKWeg+-ZKgMcqxZNiQy^wP&^mC z_mK^@6oMZb3;61W<428l;wxkx&#Q>ag*t>D@-W$cNEv?~NVoxBJo%z7tdvU$;bilt zR}|{Bl)|U;&^%SDAXv1Fmj+X5EOg~k&E`^CC+C>Ca+xXbrfD2ISR=6$|JXg4`jx1? zLL2N)9K@lf5)?K46uMb}T>!ZK{Yw^^K-OMjX&3ZRu@gEfo@xVd=TUi4WaHROurd(965`rtvrxAvK{2=#NI805^(i*%4gIrp?Rg;_0 zA2Y9fxh$3|!&kHXSi>g-|RG==Wly_f=VPeEb zmxD8FVDbgs69}n(rdRGLx@U$_N@_WHS9g>CZz7d94dUNQ>7dag4EcEP{&BA)51h>w zL)Qqp66qR3S3kOX(AAqRY;>PN*J#z1M$gKXO3x{(YaBg~Rb6 Date: Tue, 11 Feb 2020 21:34:43 -0700 Subject: [PATCH 39/51] add function for reader example folder --- NAMESPACE | 2 ++ R/package.R | 2 +- R/utils.R | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index ede87ddc..dc45ab4a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -96,6 +96,7 @@ export(iso_get_problems_summary) export(iso_get_raw_data) export(iso_get_reader_example) export(iso_get_reader_examples) +export(iso_get_reader_examples_folder) export(iso_get_resistors_info) export(iso_get_standards_info) export(iso_get_supported_file_types) @@ -227,6 +228,7 @@ importFrom(rlang,"!!") importFrom(rlang,":=") importFrom(rlang,.data) importFrom(rlang,UQ) +importFrom(rlang,as_label) importFrom(rlang,enexpr) importFrom(rlang,enquo) importFrom(rlang,eval_tidy) diff --git a/R/package.R b/R/package.R index 944a0af9..b996a50e 100644 --- a/R/package.R +++ b/R/package.R @@ -1,7 +1,7 @@ #' @keywords internal "_PACKAGE" -#' @importFrom rlang expr enexpr enquo quo quos UQ !! !!! := get_expr quo_squash quo_is_null quo_is_symbol is_quosure is_empty is_integerish eval_tidy sym new_formula f_lhs f_rhs +#' @importFrom rlang expr enexpr enquo quo quos UQ !! !!! := get_expr quo_squash quo_is_null quo_is_symbol is_quosure is_empty is_integerish eval_tidy sym new_formula f_lhs f_rhs as_label #' @importFrom tidyselect everything starts_with ends_with matches #' @importFrom tibble tibble is_tibble #' @importFrom dplyr vars n select rename arrange desc mutate mutate_at mutate_if filter distinct as_data_frame left_join right_join full_join data_frame bind_rows bind_cols group_by ungroup tally summarize do case_when diff --git a/R/utils.R b/R/utils.R index 2cd1b633..2a34f315 100644 --- a/R/utils.R +++ b/R/utils.R @@ -197,17 +197,22 @@ iso_get_reader_example <- function(filename) { #' iso_get_reader_examples() #' @export iso_get_reader_examples <- function() { - - # global vars - extension <- filename <- format <- path <- type <- description <- NULL - file_types <- iso_get_supported_file_types() iso_expand_paths( ".", extensions = file_types$extension, root = system.file(package = "isoreader", "extdata")) %>% - mutate(filename = basename(path)) %>% + mutate(filename = basename(.data$path)) %>% match_to_supported_file_types(file_types) %>% - arrange(type, extension, filename) %>% - select(filename, type, description) + arrange(.data$type, .data$extension, .data$filename) %>% + select(.data$filename, .data$type, .data$software, .data$description) +} + +#' @rdname iso_get_reader_example +#' @details \code{iso_get_reader_examples_folder}: path to the location of the reader examples +#' @examples +#' iso_get_reader_examples_folder() +#' @export +iso_get_reader_examples_folder <- function() { + return(system.file(package = "isoreader", "extdata")) } # file paths ==== From bb776fe194eaae52d15e80cc8653b6a367bf44d2 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Tue, 11 Feb 2020 21:38:41 -0700 Subject: [PATCH 40/51] update documentation --- .Rbuildignore | 4 +- .gitignore | 2 + _pkgdown.yml | 24 +- docs/404.html | 47 +- docs/LICENSE-text.html | 47 +- docs/LICENSE.html | 445 ------------------ docs/articles/advanced.html | 276 ----------- docs/articles/continuous_flow.html | 74 ++- .../figure-html/all data-1.png | Bin 573378 -> 0 bytes .../figure-html/ratios-1.png | Bin 280287 -> 0 bytes .../figure-html/select masses-1.png | Bin 293025 -> 0 bytes .../figure-html/signal conversion-1.png | Bin 300109 -> 0 bytes .../figure-html/styling-1.png | Bin 235392 -> 0 bytes .../figure-html/time conversion-1.png | Bin 213727 -> 0 bytes .../figure-html/time window-1.png | Bin 157874 -> 0 bytes docs/articles/development.html | 118 +++-- docs/articles/dual_inlet.Rmd | 28 -- docs/articles/dual_inlet.html | 77 +-- .../figure-html/all data-1.png | Bin 549831 -> 0 bytes .../figure-html/all_data-1.png | Bin 549831 -> 0 bytes .../figure-html/plot styling-1.png | Bin 141790 -> 0 bytes .../figure-html/pressure-1.png | Bin 62231 -> 0 bytes .../dual_inlet_files/figure-html/ratios-1.png | Bin 181834 -> 0 bytes .../figure-html/select masses-1.png | Bin 164263 -> 0 bytes .../figure-html/select_masses-1.png | Bin 164263 -> 0 bytes .../figure-html/signal conversion-1.png | Bin 140646 -> 0 bytes .../figure-html/signal_conversion-1.png | Bin 140646 -> 0 bytes .../figure-html/unnamed-chunk-10-1.png | Bin 371546 -> 0 bytes .../figure-html/unnamed-chunk-11-1.png | Bin 214649 -> 0 bytes .../figure-html/unnamed-chunk-12-1.png | Bin 241466 -> 0 bytes .../figure-html/unnamed-chunk-13-1.png | Bin 179614 -> 0 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 612263 -> 0 bytes docs/articles/index.html | 49 +- docs/articles/introduction.Rmd | 40 -- docs/articles/isoreader_iarc.Rmd | 57 --- docs/articles/operations.html | 420 ++++++++++++++++- .../operations_files/figure-html/test1-1.png | Bin 146174 -> 0 bytes .../operations_files/figure-html/test2-1.png | Bin 31003 -> 0 bytes docs/articles/quick_start.html | 239 ++++++++++ .../pagedtable-1.1/css/pagedtable.css | 2 +- .../pagedtable-1.1/js/pagedtable.js | 0 docs/articles/scan.html | 389 +++++++++++++++ .../pagedtable-1.1/css/pagedtable.css | 2 +- .../pagedtable-1.1/js/pagedtable.js | 0 docs/articles/test.html | 321 ------------- .../figure-html/unnamed-chunk-1-1.png | Bin 85507 -> 0 bytes .../figure-html/unnamed-chunk-1-2.png | Bin 85353 -> 0 bytes docs/authors.html | 47 +- docs/index.html | 83 +++- docs/jquery.sticky-kit.min.js | 9 - docs/news/index.html | 125 ----- docs/pkgdown.yml | 2 + docs/reference/extract_data.html | 64 ++- docs/reference/extract_substring.html | 74 ++- docs/reference/extract_word.html | 76 ++- docs/reference/file_readers.html | 107 +++-- docs/reference/index.html | 55 ++- docs/reference/iso_add_file_info.html | 79 ++-- docs/reference/iso_caching.html | 67 ++- docs/reference/iso_calculate_ratios.html | 56 ++- docs/reference/iso_cleanup_reader_cache.html | 56 ++- docs/reference/iso_convert_signals.html | 56 ++- docs/reference/iso_convert_time.html | 56 ++- docs/reference/iso_data_structure.html | 60 ++- docs/reference/iso_debug_mode.html | 56 ++- docs/reference/iso_double_with_units.html | 69 ++- docs/reference/iso_expand_paths.html | 67 ++- docs/reference/iso_export_to_excel.html | 81 +++- docs/reference/iso_export_to_feather.html | 82 ++-- docs/reference/iso_export_to_rda.html | 67 ++- docs/reference/iso_filter_files.html | 69 ++- .../iso_filter_files_with_problems.html | 76 ++- .../iso_find_absolute_path_roots.html | 67 ++- docs/reference/iso_get_bgrd_data.html | 80 +++- docs/reference/iso_get_data.html | 89 ++-- docs/reference/iso_get_data_summary.html | 58 ++- .../iso_get_default_reader_parameters.html | 66 ++- docs/reference/iso_get_file_info.html | 76 +-- docs/reference/iso_get_problems.html | 67 ++- docs/reference/iso_get_problems_summary.html | 69 ++- docs/reference/iso_get_raw_data.html | 93 ++-- docs/reference/iso_get_reader_example.html | 90 ++-- docs/reference/iso_get_resistors_info.html | 78 ++- docs/reference/iso_get_standards_info.html | 79 +++- .../iso_get_supported_file_types.html | 60 ++- docs/reference/iso_get_units.html | 69 ++- docs/reference/iso_get_vendor_data_table.html | 82 ++-- docs/reference/iso_has_problems.html | 69 ++- docs/reference/iso_info_messages.html | 67 ++- docs/reference/iso_is_double_with_units.html | 69 ++- docs/reference/iso_make_units_explicit.html | 68 ++- docs/reference/iso_make_units_implicit.html | 68 ++- docs/reference/iso_mutate_file_info.html | 69 ++- .../iso_omit_files_with_problems.html | 56 ++- docs/reference/iso_parse_file_info.html | 82 +++- .../iso_plot_continuous_flow_data.html | 56 ++- docs/reference/iso_plot_dual_inlet_data.html | 56 ++- docs/reference/iso_plot_raw_data.html | 56 ++- docs/reference/iso_printing.html | 59 ++- docs/reference/iso_problem_functions.html | 68 ++- docs/reference/iso_read_continuous_flow.html | 80 +++- docs/reference/iso_read_dual_inlet.html | 81 +++- docs/reference/iso_read_files.html | 81 +++- docs/reference/iso_read_scan.html | 131 +++++- docs/reference/iso_rename_file_info.html | 69 ++- docs/reference/iso_reread_files.html | 76 ++- docs/reference/iso_root_paths.html | 67 ++- docs/reference/iso_save.html | 67 ++- docs/reference/iso_select_file_info.html | 69 ++- .../iso_set_default_read_parameters.html | 78 ++- .../reference/iso_shorten_relative_paths.html | 66 ++- .../iso_show_default_reader_parameters.html | 67 ++- docs/reference/iso_strip_units.html | 69 ++- docs/reference/isoread.html | 56 ++- docs/reference/isoreader-package.html | 59 ++- .../reference/print.binary_structure_map.html | 65 ++- docs/reference/read_iso_file.html | 74 ++- docs/reference/reexports.html | 63 ++- docs/reference/set_temp.html | 56 ++- .../vec_arith.iso_double_with_units.html | 56 ++- .../vec_cast.iso_double_with_units.html | 56 ++- .../vec_ptype2.iso_double_with_units.html | 56 ++- docs/sitemap.txt | 48 -- docs/sitemap.xml | 6 + man/iso_get_reader_example.Rd | 6 + 125 files changed, 4796 insertions(+), 3072 deletions(-) delete mode 100644 docs/LICENSE.html delete mode 100644 docs/articles/advanced.html delete mode 100644 docs/articles/continuous_flow_files/figure-html/all data-1.png delete mode 100644 docs/articles/continuous_flow_files/figure-html/ratios-1.png delete mode 100644 docs/articles/continuous_flow_files/figure-html/select masses-1.png delete mode 100644 docs/articles/continuous_flow_files/figure-html/signal conversion-1.png delete mode 100644 docs/articles/continuous_flow_files/figure-html/styling-1.png delete mode 100644 docs/articles/continuous_flow_files/figure-html/time conversion-1.png delete mode 100644 docs/articles/continuous_flow_files/figure-html/time window-1.png delete mode 100644 docs/articles/dual_inlet.Rmd delete mode 100644 docs/articles/dual_inlet_files/figure-html/all data-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/all_data-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/plot styling-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/pressure-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/ratios-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/select masses-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/select_masses-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/signal conversion-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/signal_conversion-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/unnamed-chunk-10-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/unnamed-chunk-11-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/unnamed-chunk-12-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/unnamed-chunk-13-1.png delete mode 100644 docs/articles/dual_inlet_files/figure-html/unnamed-chunk-9-1.png delete mode 100644 docs/articles/introduction.Rmd delete mode 100644 docs/articles/isoreader_iarc.Rmd delete mode 100644 docs/articles/operations_files/figure-html/test1-1.png delete mode 100644 docs/articles/operations_files/figure-html/test2-1.png create mode 100644 docs/articles/quick_start.html rename docs/{index_files => articles/quick_start_files}/pagedtable-1.1/css/pagedtable.css (97%) rename docs/articles/{advanced_files => quick_start_files}/pagedtable-1.1/js/pagedtable.js (100%) create mode 100644 docs/articles/scan.html rename docs/articles/{advanced_files => scan_files}/pagedtable-1.1/css/pagedtable.css (97%) rename docs/{index_files => articles/scan_files}/pagedtable-1.1/js/pagedtable.js (100%) delete mode 100644 docs/articles/test.html delete mode 100644 docs/articles/test_files/figure-html/unnamed-chunk-1-1.png delete mode 100644 docs/articles/test_files/figure-html/unnamed-chunk-1-2.png delete mode 100644 docs/jquery.sticky-kit.min.js delete mode 100644 docs/news/index.html delete mode 100644 docs/sitemap.txt diff --git a/.Rbuildignore b/.Rbuildignore index d5d1bfb9..4f958ede 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -16,4 +16,6 @@ ^README\.Rmd$ ^logo\.png$ ^vignettes/cache$ -^vignettes/fig_output$ \ No newline at end of file +^vignettes/fig_output$ +^doc$ +^Meta$ diff --git a/.gitignore b/.gitignore index 385651c0..32e51937 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ vignettes/**/*.pdf # documentation !/docs/* inst/doc +doc +Meta diff --git a/_pkgdown.yml b/_pkgdown.yml index c7744d44..4e26b156 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -17,14 +17,22 @@ navbar: title: isoreader type: default left: - - text: Dual Inlet - href: articles/dual_inlet.html - - text: Continuous Flow - href: articles/continuous_flow.html - - text: Operations - href: articles/operations.html - - text: Development - href: articles/development.html + - text: Quick Start + href: articles/quick_start.html + - text: Full Examples + menu: + - text: Dual Inlet + href: articles/dual_inlet.html + - text: Continuous Flow + href: articles/continuous_flow.html + - text: Scan + href: articles/scan.html + - text: Advanced + menu: + - text: Operations + href: articles/operations.html + - text: Development + href: articles/development.html - text: Reference href: reference/index.html right: diff --git a/docs/404.html b/docs/404.html index 54e2b4b5..50b0e39b 100644 --- a/docs/404.html +++ b/docs/404.html @@ -8,11 +8,13 @@ Page not found (404) • Isoreader + + @@ -32,10 +34,12 @@ + + @@ -73,29 +77,52 @@ Isoreader - 1.0.11 + 1.0.17

@@ -415,7 +439,7 @@

#> Info: aggregating raw data from 7 data file(s)
# get specific raw data and add some file information
@@ -456,7 +480,7 @@ 

iso_files %>% iso_get_vendor_data_table( # select peak number, ret. time, overall intensity and all H delta columns - select = c(Nr., Rt, area = `rIntensity All`, matches("^d \\d+H")), + select = c(Nr., Rt, area = `rIntensity All`, matches("^d \\d+H")), # include the Analysis number fron the file info and rename it to 'run' include_file_info = c(run = Analysis) ) %>% rmarkdown::paged_table() @@ -472,7 +496,7 @@

iso_get_vendor_data_table( with_explicit_units = TRUE, # select peak number, ret. time, overall intensity and all H delta columns - select = c(Nr., matches("^Rt"), matches("rIntensity All"), matches("^d \\d+H")), + select = c(Nr., matches("^Rt"), matches("rIntensity All"), matches("^d \\d+H")), # include the Analysis number fron the file info and rename it to 'run' include_file_info = c(run = Analysis) ) %>% rmarkdown::paged_table() @@ -504,8 +528,8 @@

iso_files <- iso_read_continuous_flow("iso_files_export.cf.rds") #> Info: preparing to read 1 data files (all will be cached)... #> Info: reading file 'iso_files_export.cf.rds' with '.cf.rds' reader -#> Info: loaded data for 7 data files from R Data Storage - checking load... -#> Info: finished reading 1 files in 0.13 secs +#> Info: loaded data for 7 data files from R Data Storage - checking loaded fi... +#> Info: finished reading 1 files in 0.11 secs iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() #> Info: aggregating data summary from 7 data file(s)

@@ -175,13 +196,13 @@

#> Info: reading file 'dual_inlet_example.caf' with '.caf' reader #> Info: starting file #2, named 'dual_inlet_example.caf' #> Info: finished file #2 -#> Info: finished reading 2 files in 7.07 secs +#> Info: finished reading 2 files in 5.84 secs #> Data from 2 dual inlet iso files: #> # A tibble: 2 x 6 -#> file_id raw_data file_info method_info vendor_data_tab… file_path -#> <chr> <chr> <chr> <chr> <chr> <chr> -#> 1 dual_inl… 15 cycles, … 16 entries standards,… 15 rows, 7 colu… dual_inle… -#> 2 dual_inl… 8 cycles, 6… 22 entries standards,… 8 rows, 9 colum… dual_inle… +#> file_id raw_data file_info method_info vendor_data_tab… file_path +#> <chr> <chr> <chr> <chr> <chr> <chr> +#> 1 dual_inle… 15 cycles, 8 i… 16 entri… standards, … 15 rows, 7 colu… dual_inlet… +#> 2 dual_inle… 8 cycles, 6 io… 22 entri… standards, … 8 rows, 9 colum… dual_inlet… isoreader:::initialize_options() # reset all isoreader options @@ -215,7 +236,7 @@

rmarkdown::paged_table()

Additional information can be gleaned from the so-called control blocks, which are larger structural elements of Isodat binary files and are kept in a data frame wihin the binary object (again only available in debug mode).

@@ -247,6 +268,7 @@

@@ -448,7 +471,7 @@

iso_files %>% iso_get_vendor_data_table( # select cycle and all carbon columns - select = c(cycle, matches("C")), + select = c(cycle, matches("C")), # include the Identifier 1 fron the file info and rename it to 'id' include_file_info = c(id = `Identifier 1`) ) %>% rmarkdown::paged_table() @@ -480,16 +503,16 @@

iso_read_dual_inlet("iso_files_export.di.rds") #> Info: preparing to read 1 data files (all will be cached)... #> Info: reading file 'iso_files_export.di.rds' with '.di.rds' reader -#> Info: loaded data for 4 data files from R Data Storage - checking load... -#> Info: finished reading 1 files in 0.11 secs +#> Info: loaded data for 4 data files from R Data Storage - checking loaded fi... +#> Info: finished reading 1 files in 0.13 secs #> Data from 4 dual inlet iso files: #> # A tibble: 4 x 6 -#> file_id raw_data file_info method_info vendor_data_tab… file_path -#> <chr> <chr> <chr> <chr> <chr> <chr> -#> 1 dual_inl… 15 cycles, … 16 entries standards,… 15 rows, 7 colu… dual_inle… -#> 2 dual_inl… 7 cycles, 6… 15 entries standards,… 7 rows, 8 colum… dual_inle… -#> 3 dual_inl… 8 cycles, 6… 22 entries standards,… 8 rows, 9 colum… dual_inle… -#> 4 dual_inl… 82 cycles, … 8 entries no method … no vendor data … dual_inle… +#> file_id raw_data file_info method_info vendor_data_tab… file_path +#> <chr> <chr> <chr> <chr> <chr> <chr> +#> 1 dual_inle… 15 cycles, 8 … 16 entries standards, … 15 rows, 7 colu… dual_inlet… +#> 2 dual_inle… 7 cycles, 6 i… 15 entries standards, … 7 rows, 8 colum… dual_inlet… +#> 3 dual_inle… 8 cycles, 6 i… 22 entries standards, … 8 rows, 9 colum… dual_inlet… +#> 4 dual_inle… 82 cycles, 6 … 8 entries no method i… no vendor data … dual_inlet… diff --git a/docs/articles/introduction.Rmd b/docs/articles/introduction.Rmd deleted file mode 100644 index 8575f5a5..00000000 --- a/docs/articles/introduction.Rmd +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "Untitled" -output: html_document ---- - -# Illustrate basic functionality - - -This document was generated with isoreader version `r packageVersion("isoreader")`. - - -```{r} -#iso_filter_files_with_problems() -``` - -```{r} -#iso_cleanup_reader_cache() -``` - -The underlying isoreader package requires unique file names within a dataset so files with the same name cannot be combined into a list of iso_files. Appropriate warnings will be triggered if this happens accidentally. It is generally recommended as a best practice to setup IRMS instrumentation such that data files are automatically generated with a unique prefix (e.g. an incremental universal run number). - - - -```{r} -set.seed(123) -data_frame( - analysis = 1:10, - prep = c(rep("a", 3), rep("b", 4), rep("a", 3)) -) %>% - arrange(analysis) %>% - mutate( - new_sample = prep != c("", head(prep, -1)), - batch = cumsum(new_sample) - ) %>% - group_by(batch) %>% - summarize( - avg = mean(analysis) - ) -``` - diff --git a/docs/articles/isoreader_iarc.Rmd b/docs/articles/isoreader_iarc.Rmd deleted file mode 100644 index 843daa64..00000000 --- a/docs/articles/isoreader_iarc.Rmd +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: 'Isoreader iarc example' -output: html_document ---- - -```{r} -library(isoreader) -library(dplyr) -``` - - -```{r} -devtools::load_all(".") -b3_indiana <- iso_read_continuous_flow("inst/extdata/elementar/B3_Indiana_GC-H.iarc") -``` - - - -```{r} -b3_indiana[1:5] %>% - iso_convert_signals(to = "pA") %>% - iso_plot_raw_data(c(2,3)) -``` - - - -```{r} -devtools::load_all(".") -isoreader:::iso_turn_debug_on(catch_errors = FALSE) -EA_CN_Demo <- iso_read_continuous_flow("inst/extdata/elementar/EA_CN_Demo.iarc", cache = FALSE) -``` - -```{r} -EA_CN_Demo[1:5] %>% - iso_convert_signals(to = "pA") %>% - iso_calculate_ratios(c("29/28", "45/44")) %>% - iso_plot_raw_data() -``` - - -```{r} -devtools::load_all(".") -isoreader:::iso_turn_debug_on(catch_errors = FALSE) -GC_Demo <- iso_read_continuous_flow("inst/extdata/elementar/GC_Demo_Calibrated.iarc") -``` - -```{r} -GC_Demo[1:3] %>% - iso_plot_raw_data() -``` - - - -```{r} -GC_short <- iso_read_continuous_flow("inst/extdata/elementar/B3_Indiana_GC-C.iarc") -``` - diff --git a/docs/articles/operations.html b/docs/articles/operations.html index c7e152aa..92b45712 100644 --- a/docs/articles/operations.html +++ b/docs/articles/operations.html @@ -37,23 +37,47 @@ Isoreader - 1.0.11 + 1.0.17 diff --git a/docs/reference/extract_word.html b/docs/reference/extract_word.html index 3f6123f0..b9bd80b9 100644 --- a/docs/reference/extract_word.html +++ b/docs/reference/extract_word.html @@ -8,11 +8,13 @@ Extract words from text — extract_word • Isoreader + + @@ -32,14 +34,15 @@ - + + @@ -77,29 +80,52 @@ Isoreader - 1.0.11 + 1.0.17
-

This extracts words from text, by default looks for continuous sequences of numbers and/or letters. Can adjust whether characters such as "_", "-", " ", and "." should be counted as part of a word or separate them and whether numbers should be included.

-
-
extract_word(string, capture_n = 1, include_numbers = TRUE,
-  include_underscore = FALSE, include_dash = FALSE,
-  include_space = FALSE, include_colon = FALSE,
-  missing = NA_character_)
- +
extract_word(
+  string,
+  capture_n = 1,
+  include_numbers = TRUE,
+  include_underscore = FALSE,
+  include_dash = FALSE,
+  include_space = FALSE,
+  include_colon = FALSE,
+  missing = NA_character_
+)
+

Arguments

@@ -173,12 +203,12 @@

Arg

what to replace missing values with? Note that values can be missing because there are not enough captured matches or because the actual capture_bracket is empty.

- +

See also

-

Other data extraction functions: extract_data, - extract_substring

- +

Other data extraction functions: +extract_data, +extract_substring()

Examples

x_text <- extract_word(c("sample number16.2", "sample number7b"), @@ -191,9 +221,7 @@

Examp

Contents

diff --git a/docs/reference/file_readers.html b/docs/reference/file_readers.html index 41c9741e..be0294f7 100644 --- a/docs/reference/file_readers.html +++ b/docs/reference/file_readers.html @@ -8,11 +8,13 @@ Register file readers — iso_register_dual_inlet_file_reader • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Register file extensions and reader functions for different data files. Isoreader automatically registers all built-in file readers so this function is usually only needed when registering additional readers provided for testing purposes from outside of the isoreader package. Note that file extensions are case-insensitive, i.e. a reader for .ext will also recognize .Ext and .EXT

-
-
iso_register_dual_inlet_file_reader(extension, func,
-  description = NA_character_, cacheable = TRUE, overwrite = FALSE,
-  env = find_func(func))
+    
iso_register_dual_inlet_file_reader(
+  extension,
+  func,
+  description = NA_character_,
+  software = NA_character_,
+  cacheable = TRUE,
+  overwrite = FALSE,
+  env = find_func(func)
+)
+
+iso_register_continuous_flow_file_reader(
+  extension,
+  func,
+  description = NA_character_,
+  software = NA_character_,
+  cacheable = TRUE,
+  overwrite = FALSE,
+  env = find_func(func)
+)
+
+iso_register_scan_file_reader(
+  extension,
+  func,
+  description = NA_character_,
+  software = NA_character_,
+  cacheable = TRUE,
+  overwrite = FALSE,
+  env = find_func(func)
+)
-iso_register_continuous_flow_file_reader(extension, func, - description = NA_character_, cacheable = TRUE, overwrite = FALSE, - env = find_func(func))
-

Arguments

@@ -153,6 +199,10 @@

Arg

+ + + + @@ -166,28 +216,29 @@

Arg

description

what is this file type about?

software

what is the software program that creates this filetype?

cacheable

whether this file type is cacheable. If TRUE (the default), user requests to cache the file will be honored. If FALSE, this file type will never be cached no matter what the user requests.

the environment where to find the function, by default this will be determined automatically and will throw an error if there is any ambiguity (e.g. the same function name in multiple packages) in which case it should be set manually

- +

Details

iso_register_dual_inlet_file_reader: use this function to register file readers for dual inlet files.

iso_register_continuous_flow_file_reader: use this function to register file readers for continuous flow files.

- +

iso_register_scan_file_reader: use this function to register file readers for scan files.

See also

-

Other file_types: iso_get_supported_file_types

-

Other file_types: iso_get_supported_file_types

- +

Other file_types: +iso_get_supported_file_types()

+

Other file_types: +iso_get_supported_file_types()

+

Other file_types: +iso_get_supported_file_types()

diff --git a/docs/reference/index.html b/docs/reference/index.html index 521a6f1e..d3cd4ff6 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -8,11 +8,13 @@ Function reference • Isoreader + + @@ -32,10 +34,12 @@ + + @@ -73,29 +77,52 @@ Isoreader - 1.0.11 + 1.0.17
-

This function makes it easy to add additional file info (iso_get_file_info) to isofile objects and data frames by a single left_join or multiple sequential left_join operations. The function provides a detailed summary of the information that was added unless quiet = TRUE. Note that one-to-many joins are not permitted (and will fail with an informative error) since this would lead to likely unintended data duplication in the isofiles. However, one-to-one and many-to-one joins are fully supported and should cover all needed use cases for this function. Also note that for each join, only the new_file_info rows that have defined non-NA, non-empty ("") values in all join_by columns will be considered for the join and that only new_file_info columns that do NOT already exist in ANY file information will be added. For changing the values of existing file information, please use iso_mutate_file_info instead.

-
# S3 method for iso_file_list
-iso_add_file_info(iso_files, new_file_info, ...,
-  quiet = default(quiet))
+iso_add_file_info(iso_files, new_file_info, ..., quiet = default(quiet))
 
 # S3 method for data.frame
-iso_add_file_info(df, new_file_info, ...,
-  quiet = default(quiet))
+iso_add_file_info(df, new_file_info, ..., quiet = default(quiet))
 
 iso_add_file_info(...)
- +

Arguments

@@ -164,37 +186,32 @@

Arg

a data frame of iso files data retrieved by any of the data retrieval functions (e.g. iso_get_file_info, iso_get_raw_data, etc.

- +

Value

the original iso files or data frame with the new file info added in.

-

Details

Single left_join: this is the most common use of this function and basically a simple left join operation (with some additional safety checks). Specify a single join_by in the ..., such as e.g. c("file_id") to add additional file information joining by the file_id column.

Multiple sequential left_join: this use case is for applying a set of increasingly more specific join_by rules. For example, ... = c("Identifier 1", "Identifier 2"), c("file_id") would serve to first add one set of new file information for all isofiles based on their Identifier 1 and Identifier 2 columns and then overwrite the new information with more specific details for a subset of isofiles based on their file_id column, all based on a single overview new_file_info data frame. Basically, each set of join_by conditions specified in ... must describe a valid left_join join_by parameter to merge the new_file_info with the existing file info. Each set of new_file_info data can overwrite the previous join_by matches such that the last set of join_by column(s) provided in ... will overwrite all previous matches for which it applies, even if they have already been a match for a previous column.

-

See also

- - + diff --git a/docs/reference/iso_caching.html b/docs/reference/iso_caching.html index 1c59544c..dc8535c1 100644 --- a/docs/reference/iso_caching.html +++ b/docs/reference/iso_caching.html @@ -8,11 +8,13 @@ Turn caching on/off — iso_caching • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

These functions turn caching of data files (and reading from cache) on/off in all subsequent isoread calls by changing the global settings for the cache parameter. Can be called stand alone or within a pipeline.

-
iso_turn_reader_caching_on(data = NULL)
 
 iso_turn_reader_caching_off(data = NULL)
- +

Arguments

@@ -142,23 +166,22 @@

Arg

a data frame - returned invisibly as is if provided (e.g. in the middle of a pipeline)

- +

See also

- - + diff --git a/docs/reference/iso_calculate_ratios.html b/docs/reference/iso_calculate_ratios.html index d4c9f061..4294da5b 100644 --- a/docs/reference/iso_calculate_ratios.html +++ b/docs/reference/iso_calculate_ratios.html @@ -8,11 +8,13 @@ moved to isoprocessor — iso_calculate_ratios • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

moved to isoprocessor

-
iso_calculate_ratios(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_cleanup_reader_cache.html b/docs/reference/iso_cleanup_reader_cache.html index 63e4603e..caac23b1 100644 --- a/docs/reference/iso_cleanup_reader_cache.html +++ b/docs/reference/iso_cleanup_reader_cache.html @@ -8,11 +8,13 @@ Cleanup old cached files — iso_cleanup_reader_cache • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Removes old cached files generated by isoreader versions different from the currently installed package. Removes all cached files if clean_all is set to TRUE.

-
iso_cleanup_reader_cache(all = FALSE)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

if set to TRUE, all cached files will be removed regardless of their version

- + diff --git a/docs/reference/iso_convert_signals.html b/docs/reference/iso_convert_signals.html index 9a58974c..db5da448 100644 --- a/docs/reference/iso_convert_signals.html +++ b/docs/reference/iso_convert_signals.html @@ -8,11 +8,13 @@ moved to isoprocessor — iso_convert_signals • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

moved to isoprocessor

-
iso_convert_signals(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_convert_time.html b/docs/reference/iso_convert_time.html index 0db7e8cc..2a216662 100644 --- a/docs/reference/iso_convert_time.html +++ b/docs/reference/iso_convert_time.html @@ -8,11 +8,13 @@ moved to isoprocessor — iso_convert_time • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

moved to isoprocessor

-
iso_convert_time(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_data_structure.html b/docs/reference/iso_data_structure.html index 2352c94d..89332bdd 100644 --- a/docs/reference/iso_data_structure.html +++ b/docs/reference/iso_data_structure.html @@ -8,11 +8,13 @@ Isoreader data structure functions — iso_is_file • Isoreader + + @@ -32,18 +34,20 @@ - + + @@ -81,29 +85,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_is_file(x)
@@ -150,8 +176,10 @@ 

Isoreader data structure functions

iso_is_continuous_flow(x) +iso_is_scan(x) + iso_as_file_list(..., discard_duplicates = TRUE)
- +

Arguments

@@ -168,14 +196,14 @@

Arg

whether to automatically discard files with duplicate file IDs (i.e. duplicate file names). If TRUE (the default), only the first files are kept and any files with the same file ID are discarded. If FALSE, all duplicate files are kept but their file IDs are appended with suffix #1, #2, etc.

- + diff --git a/docs/reference/iso_debug_mode.html b/docs/reference/iso_debug_mode.html index a2082cee..592054d4 100644 --- a/docs/reference/iso_debug_mode.html +++ b/docs/reference/iso_debug_mode.html @@ -8,11 +8,13 @@ Debugging functions — iso_debug_mode • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

For troubleshooting. Not exported.

-
iso_turn_debug_on(data = NULL, catch_errors = TRUE, cache = FALSE)
@@ -137,7 +161,7 @@ 

Debugging functions

set_read_file_event_expr(event_expr = NULL) set_finish_file_event_expr(event_expr = NULL)
- +

Arguments

@@ -158,14 +182,14 @@

Arg

an expression to evaluate in the context of reading individual iso files (evaluated in the local environment at the beginning of a file read)

- + diff --git a/docs/reference/iso_double_with_units.html b/docs/reference/iso_double_with_units.html index 690c18a7..eb533bc7 100644 --- a/docs/reference/iso_double_with_units.html +++ b/docs/reference/iso_double_with_units.html @@ -8,11 +8,13 @@ Generate a numeric (double) value with units — iso_double_with_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

This function generates a number with units that work well within data frames and tibbles and implement safety checks on numerical operations with numbers that have different units. To retrieve the numerical value without units, use iso_strip_units (works for single variables and data frames/tibbles) or simply as.numeric (for single variables). To retrieve the unit use iso_get_units. Note that to correctly combine data frames / tibbles that have values with units in them, use vec_rbind instead of rbind or bind_rows. vec_rbind will combine columns that have values with units if they have the same unit and otherwise convert to a simple number with a warning. The other functions will either fail or reduce the unit values to plain numbers with a cryptic warning message about not preserving attributes.

-
iso_double_with_units(x = double(), units = "undefined units")
- +

Arguments

@@ -144,24 +168,23 @@

Arg

the units the numeric value is in, by default "undefined units" but this parameter should always be supplied when working with real data that has units

- +

See also

- - + diff --git a/docs/reference/iso_expand_paths.html b/docs/reference/iso_expand_paths.html index 0b5ae37b..ae7aca00 100644 --- a/docs/reference/iso_expand_paths.html +++ b/docs/reference/iso_expand_paths.html @@ -8,11 +8,13 @@ Expand file paths — iso_expand_paths • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_expand_paths(path, extensions = c(), root = ".")
- +

Arguments

@@ -148,28 +172,25 @@

Arg

root for relative paths. Can be relative to the current working directory (e.g. "data") or an absolute path on the file system (e.g. "/Users/..." or "C:/Data/.."). The default is the current working directory ("."). Can be supplied as a vector of same length as the provided paths if the paths have different roots.

- +

Value

data frame with columns root (root as provided) and path of all the found files.

-

See also

- - + diff --git a/docs/reference/iso_export_to_excel.html b/docs/reference/iso_export_to_excel.html index 7233bfb7..0ef038fc 100644 --- a/docs/reference/iso_export_to_excel.html +++ b/docs/reference/iso_export_to_excel.html @@ -8,11 +8,13 @@ Export data to Excel — iso_export_to_excel • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17 -
iso_export_to_excel(iso_files, filepath, include_raw_data = TRUE,
-  include_file_info = TRUE, include_method_info = TRUE,
-  include_vendor_data_table = TRUE, include_problems = TRUE,
-  with_explicit_units = FALSE, quiet = default(quiet))
- +
iso_export_to_excel(
+  iso_files,
+  filepath,
+  include_raw_data = TRUE,
+  include_file_info = TRUE,
+  include_method_info = TRUE,
+  include_vendor_data_table = TRUE,
+  include_problems = TRUE,
+  with_explicit_units = FALSE,
+  quiet = default(quiet)
+)
+

Arguments

@@ -175,27 +206,25 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

Value

returns the iso_files object invisibly for use in pipelines

-

See also

- - +

Other export functions: +iso_export_to_feather(), +iso_export_to_rda(), +iso_save()

diff --git a/docs/reference/iso_export_to_feather.html b/docs/reference/iso_export_to_feather.html index 36c1e36a..f1c53c3f 100644 --- a/docs/reference/iso_export_to_feather.html +++ b/docs/reference/iso_export_to_feather.html @@ -8,11 +8,13 @@ Export to feather — iso_export_to_feather • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17 -
iso_export_to_feather(iso_files, filepath_prefix,
-  include_raw_data = TRUE, include_file_info = TRUE,
-  include_method_info = TRUE, include_vendor_data_table = TRUE,
-  include_problems = TRUE, with_explicit_units = FALSE,
-  quiet = default(quiet))
- +
iso_export_to_feather(
+  iso_files,
+  filepath_prefix,
+  include_raw_data = TRUE,
+  include_file_info = TRUE,
+  include_method_info = TRUE,
+  include_vendor_data_table = TRUE,
+  include_problems = TRUE,
+  with_explicit_units = FALSE,
+  quiet = default(quiet)
+)
+

Arguments

@@ -176,27 +206,25 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

Value

returns the iso_files object invisibly for use in pipelines

-

See also

-

Other export functions: iso_export_to_excel, - iso_export_to_rda, iso_save

- +

Other export functions: +iso_export_to_excel(), +iso_export_to_rda(), +iso_save()

diff --git a/docs/reference/iso_export_to_rda.html b/docs/reference/iso_export_to_rda.html index ce654625..b68a0852 100644 --- a/docs/reference/iso_export_to_rda.html +++ b/docs/reference/iso_export_to_rda.html @@ -8,11 +8,13 @@ Export data to R Data Archive (.rda) (deprecated) — iso_export_to_rda • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

This function is deprecated. Please use iso_save instead to save collections of isofiles.

-
iso_export_to_rda(iso_files, filepath, quiet = default(quiet))
- +

Arguments

@@ -148,28 +172,25 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

Value

returns the iso_files object invisibly for use in pipelines

-

See also

-

Other export functions: iso_export_to_excel, - iso_export_to_feather, - iso_save

- +

Other export functions: +iso_export_to_excel(), +iso_export_to_feather(), +iso_save()

diff --git a/docs/reference/iso_filter_files.html b/docs/reference/iso_filter_files.html index c7abb83d..d64c86da 100644 --- a/docs/reference/iso_filter_files.html +++ b/docs/reference/iso_filter_files.html @@ -8,11 +8,13 @@ Filter iso_files — iso_filter_files • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Filter for specific isofiles using file info columns (iso_get_file_info). Works just like dplyr's filter except that it provides the user with some information on what has been filtered. Returns NULL if none of the isofiles' file info matches the filter criteria. You can also use filter directly to filter collections of iso_file objects.

-
iso_filter_files(iso_files, ..., quiet = default(quiet))
- +

Arguments

@@ -148,24 +172,23 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_filter_files_with_problems.html b/docs/reference/iso_filter_files_with_problems.html index 532454d8..c46917e0 100644 --- a/docs/reference/iso_filter_files_with_problems.html +++ b/docs/reference/iso_filter_files_with_problems.html @@ -8,11 +8,13 @@ Filter out problematic files — iso_filter_files_with_problems • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17 -
iso_filter_files_with_problems(iso_files,
-  remove_files_with_errors = TRUE, remove_files_with_warnings = FALSE,
-  quiet = default(quiet))
- +
iso_filter_files_with_problems(
+  iso_files,
+  remove_files_with_errors = TRUE,
+  remove_files_with_warnings = FALSE,
+  quiet = default(quiet)
+)
+

Arguments

@@ -154,23 +181,22 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_find_absolute_path_roots.html b/docs/reference/iso_find_absolute_path_roots.html index e9ee242c..fba019d4 100644 --- a/docs/reference/iso_find_absolute_path_roots.html +++ b/docs/reference/iso_find_absolute_path_roots.html @@ -8,11 +8,13 @@ Find roots for absolute paths — iso_find_absolute_path_roots • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_find_absolute_path_roots(path, root = ".", check_existence = TRUE)
- +

Arguments

@@ -148,28 +172,25 @@

Arg

whether to check for the existence of the paths

- +

Value

a data frame with the root directories and paths relative to the root - order of input paths is preserved

-

See also

- - +

Other file system functions: +iso_expand_paths(), +iso_root_paths(), +iso_shorten_relative_paths()

diff --git a/docs/reference/iso_get_bgrd_data.html b/docs/reference/iso_get_bgrd_data.html index ff8bb282..c949f4d2 100644 --- a/docs/reference/iso_get_bgrd_data.html +++ b/docs/reference/iso_get_bgrd_data.html @@ -8,11 +8,13 @@ Aggregate background data — iso_get_bgrd_data • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Aggregate the background data from the provided iso_files. Can aggregate either in a wide table (for easy overview) or a gathered long table (for plotting and further data processing). The background data is only available if the iso_files were read with parameter read_raw_data=TRUE.

-
-
iso_get_bgrd_data(iso_files, select = everything(), gather = FALSE,
-  include_file_info = NULL, quiet = default(quiet))
- +
iso_get_bgrd_data(
+  iso_files,
+  select = everything(),
+  gather = FALSE,
+  include_file_info = NULL,
+  quiet = default(quiet)
+)
+

Arguments

@@ -157,25 +186,24 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_get_data.html b/docs/reference/iso_get_data.html index c38f4e7d..5dba5beb 100644 --- a/docs/reference/iso_get_data.html +++ b/docs/reference/iso_get_data.html @@ -8,11 +8,13 @@ Aggregate all isofiles data — iso_get_data • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data iso_get_raw_data, iso_get_file_info, iso_get_vendor_data_table, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To unnest any of the specific data types (e.g. raw_data), make sure to filter first for the files that have this data type available (e.g. filter(has_raw_data)).

-
-
iso_get_data(iso_files, include_file_info = everything(),
-  include_raw_data = everything(),
-  include_vendor_data_table = everything(), gather = FALSE,
-  with_explicit_units = with_units, with_units = FALSE,
-  with_ratios = FALSE, quiet = default(quiet))
- +
iso_get_data(
+  iso_files,
+  include_file_info = everything(),
+  include_raw_data = everything(),
+  include_vendor_data_table = everything(),
+  gather = FALSE,
+  with_explicit_units = with_units,
+  with_units = FALSE,
+  with_ratios = FALSE,
+  quiet = default(quiet)
+)
+

Arguments

@@ -176,31 +206,28 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

Value

data_frame with file_ids, file_types and nested data frames for each data type (file_info, raw_data, vendor_data_table, etc.)

-

See also

- - + diff --git a/docs/reference/iso_get_data_summary.html b/docs/reference/iso_get_data_summary.html index d1144aa7..16c08516 100644 --- a/docs/reference/iso_get_data_summary.html +++ b/docs/reference/iso_get_data_summary.html @@ -8,11 +8,13 @@ Get data summary — iso_get_data_summary • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Summarize the data information from one or multiple iso files.

-
iso_get_data_summary(iso_files, quiet = default(quiet))
- +

Arguments

@@ -144,20 +168,18 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

Value

a data_frame that summarizes the data in the iso_files

- diff --git a/docs/reference/iso_get_default_reader_parameters.html b/docs/reference/iso_get_default_reader_parameters.html index 0de85954..6ef1b219 100644 --- a/docs/reference/iso_get_default_reader_parameters.html +++ b/docs/reference/iso_get_default_reader_parameters.html @@ -8,11 +8,13 @@ Get the current default parameters — iso_get_default_reader_parameters • Isoreader + + @@ -32,8 +34,8 @@ - + @@ -79,29 +82,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_get_default_reader_parameters()
- + +

See also

- - + diff --git a/docs/reference/iso_get_file_info.html b/docs/reference/iso_get_file_info.html index 4f3d1c87..fe1373ad 100644 --- a/docs/reference/iso_get_file_info.html +++ b/docs/reference/iso_get_file_info.html @@ -8,11 +8,13 @@ Aggregate file info — iso_get_file_info • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Combine file information from multiple iso_files. By default all information is included but specific columns can be targeted using the select parameter, which uses the iso_select_file_info function to select and/or rename columns. File information beyond file_id, file_root, file_path and file_datetime is only available if the iso_files were read with parameter read_file_info=TRUE.

-
-
iso_get_file_info(iso_files, select = everything(),
-  quiet = default(quiet))
- +
iso_get_file_info(iso_files, select = everything(), quiet = default(quiet))
+

Arguments

@@ -149,31 +172,28 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

Note

File info entries with multiple values remain nested multi-value (=list) columns and can be unnested using unnest.

-

See also

- - + diff --git a/docs/reference/iso_get_problems.html b/docs/reference/iso_get_problems.html index 6f6d1a52..968367b6 100644 --- a/docs/reference/iso_get_problems.html +++ b/docs/reference/iso_get_problems.html @@ -8,11 +8,13 @@ Retrieve parsing problems — iso_get_problems • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_get_problems(iso_files)
- +

Arguments

@@ -140,23 +164,22 @@

Arg

collection of iso_file objects

- +

See also

- - + diff --git a/docs/reference/iso_get_problems_summary.html b/docs/reference/iso_get_problems_summary.html index 6d968cf4..cd00bb47 100644 --- a/docs/reference/iso_get_problems_summary.html +++ b/docs/reference/iso_get_problems_summary.html @@ -8,11 +8,13 @@ Retrieve a summary of the problems — iso_get_problems_summary • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_get_problems_summary(iso_files, problem_files_only = TRUE)
- +

Arguments

@@ -144,29 +168,26 @@

Arg

whether to list only problem files or all files

- +

Value

data frame with file_id and number of encountered errors and warnings

-

See also

- - + diff --git a/docs/reference/iso_get_raw_data.html b/docs/reference/iso_get_raw_data.html index 46cb2d1c..9a4743ad 100644 --- a/docs/reference/iso_get_raw_data.html +++ b/docs/reference/iso_get_raw_data.html @@ -8,11 +8,13 @@ Aggregate raw data — iso_get_raw_data • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Aggregate the raw ion data from the provided iso_files. Can aggregate either in a wide table (for easy overview) or a gathered long table (for plotting and further data processing). The raw data is only available if the iso_files were read with parameter read_raw_data=TRUE.

-
-
iso_get_raw_data(iso_files, select = everything(), gather = FALSE,
-  include_file_info = NULL, quiet = default(quiet))
- +
iso_get_raw_data(
+  iso_files,
+  select = everything(),
+  gather = FALSE,
+  include_file_info = NULL,
+  quiet = default(quiet)
+)
+

Arguments

@@ -157,31 +186,31 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_get_reader_example.html b/docs/reference/iso_get_reader_example.html index ff214a60..2a9db6d8 100644 --- a/docs/reference/iso_get_reader_example.html +++ b/docs/reference/iso_get_reader_example.html @@ -8,11 +8,13 @@ Example files — iso_get_reader_example • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

The isoreader package comes with a few example files to make it easy to illustrate the functionality.

-
iso_get_reader_example(filename)
 
-iso_get_reader_examples()
- +iso_get_reader_examples() + +iso_get_reader_examples_folder()
+

Arguments

@@ -142,35 +168,35 @@

Arg

the name of the example file for which to retrieve the system path

- +

Details

iso_get_reader_example: retrieve the path to an isoreader example file

iso_get_reader_examples: list of all available isoreader example files

- +

iso_get_reader_examples_folder: path to the location of the reader examples

Examples

-
iso_get_reader_examples()
#> Registered S3 method overwritten by 'R.oo': -#> method from -#> throw.default R.methodsS3
#> # A tibble: 8 x 3 -#> filename type description -#> <chr> <chr> <chr> -#> 1 continuous_flow_example.cf continuous f… Isodat Continuous Flow file format (… -#> 2 continuous_flow_example.d… continuous f… Isodat Continuous Flow file format (… -#> 3 linearity_example.dxf continuous f… Isodat Continuous Flow file format (… -#> 4 continuous_flow_example.i… continuous f… IonOS Continous Flow data archieve -#> 5 dual_inlet_example.caf dual inlet Isodat Dual Inlet file format (older) -#> 6 dual_inlet_example.did dual inlet Isodat Dual Inlet file format (newer) -#> 7 dual_inlet_example2.did dual inlet Isodat Dual Inlet file format (newer) -#> 8 dual_inlet_nu_example.txt dual inlet Nu Dual Inlet file format
+
iso_get_reader_examples()
#> # A tibble: 12 x 4 +#> filename type software description +#> <chr> <chr> <chr> <chr> +#> 1 continuous_flow_example… continuous f… Isodat Continuous Flow file format … +#> 2 continuous_flow_example… continuous f… Isodat Continuous Flow file format … +#> 3 linearity_example.dxf continuous f… Isodat Continuous Flow file format … +#> 4 continuous_flow_example… continuous f… ionOS Continous Flow data archieve +#> 5 dual_inlet_example.caf dual inlet Isodat Dual Inlet file format (olde… +#> 6 dual_inlet_example.did dual inlet Isodat Dual Inlet file format (newe… +#> 7 dual_inlet_example2.did dual inlet Isodat Dual Inlet file format (newe… +#> 8 dual_inlet_nu_example.t… dual inlet Nu Dual Inlet file format +#> 9 background_scan_example… scan Isodat Scan file format +#> 10 full_scan_example.scn scan Isodat Scan file format +#> 11 peak_shape_scan_example… scan Isodat Scan file format +#> 12 time_scan_example.scn scan Isodat Scan file format
iso_get_reader_examples_folder()
#> [1] "/private/var/folders/8z/hxk97xk1763cz5crb3wp7h840000gn/T/RtmpHjZNFY/temp_libpath182775933fe17/isoreader/extdata"
-

Aggregates the resistor information recovered from the provided iso_files. This information is only available if the iso_files were read with parameter read_method_info=TRUE and only linked to specific masses if the iso_files were additionally read with parametr read_raw_data=TRUE.

-
-
iso_get_resistors_info(iso_files, include_file_info = NULL,
-  quiet = default(quiet))
- +
iso_get_resistors_info(
+  iso_files,
+  include_file_info = NULL,
+  quiet = default(quiet)
+)
+

Arguments

@@ -149,25 +176,24 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_get_standards_info.html b/docs/reference/iso_get_standards_info.html index 70ad5490..764116d9 100644 --- a/docs/reference/iso_get_standards_info.html +++ b/docs/reference/iso_get_standards_info.html @@ -8,11 +8,13 @@ Aggregate standards from methods info — iso_get_standards_info • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter with_ratios to exclude/include the ratios. This information is only available if the iso_files were read with parameter read_method_info=TRUE.

-
-
iso_get_standards_info(iso_files, with_ratios = FALSE,
-  include_file_info = NULL, quiet = default(quiet))
- +
iso_get_standards_info(
+  iso_files,
+  with_ratios = FALSE,
+  include_file_info = NULL,
+  quiet = default(quiet)
+)
+

Arguments

@@ -153,25 +181,24 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_get_supported_file_types.html b/docs/reference/iso_get_supported_file_types.html index 338d8f67..03adbf0a 100644 --- a/docs/reference/iso_get_supported_file_types.html +++ b/docs/reference/iso_get_supported_file_types.html @@ -8,11 +8,13 @@ Supported file types — iso_get_supported_file_types • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_get_supported_file_types()
- + +

See also

- - + diff --git a/docs/reference/iso_get_units.html b/docs/reference/iso_get_units.html index 691c4d50..2452dd1c 100644 --- a/docs/reference/iso_get_units.html +++ b/docs/reference/iso_get_units.html @@ -8,11 +8,13 @@ Retrieve number units — iso_get_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

This function returns the units of a numerical value generated by iso_double_with_units. It returns NA) for unitless variables. Returns a column-named vector of units if x is a data frame / tibble. Returns the direct units of x in all other cases.

-
iso_get_units(x)
- +

Arguments

@@ -140,24 +164,23 @@

Arg

variable to get the units for (vector or data frame)

- +

See also

- - + diff --git a/docs/reference/iso_get_vendor_data_table.html b/docs/reference/iso_get_vendor_data_table.html index de4a6884..ad14bba6 100644 --- a/docs/reference/iso_get_vendor_data_table.html +++ b/docs/reference/iso_get_vendor_data_table.html @@ -8,11 +8,13 @@ Aggregate vendor computed table data — iso_get_vendor_data_table • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Aggregate data from the vendor-computed data table. This information is only available if the iso_files were read with parameter read_vendor_data_table=TRUE.

-
-
iso_get_vendor_data_table(iso_files, with_units = FALSE,
-  select = everything(), include_file_info = NULL,
-  with_explicit_units = with_units, quiet = default(quiet))
- +
iso_get_vendor_data_table(
+  iso_files,
+  with_units = FALSE,
+  select = everything(),
+  include_file_info = NULL,
+  with_explicit_units = with_units,
+  quiet = default(quiet)
+)
+

Arguments

@@ -162,25 +191,24 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_has_problems.html b/docs/reference/iso_has_problems.html index bfa62710..fb97a168 100644 --- a/docs/reference/iso_has_problems.html +++ b/docs/reference/iso_has_problems.html @@ -8,11 +8,13 @@ Check for parsing problems — iso_has_problems • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Check for parsing problems

-
iso_has_problems(iso_files)
- +

Arguments

@@ -140,29 +164,26 @@

Arg

collection of iso_file objects

- +

Value

boolean

-

See also

- - + diff --git a/docs/reference/iso_info_messages.html b/docs/reference/iso_info_messages.html index 77e75bb2..77e25297 100644 --- a/docs/reference/iso_info_messages.html +++ b/docs/reference/iso_info_messages.html @@ -8,11 +8,13 @@ Turn information messages on/off — iso_info_messages • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

These functions turn information messages on/off in all subsequent function calls by changing the global settings for the quiet parameter of most isoreader functions. These functions can be called stand alone or within a pipeline to turn messages on/off at a certain point during the pipeline.

-
iso_turn_info_messages_on(data = NULL)
 
 iso_turn_info_messages_off(data = NULL)
- +

Arguments

@@ -142,23 +166,22 @@

Arg

a data frame - returned invisibly as is if provided (e.g. in the middle of a pipeline)

- +

See also

- - + diff --git a/docs/reference/iso_is_double_with_units.html b/docs/reference/iso_is_double_with_units.html index 4a6bb894..c96cd4e6 100644 --- a/docs/reference/iso_is_double_with_units.html +++ b/docs/reference/iso_is_double_with_units.html @@ -8,11 +8,13 @@ Check if a value has units — iso_is_double_with_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_is_double_with_units(x)
- +

Arguments

@@ -140,24 +164,23 @@

Arg

vector to check for whether it is a double with units

- +

See also

- - + diff --git a/docs/reference/iso_make_units_explicit.html b/docs/reference/iso_make_units_explicit.html index 6bd48c95..338beac6 100644 --- a/docs/reference/iso_make_units_explicit.html +++ b/docs/reference/iso_make_units_explicit.html @@ -8,11 +8,13 @@ Make units explicit — iso_make_units_explicit • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_make_units_explicit(df, prefix = " [", suffix = "]")
- +

Arguments

@@ -148,15 +172,15 @@

Arg

the suffix for the units

- +

See also

- - +

Examples

# a data frame with implicit units @@ -192,9 +216,7 @@

Examp

Contents

diff --git a/docs/reference/iso_make_units_implicit.html b/docs/reference/iso_make_units_implicit.html index 053a1fb4..b8cdff44 100644 --- a/docs/reference/iso_make_units_implicit.html +++ b/docs/reference/iso_make_units_implicit.html @@ -8,11 +8,13 @@ Make units implicit — iso_make_units_implicit • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_make_units_implicit(df, prefix = " [", suffix = "]")
- +

Arguments

@@ -148,15 +172,15 @@

Arg

the suffix for the units

- +

See also

- - +

Examples

# generate implicit units @@ -193,9 +217,7 @@

Examp

Contents

diff --git a/docs/reference/iso_mutate_file_info.html b/docs/reference/iso_mutate_file_info.html index ca1f6c70..72e0772e 100644 --- a/docs/reference/iso_mutate_file_info.html +++ b/docs/reference/iso_mutate_file_info.html @@ -8,11 +8,13 @@ Mutate file info — iso_mutate_file_info • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Mutate the file info (iso_get_file_info) within isofile objects by changing existing columns or introducing new ones. Works just like dplyr's mutate. You can also use mutate directly but it will not provide summary information on the operation. Note that this will create missing columns that exist in some but not all of the passed in isofile objects in all isofile objects (filling them with NAs) the same way that iso_get_file_info does.

-
iso_mutate_file_info(iso_files, ..., quiet = default(quiet))
- +

Arguments

@@ -148,24 +172,23 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_omit_files_with_problems.html b/docs/reference/iso_omit_files_with_problems.html index ea41895b..04c81ebd 100644 --- a/docs/reference/iso_omit_files_with_problems.html +++ b/docs/reference/iso_omit_files_with_problems.html @@ -8,11 +8,13 @@ Renamed to iso_filter_files_with_problems — iso_omit_files_with_problems • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
iso_omit_files_with_problems(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_parse_file_info.html b/docs/reference/iso_parse_file_info.html index 5307728e..66e3d8a3 100644 --- a/docs/reference/iso_parse_file_info.html +++ b/docs/reference/iso_parse_file_info.html @@ -8,11 +8,13 @@ Parse file info — iso_parse_file_info • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Convenience function to batch parse file info (iso_get_file_info) columns in isofile objects for the most common parsing calls. Uses the parse_ functions exported from readr and described in extract_data. Note that for less common parsing calls or calls that require additional parameters to the parsing function, it is better to parse columns one-by-one using iso_mutate_file_info instead.

-
-
iso_parse_file_info(iso_files, number = c(), double = c(),
-  integer = c(), logical = c(), datetime = c(), text = c(),
-  quiet = default(quiet))
- +
iso_parse_file_info(
+  iso_files,
+  number = c(),
+  double = c(),
+  integer = c(),
+  logical = c(),
+  datetime = c(),
+  text = c(),
+  quiet = default(quiet)
+)
+

Arguments

@@ -170,24 +201,23 @@

Arg

whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

- +

See also

- - + diff --git a/docs/reference/iso_plot_continuous_flow_data.html b/docs/reference/iso_plot_continuous_flow_data.html index 046e1b99..6097edac 100644 --- a/docs/reference/iso_plot_continuous_flow_data.html +++ b/docs/reference/iso_plot_continuous_flow_data.html @@ -8,11 +8,13 @@ moved to isoprocessor — iso_plot_continuous_flow_data • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

moved to isoprocessor

-
iso_plot_continuous_flow_data(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_plot_dual_inlet_data.html b/docs/reference/iso_plot_dual_inlet_data.html index dc1ec353..de0a5699 100644 --- a/docs/reference/iso_plot_dual_inlet_data.html +++ b/docs/reference/iso_plot_dual_inlet_data.html @@ -8,11 +8,13 @@ moved to isoprocessor — iso_plot_dual_inlet_data • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

moved to isoprocessor

-
iso_plot_dual_inlet_data(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_plot_raw_data.html b/docs/reference/iso_plot_raw_data.html index 82445330..93620aa6 100644 --- a/docs/reference/iso_plot_raw_data.html +++ b/docs/reference/iso_plot_raw_data.html @@ -8,11 +8,13 @@ moved to isoprocessor — iso_plot_raw_data • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

moved to isoprocessor

-
iso_plot_raw_data(...)
- +

Arguments

@@ -140,14 +164,14 @@

Arg

deprecated

- + diff --git a/docs/reference/iso_printing.html b/docs/reference/iso_printing.html index d5a90c77..e26d8fa0 100644 --- a/docs/reference/iso_printing.html +++ b/docs/reference/iso_printing.html @@ -8,11 +8,13 @@ Isofile printing — print.iso_file_list • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

Print summary of individual iso_files (dual inlet or continuous flow) or collection of iso_files.

-
# S3 method for iso_file_list
@@ -140,8 +164,11 @@ 

Isofile printing

print(x, ..., show_problems = TRUE) # S3 method for continuous_flow +print(x, ..., show_problems = TRUE) + +# S3 method for scan print(x, ..., show_problems = TRUE)
- +

Arguments

@@ -158,14 +185,14 @@

Arg

whether to show encountered problems

- + diff --git a/docs/reference/iso_problem_functions.html b/docs/reference/iso_problem_functions.html index 9323f2b5..d859f245 100644 --- a/docs/reference/iso_problem_functions.html +++ b/docs/reference/iso_problem_functions.html @@ -8,11 +8,13 @@ Problem Functions Overview — iso_problem_functions • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
-

The following functions to check for and deal with problems are available.

-
- + +

Details

@@ -142,24 +167,21 @@

Details
  • iso_filter_files_with_problems

  • -

    See also

    - - + diff --git a/docs/reference/iso_read_continuous_flow.html b/docs/reference/iso_read_continuous_flow.html index 0ca67397..2da6d928 100644 --- a/docs/reference/iso_read_continuous_flow.html +++ b/docs/reference/iso_read_continuous_flow.html @@ -8,11 +8,13 @@ Load continuous flow data — iso_read_continuous_flow • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Load continuous flow data

    -
    -
    iso_read_continuous_flow(..., root = ".",
    +    
    iso_read_continuous_flow(
    +  ...,
    +  root = ".",
       read_raw_data = default(read_raw_data),
       read_file_info = default(read_file_info),
       read_method_info = default(read_method_info),
       read_vendor_data_table = default(read_vendor_data_table),
    -  discard_duplicates = TRUE, parallel = FALSE,
    -  parallel_plan = future::multiprocess, cache = default(cache),
    -  cache_files_with_errors = TRUE, read_cache = default(cache),
    -  quiet = default(quiet))
    - + discard_duplicates = TRUE, + parallel = FALSE, + parallel_plan = future::multisession, + cache = default(cache), + cache_files_with_errors = TRUE, + read_cache = default(cache), + quiet = default(quiet) +)
    +

    Arguments

    @@ -177,7 +207,7 @@

    Arg

    - + @@ -196,20 +226,20 @@

    Arg

    parallel_plan

    which parallel processing strategy to use, see plan, typically future::multiprocess (the default, uses multicore if supported by the operating system, otherwise multisession), future::multisession or future::multicore.

    which parallel processing strategy to use, see plan, typically future::multisession for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use future::multicore.

    cache

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    See also

    -

    Other isoread functions for different types of IRMS data: iso_read_dual_inlet

    - +

    Other isoread functions for different types of IRMS data: +iso_read_dual_inlet(), +iso_read_scan()

    diff --git a/docs/reference/iso_read_dual_inlet.html b/docs/reference/iso_read_dual_inlet.html index 6a51d71c..c268c80c 100644 --- a/docs/reference/iso_read_dual_inlet.html +++ b/docs/reference/iso_read_dual_inlet.html @@ -8,11 +8,13 @@ Load dual inlet data — iso_read_dual_inlet • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Load dual inlet data

    -
    -
    iso_read_dual_inlet(..., root = ".",
    +    
    iso_read_dual_inlet(
    +  ...,
    +  root = ".",
       read_raw_data = default(read_raw_data),
       read_file_info = default(read_file_info),
       read_method_info = default(read_method_info),
       read_vendor_data_table = default(read_vendor_data_table),
    -  nu_masses = c(), discard_duplicates = TRUE, parallel = FALSE,
    -  parallel_plan = future::multiprocess, cache = default(cache),
    -  cache_files_with_errors = TRUE, read_cache = default(cache),
    -  quiet = default(quiet))
    - + nu_masses = c(), + discard_duplicates = TRUE, + parallel = FALSE, + parallel_plan = future::multisession, + cache = default(cache), + cache_files_with_errors = TRUE, + read_cache = default(cache), + quiet = default(quiet) +)
    +

    Arguments

    @@ -181,7 +212,7 @@

    Arg

    - + @@ -200,20 +231,20 @@

    Arg

    parallel_plan

    which parallel processing strategy to use, see plan, typically future::multiprocess (the default, uses multicore if supported by the operating system, otherwise multisession), future::multisession or future::multicore.

    which parallel processing strategy to use, see plan, typically future::multisession for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use future::multicore.

    cache

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    See also

    -

    Other isoread functions for different types of IRMS data: iso_read_continuous_flow

    - +

    Other isoread functions for different types of IRMS data: +iso_read_continuous_flow(), +iso_read_scan()

    diff --git a/docs/reference/iso_read_files.html b/docs/reference/iso_read_files.html index ed89ed1f..10aaeda9 100644 --- a/docs/reference/iso_read_files.html +++ b/docs/reference/iso_read_files.html @@ -8,11 +8,13 @@ Core function to read isotope data files — iso_read_files • Isoreader + + @@ -32,8 +34,8 @@ - + @@ -41,6 +43,7 @@ + @@ -78,29 +81,52 @@ Isoreader - 1.0.11 + 1.0.17 -
    iso_read_files(paths, root, supported_extensions, data_structure,
    -  read_options = c(), reader_options = list(),
    -  discard_duplicates = TRUE, parallel = FALSE,
    -  parallel_plan = future::multiprocess, cache = default(cache),
    -  cache_files_with_errors = TRUE, read_cache = default(cache),
    -  quiet = default(quiet))
    - +
    iso_read_files(
    +  paths,
    +  root,
    +  supported_extensions,
    +  data_structure,
    +  read_options = c(),
    +  reader_options = list(),
    +  discard_duplicates = TRUE,
    +  parallel = FALSE,
    +  parallel_plan = future::multisession,
    +  cache = default(cache),
    +  cache_files_with_errors = TRUE,
    +  read_cache = default(cache),
    +  quiet = default(quiet)
    +)
    +

    Arguments

    @@ -178,7 +211,7 @@

    Arg

    - + @@ -197,20 +230,18 @@

    Arg

    parallel_plan

    which parallel processing strategy to use, see plan, typically future::multiprocess (the default, uses multicore if supported by the operating system, otherwise multisession), future::multisession or future::multicore.

    which parallel processing strategy to use, see plan, typically future::multisession for compatibility with RStudio interactive mode. If supported by the operating system and running in detached mode (not interactively in RStudio) can also use future::multicore.

    cache

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    Value

    single iso_file object (if single file) or list of iso_files (iso_file_list)

    - diff --git a/docs/reference/iso_read_scan.html b/docs/reference/iso_read_scan.html index 1f84a9fb..45d7260d 100644 --- a/docs/reference/iso_read_scan.html +++ b/docs/reference/iso_read_scan.html @@ -8,11 +8,13 @@ Load scan data — iso_read_scan • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17 diff --git a/docs/reference/iso_rename_file_info.html b/docs/reference/iso_rename_file_info.html index 72c01da1..184eef8a 100644 --- a/docs/reference/iso_rename_file_info.html +++ b/docs/reference/iso_rename_file_info.html @@ -8,11 +8,13 @@ Rename file info columns — iso_rename_file_info • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Rename individual file info columns (iso_get_file_info) within isofile objects. Works just like dplyr's rename except that it can rename different columns into the same name in different iso_files depending on what exists in each file. This is very useful when working with data from multiple instruments that may have the same information (e.g. sample name) stored in different columns. You can also use rename directly but it will not provide summary information on the operation. To select specific columns to keep (discarding all others), use iso_select_file_info instead.

    -
    iso_rename_file_info(iso_files, ..., quiet = default(quiet))
    - +

    Arguments

    @@ -148,24 +172,23 @@

    Arg

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    See also

    - - + diff --git a/docs/reference/iso_reread_files.html b/docs/reference/iso_reread_files.html index a6c2da94..7683273d 100644 --- a/docs/reference/iso_reread_files.html +++ b/docs/reference/iso_reread_files.html @@ -8,11 +8,13 @@ Re-read iso_files — iso_reread_files • Isoreader + + @@ -32,8 +34,8 @@ - + @@ -41,6 +43,7 @@ + @@ -78,29 +81,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Sometimes it is useful to reload isotope files from their original data files (e.g. after upgrading to a newer version of the isoreader package). The functions described below are intended to make this very easy. However, it is only possible for iso_file objects whose file paths still point to the original raw data files.

    -
    -
    iso_reread_files(iso_files, ..., stop_if_missing = FALSE,
    -  quiet = default(quiet))
    +    
    iso_reread_files(
    +  iso_files,
    +  ...,
    +  stop_if_missing = FALSE,
    +  quiet = default(quiet)
    +)
     
    -iso_reread_storage(rds_filepaths, ..., stop_if_missing = FALSE,
    -  quiet = default(quiet))
    +iso_reread_storage(
    +  rds_filepaths,
    +  ...,
    +  stop_if_missing = FALSE,
    +  quiet = default(quiet)
    +)
     
     iso_reread_archive(...)
    - +

    Arguments

    @@ -166,29 +198,25 @@

    Arg

    the path(s) to the iso_files R data archive(s) to re-read (can be a single file or vector of files)

    - +

    Details

    iso_reread_files will re-read all the original data files for the passed in iso_files object. Returns the reread iso_file objects.

    iso_reread_storage is a convenience function for refreshing saved iso_file collections (see iso_save). It will load a specific iso_files R Data Storage file (rds_filepath), re-read all the data from the original data files and save the collection back to the same rds file. The filepaths are returned invisibly.

    Note that this function will also reread the older .rda Archive files but store them in the newer .rds format after re-reading.

    iso_reread_archive is deprecated in favour of iso_reread_storage

    -

    Note

    re-reading files with their original read parameters is not yet supported

    - diff --git a/docs/reference/iso_root_paths.html b/docs/reference/iso_root_paths.html index f7f19d38..bb6d9f1d 100644 --- a/docs/reference/iso_root_paths.html +++ b/docs/reference/iso_root_paths.html @@ -8,11 +8,13 @@ Root paths — iso_root_paths • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    iso_root_paths(path, root = ".", check_existence = TRUE)
    - +

    Arguments

    @@ -148,28 +172,25 @@

    Arg

    whether to check for the existence of the paths

    - +

    Value

    a data frame with the root directories and paths relative to the root - order of input paths is preserved

    -

    See also

    - - + diff --git a/docs/reference/iso_save.html b/docs/reference/iso_save.html index 73d50e3e..903f908e 100644 --- a/docs/reference/iso_save.html +++ b/docs/reference/iso_save.html @@ -8,11 +8,13 @@ Save data to R Data Storage (.rds) — iso_save • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    iso_save(iso_files, filepath, quiet = default(quiet))
    - +

    Arguments

    @@ -148,28 +172,25 @@

    Arg

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    Value

    returns the iso_files object invisibly for use in pipelines

    -

    See also

    - - + diff --git a/docs/reference/iso_select_file_info.html b/docs/reference/iso_select_file_info.html index 66fda868..2841f627 100644 --- a/docs/reference/iso_select_file_info.html +++ b/docs/reference/iso_select_file_info.html @@ -8,11 +8,13 @@ Select file info columns — iso_select_file_info • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Select which file info columns (iso_get_file_info) to keep within isofile objects. Works just like dplyr's select except that it can select different columns in different iso_files depending on what exists in each file (also works for on-the-fly renaming of columns). This is very useful when working with data from multiple instruments that may have the same information (e.g. sample name) stored in different columns. You can also use select directly but it will not provide summary information on the operation. To rename columns without removing all other information, use iso_rename_file_info instead.

    -
    iso_select_file_info(iso_files, ..., quiet = default(quiet))
    - +

    Arguments

    @@ -148,24 +172,23 @@

    Arg

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    See also

    - - + diff --git a/docs/reference/iso_set_default_read_parameters.html b/docs/reference/iso_set_default_read_parameters.html index 95ea5a6e..6c5387c2 100644 --- a/docs/reference/iso_set_default_read_parameters.html +++ b/docs/reference/iso_set_default_read_parameters.html @@ -8,11 +8,13 @@ Set default read options — iso_set_default_read_parameters • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Set default read options

    -
    -
    iso_set_default_read_parameters(data = NULL, read_raw_data,
    -  read_file_info, read_method_info, read_vendor_data_table,
    -  quiet = default(quiet))
    - +
    iso_set_default_read_parameters(
    +  data = NULL,
    +  read_raw_data,
    +  read_file_info,
    +  read_method_info,
    +  read_vendor_data_table,
    +  quiet = default(quiet)
    +)
    +

    Arguments

    @@ -162,23 +191,22 @@

    Arg

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - +

    See also

    - - + diff --git a/docs/reference/iso_shorten_relative_paths.html b/docs/reference/iso_shorten_relative_paths.html index 5e7144e3..aa2c5916 100644 --- a/docs/reference/iso_shorten_relative_paths.html +++ b/docs/reference/iso_shorten_relative_paths.html @@ -8,11 +8,13 @@ Shorten relative paths — iso_shorten_relative_paths • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Convenience function to shorten relative paths based on overlap with the provided root(s). Also simplifies current directory repeats (e.g. "././." becomes ".") for better legiblity. Does not check whether the original or resulting paths point to valid files or folders. Relative paths that do not start with the supplied root default back to the current working directory (.). Absolute paths are allowed but are returned as is without attempts at shortening. See iso_find_absolute_path_roots for rooting absolute paths.

    -
    iso_shorten_relative_paths(path, root = ".")
    - +

    Arguments

    @@ -144,17 +168,16 @@

    Arg

    root for relative paths. Can be relative to the current working directory (e.g. "data") or an absolute path on the file system (e.g. "/Users/..." or "C:/Data/.."). The default is the current working directory ("."). Can be supplied as a vector of same length as the provided paths if the paths have different roots.

    - +

    Value

    a data frame with the root directories and paths relative to the root - order of input paths is preserved

    -

    See also

    - - +

    Other file system functions: +iso_expand_paths(), +iso_find_absolute_path_roots(), +iso_root_paths()

    Examples

    iso_shorten_relative_paths(file.path("A", "B", "C"), "A") # root = "A", path = B/C
    #> # A tibble: 1 x 2 @@ -175,11 +198,8 @@

    Examp

    Contents

    diff --git a/docs/reference/iso_show_default_reader_parameters.html b/docs/reference/iso_show_default_reader_parameters.html index 6e7c127a..1c292409 100644 --- a/docs/reference/iso_show_default_reader_parameters.html +++ b/docs/reference/iso_show_default_reader_parameters.html @@ -8,11 +8,13 @@ Show the current default parameters — iso_show_default_reader_parameters • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Shows a table with the default function parameters for this package.

    -
    iso_show_default_reader_parameters(data = NULL, func = NULL, ...)
    - +

    Arguments

    @@ -149,23 +173,22 @@

    Arg

    additional parameters to forward to the func function

    - +

    See also

    - - + diff --git a/docs/reference/iso_strip_units.html b/docs/reference/iso_strip_units.html index 2ca7e204..4979fbc4 100644 --- a/docs/reference/iso_strip_units.html +++ b/docs/reference/iso_strip_units.html @@ -8,11 +8,13 @@ Strip units from variables — iso_strip_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    This function converts numbers with units back into unitless numbers both for single variables and data frames / tibbles. For single variables, this is equivalent to the as.numeric function.

    -
    iso_strip_units(x)
    - +

    Arguments

    @@ -140,24 +164,23 @@

    Arg

    variable to strip units from (vector or data frame)

    - +

    See also

    - - + diff --git a/docs/reference/isoread.html b/docs/reference/isoread.html index 18f760ff..79e7f169 100644 --- a/docs/reference/isoread.html +++ b/docs/reference/isoread.html @@ -8,11 +8,13 @@ Read isotope data file — isoread • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    isoread(...)
    - +

    Arguments

    @@ -140,14 +164,14 @@

    Arg

    original isoread parameters

    - + diff --git a/docs/reference/isoreader-package.html b/docs/reference/isoreader-package.html index 2b29c059..3c33e5c1 100644 --- a/docs/reference/isoreader-package.html +++ b/docs/reference/isoreader-package.html @@ -8,11 +8,13 @@ isoreader: Read IRMS data files — isoreader-package • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry.

    -
    - + +

    See also

    Useful links:

      @@ -139,18 +164,16 @@

      See a

    - diff --git a/docs/reference/print.binary_structure_map.html b/docs/reference/print.binary_structure_map.html index f9c39ef9..4ed5423f 100644 --- a/docs/reference/print.binary_structure_map.html +++ b/docs/reference/print.binary_structure_map.html @@ -8,11 +8,13 @@ Print binary structure map — print.binary_structure_map • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Print binary structure map

    -
    # S3 method for binary_structure_map
    -print(x, ..., data_as_raw = FALSE,
    -  line_break_blocks = c("cblock", "stx", "etx"), pos_info = TRUE)
    - +print( + x, + ..., + data_as_raw = FALSE, + line_break_blocks = c("cblock", "stx", "etx"), + pos_info = TRUE +)
    +

    Arguments

    @@ -158,14 +187,14 @@

    Arg

    whether to include position information

    - + diff --git a/docs/reference/read_iso_file.html b/docs/reference/read_iso_file.html index a61e0cae..32fb045a 100644 --- a/docs/reference/read_iso_file.html +++ b/docs/reference/read_iso_file.html @@ -8,11 +8,13 @@ Read individual iso file — read_iso_file • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Low level read function for an individual iso file. Usually not called directly but available for methods development.

    -
    -
    read_iso_file(ds, root, path, file_n, files_n, read_from_cache,
    -  write_to_cache, write_to_cache_if_errors, cachepath, ext, reader_fun,
    -  reader_options, reader_fun_env)
    - +
    read_iso_file(
    +  ds,
    +  root,
    +  path,
    +  file_n,
    +  files_n,
    +  read_from_cache,
    +  write_to_cache,
    +  write_to_cache_if_errors,
    +  cachepath,
    +  ext,
    +  reader_fun,
    +  reader_options,
    +  reader_fun_env
    +)
    +

    Arguments

    @@ -190,14 +226,14 @@

    Arg

    where to find the reader function

    - + diff --git a/docs/reference/reexports.html b/docs/reference/reexports.html index b546f25d..bfb92b2d 100644 --- a/docs/reference/reexports.html +++ b/docs/reference/reexports.html @@ -8,11 +8,13 @@ Objects exported from other packages — reexports • Isoreader + + @@ -32,8 +34,8 @@ - + + @@ -87,29 +90,52 @@ Isoreader - 1.0.11 + 1.0.17 - + + diff --git a/docs/reference/set_temp.html b/docs/reference/set_temp.html index 4eb9c06a..080d058f 100644 --- a/docs/reference/set_temp.html +++ b/docs/reference/set_temp.html @@ -8,11 +8,13 @@ Set temporary option — set_temp • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    Set a temporary option for parallel processing in isoprocessor.

    -
    set_temp(name, value)
    - +

    Arguments

    @@ -144,14 +168,14 @@

    Arg

    value of the temporary option

    - + diff --git a/docs/reference/vec_arith.iso_double_with_units.html b/docs/reference/vec_arith.iso_double_with_units.html index 1eeb4223..d4fad7f5 100644 --- a/docs/reference/vec_arith.iso_double_with_units.html +++ b/docs/reference/vec_arith.iso_double_with_units.html @@ -8,11 +8,13 @@ vec_arith for iso_double_with_units — vec_arith.iso_double_with_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    vec_arith for iso_double_with_units

    -
    # S3 method for iso_double_with_units
     vec_arith(op, x, y, ...)
    - +

    Arguments

    @@ -155,14 +179,14 @@

    Arg

    These dots are for future extensions and must be empty.

    - + diff --git a/docs/reference/vec_cast.iso_double_with_units.html b/docs/reference/vec_cast.iso_double_with_units.html index b95eb831..110a13c8 100644 --- a/docs/reference/vec_cast.iso_double_with_units.html +++ b/docs/reference/vec_cast.iso_double_with_units.html @@ -8,11 +8,13 @@ vec_cast for iso_double_with_units — vec_cast.iso_double_with_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    vec_cast for iso_double_with_units

    -
    # S3 method for iso_double_with_units
     vec_cast(x, to, ...)
    - +

    Arguments

    @@ -151,14 +175,14 @@

    Arg extensions and should be empty.

    - + diff --git a/docs/reference/vec_ptype2.iso_double_with_units.html b/docs/reference/vec_ptype2.iso_double_with_units.html index 73375257..955685d8 100644 --- a/docs/reference/vec_ptype2.iso_double_with_units.html +++ b/docs/reference/vec_ptype2.iso_double_with_units.html @@ -8,11 +8,13 @@ vec_ptype2 for iso_double_with_units — vec_ptype2.iso_double_with_units • Isoreader + + @@ -32,13 +34,14 @@ - + + @@ -76,29 +79,52 @@ Isoreader - 1.0.11 + 1.0.17
    -

    vec_ptype2 for iso_double_with_units

    -
    # S3 method for iso_double_with_units
     vec_ptype2(x, y, ...)
    - +

    Arguments

    @@ -149,14 +173,14 @@

    Arg

    These dots are for future extensions and must be empty.

    - + diff --git a/docs/sitemap.txt b/docs/sitemap.txt deleted file mode 100644 index 53c74aa6..00000000 --- a/docs/sitemap.txt +++ /dev/null @@ -1,48 +0,0 @@ -http://isoreader.isoverse.org//reference/extract_data.html -http://isoreader.isoverse.org//reference/extract_substring.html -http://isoreader.isoverse.org//reference/extract_word.html -http://isoreader.isoverse.org//reference/iso_caching.html -http://isoreader.isoverse.org//reference/iso_calculate_ratios.html -http://isoreader.isoverse.org//reference/iso_cleanup_reader_cache.html -http://isoreader.isoverse.org//reference/iso_convert_signals.html -http://isoreader.isoverse.org//reference/iso_convert_time.html -http://isoreader.isoverse.org//reference/iso_data_structure.html -http://isoreader.isoverse.org//reference/iso_debug_mode.html -http://isoreader.isoverse.org//reference/iso_export_to_excel.html -http://isoreader.isoverse.org//reference/iso_export_to_feather.html -http://isoreader.isoverse.org//reference/iso_export_to_rda.html -http://isoreader.isoverse.org//reference/iso_filter_files.html -http://isoreader.isoverse.org//reference/iso_get_data.html -http://isoreader.isoverse.org//reference/iso_get_data_summary.html -http://isoreader.isoverse.org//reference/iso_get_default_reader_parameters.html -http://isoreader.isoverse.org//reference/iso_get_file_info.html -http://isoreader.isoverse.org//reference/iso_get_problems.html -http://isoreader.isoverse.org//reference/iso_get_problems_summary.html -http://isoreader.isoverse.org//reference/iso_get_raw_data.html -http://isoreader.isoverse.org//reference/iso_get_reader_example.html -http://isoreader.isoverse.org//reference/iso_get_resistors_info.html -http://isoreader.isoverse.org//reference/iso_get_standards_info.html -http://isoreader.isoverse.org//reference/iso_get_supported_file_types.html -http://isoreader.isoverse.org//reference/iso_get_vendor_data_table.html -http://isoreader.isoverse.org//reference/iso_has_problems.html -http://isoreader.isoverse.org//reference/iso_info_messages.html -http://isoreader.isoverse.org//reference/iso_filter_files_with_problems.html -http://isoreader.isoverse.org//reference/iso_plot_continuous_flow_data.html -http://isoreader.isoverse.org//reference/iso_plot_dual_inlet_data.html -http://isoreader.isoverse.org//reference/iso_plot_raw_data.html -http://isoreader.isoverse.org//reference/iso_printing.html -http://isoreader.isoverse.org//reference/iso_problem_functions.html -http://isoreader.isoverse.org//reference/iso_read_continuous_flow.html -http://isoreader.isoverse.org//reference/iso_read_dual_inlet.html -http://isoreader.isoverse.org//reference/iso_read_files.html -http://isoreader.isoverse.org//reference/iso_read_scan.html -http://isoreader.isoverse.org//reference/iso_reread_files.html -http://isoreader.isoverse.org//reference/iso_set_default_read_parameters.html -http://isoreader.isoverse.org//reference/iso_show_default_reader_parameters.html -http://isoreader.isoverse.org//reference/isoread.html -http://isoreader.isoverse.org//reference/isoreader-package.html -http://isoreader.isoverse.org//reference/print.binary_structure_map.html -http://isoreader.isoverse.org//reference/reexports.html -http://isoreader.isoverse.org//articles/continuous_flow.html -http://isoreader.isoverse.org//articles/dual_inlet.html -http://isoreader.isoverse.org//articles/operations.html diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 3b47816b..98b23632 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -222,4 +222,10 @@ http://isoreader.isoverse.org//articles/operations.html + + http://isoreader.isoverse.org//articles/quick_start.html + + + http://isoreader.isoverse.org//articles/scan.html + diff --git a/man/iso_get_reader_example.Rd b/man/iso_get_reader_example.Rd index 5ca9a416..80a420cc 100644 --- a/man/iso_get_reader_example.Rd +++ b/man/iso_get_reader_example.Rd @@ -3,11 +3,14 @@ \name{iso_get_reader_example} \alias{iso_get_reader_example} \alias{iso_get_reader_examples} +\alias{iso_get_reader_examples_folder} \title{Example files} \usage{ iso_get_reader_example(filename) iso_get_reader_examples() + +iso_get_reader_examples_folder() } \arguments{ \item{filename}{the name of the example file for which to retrieve the system path} @@ -19,7 +22,10 @@ The isoreader package comes with a few example files to make it easy to illustra \code{iso_get_reader_example}: retrieve the path to an isoreader example file \code{iso_get_reader_examples}: list of all available isoreader example files + +\code{iso_get_reader_examples_folder}: path to the location of the reader examples } \examples{ iso_get_reader_examples() +iso_get_reader_examples_folder() } From a701a312e1cf2995033ac9ac4426617c60d00069 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 00:37:37 -0700 Subject: [PATCH 41/51] simplify NSE --- R/nse.R | 112 +++++++++++++++----------------------- tests/testthat/test-nse.R | 31 ++--------- 2 files changed, 49 insertions(+), 94 deletions(-) diff --git a/R/nse.R b/R/nse.R index 6d3cb3b4..84e3e906 100644 --- a/R/nse.R +++ b/R/nse.R @@ -1,52 +1,4 @@ -# Test quo expressions by confirming they can create a valid column in a mutate -# @note that global variables will be interpreted even in the context of the data frame -# ideally this will be only within the data frame -check_expressions <- function(df, ...) { - - # df name and data frame test - if (missing(df)) stop("no data frame supplied", call. = FALSE) - df_name <- enquo(df) %>% rlang::as_label() - df <- enquo(df) %>% eval_tidy() - if (!is.data.frame(df)) - glue("parameter {df_name} is not a data frame") %>% stop(call. = FALSE) - - # use a safe version of mutate to check all expresions - # (use mutate instead of eval_tidy to make sure it's absolutely valid) - safe_eval <- safely(mutate) - expr_quos <- quos(!!!list(...)) %>% - # make sure to evaluate calls to default - resolve_defaults() %>% - # make sure that the expressions are locally evaluated - map(~quo(!!get_expr(.x))) - expr_quos <- expr_quos[!map_lgl(expr_quos, quo_is_null)] - expr_errors <- map(expr_quos, ~safe_eval(df, !!.x)$error) - - # check results - ok <- map_lgl(expr_errors, ~is.null(.x)) - - # summarize if there were any errors - if (!all(ok)) { - params <- - map2_chr(names(expr_quos)[!ok], expr_quos[!ok], function(var, val) { - if (nchar(var) > 0 && var != rlang::as_label(val)) str_c(var, " = ", rlang::as_label(val)) - else rlang::as_label(val) - }) %>% - collapse("', '", last = "' and '") - errors <- map_chr(expr_errors[!ok], ~.x$message) %>% collapse("\n- ") - err_msg <- - if (sum(!ok) > 1) - glue("'{params}' are invalid expressions in data frame '{df_name}':\n- {errors}") - else - glue("'{params}' is not a valid expression in data frame '{df_name}':\n- {errors}") - stop(err_msg, call. = FALSE) - } - - return(invisible(df)) -} - -# Get the column names for a set of parameters referencing columns in a data frame. Compatible with all dplyr type column selection -# criteria including both standard and non-standard evaluation. Throws errors if expressions cannot be evaluated or if an incorrect -# number of columns are identified for a given parameter. +# Get the column names for a set of parameters referencing columns in a data frame. Compatible with all dplyr type column selection criteria including both standard and non-standard evaluation. Throws errors if expressions cannot be evaluated (unless \code{cols_must_exist = FALSE}) or if an incorrect number of columns are identified for a given parameter. To ignore missing columns entirely, set \code{cols_must_exist = FALSE} and \code{warn = FALSE}. # @param df the data frame # @param ... named expressions with variable selection criteria (anything that tidyselect::eval_select supports) # @param n_reqs named list to specify how many columns are allowed/required for each selection criterion, default for all that are not specified is 1. @@ -63,20 +15,6 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ if (!is.data.frame(df)) sprintf("parameter '%s' is not a data frame", df_name) %>% stop(call. = FALSE) - # use a safe version of eval select to get all the column names - # note: uses tidyselect:eval_select because vars_select is in questioning stage - safe_vars_select <- function(col_exp, ...) { - safe_pos <- safely(tidyselect::eval_select)(col_exp, data = df, ...) - if (is.null(safe_pos$error)) { - # assign names based on opsition indices - safe_pos$result <- rlang::set_names(names(df)[safe_pos$result], names(safe_pos$result)) - # assign any missing names with the values - names(safe_pos$result)[nchar(names(safe_pos$result)) == 0] <- - safe_pos$result[nchar(names(safe_pos$result)) == 0] - } - return(safe_pos) - } - # get colum name expressions from ... cols_exps <- list(...) %>% # make sure to evaluate calls to default @@ -86,8 +24,9 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ # naming { if(is.null(names(.))) setNames(., rep("", length(.))) else . } - cols_results <- map(cols_exps, safe_vars_select) - ok <- map_lgl(cols_results, ~is.null(.x$error)) + # find column positions + pos_results <- map(cols_exps, safe_local_eval_select, data = df) + ok <- map_lgl(pos_results, ~is.null(.x$error)) # summarize if there were any errors if (!all(ok)) { @@ -97,7 +36,9 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ else rlang::as_label(val) }) %>% collapse("', '", last = "' and '") - errors <- map_chr(cols_results[!ok], ~.x$error$message) %>% collapse("\n- ") + # have to use capture.output because rlang errors don't store their error in $error$message + errors <- map_chr(pos_results[!ok], ~stringr::str_replace(.x$error, "\n", " ")) %>% + paste(collapse = "\n- ") err_msg <- if (sum(!ok) > 1) glue("'{params}' refer to unknown columns in data frame '{df_name}':\n- {errors}") @@ -109,12 +50,12 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ } else { # just a warning and find the columns omitting those missing if (warn) warning(err_msg, immediate. = TRUE, call. = FALSE) - cols_results <- map(cols_exps, safe_vars_select, strict = FALSE) + pos_results <- map(cols_exps, safe_local_eval_select, data = df, strict = FALSE) } } # check on the number requirements for each column match - cols <- map(cols_results, ~(.x$result)) + cols <- map(pos_results, ~eval_select_pos_to_cols(.x$result, data = df)) if (any(missing <- !names(n_reqs) %in% names(cols))) glue("column requirements for unknow parameter(s) provided: {collapse(names(n_reqs[missing]), ', ')}") %>% stop(call. = FALSE) @@ -197,3 +138,38 @@ get_column_names <- function(df, ..., n_reqs = list(), type_reqs = list(), cols_ return(cols) } + +# convert positions to column names +# @param the positions +# @param df the data frame +eval_select_pos_to_cols <- function(pos, data) { + # assign names based on position indices + cols <- rlang::set_names(names(data)[pos], names(pos)) + # assign any missing names based on the values (this might be a fix for tidyselect bug) + names(cols)[nchar(names(cols)) == 0] <- cols[nchar(names(cols)) == 0] + return(cols) +} + +# note: uses tidyselect:eval_select because vars_select is in questioning stage +# note: force local scope to avoid conflicts with variables in the global environment +# force local evaluation of tidyselect::eval_select +local_eval_select <- local(function(expr, data, ...) { + tidyselect::eval_select(expr, data = data, ...) +}, as.environment(2)) + +# force local evaluation of tidyselect::eval_rename +local_eval_rename <- local(function(expr, data, ...) { + tidyselect::eval_rename(expr, data = data, ...) +}, as.environment(2)) + +# safe local evaluation of tidyselect::eval_select +# note: don't use purrr::safely as it invalidates the local scope! +# @return list(return = value, error = NULL) if successful or list(return = NULL, error = character) if failed +safe_local_eval_select <- function(expr, data, ...) { + # try catch find positions + pos <- tryCatch(local_eval_select(expr, data, ...), error = conditionMessage) + if (is.integer(pos)) + return(list(result = pos, error = NULL)) + else + return(list(result = NULL, error = pos)) +} diff --git a/tests/testthat/test-nse.R b/tests/testthat/test-nse.R index 40261d15..ee38ec94 100644 --- a/tests/testthat/test-nse.R +++ b/tests/testthat/test-nse.R @@ -1,26 +1,5 @@ context("Standard and non-standard evaluation") -test_that("Testing expression checks", { - - df <- as_data_frame(mtcars) %>% tibble::rownames_to_column() - - # basic errors - expect_error(check_expressions(), "no data frame supplied") - expect_error(check_expressions(5), "not a data frame") - - # error messages - expect_error(check_expressions(df, quo(x == 5)), "not a valid expression") - expect_error(check_expressions(df, quo(x == 5), quo(y == 42)), "invalid expressions") - - # test evaluations - expect_equal( - check_expressions(df, quo(mpg > 20), quo(ifelse(grepl("Merc", rowname), "test", rowname)), z = NULL), - df - ) - -}) - - test_that("Getting column names (with # and type requirement checks) works", { df <- tibble::as_tibble(mtcars) %>% tibble::rownames_to_column() @@ -78,11 +57,11 @@ test_that("Getting column names (with # and type requirement checks) works", { expect_equal(get_column_names(df, a = quo(c(x = starts_with("c"))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) # expected error from conversion to expressions inside get_column_names (may not work interactively!) - test <- "c" - expect_error(get_column_names(df, a = quo(c(x = starts_with(test))), n_reqs = list(a = "*")), "'test' not found") - expect_error(get_column_names(df, a = expr(c(x = starts_with(test))), n_reqs = list(a = "*")), "'test' not found") - expect_equal(get_column_names(df, a = quo(c(x = starts_with(!!test))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) - expect_equal(get_column_names(df, a = expr(c(x = starts_with(!!test))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) + ..test.. <- "c" + expect_error(get_column_names(df, a = quo(c(x = starts_with(..test..))), n_reqs = list(a = "*")), "'..test..' not found") + expect_error(get_column_names(df, a = expr(c(x = starts_with(..test..))), n_reqs = list(a = "*")), "'..test..' not found") + expect_equal(get_column_names(df, a = quo(c(x = starts_with(!!..test..))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) + expect_equal(get_column_names(df, a = expr(c(x = starts_with(!!..test..))), n_reqs = list(a = "*")), list(a = c(x1 = "cyl", x2 = "carb"))) # allow missing columns expect_warning(get_column_names(df, a = quo(x), n_reqs = list(a = "*"), cols_must_exist = FALSE), "unknown column") From 7d10f80c3173700481ed08273216161f010e0fe6 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 00:42:11 -0700 Subject: [PATCH 42/51] data aggregation more streamlined --- R/aggregate_data.R | 288 +++++++++++++++++---------- tests/testthat/test-aggregate-data.R | 202 +++++++++++++------ 2 files changed, 323 insertions(+), 167 deletions(-) diff --git a/R/aggregate_data.R b/R/aggregate_data.R index 93415284..07405205 100644 --- a/R/aggregate_data.R +++ b/R/aggregate_data.R @@ -103,7 +103,7 @@ get_raw_data_info <- function(iso_files) { glue("cannot process '{class(iso_files[[1]])[1]}' in get_raw_data_info") %>% stop(call. = FALSE) } - return(select(raw_data_sum, file_id, raw_data = label)) + return(dplyr::select(raw_data_sum, file_id, raw_data = label)) } # summary of file info @@ -188,93 +188,121 @@ get_vendor_data_table_info <- function(iso_files) { # Specific data aggregation calls ===== +#' DEPRECATED +#' +#' Please use \link{iso_get_all_data} instead. +#' +#' @export +iso_get_data <- function(...) { + warning("'iso_get_data()' is deprecated in favor of the more descriptive 'iso_get_all_data()'. Please use 'iso_get_all_data()' directly to avoid this warning.", immediate. = TRUE, call. = FALSE) + iso_get_all_data(...) +} + #' Aggregate all isofiles data #' -#' This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data \code{\link{iso_get_raw_data}}, \code{\link{iso_get_file_info}}, \code{\link{iso_get_vendor_data_table}}, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To \code{\link[tidyr]{unnest}} any of the specific data types (e.g. \code{raw_data}), make sure to filter first for the files that have this data type available (e.g. \code{filter(has_raw_data)}). +#' This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data \code{\link{iso_get_raw_data}}, \code{\link{iso_get_file_info}}, \code{\link{iso_get_vendor_data_table}}, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To \code{\link[tidyr]{unnest}} any of the specific data types (e.g. \code{raw_data}), make sure to filter first for the files that have this data type available (e.g. \code{filter(has_raw_data)}). Exclude specific types of information by setting its \code{include...} parameter to \code{NULL} (Note: for historical reasons, setting it to \code{FALSE} will also include the information). #' #' @inheritParams iso_get_raw_data -#' @inheritParams iso_get_standards_info +#' @inheritParams iso_get_standards #' @inheritParams iso_get_vendor_data_table -#' @param include_raw_data which columns from the raw data to include use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. -#' @param include_vendor_data_table which columns from the vendor data table to include - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. +#' @param include_raw_data which columns from the raw data to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data. +#' @param include_standards which columns from the standards info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}. Set to NULL to include no standards info. +#' #' @param include_resistors which columns from the resistors info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info. +#' @param include_vendor_data_table which columns from the vendor data table to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set parameter \code{with_explicit_units = TRUE} to make column units explicit (keep in mind that this will require specific \code{include_vendor_data_table} column selections to reflect the column names including the units). Set to NULL to include no vendor data table. +#' @param include_problems which columns from problems to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes none of the read problems by default. Set to \code{include_problems = everything()} to include all columns. #' @return data_frame with file_ids, file_types and nested data frames for each data type (file_info, raw_data, vendor_data_table, etc.) #' @family data retrieval functions #' @export -iso_get_data <- function( +iso_get_all_data <- function( iso_files, - include_file_info = everything(), include_raw_data = everything(), include_vendor_data_table = everything(), - gather = FALSE, with_explicit_units = with_units, with_units = FALSE, with_ratios = FALSE, quiet = default(quiet)) { - + include_file_info = everything(), include_raw_data = everything(), + include_standards = everything(), include_resistors = everything(), + include_vendor_data_table = everything(), + include_problems = NULL, + gather = FALSE, with_explicit_units = with_units, + with_units = FALSE, quiet = default(quiet)) { + + # info iso_files <- iso_as_file_list(iso_files) if (!quiet) sprintf("Info: aggregating all data from %d data file(s)", length(iso_files)) %>% message() - # global vars - vendor_data_table <- NULL + # is di or cf? + di_or_cf <- iso_is_continuous_flow(iso_files) || iso_is_dual_inlet(iso_files) + + # select expressions + include_file_info_exp <- rlang::enexpr(include_file_info) + include_file_info <- !rlang::as_label(include_file_info_exp) %in% c("NULL", "FALSE") + include_raw_data_exp <- rlang::enexpr(include_raw_data) + include_raw_data <- !rlang::as_label(include_raw_data_exp) %in% c("NULL", "FALSE") + include_standards_exp <- rlang::enexpr(include_standards) + include_standards <- di_or_cf && !rlang::as_label(include_standards_exp) %in% c("NULL", "FALSE") + include_resistors_exp <- rlang::enexpr(include_resistors) + include_resistors <- !rlang::as_label(include_resistors_exp) %in% c("NULL", "FALSE") + include_vendor_data_table_exp <- rlang::enexpr(include_vendor_data_table) + include_vendor_data_table <- di_or_cf && !rlang::as_label(include_vendor_data_table_exp) %in% c("NULL", "FALSE") + include_problems_exp <- rlang::enexpr(include_problems) + include_problems <- !rlang::as_label(include_problems_exp) %in% c("NULL", "FALSE") # file class file_class <- - data_frame( + tibble( file_id = names(iso_files), file_type = map_chr(iso_files, ~class(.x)[1]) ) + # all file data + # note that this uses the iso_get_... functions to have some built in error + # checking although a straight up map(~.x$...) would be faster + + # data merge function + merge_with_file_class <- function(new_df, col_name) { + nested_df <- nest(new_df[c(),], !!col_name := c(-file_id)) + if (ncol(new_df) > 1) + nested_df <- nest(new_df, !!col_name := c(-file_id)) + nested_df <- bind_rows(nested_df, tibble(file_id = setdiff(file_class$file_id, nested_df$file_id), !!col_name := list(tibble()))) + left_join(file_class, nested_df, by = "file_id") + } + # file info - include_file_info_quo <- enquo(include_file_info) - file_info <- iso_get_file_info(iso_files, select = !!include_file_info_quo, quiet = TRUE) - if (ncol(file_info) > 1) - file_info <- nest(file_info, file_info = c(-file_id)) - else - file_info <- tibble(file_id = character(0), file_info = list(NULL)) + if (include_file_info) { + file_class <- iso_get_file_info(iso_files, select = !!include_file_info_exp, quiet = TRUE) %>% + merge_with_file_class("file_info") + } # raw data - include_raw_data_quo <- enquo(include_raw_data) - raw_data <- iso_get_raw_data(iso_files, select = !!include_raw_data_quo, gather = gather, quiet = TRUE) - if (ncol(raw_data) > 1) - raw_data <- nest(raw_data, raw_data = c(-file_id)) - else - raw_data <- tibble(file_id = character(0), raw_data = list(NULL)) + if (include_raw_data) { + file_class <- iso_get_raw_data(iso_files, select = !!include_raw_data_exp, gather = gather, quiet = TRUE) %>% + merge_with_file_class("raw_data") + } + + # standards + if (include_standards) { + file_class <- iso_get_standards(iso_files, select = !!include_standards_exp, quiet = TRUE) %>% + merge_with_file_class("standards") + } + + # resistors + if (include_resistors) { + file_class <- iso_get_resistors(iso_files, select = !!include_resistors_exp, quiet = TRUE) %>% + merge_with_file_class("resistors") + } # vendor data table (only cflow and dual inlet) - if (add_dt <- iso_is_continuous_flow(iso_files) || iso_is_dual_inlet(iso_files)) { - include_vendor_data_table_quo <- enquo(include_vendor_data_table) - dt <- iso_get_vendor_data_table(iso_files, with_explicit_units = with_explicit_units, select = !!include_vendor_data_table_quo, quiet = TRUE) - if (ncol(dt) > 1) - dt <- nest(dt, vendor_data_table = c(-file_id)) - else - dt <- tibble(file_id = character(0), vendor_data_table = list(NULL)) + if (include_vendor_data_table) { + file_class <- iso_get_vendor_data_table( + iso_files, + with_explicit_units = with_explicit_units, + select = !!include_vendor_data_table_exp, quiet = TRUE) %>% + merge_with_file_class("vendor_data_table") } - # methods_data - standards - standards <- iso_get_standards_info(iso_files, with_ratios = with_ratios, quiet = TRUE) - if (ncol(standards) > 1) - standards <- nest(standards, standards = c(-file_id)) - else - standards <- tibble(file_id = character(0), standards = list(NULL)) + # problems + if (include_problems) { + file_class <- iso_get_problems(iso_files, select = !!include_problems_exp) %>% + merge_with_file_class("problems") + } - # methods_data - resistors - resistors <- iso_get_resistors_info(iso_files, quiet = TRUE) - if (ncol(resistors) > 1) - resistors <- nest(resistors, resistors = c(-file_id)) - else - resistors <- tibble(file_id = character(0), resistors = list(NULL)) - - # combine everything - file_class %>% - left_join(file_info, by = "file_id") %>% - left_join(raw_data, by = "file_id") %>% - { if (add_dt) left_join(., dt, by = "file_id") else . } %>% - left_join(standards, by = "file_id") %>% - left_join(resistors, by = "file_id") %>% - # info about what's missing - mutate( - has_file_info = !map_lgl(file_info, is.null), - has_raw_data = !map_lgl(raw_data, is.null) - ) %>% - { if (add_dt) mutate(., has_vendor_data_table = !map_lgl(vendor_data_table, is.null)) else . } %>% - mutate( - has_standards = !map_lgl(standards, is.null), - has_resistors = !map_lgl(resistors, is.null) - ) + return(file_class) } #' Aggregate file info @@ -287,32 +315,42 @@ iso_get_data <- function( #' @note File info entries with multiple values remain nested multi-value (=list) columns and can be unnested using \link[tidyr]{unnest}. #' @export iso_get_file_info <- function(iso_files, select = everything(), quiet = default(quiet)) { + + # make sure it's an iso file list iso_files <- iso_as_file_list(iso_files) - select_quo <- enquo(select) + select_exp <- rlang::enexpr(select) if (!quiet) { glue::glue( "Info: aggregating file info from {length(iso_files)} data file(s)", - "{get_info_message_concat(select_quo, prefix = ', selecting info columns ', empty = 'everything()')}") %>% message() + "{get_info_message_concat(select_exp, prefix = ', selecting info columns ', empty = 'everything()')}") %>% message() } check_read_options(iso_files, "file_info") - # select columns - if ( rlang::as_label(select_quo) != "everything()") { - # run selection unless everything is selected - # (in which case it is a waste of time to run) - iso_files <- iso_select_file_info(iso_files, !!select_quo, quiet = TRUE) - } - # retrieve info - iso_files %>% + file_info <- iso_files %>% # retrieve file info map(~.x$file_info) %>% # combine in data frame (use safe bind to make sure different data column # types of the same name don't trip up the combination) - safe_bind_rows() %>% - # unnest aggregated data frame + safe_bind_rows() + + if(nrow(file_info) == 0) return(tibble(file_id = character(0))) + + # selecting columns + select_cols <- get_column_names(file_info, select = select_exp, n_reqs = list(select = "*"), cols_must_exist = FALSE)$select + if (!"file_id" %in% select_cols) + select_cols <- c("file_id", select_cols) # file id always included + + # final processing + file_info <- + file_info %>% + # focus on selected columns only (also takes care of the rename) + dplyr::select(!!!select_cols) %>% + # unnest aggregated columns unnest_aggregated_data_frame() + + return(file_info) } # note: consider providing a separate iso_gather_raw_data method that works just on the raw data table and could be used in other contexts @@ -323,7 +361,7 @@ iso_get_file_info <- function(iso_files, select = everything(), quiet = default( #' #' @inheritParams iso_read_files #' @param iso_files collection of iso_file objects -#' @param select which raw data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected. +#' @param select which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected. #' @family data retrieval functions #' @param gather whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the \code{select} parameter applies to the data columns BEFORE gathering. #' @param include_file_info which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns. @@ -335,23 +373,23 @@ iso_get_raw_data <- function(iso_files, select = everything(), gather = FALSE, i raw_data <- NULL iso_files <- iso_as_file_list(iso_files) - select_quo <- enquo(select) + select_exp <- rlang::enexpr(select) include_file_info_quo <- enquo(include_file_info) if (!quiet) { glue::glue( "Info: aggregating raw data from {length(iso_files)} data file(s)", - "{get_info_message_concat(select_quo, prefix = ', selecting data columns ', empty = 'everything()')}", + "{get_info_message_concat(select_exp, prefix = ', selecting data columns ', empty = 'everything()')}", "{get_info_message_concat(include_file_info_quo, prefix = ', including file info ')}") %>% message() } check_read_options(iso_files, "raw_data") # check whether there are any - if (length(iso_files) == 0) return(data_frame()) + if (length(iso_files) == 0) return(tibble()) # fetch data data <- # fetch data - data_frame( + tibble( file_id = names(iso_files), raw_data = map(iso_files, ~.x$raw_data) ) %>% @@ -361,10 +399,10 @@ iso_get_raw_data <- function(iso_files, select = everything(), gather = FALSE, i unnest(raw_data) # check for rows - if (nrow(data) == 0) return(data) + if (nrow(data) == 0) return(dplyr::select(data, file_id)) # selecting columns - select_cols <- get_column_names(data, select = select_quo, n_reqs = list(select = "*"), cols_must_exist = FALSE)$select + select_cols <- get_column_names(data, select = select_exp, n_reqs = list(select = "*"), cols_must_exist = FALSE)$select if (!"file_id" %in% select_cols) select_cols <- c("file_id", select_cols) # file id always included data <- data %>% @@ -420,12 +458,12 @@ iso_get_bgrd_data <- function(iso_files, select = everything(), gather = FALSE, iso_files <- iso_as_file_list(iso_files) if (!all(map_lgl(iso_files, iso_is_dual_inlet))) stop("background data is only available in dual inlet data files", call. = FALSE) - select_quo <- enquo(select) + select_exp <- rlang::enexpr(select) include_file_info_quo <- enquo(include_file_info) if (!quiet) { glue( "Info: aggregating background data from {length(iso_files)} data file(s)", - "{get_info_message_concat(select_quo, prefix = ', selecting data columns ', empty = 'everything()')}", + "{get_info_message_concat(select_exp, prefix = ', selecting data columns ', empty = 'everything()')}", "{get_info_message_concat(include_file_info_quo, prefix = ', including file info ')}") %>% message() } check_read_options(iso_files, "raw_data") @@ -446,10 +484,10 @@ iso_get_bgrd_data <- function(iso_files, select = everything(), gather = FALSE, unnest(bgrd_data) # check for rows - if (nrow(data) == 0) return(data) + if (nrow(data) == 0) return(dplyr::select(data, file_id)) # selecting columns - select_cols <- get_column_names(data, select = select_quo, n_reqs = list(select = "*"), cols_must_exist = FALSE)$select + select_cols <- get_column_names(data, select = select_exp, n_reqs = list(select = "*"), cols_must_exist = FALSE)$select if (!"file_id" %in% select_cols) select_cols <- c("file_id", select_cols) # file info always included data <- data %>% @@ -480,16 +518,25 @@ iso_get_bgrd_data <- function(iso_files, select = everything(), gather = FALSE, return(data) } +#' DEPRECATED +#' +#' Please use \link{iso_get_standards} instead. +#' +#' @export +iso_get_standards_info <- function(...) { + warning("'iso_get_standards_info()' is deprecated in favor of the simpler 'iso_get_standards()'. Please use 'iso_get_standards()' directly to avoid this warning.", immediate. = TRUE, call. = FALSE) + iso_get_standards(...) +} #' Aggregate standards from methods info #' #' Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter \code{with_ratios} to exclude/include the ratios. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE}. #' #' @inheritParams iso_get_raw_data -#' @param with_ratios whether to include ratios or just standard delta values +#' @param select which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}. #' @family data retrieval functions #' @export -iso_get_standards_info <- function(iso_files, with_ratios = FALSE, include_file_info = NULL, quiet = default(quiet)) { +iso_get_standards <- function(iso_files, select = everything(), include_file_info = NULL, quiet = default(quiet)) { iso_files <- iso_as_file_list(iso_files) @@ -513,23 +560,36 @@ iso_get_standards_info <- function(iso_files, with_ratios = FALSE, include_file_ # fetch data data <- # fetch data - data_frame( + tibble( file_id = names(iso_files), standards = map(iso_files, ~.x$method_info$standard), ref_ratios = map(iso_files, ~.x$method_info$reference_ratios) ) # check for rows - if (nrow(data) == 0) return(data) + if (nrow(data) == 0) return(dplyr::select(data, file_id)) # merge info - standards <- data %>% select(file_id, standards) %>% filter(!map_lgl(standards, is.null)) %>% unnest(standards) - if (with_ratios) { - ref_ratios <- data %>% select(file_id, ref_ratios) %>% filter(!map_lgl(ref_ratios, is.null)) %>% unnest(ref_ratios) - data <- left_join(standards, ref_ratios, by = c("file_id", "reference")) - } else { + standards <- data %>% + dplyr::select(file_id, standards) %>% + dplyr::filter(!map_lgl(standards, is.null)) %>% unnest(standards) + ref_ratios <- data %>% dplyr::select(file_id, ref_ratios) %>% + dplyr::filter(!map_lgl(ref_ratios, is.null)) %>% + tidyr::unnest(ref_ratios) + if ("reference" %in% names(ref_ratios)) + data <- dplyr::left_join(standards, ref_ratios, by = c("file_id", "reference")) + else data <- standards - } + + # select columns (only warn if it's not the default and cols don't exist) + select_exp <- rlang::enexpr(select) + warn <- rlang::as_label(select_exp) != rlang::as_label(formals(iso_get_standards)$select) + select_cols <- get_column_names(data, select = select_exp, n_reqs = list(select = "*"), cols_must_exist = FALSE, warn = warn)$select + if (!"file_id" %in% select_cols) + select_cols <- c("file_id", select_cols) # file info always included + + # focus on selected columns only (also takes care of the rename) + data <- dplyr::select(data, !!!select_cols) %>% unique() # if file info if (!quo_is_null(include_file_info_quo)) { @@ -539,6 +599,17 @@ iso_get_standards_info <- function(iso_files, with_ratios = FALSE, include_file_ return(data) } + +#' DEPRECATED +#' +#' Please use \link{iso_get_resistors} instead. +#' +#' @export +iso_get_resistors_info <- function(...) { + warning("'iso_get_resistors_info()' is deprecated in favor of the simpler 'iso_get_resistors()'. Please use 'iso_get_resistors()' directly to avoid this warning.", immediate. = TRUE, call. = FALSE) + iso_get_resistors(...) +} + #' Aggregate resistors from methods info #' #' Aggregates the resistor information recovered from the provided iso_files. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE} and only linked to specific masses if the iso_files were additionally read with parametr \code{read_raw_data=TRUE}. @@ -546,7 +617,7 @@ iso_get_standards_info <- function(iso_files, with_ratios = FALSE, include_file_ #' @inheritParams iso_get_raw_data #' @family data retrieval functions #' @export -iso_get_resistors_info <- function(iso_files, include_file_info = NULL, quiet = default(quiet)) { +iso_get_resistors <- function(iso_files, select = everything(), include_file_info = NULL, quiet = default(quiet)) { # global vars resistors <- NULL @@ -561,20 +632,31 @@ iso_get_resistors_info <- function(iso_files, include_file_info = NULL, quiet = check_read_options(iso_files, "method_info") # check whether there are any files - if (length(iso_files) == 0) return(data_frame()) + if (length(iso_files) == 0) return(tibble()) # fetch data data <- # fetch data - data_frame( + tibble( file_id = names(iso_files), resistors = map(iso_files, ~.x$method_info$resistors) ) %>% # make sure to include only existing raw data - filter(!map_lgl(resistors, is.null)) %>% + dplyr::filter(!map_lgl(resistors, is.null)) %>% # unnest - unnest(resistors) + tidyr::unnest(resistors) + # check for rows + if (nrow(data) == 0) return(dplyr::select(data, file_id)) + + # select columns + select_cols <- get_column_names(data, select = enquo(select), n_reqs = list(select = "*"), cols_must_exist = FALSE)$select + if (!"file_id" %in% select_cols) + select_cols <- c("file_id", select_cols) # file info always included + + # focus on selected columns only (also takes care of the rename) + data <- dplyr::select(data, !!!select_cols) + # if file info if (!quo_is_null(include_file_info_quo)) { info <- iso_get_file_info(iso_files, select = !!include_file_info_quo, quiet = TRUE) @@ -590,7 +672,7 @@ iso_get_resistors_info <- function(iso_files, include_file_info = NULL, quiet = #' @inheritParams iso_get_raw_data #' @inheritParams iso_get_file_info #' @param with_units this parameter has been DEPRECATED with the introduction of unit-data types (see \code{\link{iso_double_with_units}}) and will be removed in future versions of isoreader. Please use \code{with_explicit_units} instead if you really want columns to have units explicitly in the column name. Alternatively, consider working with the new implicit unit system and convert vendor data tables as needed with \code{\link{iso_make_units_explicit}} and \code{\link{iso_make_units_implicit}}. -#' @param with_explicit_units whether to include units in the column headers instead of the column data types (see \code{\link{iso_double_with_units}}) +#' @param with_explicit_units whether to include units in the column headers of the returned data frame instead of the column data types (see \code{\link{iso_double_with_units}}). Note that any \code{select} conditions have to refer to the column names including the full units. #' @family data retrieval functions #' @export iso_get_vendor_data_table <- function( @@ -627,7 +709,7 @@ iso_get_vendor_data_table <- function( } # check whether there are any files - if (length(iso_files) == 0) return(data_frame()) + if (length(iso_files) == 0) return(tibble()) # get vendor data column <- units <- NULL # global vars @@ -643,7 +725,7 @@ iso_get_vendor_data_table <- function( filter(map_lgl(dt, ~!is.null(.x) & nrow(.x) > 0)) # check for any rows - if (nrow(vendor_data_table) == 0) return(vendor_data_table) + if (nrow(vendor_data_table) == 0) return(dplyr::select(vendor_data_table, file_id)) # make units explicit if wanted if (with_explicit_units) { @@ -652,7 +734,7 @@ iso_get_vendor_data_table <- function( } # unnest - vendor_data_table <- dplyr::select(vendor_data_table, file_id, dt) %>% unnest(dt) + vendor_data_table <- dplyr::select(vendor_data_table, .data$file_id, .data$dt) %>% unnest(.data$dt) # get include information select_cols <- get_column_names(vendor_data_table, select = enquo(select), n_reqs = list(select = "*"), cols_must_exist = FALSE)$select diff --git a/tests/testthat/test-aggregate-data.R b/tests/testthat/test-aggregate-data.R index 40ef8805..2dfb6645 100644 --- a/tests/testthat/test-aggregate-data.R +++ b/tests/testthat/test-aggregate-data.R @@ -48,7 +48,7 @@ test_that("test that aggregation functions refuse to work with non iso_files", { expect_error(iso_get_data_summary(1), "encountered incompatible data type") expect_error(iso_get_raw_data(1), "encountered incompatible data type") expect_error(iso_get_file_info(1), "encountered incompatible data type") - expect_error(iso_get_standards_info(1), "encountered incompatible data type") + expect_error(iso_get_standards(1), "encountered incompatible data type") expect_error(iso_get_vendor_data_table(1), "encountered incompatible data type") }) @@ -144,10 +144,11 @@ test_that("test that aggregating file info works", { expect_equal(names(iso_get_file_info(iso_file1, select = c(file_datetime, only_a))), c("file_id", "file_datetime", "only_a")) expect_equal(names(iso_get_file_info(iso_file1, select = c(x = file_datetime, y = only_a))), c("file_id", "x", "y")) expect_equal(names(iso_get_file_info(iso_file1, select = starts_with("file"))), c("file_id", "file_root", "file_path", "file_subpath", "file_datetime")) - expect_error(iso_get_file_info(iso_file1, select = c(x = file_id)), "renaming.*file_id.*may lead to unpredictable") - expect_equal(names(iso_get_file_info(iso_file1, select = c(starts_with("file")))), c("file_id", "file_root", "file_path", "file_subpath", "file_datetime")) + expect_warning(iso_get_file_info(iso_file1, select = c(DNE)), "unknown column") + expect_equal(names(iso_get_file_info(c(iso_file1, iso_file2), select = c(starts_with("file"), "only_a"))), c("file_id", "file_root", "file_path", "file_subpath", "file_datetime", "only_a")) # note: not sure how to implement but this should probably throw a warning - expect_equal(names(iso_get_file_info(iso_file2, select = c("file_datetime", "only_a"))), c("file_id", "file_datetime")) + expect_warning(out <- names(iso_get_file_info(iso_file2, select = c("file_datetime", "only_a"))), "unknown column") + expect_equal(out, c("file_id", "file_datetime")) }) @@ -201,34 +202,60 @@ test_that("test that aggregeting raw data works", { test_that("test that aggregating of methods standards works", { - expect_error(iso_get_standards_info(make_iso_file_data_structure("NA")), "only dual inlet and continuous flow files") - expect_error(iso_get_standards_info(make_scan_data_structure("NA")), "scan files don't have") + expect_error(iso_get_standards(make_iso_file_data_structure("NA")), "only dual inlet and continuous flow files") + expect_error(iso_get_standards(make_scan_data_structure("NA")), "scan files don't have") iso_file <- make_di_data_structure("NA") - expect_warning(iso_get_standards_info(iso_file), "read without extracting the method info") + expect_warning(iso_get_standards_info(iso_file), "deprecated") + expect_warning(iso_get_standards(iso_file), "read without extracting the method info") # test data iso_file$read_options$method_info <- TRUE iso_file$read_options$file_info <- TRUE - iso_file1 <- modifyList(iso_file, list(file_info = list(file_id = "a"), - method_info = list(standards = tibble(standard = "test a")))) - iso_file2 <- modifyList(iso_file, list(file_info = list(file_id = "b"), - method_info = list(standards = tibble(standard = "test a")))) - - expect_message(iso_get_standards_info(c(iso_file1, iso_file2), quiet = FALSE), "aggregating") - expect_silent(iso_get_standards_info(c(iso_file1, iso_file2), quiet = TRUE)) - expect_equal(iso_get_standards_info(c(iso_file1, iso_file2)), + ref_ratios <- tibble(reference = "x", element = "X") + iso_file1 <- modifyList(iso_file, list( + file_info = list(file_id = "a"), + method_info = list( + standards = tibble( + standard = "test a", reference = "x" + ), + reference_ratios = ref_ratios + ) + )) + iso_file2 <- + modifyList(iso_file, list( + file_info = list(file_id = "b"), + method_info = list( + standards = tibble( + standard = "test b", reference = "x" + ), + reference_ratios = ref_ratios + ) + )) + + expect_message(iso_get_standards(c(iso_file1, iso_file2), quiet = FALSE), "aggregating") + expect_silent(iso_get_standards(c(iso_file1, iso_file2), quiet = TRUE)) + + # select_specific columns + expect_equal(iso_get_standards(c(iso_file1, iso_file2), select = file_id:reference), data <- bind_rows(mutate(iso_file1$method_info$standards, file_id="a"), mutate(iso_file2$method_info$standards, file_id="b"))) + expect_equal(iso_get_standards(c(iso_file1, iso_file2)), + left_join(data, ref_ratios, by = "reference")) + expect_equal(iso_get_standards(c(iso_file1, iso_file2), select = c(file_id)), select(data, file_id)) + expect_equal(iso_get_standards(c(iso_file1, iso_file2), select = c()), select(data, file_id)) + expect_equal(iso_get_standards(c(iso_file1, iso_file2), select = NULL), select(data, file_id)) + expect_warning(iso_get_standards(c(iso_file1, iso_file2), select = c(file_id, DNA)), "unknown column") + # include file info iso_file1 <- modifyList(iso_file1, list(file_info = list(test_info = "x"))) iso_file2 <- modifyList(iso_file2, list(file_info = list(test_info = "y"))) - expect_true("test_info" %in% names(agg <- iso_get_standards_info(c(iso_file1, iso_file2), include_file_info = c("test_info")))) + expect_true("test_info" %in% names(agg <- iso_get_standards(c(iso_file1, iso_file2), include_file_info = c("test_info")))) expect_equal(unique(agg$test_info), c("x", "y")) # make sure that files that have no raw data do not get added back in by including file info expect_equal( - suppressWarnings(iso_get_standards_info( + suppressWarnings(iso_get_standards( c(make_di_data_structure("NA"), iso_file1, iso_file2), include_file_info = c("test_info")))$test_info %>% unique(), c("x", "y") @@ -242,7 +269,8 @@ test_that("test that aggregating of methods standards works", { test_that("test that aggregating of resistors works", { iso_file <- make_iso_file_data_structure("NA") - expect_warning(iso_get_resistors_info (iso_file), "read without extracting the method info") + expect_warning(iso_get_resistors_info(iso_file), "deprecated") + expect_warning(iso_get_resistors (iso_file), "read without extracting the method info") # test data iso_file$read_options$method_info <- TRUE @@ -252,20 +280,27 @@ test_that("test that aggregating of resistors works", { iso_file2 <- modifyList(iso_file, list(file_info = list(file_id = "b"), method_info = list(resistors = tibble(cup = 1:3, R.Ohm = c(3e9, 1e11, 1e12))))) - expect_message(iso_get_resistors_info (c(iso_file1, iso_file2), quiet = FALSE), "aggregating") - expect_silent(iso_get_resistors_info (c(iso_file1, iso_file2), quiet = TRUE)) - expect_equal(iso_get_resistors_info (c(iso_file1, iso_file2)), + expect_message(iso_get_resistors (c(iso_file1, iso_file2), quiet = FALSE), "aggregating") + expect_silent(iso_get_resistors (c(iso_file1, iso_file2), quiet = TRUE)) + expect_equal(iso_get_resistors (c(iso_file1, iso_file2)), data <- bind_rows(mutate(iso_file1$method_info$resistors, file_id="a"), mutate(iso_file2$method_info$resistors, file_id="b"))) + + # select specific columns + expect_equal(iso_get_resistors(c(iso_file1, iso_file2), select = c(-cup)), select(data, -cup)) + expect_equal(iso_get_resistors(c(iso_file1, iso_file2), select = c()), select(data, file_id)) + expect_equal(iso_get_resistors(c(iso_file1, iso_file2), select = NULL), select(data, file_id)) + expect_warning(iso_get_resistors(c(iso_file1, iso_file2), select = c(file_id, test)), "unknown column") + # include file info iso_file1 <- modifyList(iso_file1, list(file_info = list(test_info = "x"))) iso_file2 <- modifyList(iso_file2, list(file_info = list(test_info = "y"))) - expect_true("test_info" %in% names(agg <- iso_get_resistors_info (c(iso_file1, iso_file2), include_file_info = c("test_info")))) + expect_true("test_info" %in% names(agg <- iso_get_resistors (c(iso_file1, iso_file2), include_file_info = c("test_info")))) expect_equal(unique(agg$test_info), c("x", "y")) # make sure that files that have no raw data do not get added back in by including file info expect_equal( - suppressWarnings(iso_get_resistors_info ( + suppressWarnings(iso_get_resistors ( c(make_iso_file_data_structure("NA"), iso_file1, iso_file2), include_file_info = c("test_info")))$test_info %>% unique(), c("x", "y") @@ -350,73 +385,112 @@ test_that("test that total data aggregation works", { # general warning messages iso_file <- make_cf_data_structure("NA") - expect_warning(data <- iso_get_data(iso_file), "read without extracting the file info") - expect_warning(data <- iso_get_data(iso_file), "read without extracting the raw data") - expect_warning(data <- iso_get_data(iso_file), "read without extracting the vendor data table") - expect_warning(data <- iso_get_data(iso_file), "read without extracting the method info") + expect_warning(iso_get_data(iso_file), "deprecated") + expect_warning(iso_get_all_data(iso_file), "read without extracting the file info") + expect_warning(iso_get_all_data(iso_file), "read without extracting the raw data") + expect_warning(iso_get_all_data(iso_file), "read without extracting the vendor data table") + expect_warning(iso_get_all_data(iso_file), "read without extracting the method info") # total get_data structure - expect_equal(names(data), c("file_id", "file_type", "file_info", - "raw_data", "vendor_data_table", "standards", "resistors", - "has_file_info", "has_raw_data", "has_vendor_data_table", "has_standards", "has_resistors")) - expect_equal(data %>% select(starts_with("has")), - tibble(has_file_info = TRUE, has_raw_data = FALSE, has_vendor_data_table = FALSE, has_standards = FALSE, has_resistors = FALSE)) - expect_equal(data$file_type, "continuous_flow") - - # info messages iso_file$read_options$file_info <- TRUE iso_file$read_options$method_info <- TRUE iso_file$read_options$raw_data <- TRUE iso_file$read_options$vendor_data_table <- TRUE + expect_equal( # default + names(iso_get_all_data(iso_file)), + c("file_id", "file_type", "file_info", "raw_data", "standards", "resistors", "vendor_data_table") + ) + expect_equal( # with problems + names(iso_get_all_data(iso_file, include_problems = everything())), + c("file_id", "file_type", "file_info", "raw_data", "standards", "resistors", "vendor_data_table", "problems") + ) + expect_equal( # without raw data + names(iso_get_all_data(iso_file, include_raw_data = NULL)), + c("file_id", "file_type", "file_info", "standards", "resistors", "vendor_data_table") + ) + expect_equal( # without file_info + names(iso_get_all_data(iso_file, include_file_info = NULL)), + c("file_id", "file_type", "raw_data", "standards", "resistors", "vendor_data_table") + ) + expect_equal( # without standards + names(iso_get_all_data(iso_file, include_standards = NULL)), + c("file_id", "file_type", "file_info", "raw_data", "resistors", "vendor_data_table") + ) + expect_equal( # without resistors + names(iso_get_all_data(iso_file, include_resistors = NULL)), + c("file_id", "file_type", "file_info", "raw_data", "standards", "vendor_data_table") + ) + expect_equal( # without vendor_data_table + names(iso_get_all_data(iso_file, include_vendor_data_table = NULL)), + c("file_id", "file_type", "file_info", "raw_data", "standards", "resistors") + ) + expect_equal(iso_get_all_data(iso_file)$file_type, "continuous_flow") + + # info messages iso_file1 <- modifyList(iso_file, list(file_info = list(file_id = "a"))) iso_file2 <- modifyList(iso_file, list(file_info = list(file_id = "b"))) - expect_message(iso_get_data(iso_file), "aggregating all data from 1 data file") - expect_message(iso_get_data(c(iso_file1, iso_file2)), "aggregating all data from 2 data file") - - # vendor data table - iso_file1$vendor_data_table <- tibble(column1 = "col1 a", column2 = "col2 a") - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_vendor_data_table, c(TRUE, FALSE)) - iso_file2$vendor_data_table <- tibble(column1 = "col1 b", column2 = "col2 b") - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_vendor_data_table, c(TRUE, TRUE)) - expect_true(is_tibble(out <- iso_get_data(c(iso_file1, iso_file2)) %>% unnest(vendor_data_table))) - expect_equal(select(out,column1, column2), bind_rows(iso_file1$vendor_data_table, iso_file2$vendor_data_table) ) - expect_true(is_tibble(out <- iso_get_data(c(iso_file1, iso_file2), include_vendor_data_table = c(x = column1)) %>% unnest(vendor_data_table))) - expect_equal(out$x, bind_rows(iso_file1$vendor_data_table, iso_file2$vendor_data_table)$column1) + expect_message(iso_get_all_data(iso_file), "aggregating all data from 1 data file") + expect_message(iso_get_all_data(c(iso_file1, iso_file2)), "aggregating all data from 2 data file") # file_info - iso_file1$file_info$test <- 42 - expect_true(is_tibble(out <- iso_get_data(c(iso_file1, iso_file2), include_file_info = c(x = test)) %>% unnest(file_info))) - expect_equal(out$x, c(42, NA_real_)) + expect_warning(iso_get_all_data(c(iso_file1, iso_file2), include_file_info = x), "unknown column") + iso_file1$file_info$a <- 42 + expect_equal(iso_get_all_data(c(iso_file1, iso_file2), include_file_info = c(x = a)) %>% unnest(file_info) %>% dplyr::pull(x), c(42, NA_real_)) # raw data + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(raw_data) %>% nrow(), 0L) iso_file1$raw_data <- tibble(tp = 1:10, time.s = tp*0.2, v44.mV = runif(10), v46.mV = runif(10), `r46/44` = v46.mV/v44.mV) - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_raw_data, c(TRUE, FALSE)) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(raw_data) %>% dplyr::pull(file_id) %>% unique(), "a") iso_file2$raw_data <- tibble(tp = 1:10, time.s = tp*0.2, v44.mV = runif(10), v46.mV = runif(10), v45.mV = runif(10)) - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_raw_data, c(TRUE, TRUE)) - expect_true(is_tibble(out <- iso_get_data(c(iso_file1, iso_file2), include_file_info = c(x = test)) %>% unnest(raw_data))) - expect_equal(select(out, -file_id, -file_type, -starts_with("has"), - -file_info, -vendor_data_table, -standards, -resistors), - bind_rows(iso_file1$raw_data, iso_file2$raw_data)) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(raw_data) %>% dplyr::pull(file_id) %>% unique(), c("a", "b")) + expect_false("v44.mV" %in% (iso_get_all_data(c(iso_file1, iso_file2), include_raw_data = c(-v44.mV)) %>% unnest(raw_data) %>% names())) # standards + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(standards) %>% nrow(), 0L) iso_file1 <- modifyList(iso_file, list(file_info = list(file_id = "a"), method_info = list(standards = tibble(standard = "test a")))) - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_standards, c(TRUE, FALSE)) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(standards) %>% dplyr::pull(file_id) %>% unique(), "a") iso_file2 <- modifyList(iso_file, list(file_info = list(file_id = "b"), method_info = list(standards = tibble(standard = "test a")))) - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_standards, c(TRUE, TRUE)) - expect_true(is_tibble(out <- iso_get_data(c(iso_file1, iso_file2)) %>% unnest(standards))) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(standards) %>% dplyr::pull(file_id) %>% unique(), c("a", "b")) + expect_true(is_tibble(out <- iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(standards))) expect_equal(select(out, standard), bind_rows(iso_file1$method_info$standards, iso_file2$method_info$standards)) - + expect_false("standard" %in% names(iso_get_all_data(c(iso_file1, iso_file2), include_standards = c(-standard)) %>% unnest(standards))) + # resistors + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(resistors) %>% nrow(), 0L) iso_file1 <- modifyList(iso_file, list(file_info = list(file_id = "a"), method_info = list(resistors = tibble(cup = 1:3, R.Ohm = c(1e9, 1e10, 1e11))))) - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_resistors, c(TRUE, FALSE)) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(resistors) %>% dplyr::pull(file_id) %>% unique(), "a") iso_file2 <- modifyList(iso_file, list(file_info = list(file_id = "b"), method_info = list(resistors = tibble(cup = 1:3, R.Ohm = c(3e9, 1e11, 1e12))))) - expect_equal(iso_get_data(c(iso_file1, iso_file2))$has_resistors, c(TRUE, TRUE)) - expect_true(is_tibble(out <- iso_get_data(c(iso_file1, iso_file2)) %>% unnest(resistors))) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(resistors) %>% dplyr::pull(file_id) %>% unique(), c("a", "b")) + expect_true(is_tibble(out <- iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(resistors))) expect_equal(select(out, cup, R.Ohm), bind_rows(iso_file1$method_info$resistors, iso_file2$method_info$resistors)) - + expect_true(is_tibble(out <- iso_get_all_data(c(iso_file1, iso_file2), include_resistors = c(-cup)) %>% unnest(resistors))) + expect_false("cup" %in% names(out)) + + # vendor data table + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(vendor_data_table) %>% nrow(), 0L) + iso_file1$vendor_data_table <- tibble(column1 = "col1 a", column2 = "col2 a") + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(vendor_data_table) %>% dplyr::pull(file_id) %>% unique(), "a") + iso_file2$vendor_data_table <- tibble(column1 = "col1 b", column2 = "col2 b") + expect_equal(iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(vendor_data_table) %>% dplyr::pull(file_id) %>% unique(), c("a", "b")) + expect_true(is_tibble(out <- iso_get_all_data(c(iso_file1, iso_file2)) %>% unnest(vendor_data_table))) + expect_equal(select(out,column1, column2), bind_rows(iso_file1$vendor_data_table, iso_file2$vendor_data_table) ) + expect_true(is_tibble(out <- iso_get_all_data(c(iso_file1, iso_file2), include_vendor_data_table = c(x = column1)) %>% unnest(vendor_data_table))) + expect_equal(out$x, bind_rows(iso_file1$vendor_data_table, iso_file2$vendor_data_table)$column1) + + # problems + expect_equal(iso_get_all_data(c(iso_file1, iso_file2), include_problems = everything()) %>% unnest(problems) %>% nrow(), 0L) + iso_file1 <- iso_file1 %>% register_error("test", warn = FALSE) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2), include_problems = everything()) %>% unnest(problems) %>% dplyr::pull(file_id) %>% unique(), "a") + iso_file2 <- iso_file2 %>% register_error("test2", warn = FALSE) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2), include_problems = everything()) %>% unnest(problems) %>% dplyr::pull(file_id) %>% unique(), c("a", "b")) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2), include_problems = everything()) %>% select(file_id, problems) %>% unnest(problems), + iso_get_problems(c(iso_file1, iso_file2))) + expect_equal(iso_get_all_data(c(iso_file1, iso_file2), include_problems = c(test = type)) %>% select(file_id, problems) %>% unnest(problems), + iso_get_problems(c(iso_file1, iso_file2)) %>% select(file_id, test = type)) + }) From 7d2765076ca2b8bb46b017ac7332c5fbc7e95a6b Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 00:42:45 -0700 Subject: [PATCH 43/51] file info select and rename using local_eval functions for scoping safety --- R/file_info_operations.R | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/R/file_info_operations.R b/R/file_info_operations.R index f06ec688..f1a68450 100644 --- a/R/file_info_operations.R +++ b/R/file_info_operations.R @@ -43,14 +43,12 @@ iso_select_file_info.iso_file_list <- function(iso_files, ..., quiet = default(q changed <- to <- from <- NULL # variables for all files - select_expr <- rlang::expr(c(...)) + select_expr <- rlang::expr(c(!!!rlang::enexprs(...))) # run select isofiles_select <- map(iso_files, function(isofile) { # select positions (always include file_id) - file_id_pos <- tidyselect::eval_select(rlang::expr(file_id), data = isofile$file_info) - pos <- tidyselect::eval_select(select_expr, data = isofile$file_info, strict = FALSE) - if (!file_id_pos %in% pos) pos <- c(file_id_pos, pos) + pos <- local_eval_select(select_expr, data = isofile$file_info, strict = FALSE, include = "file_id") # selected variables vars <- tibble( file_id = isofile$file_info$file_id, @@ -140,12 +138,12 @@ iso_rename_file_info.iso_file_list <- function(iso_files, ..., quiet = default(q changed <- to <- from <- NULL # variables for all files - rename_expr <- rlang::expr(c(...)) + rename_expr <- rlang::expr(c(!!!rlang::enexprs(...))) # run select isofiles_rename <- map(iso_files, function(isofile) { # rename positions - pos <- tidyselect::eval_rename(rename_expr, data = isofile$file_info, strict = FALSE) + pos <- local_eval_rename(rename_expr, data = isofile$file_info, strict = FALSE) # selected variables vars <- tibble( file_id = isofile$file_info$file_id, From 0b7c2472ba7ba9becd82db1d4a956c27ab070ae0 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 00:46:48 -0700 Subject: [PATCH 44/51] update export functions to use iso_get_data --- R/isodata_structures.R | 10 +++++----- R/problems.R | 10 +++++++--- R/utils.R | 4 ++-- tests/testthat/test-data-structures.R | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/R/isodata_structures.R b/R/isodata_structures.R index 4816349f..3f0c6b94 100644 --- a/R/isodata_structures.R +++ b/R/isodata_structures.R @@ -197,14 +197,14 @@ iso_as_file_list <- function(..., discard_duplicates = TRUE) { } # propagate problems - all_problems <- map(iso_list, ~get_problems(.x) %>% mutate(file_id = .x$file_info$file_id)) %>% bind_rows() + all_problems <- map(iso_list, ~get_problems(.x) %>% mutate(file_id = .x$file_info$file_id)) %>% + bind_rows() %>% dplyr::select(file_id, everything()) } # problems - if (nrow(all_problems)) { - all_problems <- all_problems %>% - unique() %>% # remove duplicate entries - select(file_id, everything()) + if (nrow(all_problems) > 0) { + # remove duplicate entries + all_problems <- unique(all_problems) } # generate structure diff --git a/R/problems.R b/R/problems.R index e3de8d13..524b4f56 100644 --- a/R/problems.R +++ b/R/problems.R @@ -37,14 +37,18 @@ readr::problems #' Retrieve parsing problems #' -#' This is identical to the readr \code{\link[readr]{problems}} function. +#' This function retrieves parsing problems encountered during the reading of a set of iso files. #' #' @importFrom readr problems #' @inheritParams iso_get_raw_data #' @family problem functions #' @export -iso_get_problems <- function(iso_files) { - problems(iso_files) +iso_get_problems <- function(iso_files, select = everything()) { + probs <- problems(iso_as_file_list(iso_files)) + select_cols <- get_column_names(probs, select = enquo(select), n_reqs = list(select = "*"), cols_must_exist = FALSE)$select + if (!"file_id" %in% select_cols) + select_cols <- c("file_id", select_cols) # file info always included + return(dplyr::select(probs, !!!select_cols)) } #' @importFrom readr stop_for_problems diff --git a/R/utils.R b/R/utils.R index 2a34f315..a4e95dd4 100644 --- a/R/utils.R +++ b/R/utils.R @@ -808,9 +808,9 @@ find_parent_call <- function(current_func) { # convience function for information message get_info_message_concat <- function(variable, prefix = "", suffix = "", empty = c(), quotes = TRUE){ - if (is_quosure(variable)) { - if (quo_is_null(variable)) return("") + if (is_quosure(variable) || rlang::is_expression(variable)) { variable <- rlang::as_label(variable) + if (variable == "NULL") return("") } if (is_empty(variable)) return("") variable <- setdiff(variable, empty) diff --git a/tests/testthat/test-data-structures.R b/tests/testthat/test-data-structures.R index a10944d4..1991fabe 100644 --- a/tests/testthat/test-data-structures.R +++ b/tests/testthat/test-data-structures.R @@ -98,11 +98,11 @@ test_that("test that iso_file list checks work", { expect_equal(iso_as_file_list() %>% iso_get_data_summary() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_raw_data() %>% nrow(), 0) expect_equal(iso_as_file_list() %>% iso_get_file_info() %>% nrow(), 0) - expect_equal(iso_as_file_list() %>% iso_get_resistors_info() %>% nrow(), 0) + expect_equal(iso_as_file_list() %>% iso_get_resistors() %>% nrow(), 0) # expected errors expect_error(iso_as_file_list() %>% iso_get_vendor_data_table(), "only dual inlet.*continuous flow") - expect_error(iso_as_file_list() %>% iso_get_standards_info(), "only dual inlet.*continuous flow") + expect_error(iso_as_file_list() %>% iso_get_standards(), "only dual inlet.*continuous flow") expect_error(iso_as_file_list(1, error = "test"), "encountered incompatible data type") expect_false(iso_is_file_list(42)) expect_false(iso_is_file_list(make_iso_file_data_structure("NA"))) From 8e34aa584d9333c7f1792fe04b7547957037b232 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 00:46:55 -0700 Subject: [PATCH 45/51] use iso_get_all_data in export functions --- R/export.R | 202 +++++++++++++++++++++++------------ tests/testthat/test-export.R | 122 ++++++++++++++------- 2 files changed, 219 insertions(+), 105 deletions(-) diff --git a/R/export.R b/R/export.R index 90f36705..faf49921 100644 --- a/R/export.R +++ b/R/export.R @@ -19,64 +19,94 @@ iso_export_to_rda <- function(iso_files, filepath, quiet = default(quiet)) { #' Export data to Excel #' -#' This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file but they are only exported if the corresponding \code{include_} parameter is set to \code{TRUE} and only for data types for which this type of data is available and was read (see \code{\link{iso_read_dual_inlet}}, \code{\link{iso_read_continuous_flow}} for details on read parameters). Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export. +#' This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file. Use the various \code{include_...} parameters to specifiy what information to include. Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export. #' #' @inheritParams iso_save -#' @param include_raw_data whether to include the raw data in the export (if available) -#' @param include_file_info whether to include the file info in the export (if available) -#' @param include_method_info whether to include methods infor in the export (if available) -#' @param include_vendor_data_table whether to include the vendor data table in the export (if available) -#' @param include_problems whether to include the problems table -#' @inheritParams iso_get_vendor_data_table +#' @inheritParams iso_get_all_data +#' @param include_method_info deprecated in favor of the more specific include_standards and include_resistors #' @family export functions #' @return returns the iso_files object invisibly for use in pipelines #' @export -iso_export_to_excel <- function(iso_files, filepath, - include_raw_data = TRUE, include_file_info = TRUE, - include_method_info = TRUE, include_vendor_data_table = TRUE, - include_problems = TRUE, with_explicit_units = FALSE, - quiet = default(quiet)) { +iso_export_to_excel <- function( + iso_files, filepath, + include_file_info = everything(), include_raw_data = everything(), + include_standards = !!enexpr(include_method_info), include_resistors = !!enquo(include_method_info), + include_vendor_data_table = everything(), include_problems = everything(), + with_explicit_units = FALSE, + include_method_info = everything(), + quiet = default(quiet)) { # safety checks if(!iso_is_object(iso_files)) stop("can only export iso files or lists of iso files", call. = FALSE) - filepath <- get_excel_export_filepath(iso_files, filepath) - - # save iso_files export_iso_files <- iso_as_file_list(iso_files) + filepath <- get_excel_export_filepath(export_iso_files, filepath) + + # include method info message + if (!missing(include_method_info)) { + warning("the 'include_method_info' parameter was deprecated in favor of the more specific 'include_resistors' and 'include_standards' parameters. Please use those directly instead in the future.", immediate. = TRUE, call. = FALSE) + } + + # info message if (!quiet) { sprintf("Info: exporting data from %d iso_files into Excel '%s'", length(export_iso_files), str_replace(filepath, "^\\.(/|\\\\)", "")) %>% message() } + # get all data + all_data <- iso_get_all_data( + export_iso_files, + include_file_info = !!enexpr(include_file_info), + include_raw_data = !!enexpr(include_raw_data), + include_standards = !!enexpr(include_standards), + include_resistors = !!enexpr(include_resistors), + include_vendor_data_table = !!enexpr(include_vendor_data_table), + include_problems = !!enexpr(include_problems), + with_explicit_units = with_explicit_units, + quiet = FALSE + ) + # make excel workbook wb <- createWorkbook() - if (include_raw_data) { - raw_data <- iso_get_raw_data(export_iso_files, quiet = TRUE) - add_excel_sheet(wb, "raw data", raw_data) - } - if (include_file_info) { - # note: this takes care of nested vectors, they get concatenated with ', ' - file_info <- iso_get_file_info(export_iso_files, quiet = TRUE) %>% collapse_list_columns() + + # file info + if ("file_info" %in% names(all_data)) { + # note: collapse_list_columns takes care of nested vectors, they get concatenated with ', ' + file_info <- + all_data %>% select(file_id, file_info) %>% + unnest(file_info) %>% + collapse_list_columns() add_excel_sheet(wb, "file info", file_info) } - if (include_method_info) { - resistors <- iso_get_resistors_info (export_iso_files, quiet = TRUE) - if (iso_is_scan(export_iso_files)) { - # scan - add_excel_sheet(wb, "method info", resistors) - } else { - # cf and dual inlet - standards <- iso_get_standards_info(export_iso_files, quiet = TRUE) - add_excel_sheet(wb, "method info", standards, resistors) - } + + # raw data + if ("raw_data" %in% names(all_data)) { + raw_data <- all_data %>% select(file_id, raw_data) %>% unnest(raw_data) + add_excel_sheet(wb, "raw data", raw_data) } - if (include_vendor_data_table && !iso_is_scan(export_iso_files)) { - vendor_data <- iso_get_vendor_data_table(export_iso_files, with_explicit_units = with_explicit_units, quiet = TRUE) %>% - iso_strip_units() + + # standards + if ("standards" %in% names(all_data)) { + standards <- all_data %>% select(file_id, standards) %>% unnest(standards) + add_excel_sheet(wb, "standards", standards) + } + + # resistors + if ("resistors" %in% names(all_data)) { + resistors <- all_data %>% select(file_id, resistors) %>% unnest(resistors) + add_excel_sheet(wb, "resistors", resistors) + } + + # vendor data table + if ("vendor_data_table" %in% names(all_data)) { + vendor_data <- all_data %>% select(file_id, vendor_data_table) %>% + unnest(vendor_data_table) %>% iso_strip_units() add_excel_sheet(wb, "vendor data table", vendor_data) } - if (include_problems) { - add_excel_sheet(wb, "problems", problems(iso_files)) + + # problems + if ("problems" %in% names(all_data)) { + problems <- all_data %>% select(file_id, problems) %>% unnest(problems) + add_excel_sheet(wb, "problems", problems) } saveWorkbook(wb, filepath, overwrite = TRUE) @@ -161,51 +191,89 @@ add_excel_sheet <- function(wb, sheet_name, ..., dbl_digits = 2, col_max_width = #' #' This function exports the passed in iso_files to the Python and R shared feather file format. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate feather files that are saved with the provided \code{filepath_prefix} as prefix. All are only exported if the corresponding \code{include_} parameter is set to \code{TRUE} and only for data types for which this type of data is available and was read (see \code{\link{iso_read_dual_inlet}}, \code{\link{iso_read_continuous_flow}} for details on read parameters). Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in feather output. #' +#' @inheritParams iso_save #' @inheritParams iso_export_to_excel -#' @param filepath_prefix the path (folder and filename) prefix for the exported feather files. The correct suffix for different kinds of data and file extension is automatically added -#' @inheritParams iso_get_vendor_data_table #' @family export functions #' @return returns the iso_files object invisibly for use in pipelines #' @export -iso_export_to_feather <- function(iso_files, filepath_prefix, - include_raw_data = TRUE, include_file_info = TRUE, - include_method_info = TRUE, include_vendor_data_table = TRUE, - include_problems = TRUE, with_explicit_units = FALSE, - quiet = default(quiet)) { +iso_export_to_feather <- function( + iso_files, filepath_prefix, + include_file_info = everything(), include_raw_data = everything(), + include_standards = !!enexpr(include_method_info), include_resistors = !!enquo(include_method_info), + include_vendor_data_table = everything(), include_problems = everything(), + with_explicit_units = FALSE, + include_method_info = everything(), + quiet = default(quiet)) { # safety checks if(!iso_is_object(iso_files)) stop("can only export iso files or lists of iso files", call. = FALSE) + export_iso_files <- iso_as_file_list(iso_files) + filepaths <- get_feather_export_filepaths(export_iso_files, filepath_prefix) - # save iso_files - # note: not sure yet how to best implement different data types such as scan here - filepaths <- get_feather_export_filepaths(iso_files, filepath_prefix) + # include method info message + if (!missing(include_method_info)) { + warning("the 'include_method_info' parameter was deprecated in favor of the more specific 'include_resistors' and 'include_standards' parameters. Please use those directly instead in the future.", immediate. = TRUE, call. = FALSE) + } + + # info if (!quiet) { sprintf("Info: exporting data from %d iso_files into %s files at '%s'", length(iso_as_file_list(iso_files)), filepaths[['ext']], str_replace(filepaths[['base']], "^\\.(/|\\\\)", "")) %>% message() } + + # get all data + all_data <- iso_get_all_data( + export_iso_files, + include_file_info = !!enexpr(include_file_info), + include_raw_data = !!enexpr(include_raw_data), + include_standards = !!enexpr(include_standards), + include_resistors = !!enexpr(include_resistors), + include_vendor_data_table = !!enexpr(include_vendor_data_table), + include_problems = !!enexpr(include_problems), + with_explicit_units = with_explicit_units, + quiet = FALSE + ) - # make feather files in temporary dir - if (include_raw_data) - write_feather(iso_get_raw_data(iso_files, quiet = TRUE), filepaths[['raw_data']]) - - if (include_file_info) - # note: this takes care of nested vectors, they get concatenated with ', ' - write_feather(iso_get_file_info(iso_files, quiet = TRUE) %>% collapse_list_columns(), - filepaths[['file_info']]) + # create feather files in temporary dir + # file info + if ("file_info" %in% names(all_data)) { + # note: collapse_list_columns takes care of nested vectors, they get concatenated with ', ' + all_data %>% select(file_id, file_info) %>% + unnest(file_info) %>% + collapse_list_columns() %>% + write_feather(filepaths[['file_info']]) + } - if (include_method_info) { - if (!iso_is_scan(iso_files)) - write_feather(iso_get_standards_info(iso_files, quiet = TRUE), filepaths[['method_info_standards']]) - write_feather(iso_get_resistors_info (iso_files, quiet = TRUE), filepaths[['method_info_resistors']]) + # raw data + if ("raw_data" %in% names(all_data)) { + all_data %>% select(file_id, raw_data) %>% unnest(raw_data) %>% + write_feather(filepaths[['raw_data']]) } - if (include_vendor_data_table && !iso_is_scan(iso_files)) - write_feather( - iso_get_vendor_data_table(iso_files, with_explicit_units = with_explicit_units, quiet = TRUE) %>% iso_strip_units(), - filepaths[['vendor_data_table']]) + # standards + if ("standards" %in% names(all_data)) { + all_data %>% select(file_id, standards) %>% unnest(standards) %>% + write_feather(filepaths[['method_info_standards']]) + } + + # resistors + if ("resistors" %in% names(all_data)) { + all_data %>% select(file_id, resistors) %>% unnest(resistors) %>% + write_feather(filepaths[['method_info_resistors']]) + } - if (include_problems) - write_feather(problems(iso_files), filepaths[['problems']]) + # vendor data table + if ("vendor_data_table" %in% names(all_data)) { + all_data %>% select(file_id, vendor_data_table) %>% + unnest(vendor_data_table) %>% iso_strip_units() %>% + write_feather(filepaths[['vendor_data_table']]) + } + + # problems + if ("problems" %in% names(all_data)) { + all_data %>% select(file_id, problems) %>% unnest(problems) %>% + write_feather(filepaths[['problems']]) + } return(invisible(iso_files)) } @@ -256,8 +324,8 @@ get_feather_export_filepaths <- function(iso_files, filepath) { ext = ext, raw_data = str_c(filepath, "_raw_data", ext), file_info = str_c(filepath, "_file_info", ext), - method_info_standards = str_c(filepath, "_method_info-standards", ext), - method_info_resistors = str_c(filepath, "_method_info-resistors", ext), + method_info_standards = str_c(filepath, "_standards", ext), + method_info_resistors = str_c(filepath, "_resistors", ext), vendor_data_table = str_c(filepath, "_vendor_data_table", ext), problems = str_c(filepath, "_problems", ext) ) diff --git a/tests/testthat/test-export.R b/tests/testthat/test-export.R index 0b6676a8..124a1c10 100644 --- a/tests/testthat/test-export.R +++ b/tests/testthat/test-export.R @@ -2,8 +2,11 @@ context("Export functions") di_example <- iso_read_dual_inlet(iso_get_reader_example("dual_inlet_example.did")) cf_example <- iso_read_continuous_flow(iso_get_reader_example("continuous_flow_example.cf")) +capture.output(cf_err_example <- suppressMessages(iso_read_continuous_flow(system.file("errdata", "cf_without_data.dxf", package = "isoreader")))) scan_example <- iso_read_scan(iso_get_reader_example("peak_shape_scan_example.scn")) +# iso_save ===== + test_that("test that export to rda works properly", { expect_error(iso_save(42), "can only export iso files") expect_error(iso_save(make_cf_data_structure("NA")), "no filepath provided") @@ -61,18 +64,31 @@ test_that("test that export to rda works properly", { expect_true(file.remove(str_c(filepath, ".di.rds"))) # export real data files - continuous flow - expect_message(iso_save(cf_example, filepath, quiet = FALSE), "exporting data .* into R Data Storage") + expect_message(iso_save(c(cf_example, cf_err_example), filepath, quiet = FALSE), "exporting data .* into R Data Storage") expect_true(file.exists(str_c(filepath, ".cf.rds"))) - expect_message(cf_example_back <- iso_read_continuous_flow(str_c(filepath, ".cf.rds"), quiet = FALSE), "reading file") - expect_equal(cf_example$raw_data, cf_example_back$raw_data) + expect_message(capture.output(cf_examples_back <- iso_read_continuous_flow(str_c(filepath, ".cf.rds"), quiet = FALSE)), "reading file") + expect_equal(cf_example$raw_data, cf_examples_back[[1]]$raw_data) expect_equal(cf_example$file_info %>% unnest_aggregated_data_frame(), - cf_example_back$file_info %>% unnest_aggregated_data_frame()) - expect_equal(cf_example$method_info$standards, cf_example_back$method_info$standards) - expect_equal(cf_example$method_info$resistors, cf_example_back$method_info$resistors) - expect_equal(cf_example$vendor_data_table, cf_example_back$vendor_data_table) + cf_examples_back[[1]]$file_info %>% unnest_aggregated_data_frame()) + expect_equal(cf_example$method_info$standards, cf_examples_back[[1]]$method_info$standards) + expect_equal(cf_example$method_info$resistors, cf_examples_back[[1]]$method_info$resistors) + expect_equal(cf_example$vendor_data_table, cf_examples_back[[1]]$vendor_data_table) + expect_equal(iso_get_problems(cf_err_example), iso_get_problems(cf_examples_back)) expect_true(file.remove(str_c(filepath, ".cf.rds"))) + + # export real data files - scan + expect_message(iso_save(scan_example, filepath, quiet = FALSE), "exporting data .* into R Data Storage") + expect_true(file.exists(str_c(filepath, ".scan.rds"))) + expect_message(scan_example_back <- iso_read_scan(str_c(filepath, ".scan.rds"), quiet = FALSE), "reading file") + expect_equal(scan_example$raw_data, scan_example_back$raw_data) + expect_equal(scan_example$file_info %>% unnest_aggregated_data_frame(), + scan_example_back$file_info %>% unnest_aggregated_data_frame()) + expect_equal(scan_example$method_info$resistors, scan_example_back$method_info$resistors) + expect_true(file.remove(str_c(filepath, ".scan.rds"))) }) +# excel expor ====== + library(readxl) test_that("test that export to Excel works properly", { expect_error(iso_export_to_excel(42), "can only export iso files") @@ -110,10 +126,16 @@ test_that("test that export to Excel works properly", { readxl::read_excel(str_c(filepath, ".cf.xlsx"), "file info", col_types = c("text", "text", "text", "text", "numeric", "text")) %>% mutate(file_datetime = as.integer(file_datetime))) + expect_equal(iso_get_standards(cf), + readxl::read_excel(str_c(filepath, ".cf.xlsx"), "standards", col_types = c("text", "text"))) + expect_equal(iso_get_resistors(cf), + readxl::read_excel(str_c(filepath, ".cf.xlsx"), "resistors", col_types = c("text", "numeric", "numeric")) %>% + mutate(cup = as.integer(cup))) expect_equal(iso_get_vendor_data_table(cf), readxl::read_excel(str_c(filepath, ".cf.xlsx"), "vendor data table") %>% mutate(x = as.integer(x))) - # TODO: also test the standards and resistor values (from the same tab) + expect_equal(iso_get_problems(cf) %>% select(file_id), + readxl::read_excel(str_c(filepath, ".cf.xlsx"), "problems", col_types = c("text"))) expect_true(file.remove(str_c(filepath, ".cf.xlsx"))) # export real data files - dual inlet @@ -121,42 +143,57 @@ test_that("test that export to Excel works properly", { expect_true(file.exists(str_c(filepath, ".di.xlsx"))) expect_equal(iso_get_raw_data(di_example) %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif), - read_excel(str_c(filepath, ".di.xlsx"), "raw data") %>% + readxl::read_excel(str_c(filepath, ".di.xlsx"), "raw data") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) expect_equal(iso_get_file_info(di_example) %>% collapse_list_columns() %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% dplyr::select_if(function(x) !is.na(x)) %>% select(-file_datetime), # never exactly identical, - read_excel(str_c(filepath, ".di.xlsx"), "file info") %>% + readxl::read_excel(str_c(filepath, ".di.xlsx"), "file info") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% dplyr::select_if(function(x) !is.na(x)) %>% select(-file_datetime)) + expect_equal(iso_get_standards(di_example), + readxl::read_excel(str_c(filepath, ".di.xlsx"), "standards")) + expect_equal(iso_get_resistors(di_example), + readxl::read_excel(str_c(filepath, ".di.xlsx"), "resistors") %>% + mutate(cup = as.integer(cup))) expect_equal(iso_get_vendor_data_table(di_example) %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif), - read_excel(str_c(filepath, ".di.xlsx"), "vendor data table") %>% + readxl::read_excel(str_c(filepath, ".di.xlsx"), "vendor data table") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) + expect_equal(iso_get_problems(di_example) %>% select(file_id), + readxl::read_excel(str_c(filepath, ".di.xlsx"), "problems", col_types = c("text"))) expect_true(file.remove(str_c(filepath, ".di.xlsx"))) # export real data files - continuous flow - expect_message(iso_export_to_excel(cf_example, filepath, quiet = FALSE), "exporting data .* into Excel") + cf_examples <- c(cf_example, cf_err_example) + expect_message(iso_export_to_excel(cf_examples, filepath, quiet = FALSE), "exporting data .* into Excel") expect_true(file.exists(str_c(filepath, ".cf.xlsx"))) - expect_equal(iso_get_raw_data(cf_example) %>% + expect_equal(iso_get_raw_data(cf_examples) %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif), read_excel(str_c(filepath, ".cf.xlsx"), "raw data") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) - expect_equal(iso_get_file_info(cf_example) %>% collapse_list_columns() %>% + expect_equal(iso_get_file_info(cf_examples) %>% collapse_list_columns() %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% - dplyr::select_if(function(x) !is.na(x)) %>% + dplyr::select_if(function(x) !all(is.na(x))) %>% select(-file_datetime), # never exactly identical, read_excel(str_c(filepath, ".cf.xlsx"), "file info") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% - dplyr::select_if(function(x) !is.na(x)) %>% + dplyr::select_if(function(x) !all(is.na(x))) %>% select(-file_datetime)) - expect_equal(iso_get_vendor_data_table(cf_example) %>% + expect_equal(iso_get_standards(cf_examples), + readxl::read_excel(str_c(filepath, ".cf.xlsx"), "standards")) + expect_equal(iso_get_resistors(cf_examples), + readxl::read_excel(str_c(filepath, ".cf.xlsx"), "resistors") %>% + mutate(cup = as.integer(cup))) + expect_equal(iso_get_vendor_data_table(cf_examples) %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% iso_strip_units(), read_excel(str_c(filepath, ".cf.xlsx"), "vendor data table") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) + expect_equal(iso_get_problems(cf_examples), + readxl::read_excel(str_c(filepath, ".cf.xlsx"), "problems", col_types = c("text", "text", "text", "text"))) expect_true(file.remove(str_c(filepath, ".cf.xlsx"))) # export real data files with explicit units @@ -183,15 +220,18 @@ test_that("test that export to Excel works properly", { dplyr::mutate_if(.predicate = is.numeric, .funs = signif) %>% dplyr::select_if(function(x) !is.na(x)) %>% select(-file_datetime)) - expect_equal(iso_get_resistors_info(scan_example) %>% + expect_equal(iso_get_resistors(scan_example) %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif), - read_excel(str_c(filepath, ".scan.xlsx"), "method info") %>% - dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) + read_excel(str_c(filepath, ".scan.xlsx"), "resistors") %>% + dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) + expect_equal(iso_get_problems(scan_example) %>% select(file_id), + readxl::read_excel(str_c(filepath, ".scan.xlsx"), "problems", col_types = c("text"))) expect_true(file.remove(str_c(filepath, ".scan.xlsx"))) }) +# feather export ====== library(feather) test_that("test that export to Feather works properly", { @@ -222,14 +262,15 @@ test_that("test that export to Feather works properly", { expect_equal(cf$vendor_data_table, cf_out$vendor_data_table) expect_true(file.exists(str_c(filepath, "_raw_data.cf.feather"))) expect_true(file.exists(str_c(filepath, "_file_info.cf.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-standards.cf.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-resistors.cf.feather"))) + expect_true(file.exists(str_c(filepath, "_standards.cf.feather"))) + expect_true(file.exists(str_c(filepath, "_resistors.cf.feather"))) expect_true(file.exists(str_c(filepath, "_vendor_data_table.cf.feather"))) + expect_true(file.exists(str_c(filepath, "_problems.cf.feather"))) # note for comparisons: rounding is NOT necessary because storage is equivalent to values in R expect_equal(iso_get_raw_data(cf), read_feather(str_c(filepath, "_raw_data.cf.feather"))) expect_equal(iso_get_file_info(cf) %>% collapse_list_columns(), read_feather(str_c(filepath, "_file_info.cf.feather"))) - expect_equal(iso_get_standards_info(cf), read_feather(str_c(filepath, "_method_info-standards.cf.feather"))) - expect_equal(iso_get_resistors_info (cf), read_feather(str_c(filepath, "_method_info-resistors.cf.feather"))) + expect_equal(iso_get_standards(cf), read_feather(str_c(filepath, "_standards.cf.feather"))) + expect_equal(iso_get_resistors (cf), read_feather(str_c(filepath, "_resistors.cf.feather"))) expect_equal(iso_get_vendor_data_table(cf), read_feather(str_c(filepath, "_vendor_data_table.cf.feather"))) expect_true(all(file.remove(list.files(dirname(filepath), pattern = "\\.cf\\.feather$", full.names = TRUE)))) @@ -237,31 +278,36 @@ test_that("test that export to Feather works properly", { expect_message(iso_export_to_feather(di_example, filepath, quiet = FALSE), "exporting data .* into .di.feather") expect_true(file.exists(str_c(filepath, "_raw_data.di.feather"))) expect_true(file.exists(str_c(filepath, "_file_info.di.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-standards.di.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-resistors.di.feather"))) + expect_true(file.exists(str_c(filepath, "_standards.di.feather"))) + expect_true(file.exists(str_c(filepath, "_resistors.di.feather"))) expect_true(file.exists(str_c(filepath, "_vendor_data_table.di.feather"))) + expect_true(file.exists(str_c(filepath, "_problems.di.feather"))) # note for comparisons: rounding is NOT necessary because storage is equivalent to values in R expect_equal(iso_get_raw_data(di_example), read_feather(str_c(filepath, "_raw_data.di.feather"))) expect_equal(iso_get_file_info(di_example) %>% collapse_list_columns(), read_feather(str_c(filepath, "_file_info.di.feather"))) - expect_equal(iso_get_standards_info(di_example), read_feather(str_c(filepath, "_method_info-standards.di.feather"))) - expect_equal(iso_get_resistors_info (di_example), read_feather(str_c(filepath, "_method_info-resistors.di.feather"))) + expect_equal(iso_get_standards(di_example), read_feather(str_c(filepath, "_standards.di.feather"))) + expect_equal(iso_get_resistors (di_example), read_feather(str_c(filepath, "_resistors.di.feather"))) expect_equal(iso_get_vendor_data_table(di_example), read_feather(str_c(filepath, "_vendor_data_table.di.feather"))) + expect_equal(iso_get_problems(di_example) %>% select(file_id), read_feather(str_c(filepath, "_problems.di.feather"))) expect_true(all(file.remove(list.files(dirname(filepath), pattern = "\\.di\\.feather$", full.names = TRUE)))) # export real data files - continuous flow - expect_message(iso_export_to_feather(cf_example, filepath, quiet = FALSE), "exporting data .* into .cf.feather") + cf_examples <- c(cf_example, cf_err_example) + expect_message(iso_export_to_feather(cf_examples, filepath, quiet = FALSE), "exporting data .* into .cf.feather") expect_true(file.exists(str_c(filepath, "_raw_data.cf.feather"))) expect_true(file.exists(str_c(filepath, "_file_info.cf.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-standards.cf.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-resistors.cf.feather"))) + expect_true(file.exists(str_c(filepath, "_standards.cf.feather"))) + expect_true(file.exists(str_c(filepath, "_resistors.cf.feather"))) expect_true(file.exists(str_c(filepath, "_vendor_data_table.cf.feather"))) + expect_true(file.exists(str_c(filepath, "_problems.cf.feather"))) # note for comparisons: rounding is NOT necessary because storage is equivalent to values in R - expect_equal(iso_get_raw_data(cf_example), read_feather(str_c(filepath, "_raw_data.cf.feather"))) - expect_equal(iso_get_file_info(cf_example) %>% collapse_list_columns(), read_feather(str_c(filepath, "_file_info.cf.feather"))) - expect_equal(iso_get_standards_info(cf_example), read_feather(str_c(filepath, "_method_info-standards.cf.feather"))) - expect_equal(iso_get_resistors_info (cf_example), read_feather(str_c(filepath, "_method_info-resistors.cf.feather"))) - expect_equal(iso_get_vendor_data_table(cf_example) %>% iso_strip_units(), + expect_equal(iso_get_raw_data(cf_examples), read_feather(str_c(filepath, "_raw_data.cf.feather"))) + expect_equal(iso_get_file_info(cf_examples) %>% collapse_list_columns(), read_feather(str_c(filepath, "_file_info.cf.feather"))) + expect_equal(iso_get_standards(cf_examples), read_feather(str_c(filepath, "_standards.cf.feather"))) + expect_equal(iso_get_resistors (cf_examples), read_feather(str_c(filepath, "_resistors.cf.feather"))) + expect_equal(iso_get_vendor_data_table(cf_examples) %>% iso_strip_units(), read_feather(str_c(filepath, "_vendor_data_table.cf.feather"))) + expect_equal(iso_get_problems(cf_examples), read_feather(str_c(filepath, "_problems.cf.feather"))) expect_true(all(file.remove(list.files(dirname(filepath), pattern = "\\.cf\\.feather$", full.names = TRUE)))) # export with explicit units @@ -275,11 +321,11 @@ test_that("test that export to Feather works properly", { expect_message(iso_export_to_feather(scan_example, filepath, quiet = FALSE), "exporting data .* into .scan.feather") expect_true(file.exists(str_c(filepath, "_raw_data.scan.feather"))) expect_true(file.exists(str_c(filepath, "_file_info.scan.feather"))) - expect_true(file.exists(str_c(filepath, "_method_info-resistors.scan.feather"))) + expect_true(file.exists(str_c(filepath, "_resistors.scan.feather"))) # note for comparisons: rounding is NOT necessary because storage is equivalent to values in R expect_equal(iso_get_raw_data(scan_example), read_feather(str_c(filepath, "_raw_data.scan.feather"))) expect_equal(iso_get_file_info(scan_example) %>% collapse_list_columns(), read_feather(str_c(filepath, "_file_info.scan.feather"))) - expect_equal(iso_get_resistors_info (scan_example), read_feather(str_c(filepath, "_method_info-resistors.scan.feather"))) + expect_equal(iso_get_resistors (scan_example), read_feather(str_c(filepath, "_resistors.scan.feather"))) expect_true(all(file.remove(list.files(dirname(filepath), pattern = "\\.scan\\.feather$", full.names = TRUE)))) From 4275cd19530b2ce55b29e78fa8a667c8358e69de Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 00:57:02 -0700 Subject: [PATCH 46/51] change data_frame to tibble throughout --- DESCRIPTION | 2 +- R/aggregate_data.R | 18 +++--- R/isodata_structures.R | 2 +- R/isoread.R | 4 +- R/isoread_caf.R | 10 +-- R/isoread_cf.R | 2 +- R/isoread_did.R | 12 ++-- R/isoread_dxf.R | 4 +- R/isoread_flow_iarc.R | 2 +- R/isoread_isodat.R | 18 +++--- R/isoread_nu.R | 8 +-- R/isoread_scn.R | 2 +- R/problems.R | 8 +-- R/settings.R | 2 +- R/utils.R | 22 +++---- R/utils_binary_files.R | 14 ++-- R/utils_xml_files.R | 10 +-- tests/testthat/test-data-structures.R | 16 ++--- tests/testthat/test-isoread.R | 2 +- tests/testthat/test-nse.R | 2 +- tests/testthat/test-problems.R | 14 ++-- tests/testthat/test-settings.R | 4 +- tests/testthat/test-utils.R | 92 +++++++++++++-------------- 23 files changed, 135 insertions(+), 135 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c2fbea53..f05bb1ca 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: isoreader Title: Read IRMS data files Description: R interface to IRMS (isotope ratio mass spectrometry) file formats typically used in stable isotope geochemistry. -Version: 1.0.17 +Version: 1.1.0 Authors@R: person("Sebastian", "Kopf", email = "sebastian.kopf@colorado.edu", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-2044-0201")) URL: https://github.com/isoverse/isoreader BugReports: https://github.com/isoverse/isoreader/issues diff --git a/R/aggregate_data.R b/R/aggregate_data.R index 07405205..7a9639a6 100644 --- a/R/aggregate_data.R +++ b/R/aggregate_data.R @@ -27,10 +27,10 @@ iso_get_data_summary <- function(iso_files, quiet = default(quiet)) { message() } - if (length(iso_files) == 0) return(data_frame()) + if (length(iso_files) == 0) return(tibble()) # aggregate all the info - data_frame( + tibble( file_id = names(iso_files), file_path_ = map_chr( iso_files, @@ -63,12 +63,12 @@ get_raw_data_info <- function(iso_files) { # make sure to not process empty list if (length(iso_files) == 0) - return(data_frame(file_id = character(), raw_data = character())) + return(tibble(file_id = character(), raw_data = character())) raw_data_not_read <- "raw data not read" # retrieve the raw data info raw_data_sum <- - data_frame( + tibble( file_id = names(iso_files), read_raw_data = map_lgl(iso_files, ~.x$read_options$raw_data), all_ions = map(iso_files, ~names(.x$raw_data) %>% str_subset("^[iIvV](\\d+)\\.")), @@ -139,10 +139,10 @@ get_method_info_info <- function(iso_files) { # make sure to not process empty list if (length(iso_files) == 0) { - data_frame(file_id = character(), method_info = character()) + tibble(file_id = character(), method_info = character()) } else { # retrieve the raw data info - data_frame( + tibble( file_id = names(iso_files), read_method_info = map_lgl(iso_files, ~.x$read_options$method_info), has_standards = map_lgl(iso_files, ~!is.null(.x$method_info$standards)), @@ -469,12 +469,12 @@ iso_get_bgrd_data <- function(iso_files, select = everything(), gather = FALSE, check_read_options(iso_files, "raw_data") # check whether there are any - if (length(iso_files) == 0) return(data_frame()) + if (length(iso_files) == 0) return(tibble()) # fetch data data <- # fetch data - data_frame( + tibble( file_id = names(iso_files), bgrd_data = map(iso_files, ~.x$bgrd_data) ) %>% @@ -555,7 +555,7 @@ iso_get_standards <- function(iso_files, select = everything(), include_file_inf check_read_options(iso_files, "method_info") # check whether there are any - if (length(iso_files) == 0) return(data_frame()) + if (length(iso_files) == 0) return(tibble()) # fetch data data <- diff --git a/R/isodata_structures.R b/R/isodata_structures.R index 3f0c6b94..e72c68e6 100644 --- a/R/isodata_structures.R +++ b/R/isodata_structures.R @@ -161,7 +161,7 @@ iso_as_file_list <- function(..., discard_duplicates = TRUE) { # check for file_id duplicates dups <- - data_frame( + tibble( idx = 1:length(iso_list), file_id = names(iso_list) ) %>% diff --git a/R/isoread.R b/R/isoread.R index e248b581..95abdfaf 100644 --- a/R/isoread.R +++ b/R/isoread.R @@ -58,7 +58,7 @@ register_file_reader <- function(type, call, extension, func, description, softw frs <- default("file_readers", allow_null = TRUE) new_fr <- - dplyr::data_frame( + dplyr::tibble( type = type, call = call, extension = extension, func = func, cacheable = cacheable, description = description, software = software, env = env @@ -648,7 +648,7 @@ iso_reread_storage <- function(rds_filepaths, ..., stop_if_missing = FALSE, quie extension <- NULL extensions <- iso_get_supported_file_types() %>% dplyr::filter(stringr::str_detect(extension, "rd[sa]")) - file_types <- data_frame(path = rds_filepaths) %>% match_to_supported_file_types(extensions) + file_types <- tibble(path = rds_filepaths) %>% match_to_supported_file_types(extensions) if (any(missing <- !file.exists(rds_filepaths))) stop("file(s) do not exist: ", str_c(rds_filepaths[missing], collapse = ", "), call. = FALSE) diff --git a/R/isoread_caf.R b/R/isoread_caf.R index abe9fa3f..47c789ae 100644 --- a/R/isoread_caf.R +++ b/R/isoread_caf.R @@ -53,7 +53,7 @@ extract_caf_raw_voltage_data <- function(ds) { # read all masses masses_re <- re_combine(re_block("x-000"), re_block("fef-x"), re_text("rIntensity")) masses <- - data_frame( + tibble( pos = find_next_patterns(ds$binary, masses_re) + masses_re$size, # capture cup and mass data = map(pos, function(pos) { @@ -62,7 +62,7 @@ extract_caf_raw_voltage_data <- function(ds) { capture_data("cup", "text", re_block("fef-x"), data_bytes_max = 8, move_past_dots = TRUE) %>% move_to_next_pattern(re_text("rIntensity "), max_gap = 0L) %>% capture_data("mass", "text", re_block("fef-x"), data_bytes_max = 8) %>% - { as_data_frame(.$data[c("cup", "mass")]) } + { dplyr::as_tibble(.$data[c("cup", "mass")]) } }) ) %>% # unnest data @@ -111,11 +111,11 @@ extract_caf_raw_voltage_data <- function(ds) { capture_n_data("voltage", "double", n = nrow(masses), sensible = c(-1000, 100000)) # return voltage data - return(data_frame(cup = 1:nrow(masses), voltage = bin$data$voltage)) + return(tibble(cup = 1:nrow(masses), voltage = bin$data$voltage)) } # assemble voltages data frame - voltages <- data_frame( + voltages <- tibble( pos = positions + read_blocks_re$size, # note last read in the sample block is actually the "pre"-read of the standard type = ifelse(pos < sample_block_start | pos==max(pos), "standard", "sample") @@ -192,7 +192,7 @@ extract_caf_vendor_data_table <- function(ds) { # save information on the column units attr(ds$vendor_data_table, "units") <- bind_rows( - data_frame(column = c("cycle"), units = ""), + tibble(column = c("cycle"), units = ""), filter(extracted_dt$columns, column %in% names(ds$vendor_data_table))[c("column", "units")] ) return(ds) diff --git a/R/isoread_cf.R b/R/isoread_cf.R index c2b42666..22420ebc 100644 --- a/R/isoread_cf.R +++ b/R/isoread_cf.R @@ -95,7 +95,7 @@ extract_cf_raw_voltage_data <- function(ds) { ds$binary<- ds$binary %>% move_to_pos(data_start) %>% capture_data("voltages", c("float", rep("double", length(masses))), data_end_re) - voltages <- bind_rows(ds$binary$data$voltages %>% as_data_frame() %>% setNames(c("time.s", masses_columns))) + voltages <- bind_rows(ds$binary$data$voltages %>% dplyr::as_tibble() %>% setNames(c("time.s", masses_columns))) # check for data if (nrow(voltages) == 0) diff --git a/R/isoread_did.R b/R/isoread_did.R index 6fdadcf5..31711d52 100644 --- a/R/isoread_did.R +++ b/R/isoread_did.R @@ -91,21 +91,21 @@ extract_did_raw_voltage_data <- function(ds) { } # return voltage data - return(data_frame(cycle = bin$data$cycle, cup = 1:length(bin$data$voltage), voltage = bin$data$voltage)) + return(tibble(cycle = bin$data$cycle, cup = 1:length(bin$data$voltage), voltage = bin$data$voltage)) } # assemble voltages data frame voltages <- bind_rows( - data_frame(pos = standard_positions + standard_voltage_start_re$size, type = "standard"), - data_frame(pos = sample_positions + sample_voltage_start_re$size, type = "sample") + tibble(pos = standard_positions + standard_voltage_start_re$size, type = "standard"), + tibble(pos = sample_positions + sample_voltage_start_re$size, type = "sample") ) %>% mutate( voltages = map(pos, capture_voltages) ) %>% unnest(voltages) %>% # join in the mass information - left_join(data_frame(cup = 1:length(masses), mass = masses_columns), by = "cup") %>% + left_join(tibble(cup = 1:length(masses), mass = masses_columns), by = "cup") %>% # spread out the volrages select(-pos, -cup) %>% spread(mass, voltage) %>% # update cycle @@ -186,8 +186,8 @@ extract_did_vendor_data_table <- function(ds) { # vendor table ds$vendor_data_table <- bind_cols( - data_frame(cycle = vendor_dt[[1]][[1]]), - lapply(vendor_dt, `[[`, 2) %>% as_data_frame()) + tibble(cycle = vendor_dt[[1]][[1]]), + lapply(vendor_dt, `[[`, 2) %>% dplyr::as_tibble()) attr(ds$vendor_data_table, "units") <- NULL # units do not apply return(ds) } diff --git a/R/isoread_dxf.R b/R/isoread_dxf.R index a5d7eb01..4518bd78 100644 --- a/R/isoread_dxf.R +++ b/R/isoread_dxf.R @@ -152,7 +152,7 @@ extract_dxf_raw_voltage_data <- function(ds) { re_direct(".{4}", label = ".{4}"), re_null(4), re_block("fef-0"), re_block("stx")) gas_config_re <- re_combine(re_block("fef-x"), re_block("text"), re_block("fef-0")) - voltages <- data_frame() + voltages <- tibble() positions <- find_next_patterns(ds$binary, data_start_re) for (pos in positions) { @@ -190,7 +190,7 @@ extract_dxf_raw_voltage_data <- function(ds) { capture_data("voltages", c("float", rep("double", length(masses))), data_end_re) voltages <- bind_rows(voltages, ds$binary$data$voltages %>% - tibble::as_tibble() %>% setNames(c("time.s", masses_columns))) + dplyr::as_tibble() %>% setNames(c("time.s", masses_columns))) } # check for data diff --git a/R/isoread_flow_iarc.R b/R/isoread_flow_iarc.R index f7075877..8ee70a8f 100644 --- a/R/isoread_flow_iarc.R +++ b/R/isoread_flow_iarc.R @@ -186,7 +186,7 @@ read_irms_data_file <- function(iso_file, filepath, gas_config, run_time.s, data config <- gas_config$species[[dataset_attributes$Species]] # read irms data and determine which beams are used - irms_data <- h5read(filepath, "DataSet") %>% as_data_frame() + irms_data <- h5read(filepath, "DataSet") %>% dplyr::as_tibble() H5close() # garbage collect if (!"Scan" %in% names(irms_data)) diff --git a/R/isoread_isodat.R b/R/isoread_isodat.R index 70f3f215..6fabbb47 100644 --- a/R/isoread_isodat.R +++ b/R/isoread_isodat.R @@ -114,11 +114,11 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { } # store information - data_frame(name = bin$data$ref_name, config = bin$data$config, pos = bin$pos) + tibble(name = bin$data$ref_name, config = bin$data$config, pos = bin$pos) } # run refs capture - refs <- data_frame( + refs <- tibble( start_pos = find_next_patterns( ds$binary, re_combine(instrument_pre1, re_block("text"), instrument_pre2)), data = map(start_pos, capture_ref_names) @@ -150,7 +150,7 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { re_direct("([^\\x00]{2})?", label = "[^00]{2}"), re_block("x-000")) # return as data frame - as_data_frame( + dplyr::as_tibble( bin$data[c("gas", "delta_code", "delta_name", "delta_value", "delta_format", "reference")] ) %>% mutate( standard = refs$name[max(which(bin$pos > refs$pos))] @@ -159,7 +159,7 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { } # run delta capture - deltas <- data_frame( + deltas <- tibble( pos = positions + delta_re$size, data = map(pos, capture_delta_values) ) %>% unnest(data) %>% @@ -190,7 +190,7 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { capture_n_data("ratio_value", "double", 1) # return as data frame - as_data_frame(bin$data[c("ratio_code", "element", "ratio_name", "ratio_value", "ratio_format")]) %>% + dplyr::as_tibble(bin$data[c("ratio_code", "element", "ratio_name", "ratio_value", "ratio_format")]) %>% mutate( reference = refs$name[max(which(bin$pos > refs$pos))] ) @@ -198,7 +198,7 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { # run ratio capture if (length(positions) > 0) { - ratios <- data_frame( + ratios <- tibble( pos = positions + ratio_re$size, data = map(pos, capture_ratio_values) ) %>% @@ -206,7 +206,7 @@ extract_isodat_reference_values <- function(ds, cap_at_fun = NULL) { select(!!!c("reference", "element", "ratio_name", "ratio_value")) } else { # no ratios defined - ratios <- data_frame(reference = character(0), element = character(0), + ratios <- tibble(reference = character(0), element = character(0), ratio_name = character(0), ratio_value = numeric(0)) } @@ -689,7 +689,7 @@ extract_isodat_main_vendor_data_table_cells <- function(ds) { capture_data("format", "text", re_block("fef-x"), move_past_dots = TRUE) # retrieve format (!not always the same) # skip data columns without formatting infromation right away - if(bin$data$format %in% c("", " ")) return(data_frame()) + if(bin$data$format %in% c("", " ")) return(tibble()) # check for columns starting with delta symbol, replace with d instead of delta symbol if (identical(bin$data$column[1:2], as.raw(c(180, 03)))) @@ -697,7 +697,7 @@ extract_isodat_main_vendor_data_table_cells <- function(ds) { col <- bin$data$column <- parse_raw_data(bin$data$column, "text") # return column information - data_frame(column = col, format = bin$data$format, continue_pos = bin$pos) + tibble(column = col, format = bin$data$format, continue_pos = bin$pos) } # capture the table cell details diff --git a/R/isoread_nu.R b/R/isoread_nu.R index e7093cc7..c17f0bc4 100644 --- a/R/isoread_nu.R +++ b/R/isoread_nu.R @@ -269,7 +269,7 @@ parse_nu_data <- function(data, n_blocks, n_channels, masses = c()) { # check for problems (log & return empty dta frames) if (n_problems(zero_data) > 0 || n_problems(raw_data) > 0) { - retval <- list(bgrd_data = data_frame(), raw_data = data_frame()) %>% + retval <- list(bgrd_data = tibble(), raw_data = tibble()) %>% set_problems(combined_problems(zero_data, raw_data)) return(retval) } @@ -312,7 +312,7 @@ parse_nu_zero_data <- function(raw_data, masses = c()) { df <- raw_data %>% group_by(block) %>% - mutate(data = map(data, ~data_frame(channel = seq_along(.x), intensities = stringr::str_split(.x, "\\s+")))) %>% + mutate(data = map(data, ~tibble(channel = seq_along(.x), intensities = stringr::str_split(.x, "\\s+")))) %>% select(block, n_channels, zero_length, data) # safety checks @@ -343,7 +343,7 @@ parse_nu_raw_data <- function(raw_data, masses = c()) { # convert string data to numeric data mutate( type = ifelse(is_ref, "standard", "sample"), - data = map(data, ~data_frame(channel = seq_along(.x), intensities = stringr::str_split(.x, "\\s+"))) + data = map(data, ~tibble(channel = seq_along(.x), intensities = stringr::str_split(.x, "\\s+"))) ) %>% select(block, n_channels, n_cycles, cycle_length, data, cycle, type) @@ -464,7 +464,7 @@ group_lines <- function(lines, group_regexp) { group <- line <- start <- end <- NULL stopifnot(is(lines, "character")) - data_frame( + tibble( lines = lines, line = seq_along(lines), group = cumsum(str_detect(lines, group_regexp)) diff --git a/R/isoread_scn.R b/R/isoread_scn.R index d885c4d1..be04b555 100644 --- a/R/isoread_scn.R +++ b/R/isoread_scn.R @@ -168,7 +168,7 @@ extract_scn_raw_voltage_data <- function(ds) { "voltages", c("float", rep("double", ds$binary$data$n_traces)), ds$binary$data$n_points ) - voltages <- tibble::as_tibble(ds$binary$data$voltages) + voltages <- dplyr::as_tibble(ds$binary$data$voltages) # safety check if (ncol(voltages) - 1L != nrow(ds$binary$data$config)) { diff --git a/R/problems.R b/R/problems.R index 524b4f56..725acebb 100644 --- a/R/problems.R +++ b/R/problems.R @@ -72,7 +72,7 @@ iso_get_problems_summary <- function(iso_files, problem_files_only = TRUE) { error <- warning <- type <- NULL # tally up problems - probs_templ <- data_frame(file_id = character(0), error = integer(0), warning = integer(0)) + probs_templ <- tibble(file_id = character(0), error = integer(0), warning = integer(0)) if (n_problems(iso_files) > 0) { probs <- problems(iso_files) %>% # tally up number of warnings/errors per file @@ -88,7 +88,7 @@ iso_get_problems_summary <- function(iso_files, problem_files_only = TRUE) { if (!problem_files_only) { # merge with file list to get all listed - probs <- data_frame( + probs <- tibble( file_id = names(iso_files) ) %>% left_join(probs, by = "file_id") @@ -155,7 +155,7 @@ iso_filter_files_with_problems <- function(iso_files, remove_files_with_errors = register_problem <- function(obj, type = NA_character_, details = NA_character_, ..., func = find_parent_call("register_problem"), keep_duplicates = FALSE) { if (func == "NULL") func <- NA_character_ - problem <- data_frame(type = type, func = func, details = details, ...) + problem <- tibble(type = type, func = func, details = details, ...) if (iso_is_file_list(obj)) { obj <- as.list(obj) for (i in 1:length(obj)) { @@ -218,7 +218,7 @@ combined_problems <- function(...) { # isoreader problems structure get_problems_structure <- function() { - data_frame( + tibble( type = character(), func = character(), details = character() diff --git a/R/settings.R b/R/settings.R index a0062ab1..3afae7a8 100644 --- a/R/settings.R +++ b/R/settings.R @@ -75,7 +75,7 @@ iso_get_default_reader_parameters <- function() { c("quiet", "cache", "cache_dir", "read_raw_data", "read_file_info", "read_method_info", "read_vendor_data_table") %>% sapply(function(x) list(default(!!x))) %>% { - data_frame(parameter = names(.), + tibble(parameter = names(.), value = as.character(unlist(.))) } } diff --git a/R/utils.R b/R/utils.R index a4e95dd4..c3549eb1 100644 --- a/R/utils.R +++ b/R/utils.R @@ -253,7 +253,7 @@ get_paths_data_frame <- function(path, root, check_existence = TRUE) { # paths data frame paths <- - data_frame( + tibble( i = 1:max(length(root), length(path)), root = root, path = path, @@ -477,7 +477,7 @@ iso_shorten_relative_paths <- function(path, root = ".") { # generate paths dataframe (WITHOUT concatenating path and root ulnike get_paths_data_frame) paths <- - data_frame( + tibble( i = 1:max(length(root), length(path)), path = path, root = root, @@ -532,7 +532,7 @@ iso_find_absolute_path_roots <- function(path, root = ".", check_existence = TRU absolute <- is_dir <- full_path <- rel_root_folders <- path_folders <- abs_root_folders <- has_rel_root <- new_path <- i <- NULL # anything to work with? - if(length(path) == 0) return(data_frame(root = character(0), path = character(0))) + if(length(path) == 0) return(tibble(root = character(0), path = character(0))) # generate data frame and check existence paths <- get_paths_data_frame(path, root, check_existence = check_existence) @@ -674,16 +674,16 @@ generate_cache_filepaths <- function(filepaths, read_options = list()) { unf(obj)$hash %>% str_c(collapse = "") } - # cached files versioning --> + # cached files versioning --> # include minor if v < 1.0, afterwards go by major version (2.0, 3.0, etc.) - iso_v <- + iso_v <- packageVersion("isoreader") %>% { if (.$major < 1) paste0(.$major, ".", .$minor) else paste0(.$major, ".0") } - + file_info <- file.info(filepaths) %>% - as_data_frame() %>% + dplyr::as_tibble() %>% rownames_to_column() %>% select(filepath = rowname, size = size, modified = mtime) %>% mutate( @@ -731,13 +731,13 @@ load_cached_iso_file <- function(filepath, check_version = TRUE) { same_as_isoreader_version <- function(version, isoreader_version = packageVersion("isoreader")) { file_version <- version$major * 10 - if (version$major < 1) + if (version$major < 1) file_version <- file_version + version$minor - + package_version <- isoreader_version$major * 10 - if (isoreader_version$major < 1) + if (isoreader_version$major < 1) package_version <- package_version + isoreader_version$minor - + return(file_version == package_version) } diff --git a/R/utils_binary_files.R b/R/utils_binary_files.R index ccc1f398..b7df7c0a 100644 --- a/R/utils_binary_files.R +++ b/R/utils_binary_files.R @@ -13,7 +13,7 @@ read_binary_file <- function(filepath) { structure( list( raw = raw(), - C_blocks = data_frame(), + C_blocks = tibble(), data = list(), pos = 1L, # current position within the file max_pos = NULL, # max position to consider during operations @@ -536,7 +536,7 @@ get_ctrl_blocks_config <- function() { get_ctrl_blocks_config_df <- function() { get_ctrl_blocks_config() %>% { - data_frame( + tibble( block = names(.), regexp = map_chr(., "regexp"), hexadecimal = map_chr( @@ -809,7 +809,7 @@ map_binary_structure <- function(bfile, length = 100, start = bfile$pos, ctrl_bl # data blocks processing data_block_configs <- get_data_blocks_config() %>% {.[map_lgl(., "auto")] } data_block_types <- names(data_block_configs) - data_matches <- data_frame(data_type = data_block_types, matches = FALSE, + data_matches <- tibble(data_type = data_block_types, matches = FALSE, trailing_zeros = 0, n_values = 0, rep_value = NA_character_, min_value = NA_real_, max_value = NA_real_) @@ -894,7 +894,7 @@ generate_binary_structure_map_printout <- function(bsm, data_as_raw = FALSE, lin data_overview <- lapply(bsm$blocks, function(block) { if (block$type == "data") - data_frame( + tibble( rep_value = sprintf("{%s}", str_c(as.character(block$raw), collapse = " ")), start = block$start) else NULL @@ -925,14 +925,14 @@ generate_binary_structure_map_printout <- function(bsm, data_as_raw = FALSE, lin # combine data_text <- with(data, str_c("{", rep_value, "}", trailing00_block)) - if (length(data_text) > 1) data_frame(rep_value = str_c("{", str_c(data_text, collapse = "|"), "}")) - else data_frame(rep_value = data_text) + if (length(data_text) > 1) tibble(rep_value = str_c("{", str_c(data_text, collapse = "|"), "}")) + else tibble(rep_value = data_text) }) } } # process blocks and block data for printing - if (nrow(data_overview) == 0) data_overview <- data_frame(start = integer(0), rep_value = character(0)) + if (nrow(data_overview) == 0) data_overview <- tibble(start = integer(0), rep_value = character(0)) all_blocks <- blocks %>% left_join(data_overview, by = "start") # indentation diff --git a/R/utils_xml_files.R b/R/utils_xml_files.R index 7859086c..1bd16721 100644 --- a/R/utils_xml_files.R +++ b/R/utils_xml_files.R @@ -10,7 +10,7 @@ map_xml_children <- function(nodes, select = NULL) { # map all as text ignoring everything that does not have exactly 1 value map_chr(function(x) if (length(x) == 1) x[[1]] else NA_character_) %>% # convert to data frame - as.list() %>% as_data_frame() + as.list() %>% dplyr::as_tibble() }) } @@ -64,7 +64,7 @@ process_iarc_info_xml <- function(filepath) { # process iarc methods xml files process_iarc_methods_xml <- function(filepaths) { - if (length(filepaths) == 0) return(data_frame()) + if (length(filepaths) == 0) return(tibble()) method_params <- filepaths %>% @@ -123,7 +123,7 @@ process_iarc_tasks_xml <- function(filepaths, method_parameters) { ) %>% left_join(method_parameters, by = c("MethodId" = "MethodId", "ParameterIdentifier" = "Id")) } else { - task_values <- data_frame() + task_values <- tibble() } # @NOTE: TypeIdentifier in the method_parameters holds the data type but even for numbers it seems to always be "String", currently not processed further (i.e. not turned into a different data type) @@ -146,7 +146,7 @@ process_iarc_tasks_xml <- function(filepaths, method_parameters) { filename = basename(task_file), # combine task info with task values info = - task_info %>% as_data_frame() %>% + task_info %>% dplyr::as_tibble() %>% { if (nrow(task_values) > 0) { left_join(., @@ -213,7 +213,7 @@ process_iarc_processing_xml <- function(processing_list_id, filepath) { xml_find_all(".//SerialisablePropertyBag[Identifier[.='{42D28191-A6E9-4B7B-8C3D-0F0037624F7D}']]") %>% map(xml_fetch_container_value, c("NumeratorBeamChannel", "DenominatorBeamChannel", "Label")) %>% bind_rows() - if (nrow(ratio_defs) == 0) return (data_frame(channel = character(), mass = character())) + if (nrow(ratio_defs) == 0) return (tibble(channel = character(), mass = character())) # derive channel defintions channel_defs <- diff --git a/tests/testthat/test-data-structures.R b/tests/testthat/test-data-structures.R index 1991fabe..5d517728 100644 --- a/tests/testthat/test-data-structures.R +++ b/tests/testthat/test-data-structures.R @@ -112,7 +112,7 @@ test_that("test that iso_file list checks work", { # can set file path for data structures ==== test_that("can set file path for data structures", { - expect_error(set_ds_file_path(data_frame()), "can only set path for iso_file data structures") + expect_error(set_ds_file_path(tibble()), "can only set path for iso_file data structures") expect_silent(ds <- make_iso_file_data_structure("NA")) expect_error(set_ds_file_path(ds, "DNE", "DNE"), "does not exist") @@ -170,16 +170,16 @@ test_that("test that isofils objects can be combined and subset", { "duplicate files kept") expect_is(iso_filesABA, "iso_file_list") expect_equal(names(iso_filesABA), c("A#1", "B", "A#2")) - expect_equal(problems(iso_filesABA) %>% select(file_id, type), data_frame(file_id = c("A#1", "A#2"), type = "warning")) - expect_equal(problems(iso_filesABA[[1]]) %>% select(type), data_frame(type = "warning")) - expect_equal(problems(iso_filesABA[[2]]) %>% select(type), data_frame(type = character(0))) - expect_equal(problems(iso_filesABA[[3]]) %>% select(type), data_frame(type = "warning")) + expect_equal(problems(iso_filesABA) %>% select(file_id, type), tibble(file_id = c("A#1", "A#2"), type = "warning")) + expect_equal(problems(iso_filesABA[[1]]) %>% select(type), tibble(type = "warning")) + expect_equal(problems(iso_filesABA[[2]]) %>% select(type), tibble(type = character(0))) + expect_equal(problems(iso_filesABA[[3]]) %>% select(type), tibble(type = "warning")) expect_equal(problems(c(iso_fileA, iso_fileA)), problems(c(iso_fileA, iso_fileA, iso_fileA))) ## combining identical files (with discarding duplicates, i.e. default behavior) expect_message(iso_filesABA <- c(iso_fileA, iso_fileB, iso_fileA), "duplicate files encountered, only first kept") - expect_equal(problems(iso_filesABA) %>% select(file_id, type), data_frame(file_id = "A", type = "warning")) + expect_equal(problems(iso_filesABA) %>% select(file_id, type), tibble(file_id = "A", type = "warning")) expect_equal(names(iso_filesABA), c("A", "B")) ## propagating problems @@ -190,10 +190,10 @@ test_that("test that isofils objects can be combined and subset", { "iso_file_list" ) expect_equal(problems(iso_filesAB_probs) %>% select(file_id, details), - data_frame(file_id = c("A", "B"), details = paste("warning", c("A", "B")))) + tibble(file_id = c("A", "B"), details = paste("warning", c("A", "B")))) expect_message(iso_files_ABB_probs <- c(iso_filesAB_probs, iso_fileB), "duplicate files encountered") expect_equal(problems(iso_files_ABB_probs) %>% select(file_id, details), - data_frame(file_id = c("A", "B", "B"), details = c("warning A", "warning B", + tibble(file_id = c("A", "B", "B"), details = c("warning A", "warning B", "duplicate files encountered, only first kept: B"))) # subsetting iso_files diff --git a/tests/testthat/test-isoread.R b/tests/testthat/test-isoread.R index 5e65ed21..c32ec8d5 100644 --- a/tests/testthat/test-isoread.R +++ b/tests/testthat/test-isoread.R @@ -19,7 +19,7 @@ test_that("test that file reader registration works", { test_that("test that parameter checks are performed when reading file", { # make sure adequate parameter supplied expect_error(iso_read_files(), "missing") - expect_error(iso_read_files(supported_extensions = data_frame()), "not in data\\: \\'extension\\', \\'func\\'") + expect_error(iso_read_files(supported_extensions = tibble()), "not in data\\: \\'extension\\', \\'func\\'") expect_error(iso_read_files( supported_extensions = get_supported_di_files(), data_structure = structure(list())), "data structure must include class \\'iso_file\\'") diff --git a/tests/testthat/test-nse.R b/tests/testthat/test-nse.R index ee38ec94..238c6472 100644 --- a/tests/testthat/test-nse.R +++ b/tests/testthat/test-nse.R @@ -2,7 +2,7 @@ context("Standard and non-standard evaluation") test_that("Getting column names (with # and type requirement checks) works", { - df <- tibble::as_tibble(mtcars) %>% tibble::rownames_to_column() + df <- dplyr::as_tibble(mtcars) %>% tibble::rownames_to_column() # basic errors expect_error(get_column_names(), "no data frame supplied") diff --git a/tests/testthat/test-problems.R b/tests/testthat/test-problems.R index df82ffef..41f87193 100644 --- a/tests/testthat/test-problems.R +++ b/tests/testthat/test-problems.R @@ -24,7 +24,7 @@ test_that("Test that problem registration and reporting works properly", { }, x) expect_equal(n_problems(y), 1) - expect_equal(problems(y) %>% select(func, details), data_frame(func = "testing", details = "problem")) + expect_equal(problems(y) %>% select(func, details), tibble(func = "testing", details = "problem")) # add another problem expect_equal({ @@ -32,7 +32,7 @@ test_that("Test that problem registration and reporting works properly", { as.character(z) }, x) expect_equal(n_problems(z), 2) - expect_equal(problems(z) %>% select(details, code), data_frame(details = c("problem", "problem2"), code = c(NA, 5))) + expect_equal(problems(z) %>% select(details, code), tibble(details = c("problem", "problem2"), code = c(NA, 5))) # stop for problems expect_error(stop_for_problems(z), "2 parsing failures") @@ -52,9 +52,9 @@ test_that("Test that problems set for iso_file lists get propagated to all files expect_is(iso_files_w_probs <- register_problem(iso_files, type = "test"), "iso_file_list") expect_true(iso_has_problems(iso_files_w_probs)) expect_equal(problems(iso_files_w_probs) %>% select(file_id, type), - data_frame(file_id = c("A", "B"), type = c("test"))) - expect_equal(problems(iso_files_w_probs[[1]]) %>% select(type), data_frame(type = "test")) - expect_equal(problems(iso_files_w_probs[[2]]) %>% select(type), data_frame(type = "test")) + tibble(file_id = c("A", "B"), type = c("test"))) + expect_equal(problems(iso_files_w_probs[[1]]) %>% select(type), tibble(type = "test")) + expect_equal(problems(iso_files_w_probs[[2]]) %>% select(type), tibble(type = "test")) }) test_that("Test that warning and error registration works properly", { @@ -65,14 +65,14 @@ test_that("Test that warning and error registration works properly", { expect_silent(y <- register_warning(x, details = "problem", warn = FALSE)) expect_equal(as.character(y), x) expect_equal(n_problems(y), 1) - expect_equal(problems(y) %>% select(type, details), data_frame(type = "warning", details = "problem")) + expect_equal(problems(y) %>% select(type, details), tibble(type = "warning", details = "problem")) # add an error expect_message(y <- register_error(x, details = "problem", warn = TRUE), "caught error - problem") expect_silent(y <- register_error(x, details = "problem", warn = FALSE)) expect_equal(as.character(y), x) expect_equal(n_problems(y), 1) - expect_equal(problems(y) %>% select(type, details), data_frame(type = "error", details = "problem")) + expect_equal(problems(y) %>% select(type, details), tibble(type = "error", details = "problem")) }) test_that("Combing problems works properly", { diff --git a/tests/testthat/test-settings.R b/tests/testthat/test-settings.R index 82335d51..833b7845 100644 --- a/tests/testthat/test-settings.R +++ b/tests/testthat/test-settings.R @@ -45,7 +45,7 @@ test_that("info messages can be switched for just one function", { }) test_that("info message functions can be part of a pipeline", { - df <- data_frame(a = 1:5) + df <- tibble(a = 1:5) expect_equal(df %>% iso_turn_info_messages_on(), df) expect_equal(df %>% iso_turn_info_messages_off(), df) }) @@ -79,7 +79,7 @@ test_that("setting default read_parameters", { expect_message(iso_set_default_read_parameters(read_method_info = FALSE, quiet=FALSE)) expect_false(default(read_file_info)) expect_false(default(read_method_info)) - df <- data_frame(a = 1:5) + df <- tibble(a = 1:5) expect_equal(iso_set_default_read_parameters(df, read_file_info = TRUE, read_method_info = TRUE, quiet=TRUE), df) expect_true(default(read_file_info)) expect_true(default(read_method_info)) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index fbc6eb95..8430e052 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -54,16 +54,16 @@ test_that("test that file extension helpers work correctly", { # match supported filed types expect_error(match_to_supported_file_types()) - expect_error(match_to_supported_file_types(data_frame(), data_frame())) + expect_error(match_to_supported_file_types(tibble(), tibble())) expect_error( - match_to_supported_file_types(data_frame(path = "test.txt"), data_frame(extension = ".csv")), + match_to_supported_file_types(tibble(path = "test.txt"), tibble(extension = ".csv")), "unexpected file extension" ) expect_equal( match_to_supported_file_types( - data_frame(path = c("test.csv", "test.dxf")), - data_frame(extension = c(".dxf", "t.dxf", ".csv"))), - data_frame(path = c("test.csv", "test.dxf"), extension = c(".csv", "t.dxf")) + tibble(path = c("test.csv", "test.dxf")), + tibble(extension = c(".dxf", "t.dxf", ".csv"))), + tibble(path = c("test.csv", "test.dxf"), extension = c(".csv", "t.dxf")) ) }) @@ -129,44 +129,44 @@ test_that("relative path shortening works correctly", { # shoretning of sequential ././. expect_equal(iso_shorten_relative_paths(file.path(".", ".", "A", "B", ".", "C")), - data_frame(root = ".", path = file.path("A", "B", "C"))) + tibble(root = ".", path = file.path("A", "B", "C"))) # shortening of relative paths expect_equal(iso_shorten_relative_paths(file.path("A", "B", "C"), "A"), - data_frame(root = "A", path = file.path("B", "C"))) + tibble(root = "A", path = file.path("B", "C"))) expect_equal(iso_shorten_relative_paths(file.path("A", "B", "C"), file.path("A", "B")), - data_frame(root = file.path("A", "B"), path = "C")) + tibble(root = file.path("A", "B"), path = "C")) expect_equal(iso_shorten_relative_paths(file.path("A", "C", "D"), file.path("A", "B")), - data_frame(root = "A", path = file.path("C", "D"))) + tibble(root = "A", path = file.path("C", "D"))) expect_equal(iso_shorten_relative_paths(file.path("A", ".", ".", "B", "C"), file.path(".", "A", "B")), - data_frame(root = file.path("A", "B"), path = "C")) + tibble(root = file.path("A", "B"), path = "C")) expect_equal(iso_shorten_relative_paths(file.path("A", "B", "C"), "B"), - data_frame(root = ".", path = file.path("A", "B", "C"))) + tibble(root = ".", path = file.path("A", "B", "C"))) expect_equal(iso_shorten_relative_paths(file.path("A", "B", "C"), file.path("A", "B", "C")), - data_frame(root = file.path("A", "B", "C"), path = ".")) + tibble(root = file.path("A", "B", "C"), path = ".")) expect_equal(iso_shorten_relative_paths(file.path("A", "B", "C"), file.path("A", "B", "C", "D")), - data_frame(root = file.path("A", "B", "C"), path = ".")) + tibble(root = file.path("A", "B", "C"), path = ".")) # path and root absolute - stay the same expect_equal(iso_shorten_relative_paths(getwd(), system.file(package = "base")), - data_frame(root = system.file(package = "base"), path = getwd())) + tibble(root = system.file(package = "base"), path = getwd())) # root gets shortened to wd if a subpath expect_equal(iso_shorten_relative_paths(file.path("A", "B"), getwd()), - data_frame(root = ".", path = file.path("A", "B"))) + tibble(root = ".", path = file.path("A", "B"))) expect_equal(iso_shorten_relative_paths(file.path("A", "B"), file.path(getwd(), "A")), - data_frame(root = "A", path = "B")) + tibble(root = "A", path = "B")) # no shortening for absolute paths (only roots) expect_equal(iso_shorten_relative_paths(getwd(), getwd()), - data_frame(root = ".", path = getwd())) + tibble(root = ".", path = getwd())) # mixed test expect_equal( iso_shorten_relative_paths( c(file.path("A", "B", "C"), file.path("A", "C"), file.path("B", "C"), getwd()), file.path("A", "B")), - data_frame(root = c(file.path("A", "B"), "A", ".", file.path("A", "B")), + tibble(root = c(file.path("A", "B"), "A", ".", file.path("A", "B")), path = c("C", "C", c(file.path("B", "C"), getwd()))) ) @@ -174,7 +174,7 @@ test_that("relative path shortening works correctly", { expect_equal( iso_root_paths( c(file.path("A", "B", "C"), file.path("A", "C"), file.path("B", "C"), getwd()), file.path("A", "B"), check_existence = FALSE), - data_frame(root = c(file.path("A", "B"), "A", ".", getwd()), + tibble(root = c(file.path("A", "B"), "A", ".", getwd()), path = c("C", "C", c(file.path("B", "C"), "."))) ) @@ -190,41 +190,41 @@ test_that("test that root folder finding works correctly", { expect_error(iso_find_absolute_path_roots(".", "DNE"), "do not exist") expect_error(iso_find_absolute_path_roots(c(".", ".", "."), c(".", ".")), "one entry or be of the same length") expect_error(iso_find_absolute_path_roots(""), "empty paths .* are not valid") - expect_equal(iso_find_absolute_path_roots(c()), data_frame(root = character(0), path = character(0))) + expect_equal(iso_find_absolute_path_roots(c()), tibble(root = character(0), path = character(0))) # general checks on relative paths (should remain unchanged) test_folder <- "test_data" # test_folder <- file.path("tests", "testthat", "test_data") # for direct testing - expect_equal(iso_find_absolute_path_roots(test_folder), data_frame(root = ".", path = test_folder)) - expect_equal(iso_find_absolute_path_roots(".", root = test_folder), data_frame(root = test_folder, path = ".")) - expect_equal(iso_find_absolute_path_roots(c(test_folder, ".")), data_frame(root = ".", path = c(test_folder, "."))) - expect_equal(iso_find_absolute_path_roots(c(test_folder, "."), "."), data_frame(root = ".", path = c(test_folder, "."))) - expect_equal(iso_find_absolute_path_roots(c(test_folder, "."), c(".", ".")), data_frame(root = ".", path = c(test_folder, "."))) - expect_equal(iso_find_absolute_path_roots(c(test_folder, "."), c(".", getwd())), data_frame(root = c(".", getwd()), path = c(test_folder, "."))) - expect_equal(iso_find_absolute_path_roots(c(".", test_folder)), data_frame(root = ".", path = c(".", test_folder))) - expect_equal(iso_find_absolute_path_roots("cf_example_H_01.cf", test_folder), data_frame(root = test_folder, path = "cf_example_H_01.cf")) + expect_equal(iso_find_absolute_path_roots(test_folder), tibble(root = ".", path = test_folder)) + expect_equal(iso_find_absolute_path_roots(".", root = test_folder), tibble(root = test_folder, path = ".")) + expect_equal(iso_find_absolute_path_roots(c(test_folder, ".")), tibble(root = ".", path = c(test_folder, "."))) + expect_equal(iso_find_absolute_path_roots(c(test_folder, "."), "."), tibble(root = ".", path = c(test_folder, "."))) + expect_equal(iso_find_absolute_path_roots(c(test_folder, "."), c(".", ".")), tibble(root = ".", path = c(test_folder, "."))) + expect_equal(iso_find_absolute_path_roots(c(test_folder, "."), c(".", getwd())), tibble(root = c(".", getwd()), path = c(test_folder, "."))) + expect_equal(iso_find_absolute_path_roots(c(".", test_folder)), tibble(root = ".", path = c(".", test_folder))) + expect_equal(iso_find_absolute_path_roots("cf_example_H_01.cf", test_folder), tibble(root = test_folder, path = "cf_example_H_01.cf")) expect_equal( iso_find_absolute_path_roots(c(test_folder, file.path(test_folder, "cf_example_H_01.cf"))), - data_frame(root = ".", path = c(test_folder, file.path(test_folder, "cf_example_H_01.cf")))) + tibble(root = ".", path = c(test_folder, file.path(test_folder, "cf_example_H_01.cf")))) # absolute paths that fit the relative path - expect_equal(iso_find_absolute_path_roots(getwd()), data_frame(root = ".", path = ".")) - expect_equal(iso_find_absolute_path_roots(file.path(getwd(), test_folder)), data_frame(root = ".", path = test_folder)) - expect_equal(iso_find_absolute_path_roots(c(file.path(getwd(), test_folder), test_folder)), data_frame(root = ".", path = c(test_folder, test_folder))) + expect_equal(iso_find_absolute_path_roots(getwd()), tibble(root = ".", path = ".")) + expect_equal(iso_find_absolute_path_roots(file.path(getwd(), test_folder)), tibble(root = ".", path = test_folder)) + expect_equal(iso_find_absolute_path_roots(c(file.path(getwd(), test_folder), test_folder)), tibble(root = ".", path = c(test_folder, test_folder))) expect_equal( iso_find_absolute_path_roots(c(file.path(getwd(), test_folder), test_folder), c(test_folder, ".")), - data_frame(root = c(test_folder, "."), path = c(".", test_folder))) + tibble(root = c(test_folder, "."), path = c(".", test_folder))) expect_equal( iso_find_absolute_path_roots(c(file.path(getwd(), test_folder), file.path(getwd(), test_folder, "cf_example_H_01.cf")), test_folder), - data_frame(root = test_folder, path = c(".", "cf_example_H_01.cf"))) + tibble(root = test_folder, path = c(".", "cf_example_H_01.cf"))) expect_equal( iso_find_absolute_path_roots(c(file.path(getwd(), test_folder), file.path(getwd(), test_folder, "cf_example_H_01.cf")), c(".", test_folder)), - data_frame(root = c(".", test_folder), path = c(test_folder, "cf_example_H_01.cf"))) + tibble(root = c(".", test_folder), path = c(test_folder, "cf_example_H_01.cf"))) # add absolute paths that don't fit the relative path (this don't work interactively if package installed in current path with devtools) td <- system.file(package = "isoreader") expect_equal( iso_find_absolute_path_roots(c(td, file.path(getwd(), test_folder), file.path(test_folder, "cf_example_H_01.cf"))), - data_frame(root = c(td, ".", "."), path = c(".", test_folder, file.path(test_folder, "cf_example_H_01.cf"))) + tibble(root = c(td, ".", "."), path = c(".", test_folder, file.path(test_folder, "cf_example_H_01.cf"))) ) expect_equal( iso_find_absolute_path_roots( @@ -232,7 +232,7 @@ test_that("test that root folder finding works correctly", { system.file("extdata", package = "isoreader"), system.file("extdata", "dual_inlet_example.did", package = "isoreader")) ), - data_frame( + tibble( root = system.file(package = "isoreader"), path = c(".", "extdata", file.path("extdata", "dual_inlet_example.did")) ) @@ -243,7 +243,7 @@ test_that("test that root folder finding works correctly", { c(system.file("extdata", package = "isoreader"), system.file("extdata", "dual_inlet_example.did", package = "isoreader")), ), - data_frame( + tibble( root = system.file("extdata", package = "isoreader"), path = c(".", "dual_inlet_example.did") ) @@ -255,7 +255,7 @@ test_that("test that root folder finding works correctly", { c(system.file("extdata", "dual_inlet_example.did", package = "isoreader"), system.file("extdata", "dual_inlet_example.did", package = "isoreader")) ), - data_frame( + tibble( root = system.file("extdata", package = "isoreader"), path = c("dual_inlet_example.did", "dual_inlet_example.did") ) @@ -280,11 +280,11 @@ test_that("test that retrieving file paths works correctly", { # check expected result direct_list <- system.file("extdata", package = "isoreader") %>% list.files(full.names = T, pattern = "\\.(dxf|did|cf)$") expect_identical( - data_frame(root = ".", path = direct_list), + tibble(root = ".", path = direct_list), system.file("extdata", package = "isoreader") %>% iso_expand_paths(c("did", "dxf", "cf")) ) expect_identical( - data_frame(root = ".", path = direct_list %>% {.[c(2,1,3:length(.))]}), + tibble(root = ".", path = direct_list %>% {.[c(2,1,3:length(.))]}), c(direct_list[2], system.file("extdata", package = "isoreader")) %>% iso_expand_paths(c("did", "dxf", "cf")) ) @@ -296,10 +296,10 @@ test_that("test that retrieving file paths works correctly", { }) test_that("test that column name checks work correctly", { - expect_error(col_check("x", data_frame(y=1:5)), "not in data") - expect_error(col_check(c("x", "y"), data_frame(x=1:5)), "not in data") - expect_silent(col_check("x", data_frame(x=1:5))) - expect_silent(col_check(c("x", "y"), data_frame(x=1:5, y="test"))) + expect_error(col_check("x", tibble(y=1:5)), "not in data") + expect_error(col_check(c("x", "y"), tibble(x=1:5)), "not in data") + expect_silent(col_check("x", tibble(x=1:5))) + expect_silent(col_check(c("x", "y"), tibble(x=1:5, y="test"))) }) @@ -330,7 +330,7 @@ test_that("test that error catching works correctly", { expect_error(exec_func_with_error_catch(function(x) stop("problem"), 1), "problem") expect_equal( {suppressMessages(iso_turn_debug_off()); default(debug)}, FALSE) expect_message(y <- exec_func_with_error_catch(function(x) stop("problem"), 1), "problem") - expect_equal(problems(y) %>% select(type, details), data_frame(type = "error", details = "problem")) + expect_equal(problems(y) %>% select(type, details), tibble(type = "error", details = "problem")) }) From 839de5fa5d2f250ef7d7b23b6fd0e9d098c48774 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 01:18:56 -0700 Subject: [PATCH 47/51] add deprecated message for with_ratios parameter --- R/aggregate_data.R | 17 ++++++++++++++--- R/export.R | 14 ++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/R/aggregate_data.R b/R/aggregate_data.R index 7a9639a6..71bd2839 100644 --- a/R/aggregate_data.R +++ b/R/aggregate_data.R @@ -220,12 +220,17 @@ iso_get_all_data <- function( include_vendor_data_table = everything(), include_problems = NULL, gather = FALSE, with_explicit_units = with_units, - with_units = FALSE, quiet = default(quiet)) { + with_units = FALSE, with_ratios = NULL, quiet = default(quiet)) { # info iso_files <- iso_as_file_list(iso_files) if (!quiet) sprintf("Info: aggregating all data from %d data file(s)", length(iso_files)) %>% message() + # deprecated parameter + if (!missing(with_ratios)) { + warning("the 'with_ratios' parameter is deprecated, please use the column selection parameter 'include_standards' to explicitly include or exclude ratio columns", immediate. = TRUE, call. = FALSE) + } + # is di or cf? di_or_cf <- iso_is_continuous_flow(iso_files) || iso_is_dual_inlet(iso_files) @@ -530,13 +535,14 @@ iso_get_standards_info <- function(...) { #' Aggregate standards from methods info #' -#' Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter \code{with_ratios} to exclude/include the ratios. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE}. +#' Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter \code{select} to exclude/include the ratios. All standards info is only available if the iso_files were read with parameter \code{read_method_info=TRUE}. #' #' @inheritParams iso_get_raw_data #' @param select which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}. +#' @param with_ratios deprecated, please use the \code{select} paramter to explicitly include or exclude ratio columns #' @family data retrieval functions #' @export -iso_get_standards <- function(iso_files, select = everything(), include_file_info = NULL, quiet = default(quiet)) { +iso_get_standards <- function(iso_files, select = everything(), include_file_info = NULL, with_ratios = NULL, quiet = default(quiet)) { iso_files <- iso_as_file_list(iso_files) @@ -552,6 +558,11 @@ iso_get_standards <- function(iso_files, select = everything(), include_file_inf get_info_message_concat(include_file_info_quo, prefix = ", including file info ")) %>% message() } + # deprecated parameter + if (!missing(with_ratios)) { + warning("the 'with_ratios' parameter is deprecated, please use the column selection parameter 'select' to explicitly include or exclude ratio columns", immediate. = TRUE, call. = FALSE) + } + check_read_options(iso_files, "method_info") # check whether there are any diff --git a/R/export.R b/R/export.R index faf49921..661b1c3c 100644 --- a/R/export.R +++ b/R/export.R @@ -34,6 +34,7 @@ iso_export_to_excel <- function( include_vendor_data_table = everything(), include_problems = everything(), with_explicit_units = FALSE, include_method_info = everything(), + with_ratios = NULL, quiet = default(quiet)) { # safety checks @@ -41,15 +42,20 @@ iso_export_to_excel <- function( export_iso_files <- iso_as_file_list(iso_files) filepath <- get_excel_export_filepath(export_iso_files, filepath) + # info message + if (!quiet) { + sprintf("Info: exporting data from %d iso_files into Excel '%s'", length(export_iso_files), + str_replace(filepath, "^\\.(/|\\\\)", "")) %>% message() + } + # include method info message if (!missing(include_method_info)) { warning("the 'include_method_info' parameter was deprecated in favor of the more specific 'include_resistors' and 'include_standards' parameters. Please use those directly instead in the future.", immediate. = TRUE, call. = FALSE) } - # info message - if (!quiet) { - sprintf("Info: exporting data from %d iso_files into Excel '%s'", length(export_iso_files), - str_replace(filepath, "^\\.(/|\\\\)", "")) %>% message() + # deprecated parameter + if (!missing(with_ratios)) { + warning("the 'with_ratios' parameter is deprecated, please use the column selection parameter 'include_standards' to explicitly include or exclude ratio columns", immediate. = TRUE, call. = FALSE) } # get all data From 90c270f05e04ea05c66b52cb4e7123a22bb311d8 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 01:19:20 -0700 Subject: [PATCH 48/51] update exported functions in namespace --- NAMESPACE | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index dc45ab4a..bb41c61a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -86,6 +86,7 @@ export(iso_export_to_rda) export(iso_filter_files) export(iso_filter_files_with_problems) export(iso_find_absolute_path_roots) +export(iso_get_all_data) export(iso_get_bgrd_data) export(iso_get_data) export(iso_get_data_summary) @@ -97,7 +98,9 @@ export(iso_get_raw_data) export(iso_get_reader_example) export(iso_get_reader_examples) export(iso_get_reader_examples_folder) +export(iso_get_resistors) export(iso_get_resistors_info) +export(iso_get_standards) export(iso_get_standards_info) export(iso_get_supported_file_types) export(iso_get_units) From b3e7a526085776c2d24e888f862185c7ae6eee82 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 01:19:30 -0700 Subject: [PATCH 49/51] update vignettes --- vignettes/continuous_flow.Rmd | 81 ++++++++++++++++++----------------- vignettes/dual_inlet.Rmd | 64 +++++++++++++-------------- vignettes/operations.Rmd | 28 ++++++++++++ vignettes/quick_start.Rmd | 9 ++++ vignettes/scan.Rmd | 47 ++++++++++++-------- 5 files changed, 139 insertions(+), 90 deletions(-) diff --git a/vignettes/continuous_flow.Rmd b/vignettes/continuous_flow.Rmd index 31e11b2c..95e6f65f 100644 --- a/vignettes/continuous_flow.Rmd +++ b/vignettes/continuous_flow.Rmd @@ -45,7 +45,7 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() ```{r} # read a few of the continuous flow examples -iso_files <- +cf_files <- iso_read_continuous_flow( iso_get_reader_example("continuous_flow_example.cf"), iso_get_reader_example("continuous_flow_example.iarc"), @@ -56,10 +56,10 @@ iso_files <- # File summary -The `iso_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. +The `cf_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. ```{r} -iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() +cf_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() ``` ## Problems @@ -67,8 +67,8 @@ iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() In case there was any trouble with reading any of the files, the following functions provide an overview summary as well as details of all errors and warnings, respectively. The examples here contain no errors but if you run into any unexpected file read problems, please file a bug report in the [isoreader issue tracker](https://github.com/isoverse/isoreader/issues). ```{r} -iso_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() -iso_files %>% iso_get_problems() %>% rmarkdown::paged_table() +cf_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() +cf_files %>% iso_get_problems() %>% rmarkdown::paged_table() ``` # File Information @@ -77,9 +77,9 @@ Detailed file information can be aggregated for all isofiles using the `iso_get_ ```{r} # all file information -iso_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() +cf_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() # select file information -iso_files %>% +cf_files %>% iso_get_file_info( select = c( # rename sample id columns from the different file types to a new ID column @@ -98,14 +98,14 @@ Rather than retrieving specific file info columns using the above example of `is ```{r} # select + rename specific file info columns -iso_files2 <- iso_files %>% +cf_files2 <- cf_files %>% iso_select_file_info( ID = `Identifier 1`, ID = `Name`, Analysis, `Peak Center`, `H3 Factor`, `Date & Time` = file_datetime ) # fetch all file info -iso_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() +cf_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` ## Filter @@ -114,12 +114,12 @@ Any collection of isofiles can also be filtered based on the available file info ```{r} # find files that have 'linearity' in the new ID field -iso_files2 %>% iso_filter_files(grepl("linearity", ID)) %>% +cf_files2 %>% iso_filter_files(grepl("linearity", ID)) %>% iso_get_file_info() %>% rmarkdown::paged_table() # find files that were run since 2015 -iso_files2 %>% +cf_files2 %>% iso_filter_files(`Date & Time` > "2015-01-01") %>% iso_get_file_info() %>% rmarkdown::paged_table() @@ -130,8 +130,8 @@ iso_files2 %>% The file information in any collection of isofiles can also be mutated using the function `iso_mutate_file_info`. This function can introduce new columns and operate on any existing columns available in the file information (even if it does not exist in all files) and supports full [dplyr](https://dplyr.tidyverse.org/reference/mutate.html) syntax. ```{r} -iso_files3 <- - iso_files2 %>% +cf_files3 <- + cf_files2 %>% iso_mutate_file_info( # update existing column ID = paste("ID:", ID), @@ -139,7 +139,7 @@ iso_files3 <- `Run since 2015?` = `Date & Time` > "2015-01-01" ) -iso_files3 %>% +cf_files3 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` @@ -167,7 +167,7 @@ new_info <- new_info %>% rmarkdown::paged_table() # adding it to the isofiles -iso_files3 %>% +cf_files3 %>% iso_add_file_info(new_info, by1 = "Run since 2015?", by2 = "file_id") %>% iso_get_file_info(select = names(new_info)) %>% rmarkdown::paged_table() @@ -180,7 +180,7 @@ Most file information is initially read as text to avoid cumbersome specificatio ```{r} # use parsing and extraction in iso_mutate_file_info -iso_files2 %>% +cf_files2 %>% iso_mutate_file_info( # change type of Peak Center to logical `Peak Center` = parse_logical(`Peak Center`), @@ -195,13 +195,13 @@ iso_files2 %>% rmarkdown::paged_table() # use parsing in iso_filter_file_info -iso_files2 %>% +cf_files2 %>% iso_filter_files(parse_number(`H3 Factor`) > 2) %>% iso_get_file_info() %>% rmarkdown::paged_table() # use iso_parse_file_info for simplified parsing of column data types -iso_files2 %>% +cf_files2 %>% iso_parse_file_info( integer = Analysis, number = `H3 Factor`, @@ -216,7 +216,7 @@ iso_files2 %>% Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette): ```{r} -iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table() +cf_files %>% iso_get_resistors() %>% rmarkdown::paged_table() ``` # Reference values @@ -225,9 +225,9 @@ As well as isotopic reference values for the different gases: ```{r} # reference delta values without ratio values -iso_files %>% iso_get_standards_info() %>% rmarkdown::paged_table() +cf_files %>% iso_get_standards(file_id:reference) %>% rmarkdown::paged_table() # reference values with ratios -iso_files %>% iso_get_standards_info(with_ratios = TRUE) %>% rmarkdown::paged_table() +cf_files %>% iso_get_standards() %>% rmarkdown::paged_table() ``` @@ -237,9 +237,9 @@ The raw data read from the IRMS files can be retrieved similarly using the `iso_ ```{r} # get raw data with default selections (all raw data, no additional file info) -iso_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() +cf_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() # get specific raw data and add some file information -iso_files %>% +cf_files %>% iso_get_raw_data( # select just time and the m/z 2 and 3 ions select = c(time.s, v2.mV, v3.mV), @@ -262,33 +262,36 @@ As with most data retrieval funtions, the `iso_get_vendor_data_table()` function ```{r} # entire vendor data table -iso_files %>% iso_get_vendor_data_table() %>% rmarkdown::paged_table() +cf_files %>% iso_get_vendor_data_table() %>% rmarkdown::paged_table() # get specific parts and add some file information -iso_files %>% +cf_files %>% iso_get_vendor_data_table( # select peak number, ret. time, overall intensity and all H delta columns select = c(Nr., Rt, area = `rIntensity All`, matches("^d \\d+H")), # include the Analysis number fron the file info and rename it to 'run' include_file_info = c(run = Analysis) ) %>% rmarkdown::paged_table() + # the data table also provides units if included in the original data file -# caution however: it may require changes to the `select` parameter -iso_files %>% +# which can be made explicit using the function iso_make_units_explicit() +cf_files %>% iso_get_vendor_data_table( - with_explicit_units = TRUE, # select peak number, ret. time, overall intensity and all H delta columns - select = c(Nr., matches("^Rt"), matches("rIntensity All"), matches("^d \\d+H")), + select = c(Nr., Rt, area = `rIntensity All`, matches("^d \\d+H")), # include the Analysis number fron the file info and rename it to 'run' include_file_info = c(run = Analysis) - ) %>% rmarkdown::paged_table() + ) %>% + # make column units explicit + iso_make_units_explicit() %>% + rmarkdown::paged_table() ``` -## For expert users: retrieving all data +# For expert users: retrieving all data -For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame. Use the `include_file_info`, `include_raw_data`, and `include_vendor_data_table` parameters to specify which columns to include. By default, everything is included: +For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame: ```{r} -all_data <- iso_files %>% iso_get_data() +all_data <- cf_files %>% iso_get_all_data() # not printed out because this data frame is very big ``` @@ -298,11 +301,11 @@ Saving entire collections of isofiles for retrieval at a later point is easily d ```{r} # export to R data archive -iso_files %>% iso_save("iso_files_export.cf.rds") +cf_files %>% iso_save("cf_files_export.cf.rds") # read back the exported R data archive -iso_files <- iso_read_continuous_flow("iso_files_export.cf.rds") -iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() +cf_files <- iso_read_continuous_flow("cf_files_export.cf.rds") +cf_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() ``` @@ -312,15 +315,15 @@ At the moment, isoreader supports export of all data to Excel and the [Feather f ```{r} # export to excel -iso_files %>% iso_export_to_excel("iso_files_export") +cf_files %>% iso_export_to_excel("cf_files_export") # data sheets available in the exported data file: -readxl::excel_sheets("iso_files_export.cf.xlsx") +readxl::excel_sheets("cf_files_export.cf.xlsx") ``` ```{r} # export to feather -iso_files %>% iso_export_to_feather("iso_files_export") +cf_files %>% iso_export_to_feather("cf_files_export") # exported feather files list.files(pattern = ".cf.feather") diff --git a/vignettes/dual_inlet.Rmd b/vignettes/dual_inlet.Rmd index eceb51c8..42cec2c4 100644 --- a/vignettes/dual_inlet.Rmd +++ b/vignettes/dual_inlet.Rmd @@ -48,7 +48,7 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() ```{r} # read dual inlet examples -iso_files <- +di_files <- iso_read_dual_inlet( iso_get_reader_example("dual_inlet_example.did"), iso_get_reader_example("dual_inlet_example2.did"), @@ -60,10 +60,10 @@ iso_files <- # File summary -The `iso_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. +The `di_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. ```{r} -iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() +di_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() ``` ## Problems @@ -71,8 +71,8 @@ iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() In case there was any trouble with reading any of the files, the following functions provide an overview summary as well as details of all errors and warnings, respectively. The examples here contain no errors but if you run into any unexpected file read problems, please file a bug report in the [isoreader issue tracker](https://github.com/isoverse/isoreader/issues). ```{r} -iso_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() -iso_files %>% iso_get_problems() %>% rmarkdown::paged_table() +di_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() +di_files %>% iso_get_problems() %>% rmarkdown::paged_table() ``` # File Information @@ -81,9 +81,9 @@ Detailed file information can be aggregated for all isofiles using the `iso_get_ ```{r} # all file information -iso_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() +di_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() # select file information -iso_files %>% +di_files %>% iso_get_file_info( select = c( # rename sample id columns from the different file types to a new ID column @@ -104,7 +104,7 @@ Rather than retrieving specific file info columns using the above example of `is ```{r} # select + rename specific file info columns -iso_files2 <- iso_files %>% +di_files2 <- di_files %>% iso_select_file_info( ID = `Identifier 1`, ID = `Sample Name`, Analysis, Method, `Peak Center`, `Date & Time` = file_datetime, @@ -112,7 +112,7 @@ iso_files2 <- iso_files %>% ) # fetch all file info -iso_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() +di_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` ## Filter @@ -121,12 +121,12 @@ Any collection of isofiles can also be filtered based on the available file info ```{r} # find files that have 'CIT' in the new ID field -iso_files2 %>% iso_filter_files(grepl("CIT", ID)) %>% +di_files2 %>% iso_filter_files(grepl("CIT", ID)) %>% iso_get_file_info() %>% rmarkdown::paged_table() # find files that were run in 2017 -iso_files2 %>% +di_files2 %>% iso_filter_files(`Date & Time` > "2017-01-01" & `Date & Time` < "2018-01-01") %>% iso_get_file_info() %>% rmarkdown::paged_table() @@ -137,7 +137,7 @@ iso_files2 %>% The file information in any collection of isofiles can also be mutated using the function `iso_mutate_file_info`. This function can introduce new columns and operate on any existing columns available in the file information (even if it does not exist in all files) and supports full [dplyr](https://dplyr.tidyverse.org/reference/mutate.html) syntax. ```{r} -iso_files3 <- iso_files2 %>% +di_files3 <- di_files2 %>% iso_mutate_file_info( # update existing column ID = paste("ID:", ID), @@ -145,7 +145,7 @@ iso_files3 <- iso_files2 %>% `Run in 2017?` = `Date & Time` > "2017-01-01" & `Date & Time` < "2018-01-01" ) -iso_files3 %>% +di_files3 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` @@ -173,7 +173,7 @@ new_info <- new_info %>% rmarkdown::paged_table() # adding it to the isofiles -iso_files3 %>% +di_files3 %>% iso_add_file_info(new_info, by1 = "Run in 2017?", by2 = "Analysis") %>% iso_get_file_info(select = names(new_info)) %>% rmarkdown::paged_table() @@ -186,7 +186,7 @@ Most file information is initially read as text to avoid cumbersome specificatio ```{r} # use parsing and extraction in iso_mutate_file_info -iso_files2 %>% +di_files2 %>% iso_mutate_file_info( # change type of Peak Center to logical `Peak Center` = parse_logical(`Peak Center`), @@ -201,13 +201,13 @@ iso_files2 %>% rmarkdown::paged_table() # use parsing in iso_filter_file_info -iso_files2 %>% +di_files2 %>% iso_filter_files(parse_integer(Analysis) > 1500) %>% iso_get_file_info() %>% rmarkdown::paged_table() # use iso_parse_file_info for simplified parsing of column data types -iso_files2 %>% +di_files2 %>% iso_parse_file_info( integer = Analysis, number = `Sample Weight`, @@ -222,7 +222,7 @@ iso_files2 %>% Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette): ```{r} -iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table() +di_files %>% iso_get_resistors() %>% rmarkdown::paged_table() ``` # Reference values @@ -231,9 +231,9 @@ As well as isotopic reference values for the different gases: ```{r} # reference delta values without ratio values -iso_files %>% iso_get_standards_info() %>% rmarkdown::paged_table() +di_files %>% iso_get_standards(file_id:reference) %>% rmarkdown::paged_table() # reference values with ratios -iso_files %>% iso_get_standards_info(with_ratios = TRUE) %>% rmarkdown::paged_table() +di_files %>% iso_get_standards() %>% rmarkdown::paged_table() ``` # Raw Data @@ -242,9 +242,9 @@ The raw data read from the IRMS files can be retrieved similarly using the `iso_ ```{r} # get raw data with default selections (all raw data, no additional file info) -iso_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() +di_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() # get specific raw data and add some file information -iso_files %>% +di_files %>% iso_get_raw_data( # select just time and the two ions select = c(type, cycle, v28.mV, v29.mV), @@ -268,9 +268,9 @@ As with most data retrieval funtions, the `iso_get_vendor_data_table()` function ```{r} # entire vendor data table -iso_files %>% iso_get_vendor_data_table() %>% rmarkdown::paged_table() +di_files %>% iso_get_vendor_data_table() %>% rmarkdown::paged_table() # get specific parts and add some file information -iso_files %>% +di_files %>% iso_get_vendor_data_table( # select cycle and all carbon columns select = c(cycle, matches("C")), @@ -279,12 +279,12 @@ iso_files %>% ) %>% rmarkdown::paged_table() ``` -## For expert users: retrieving all data +# For expert users: retrieving all data -For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame. Use the `include_file_info`, `include_raw_data`, and `include_vendor_data_table` parameters to specify which columns to include. By default, everything is included: +For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame: ```{r} -all_data <- iso_files %>% iso_get_data() +all_data <- di_files %>% iso_get_all_data() # not printed out because this data frame is very big ``` @@ -294,10 +294,10 @@ Saving entire collections of isofiles for retrieval at a later point is easily d ```{r} # export to R data archive -iso_files %>% iso_save("iso_files_export.di.rds") +di_files %>% iso_save("di_files_export.di.rds") # read back the exported R data storage -iso_read_dual_inlet("iso_files_export.di.rds") +iso_read_dual_inlet("di_files_export.di.rds") ``` @@ -307,15 +307,15 @@ At the moment, isoreader supports export of all data to Excel and the [Feather f ```{r} # export to excel -iso_files %>% iso_export_to_excel("iso_files_export") +di_files %>% iso_export_to_excel("di_files_export") # data sheets available in the exported data file: -readxl::excel_sheets("iso_files_export.di.xlsx") +readxl::excel_sheets("di_files_export.di.xlsx") ``` ```{r} # export to feather -iso_files %>% iso_export_to_feather("iso_files_export") +di_files %>% iso_export_to_feather("di_files_export") # exported feather files list.files(pattern = ".di.feather") diff --git a/vignettes/operations.Rmd b/vignettes/operations.Rmd index b7419029..4b272b33 100644 --- a/vignettes/operations.Rmd +++ b/vignettes/operations.Rmd @@ -186,6 +186,34 @@ di_files %>% iso_mutate_file_info(file_root = ".") %>% iso_reread_files() ``` +# Units +Isoreader provides a built in data type with units (`iso_double_with_units`) that can be used to easily keep track of units inside data frame. These units can be made explicit (=included in the column header), stripped altogher, or turned back to be implicit. + +```{r} +# strip all units +cf_file %>% + iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`)) %>% + iso_strip_units() %>% head(3) + +# make units explicit +cf_file %>% + iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`)) %>% + iso_make_units_explicit() %>% head(3) + +# introduce new unit columns e.g. in the file info +cf_file %>% + iso_mutate_file_info(weight = iso_double_with_units(0.42, "mg")) %>% + iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`), + include_file_info = weight) %>% + iso_make_units_explicit() %>% head(3) + +# or turn a column e.g. with custom format units in the header into implicit units +cf_file %>% + iso_mutate_file_info(weight.mg = 0.42) %>% + iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`), + include_file_info = weight.mg) %>% + iso_make_units_implicit(prefix = ".", suffix = "") %>% head(3) +``` diff --git a/vignettes/quick_start.Rmd b/vignettes/quick_start.Rmd index c25dea78..c37f8eaf 100644 --- a/vignettes/quick_start.Rmd +++ b/vignettes/quick_start.Rmd @@ -50,6 +50,9 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() # read all available examples di_files <- iso_read_dual_inlet(iso_get_reader_examples_folder()) +# save as r data storage (read back in with iso_read_dual_inlet) +iso_save(di_files, filepath = "di_save") + # export to excel iso_export_to_excel(di_files, filepath = "di_export") ``` @@ -60,6 +63,9 @@ iso_export_to_excel(di_files, filepath = "di_export") # read all available examples cf_files <- iso_read_continuous_flow(iso_get_reader_examples_folder()) +# save as r data storage (read back in with iso_read_continuous_flow) +iso_save(cf_files, filepath = "cf_save") + # export to excel iso_export_to_excel(cf_files, filepath = "cf_export") ``` @@ -70,6 +76,9 @@ iso_export_to_excel(cf_files, filepath = "cf_export") # read all available examples scan_files <- iso_read_scan(iso_get_reader_examples_folder()) +# save as r data storage (read back in with iso_read_scan) +iso_save(scan_files, filepath = "scan_save") + # export to excel iso_export_to_excel(scan_files, filepath = "scan_export") ``` diff --git a/vignettes/scan.Rmd b/vignettes/scan.Rmd index 4d4ac667..e62f7beb 100644 --- a/vignettes/scan.Rmd +++ b/vignettes/scan.Rmd @@ -50,7 +50,7 @@ iso_get_reader_examples() %>% rmarkdown::paged_table() ```{r} # read scan examples -iso_files <- +scan_files <- iso_read_scan( iso_get_reader_example("peak_shape_scan_example.scn"), iso_get_reader_example("background_scan_example.scn"), @@ -61,10 +61,10 @@ iso_files <- # File summary -The `iso_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. +The `scan_files` variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the `iso_get_data_summary()` function. ```{r} -iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() +scan_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() ``` ## Problems @@ -72,8 +72,8 @@ iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() In case there was any trouble with reading any of the files, the following functions provide an overview summary as well as details of all errors and warnings, respectively. The examples here contain no errors but if you run into any unexpected file read problems, please file a bug report in the [isoreader issue tracker](https://github.com/isoverse/isoreader/issues). ```{r} -iso_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() -iso_files %>% iso_get_problems() %>% rmarkdown::paged_table() +scan_files %>% iso_get_problems_summary() %>% rmarkdown::paged_table() +scan_files %>% iso_get_problems() %>% rmarkdown::paged_table() ``` # File Information @@ -82,7 +82,7 @@ Detailed file information can be aggregated for all isofiles using the `iso_get_ ```{r} # all file information -iso_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() +scan_files %>% iso_get_file_info(select = c(-file_root)) %>% rmarkdown::paged_table() ``` ## Select/Rename @@ -91,12 +91,12 @@ File information can also be modified across an entire collection of isofiles us ```{r} # select + rename specific file info columns -iso_files2 <- iso_files %>% +scan_files2 <- scan_files %>% iso_select_file_info(-file_root) %>% iso_rename_file_info(`Date & Time` = file_datetime) # fetch all file info -iso_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() +scan_files2 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` @@ -106,7 +106,7 @@ Any collection of isofiles can also be filtered based on the available file info ```{r} # find files that have 'CIT' in the new ID field -iso_files2 %>% +scan_files2 %>% iso_filter_files(type == "High Voltage") %>% iso_get_file_info() %>% rmarkdown::paged_table() @@ -117,13 +117,13 @@ iso_files2 %>% The file information in any collection of isofiles can also be mutated using the function `iso_mutate_file_info`. This function can introduce new columns and operate on any existing columns available in the file information (even if it does not exist in all files) and supports full [dplyr](https://dplyr.tidyverse.org/reference/mutate.html) syntax. ```{r} -iso_files3 <- iso_files2 %>% +scan_files3 <- scan_files2 %>% iso_mutate_file_info( # introduce new column `Run in 2019?` = `Date & Time` > "2019-01-01" & `Date & Time` < "2020-01-01" ) -iso_files3 %>% +scan_files3 %>% iso_get_file_info() %>% rmarkdown::paged_table() ``` @@ -133,7 +133,7 @@ iso_files3 %>% Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette): ```{r} -iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table() +scan_files %>% iso_get_resistors() %>% rmarkdown::paged_table() ``` # Raw Data @@ -142,9 +142,9 @@ The raw data read from the scan files can be retrieved similarly using the `iso_ ```{r} # get raw data with default selections (all raw data, no additional file info) -iso_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() +scan_files %>% iso_get_raw_data() %>% head(n=10) %>% rmarkdown::paged_table() # get specific raw data and add some file information -iso_files %>% +scan_files %>% iso_get_raw_data( # select just time and the two ions select = c(x, units, v44.mV, v45.mV), @@ -155,6 +155,15 @@ iso_files %>% head(n=10) %>% rmarkdown::paged_table() ``` +# For expert users: retrieving all data + +For users familiar with the nested data frames from the [tidyverse](https://www.tidyverse.org/) (particularly [tidyr](https://tidyr.tidyverse.org/)'s `nest` and `unnest`), there is an easy way to retrieve all data from the iso file objects in a single nested data frame: + +```{r} +all_data <- scan_files %>% iso_get_all_data() +# not printed out because this data frame is very big +``` + # Saving collections @@ -162,10 +171,10 @@ Saving entire collections of isofiles for retrieval at a later point is easily d ```{r} # export to R data archive -iso_files %>% iso_save("iso_files_export.scan.rds") +scan_files %>% iso_save("scan_files_export.scan.rds") # read back the exported R data storage -iso_read_scan("iso_files_export.scan.rds") +iso_read_scan("scan_files_export.scan.rds") ``` # Data Export @@ -174,15 +183,15 @@ At the moment, isoreader supports export of all data to Excel and the [Feather f ```{r} # export to excel -iso_files %>% iso_export_to_excel("iso_files_export") +scan_files %>% iso_export_to_excel("scan_files_export") # data sheets available in the exported data file: -readxl::excel_sheets("iso_files_export.scan.xlsx") +readxl::excel_sheets("scan_files_export.scan.xlsx") ``` ```{r} # export to feather -iso_files %>% iso_export_to_feather("iso_files_export") +scan_files %>% iso_export_to_feather("scan_files_export") # exported feather files list.files(pattern = ".scan.feather") From cc2c3d045d09493cf18759f1e77cbcba679199ed Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 01:22:50 -0700 Subject: [PATCH 50/51] update documentation --- _pkgdown.yml | 6 +- docs/404.html | 2 +- docs/LICENSE-text.html | 2 +- docs/articles/continuous_flow.html | 161 ++++++----- docs/articles/development.html | 10 +- docs/articles/dual_inlet.html | 136 +++++---- docs/articles/index.html | 2 +- docs/articles/operations.html | 85 +++++- docs/articles/quick_start.html | 43 ++- docs/articles/scan.html | 129 +++++---- docs/authors.html | 2 +- docs/index.html | 2 +- docs/reference/extract_data.html | 2 +- docs/reference/extract_substring.html | 2 +- docs/reference/extract_word.html | 2 +- docs/reference/file_readers.html | 2 +- docs/reference/index.html | 8 +- docs/reference/iso_add_file_info.html | 2 +- docs/reference/iso_caching.html | 2 +- docs/reference/iso_calculate_ratios.html | 2 +- docs/reference/iso_cleanup_reader_cache.html | 2 +- docs/reference/iso_convert_signals.html | 2 +- docs/reference/iso_convert_time.html | 2 +- docs/reference/iso_data_structure.html | 2 +- docs/reference/iso_debug_mode.html | 2 +- docs/reference/iso_double_with_units.html | 2 +- docs/reference/iso_expand_paths.html | 2 +- docs/reference/iso_export_to_excel.html | 46 +-- docs/reference/iso_export_to_feather.html | 39 +-- docs/reference/iso_export_to_rda.html | 2 +- docs/reference/iso_filter_files.html | 2 +- .../iso_filter_files_with_problems.html | 2 +- .../iso_find_absolute_path_roots.html | 2 +- docs/reference/iso_get_all_data.html | 266 ++++++++++++++++++ docs/reference/iso_get_bgrd_data.html | 10 +- docs/reference/iso_get_data.html | 83 +----- docs/reference/iso_get_data_summary.html | 2 +- .../iso_get_default_reader_parameters.html | 2 +- docs/reference/iso_get_file_info.html | 8 +- docs/reference/iso_get_problems.html | 12 +- docs/reference/iso_get_problems_summary.html | 2 +- docs/reference/iso_get_raw_data.html | 16 +- docs/reference/iso_get_reader_example.html | 4 +- docs/reference/iso_get_resistors.html | 225 +++++++++++++++ docs/reference/iso_get_resistors_info.html | 49 +--- docs/reference/iso_get_standards.html | 230 +++++++++++++++ docs/reference/iso_get_standards_info.html | 54 +--- .../iso_get_supported_file_types.html | 2 +- docs/reference/iso_get_units.html | 2 +- docs/reference/iso_get_vendor_data_table.html | 12 +- docs/reference/iso_has_problems.html | 2 +- docs/reference/iso_info_messages.html | 2 +- docs/reference/iso_is_double_with_units.html | 2 +- docs/reference/iso_make_units_explicit.html | 2 +- docs/reference/iso_make_units_implicit.html | 2 +- docs/reference/iso_mutate_file_info.html | 2 +- .../iso_omit_files_with_problems.html | 2 +- docs/reference/iso_parse_file_info.html | 2 +- .../iso_plot_continuous_flow_data.html | 2 +- docs/reference/iso_plot_dual_inlet_data.html | 2 +- docs/reference/iso_plot_raw_data.html | 2 +- docs/reference/iso_printing.html | 2 +- docs/reference/iso_problem_functions.html | 2 +- docs/reference/iso_read_continuous_flow.html | 2 +- docs/reference/iso_read_dual_inlet.html | 2 +- docs/reference/iso_read_files.html | 2 +- docs/reference/iso_read_scan.html | 2 +- docs/reference/iso_rename_file_info.html | 2 +- docs/reference/iso_reread_files.html | 2 +- docs/reference/iso_root_paths.html | 2 +- docs/reference/iso_save.html | 2 +- docs/reference/iso_select_file_info.html | 2 +- .../iso_set_default_read_parameters.html | 2 +- .../reference/iso_shorten_relative_paths.html | 2 +- .../iso_show_default_reader_parameters.html | 2 +- docs/reference/iso_strip_units.html | 2 +- docs/reference/isoread.html | 2 +- docs/reference/isoreader-package.html | 2 +- .../reference/print.binary_structure_map.html | 2 +- docs/reference/read_iso_file.html | 2 +- docs/reference/reexports.html | 2 +- docs/reference/set_temp.html | 2 +- .../vec_arith.iso_double_with_units.html | 2 +- .../vec_cast.iso_double_with_units.html | 2 +- .../vec_ptype2.iso_double_with_units.html | 2 +- docs/sitemap.xml | 9 + man/iso_export_to_excel.Rd | 32 ++- man/iso_export_to_feather.Rd | 27 +- man/iso_get_all_data.Rd | 61 ++++ man/iso_get_bgrd_data.Rd | 8 +- man/iso_get_data.Rd | 48 +--- man/iso_get_file_info.Rd | 6 +- man/iso_get_problems.Rd | 6 +- man/iso_get_raw_data.Rd | 14 +- man/iso_get_resistors.Rd | 35 +++ man/iso_get_resistors_info.Rd | 27 +- man/iso_get_standards.Rd | 38 +++ man/iso_get_standards_info.Rd | 30 +- man/iso_get_vendor_data_table.Rd | 10 +- 99 files changed, 1451 insertions(+), 658 deletions(-) create mode 100644 docs/reference/iso_get_all_data.html create mode 100644 docs/reference/iso_get_resistors.html create mode 100644 docs/reference/iso_get_standards.html create mode 100644 man/iso_get_all_data.Rd create mode 100644 man/iso_get_resistors.Rd create mode 100644 man/iso_get_standards.Rd diff --git a/_pkgdown.yml b/_pkgdown.yml index 4e26b156..f785953a 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -80,13 +80,13 @@ reference: These functions aggregate the data collected from one or many IRMS data files. contents: - iso_get_data_summary - - iso_get_data + - iso_get_all_data - iso_get_file_info - iso_get_raw_data - iso_get_bgrd_data - iso_get_vendor_data_table - - iso_get_resistors_info - - iso_get_standards_info + - iso_get_resistors + - iso_get_standards - title: "Exporting/saving data: Feather, Excel, and R Data Archives" desc: > diff --git a/docs/404.html b/docs/404.html index 50b0e39b..48aa5ea8 100644 --- a/docs/404.html +++ b/docs/404.html @@ -77,7 +77,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/LICENSE-text.html b/docs/LICENSE-text.html index 7b242b43..1aa50c64 100644 --- a/docs/LICENSE-text.html +++ b/docs/LICENSE-text.html @@ -77,7 +77,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/articles/continuous_flow.html b/docs/articles/continuous_flow.html index a16034e8..e4de4fa7 100644 --- a/docs/articles/continuous_flow.html +++ b/docs/articles/continuous_flow.html @@ -37,7 +37,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -106,7 +106,7 @@ +#> Info: finished reading 4 files in 8.37 secs

    File summary

    -

    The iso_files variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the iso_get_data_summary() function.

    -
    iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table() 
    +

    The cf_files variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the iso_get_data_summary() function.

    +
    - +
    @@ -192,7 +192,7 @@

    File Information

    Detailed file information can be aggregated for all isofiles using the iso_get_file_info() function which supports the full select syntax of the dplyr package to specify which columns are of interest (by default, all file information is retrieved). Additionally, file information from different file formats can be renamed to the same column name for easy of downstream processing. The following provides a few examples for how this can be used (the names of the interesting info columns may vary between different file formats):

    +#> Info: aggregating file info from 7 data file(s), selecting info columns 'c(...)' +#> Warning: 'select = c(...)' refers to unknown column(s) in data frame 'file_info': +#> - Names must be unique.
    @@ -222,7 +224,7 @@

    Select/Rename

    Rather than retrieving specific file info columns using the above example of iso_get_file_info(select = ...), these information can also be modified across an entire collection of isofiles using the iso_select_file_info() and iso_rename_file_info() functions. For example, the above example could be similarly achieved with the following use of iso_select_file_info():

    @@ -339,7 +343,7 @@

    Parse

    Most file information is initially read as text to avoid cumbersome specifications during the read process and compatibility issues between different IRMS file formats. However, many file info columns are not easily processed as text. The isoreader package therefore provides several parsing and data extraction functions to facilitate processing the text-based data (some via functionality implemented by the readr package). See code block below for examples. For a complete overview, see the ?extract_data and ?iso_parse_file_info documentation.

    
     # use iso_parse_file_info for simplified parsing of column data types
    -iso_files2 %>% 
    +cf_files2 %>% 
       iso_parse_file_info(
         integer = Analysis, 
         number = `H3 Factor`,
    @@ -401,7 +405,7 @@ 

    Resistors

    Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette):

    -
    iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table()
    +
     
    # get specific raw data and add some file information
    -iso_files %>% 
    +cf_files %>% 
       iso_get_raw_data(
         # select just time and the m/z 2 and 3 ions
         select = c(time.s, v2.mV, v3.mV),
    @@ -469,7 +473,7 @@ 

    Vendor Data Table

    As with most data retrieval funtions, the iso_get_vendor_data_table() function also allows specific column selection (by default, all columns are selected) and easy addition of file information via the include_file_info parameter (by default, none is included).

    # get specific parts and add some file information
    -iso_files %>% 
    +cf_files %>% 
       iso_get_vendor_data_table(
         # select peak number, ret. time, overall intensity and all H delta columns
         select = c(Nr., Rt, area = `rIntensity All`, matches("^d \\d+H")),
    @@ -490,47 +494,50 @@ 

    {"columns":[{"label":["file_id"],"name":[1],"type":["chr"],"align":["left"]},{"label":["run"],"name":[2],"type":["chr"],"align":["left"]},{"label":["Nr."],"name":[3],"type":["int"],"align":["right"]},{"label":["Rt"],"name":[4],"type":["S3: iso_double_with_units"],"align":["right"]},{"label":["area"],"name":[5],"type":["S3: iso_double_with_units"],"align":["right"]},{"label":["d 3H2/2H2"],"name":[6],"type":["S3: iso_double_with_units"],"align":["right"]},{"label":["d 2H/1H"],"name":[7],"type":["S3: iso_double_with_units"],"align":["right"]}],"data":[{"1":"continuous_flow_example.cf","2":"6520","3":"1","4":"286.330","5":"23408.20","6":"-160.8789","7":"-160.8789"},{"1":"continuous_flow_example.cf","2":"6520","3":"2","4":"321.233","5":"23416.43","6":"-160.3969","7":"-160.3969"},{"1":"continuous_flow_example.cf","2":"6520","3":"3","4":"611.952","5":"30831.79","6":"-154.2084","7":"-154.2084"},{"1":"continuous_flow_example.cf","2":"6520","3":"4","4":"671.517","5":"30820.41","6":"-151.9000","7":"-151.9000"},{"1":"continuous_flow_example.cf","2":"6520","3":"5","4":"747.802","5":"22771.98","6":"-218.1169","7":"-218.1169"},{"1":"continuous_flow_example.cf","2":"6520","3":"6","4":"809.457","5":"26112.21","6":"-210.4288","7":"-210.4288"},{"1":"continuous_flow_example.cf","2":"6520","3":"7","4":"860.662","5":"26587.23","6":"-151.9000","7":"-151.9000"},{"1":"continuous_flow_example.cf","2":"6520","3":"8","4":"936.529","5":"26290.90","6":"-155.5230","7":"-155.5230"},{"1":"continuous_flow_example.cf","2":"6520","3":"9","4":"1002.155","5":"23773.71","6":"-198.1282","7":"-198.1282"},{"1":"continuous_flow_example.cf","2":"6520","3":"10","4":"1055.032","5":"25686.76","6":"-151.9000","7":"-151.9000"},{"1":"continuous_flow_example.cf","2":"6520","3":"11","4":"1135.915","5":"25330.88","6":"-189.4838","7":"-189.4838"},{"1":"continuous_flow_example.cf","2":"6520","3":"12","4":"1201.332","5":"25168.19","6":"-207.8967","7":"-207.8967"},{"1":"continuous_flow_example.cf","2":"6520","3":"13","4":"1249.402","5":"26176.27","6":"-151.9000","7":"-151.9000"},{"1":"continuous_flow_example.cf","2":"6520","3":"14","4":"1333.629","5":"25778.35","6":"-168.1422","7":"-168.1422"},{"1":"continuous_flow_example.cf","2":"6520","3":"15","4":"1395.284","5":"20869.48","6":"-193.2927","7":"-193.2927"},{"1":"continuous_flow_example.cf","2":"6520","3":"16","4":"1459.447","5":"28121.88","6":"-151.9000","7":"-151.9000"},{"1":"continuous_flow_example.cf","2":"6520","3":"17","4":"1608.673","5":"28883.22","6":"-154.3318","7":"-154.3318"},{"1":"continuous_flow_example.cf","2":"6520","3":"18","4":"1739.716","5":"23336.75","6":"-160.1244","7":"-160.1244"},{"1":"continuous_flow_example.cf","2":"6520","3":"19","4":"1779.426","5":"23358.98","6":"-160.5503","7":"-160.5503"},{"1":"continuous_flow_example.dxf","2":"2921","3":"1","4":"59.983","5":"110929.75","6":"NA","7":"NA"},{"1":"continuous_flow_example.dxf","2":"2921","3":"2","4":"110.770","5":"110486.88","6":"NA","7":"NA"},{"1":"continuous_flow_example.dxf","2":"2921","3":"3","4":"155.287","5":"98168.61","6":"NA","7":"NA"},{"1":"continuous_flow_example.dxf","2":"2921","3":"4","4":"300.124","5":"240551.17","6":"NA","7":"NA"},{"1":"continuous_flow_example.dxf","2":"2921","3":"5","4":"436.183","5":"328751.58","6":"NA","7":"NA"},{"1":"continuous_flow_example.dxf","2":"2921","3":"6","4":"486.761","5":"330289.66","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"1","4":"47.234","5":"32970.31","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"2","4":"107.426","5":"65905.71","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"3","4":"167.618","5":"181512.88","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"4","4":"227.601","5":"257111.14","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"5","4":"287.793","5":"336995.32","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"6","4":"347.985","5":"421670.46","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"7","4":"408.177","5":"561563.77","6":"NA","7":"NA"},{"1":"linearity_example.dxf","2":"MAT25391411","3":"8","4":"468.996","5":"864632.08","6":"NA","7":"NA"}],"options":{"columns":{"min":{},"max":[10]},"rows":{"min":[10],"max":[10]},"pages":{}}}

    -
    -
    -

    -For expert users: retrieving all data

    -

    For users familiar with the nested data frames from the tidyverse (particularly tidyr’s nest and unnest), there is an easy way to retrieve all data from the iso file objects in a single nested data frame. Use the include_file_info, include_raw_data, and include_vendor_data_table parameters to specify which columns to include. By default, everything is included:

    - +
    +

    +For expert users: retrieving all data

    +

    For users familiar with the nested data frames from the tidyverse (particularly tidyr’s nest and unnest), there is an easy way to retrieve all data from the iso file objects in a single nested data frame:

    +
    -

    Saving collections

    Saving entire collections of isofiles for retrieval at a later point is easily done using the iso_save function which stores collections or individual isoreader file objects in the efficient R data storage format .rds (if not specified, the extension .cf.rds will be automatically appended). These saved collections can be convientiently read back using the same iso_read_continuous_flow command used for raw data files.

    +#> Info: finished reading 4 files in 9.85 secs

    File summary

    -

    The iso_files variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the iso_get_data_summary() function.

    -
    iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table()
    +

    The di_files variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the iso_get_data_summary() function.

    +
    - +
    @@ -183,7 +183,7 @@

    File Information

    Detailed file information can be aggregated for all isofiles using the iso_get_file_info() function which supports the full select syntax of the dplyr package to specify which columns are of interest (by default, all file information is retrieved). Additionally, file information from different file formats can be renamed to the same column name for easy of downstream processing. The following provides a few examples for how this can be used (the names of the interesting info columns may vary between different file formats):

    +#> Info: aggregating file info from 4 data file(s), selecting info columns 'c(...)' +#> Warning: 'select = c(...)' refers to unknown column(s) in data frame 'file_info': +#> - Names must be unique.
    @@ -215,7 +217,7 @@

    Select/Rename

    Rather than retrieving specific file info columns using the above example of iso_get_file_info(select = ...), these information can also be modified across an entire collection of isofiles using the iso_select_file_info() and iso_rename_file_info() functions. For example, the above example could be similarly achieved with the following use of iso_select_file_info():

    @@ -333,7 +337,7 @@

    Parse

    Most file information is initially read as text to avoid cumbersome specifications during the read process and compatibility issues between different IRMS file formats. However, many file info columns are not easily processed as text. The isoreader package therefore provides several parsing and data extraction functions to facilitate processing the text-based data (some via functionality implemented by the readr package). See code block below for examples. For a complete overview, see the ?extract_data and ?iso_parse_file_info documentation.

    
     # use iso_parse_file_info for simplified parsing of column data types
    -iso_files2 %>% 
    +di_files2 %>% 
       iso_parse_file_info(
         integer = Analysis, 
         number = `Sample Weight`,
    @@ -392,7 +396,7 @@ 

    Resistors

    Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette):

    -
    iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table()
    +
     
    # get specific raw data and add some file information
    -iso_files %>% 
    +di_files %>% 
       iso_get_raw_data(
         # select just time and the two ions
         select = c(type, cycle, v28.mV, v29.mV),
    @@ -460,7 +464,7 @@ 

    Vendor Data Table

    As with most data retrieval funtions, the iso_get_vendor_data_table() function also allows specific column selection (by default, all columns are selected) and easy addition of file information via the include_file_info parameter (by default, none is included).

    -
    -

    -For expert users: retrieving all data

    -

    For users familiar with the nested data frames from the tidyverse (particularly tidyr’s nest and unnest), there is an easy way to retrieve all data from the iso file objects in a single nested data frame. Use the include_file_info, include_raw_data, and include_vendor_data_table parameters to specify which columns to include. By default, everything is included:

    - +
    +

    +For expert users: retrieving all data

    +

    For users familiar with the nested data frames from the tidyverse (particularly tidyr’s nest and unnest), there is an easy way to retrieve all data from the iso file objects in a single nested data frame:

    +
    -

    Saving collections

    Saving entire collections of isofiles for retrieval at a later point is easily done using the iso_save function which stores collections or individual isoreader file objects in the efficient R data storage format .rds (if not specified, the extension .di.rds will be automatically appended). These saved collections can be convientiently read back using the same iso_read_dual_inlet command used for raw data files.

    # export to R data archive
    -iso_files %>% iso_save("iso_files_export.di.rds")
    -#> Info: exporting data from 4 iso_files into R Data Storage 'iso_files_export.di.rds'
    +di_files %>% iso_save("di_files_export.di.rds")
    +#> Info: exporting data from 4 iso_files into R Data Storage 'di_files_export.di.rds'
     
     # read back the exported R data storage
    -iso_read_dual_inlet("iso_files_export.di.rds")
    +iso_read_dual_inlet("di_files_export.di.rds")
     #> Info: preparing to read 1 data files (all will be cached)...
    -#> Info: reading file 'iso_files_export.di.rds' with '.di.rds' reader
    +#> Info: reading file 'di_files_export.di.rds' with '.di.rds' reader
     #> Info: loaded data for 4 data files from R Data Storage - checking loaded fi...
    -#> Info: finished reading 1 files in 0.13 secs
    +#> Info: finished reading 1 files in 0.25 secs
     #> Data from 4 dual inlet iso files: 
     #> # A tibble: 4 x 6
     #>   file_id    raw_data       file_info  method_info  vendor_data_tab… file_path  
    @@ -519,25 +523,33 @@ 

    Data Export

    At the moment, isoreader supports export of all data to Excel and the Feather file format (a Python/R cross-over format). Note that both export methods have similar syntax and append the appropriate file extension for each type of export file (.di.xlsx and .di.feather, respectively).

    +di_files %>% iso_export_to_excel("di_files_export") +#> Info: exporting data from 4 iso_files into Excel 'di_files_export.di.xlsx' +#> Info: aggregating all data from 4 data file(s) + +# data sheets available in the exported data file: +readxl::excel_sheets("di_files_export.di.xlsx") +#> [1] "file info" "raw data" "standards" +#> [4] "resistors" "vendor data table" "problems"
    +di_files %>% iso_export_to_feather("di_files_export") +#> Info: exporting data from 4 iso_files into .di.feather files at 'di_files_export' +#> Info: aggregating all data from 4 data file(s) + +# exported feather files +list.files(pattern = ".di.feather") +#> [1] "di_files_export_file_info.di.feather" +#> [2] "di_files_export_problems.di.feather" +#> [3] "di_files_export_raw_data.di.feather" +#> [4] "di_files_export_resistors.di.feather" +#> [5] "di_files_export_standards.di.feather" +#> [6] "di_files_export_vendor_data_table.di.feather" +#> [7] "iso_files_export_file_info.di.feather" +#> [8] "iso_files_export_method_info-resistors.di.feather" +#> [9] "iso_files_export_method_info-standards.di.feather" +#> [10] "iso_files_export_problems.di.feather" +#> [11] "iso_files_export_raw_data.di.feather" +#> [12] "iso_files_export_vendor_data_table.di.feather"
    @@ -569,9 +581,9 @@

  • Data Processing
  • +
  • For expert users: retrieving all data
  • Saving collections
  • Data Export
  • diff --git a/docs/articles/index.html b/docs/articles/index.html index 90d6ae5b..eb7f57cb 100644 --- a/docs/articles/index.html +++ b/docs/articles/index.html @@ -77,7 +77,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/articles/operations.html b/docs/articles/operations.html index 92b45712..760e3f68 100644 --- a/docs/articles/operations.html +++ b/docs/articles/operations.html @@ -37,7 +37,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -105,7 +105,7 @@ @@ -292,21 +292,21 @@

    By default, isoreader caches files as R objects to make access faster in the future. This feature can be turned off if you want to force a fresh read from the source file. Alternatively, you can clear the entire isoreader cache in your working directory to clean up previous file reads.

    # cleanup reader cache
     iso_cleanup_reader_cache(all = TRUE)
    -#> Info: removed all (15) cached isoreader files.
    +#> Info: removed all (34) cached isoreader files.
     
     # read a new file (notice the time elapsed)
     cf_file <- iso_get_reader_example("continuous_flow_example.dxf") %>% 
       iso_read_continuous_flow()
     #> Info: preparing to read 1 data files (all will be cached)...
     #> Info: reading file 'continuous_flow_example.dxf' with '.dxf' reader
    -#> Info: finished reading 1 files in 3.23 secs
    +#> Info: finished reading 1 files in 1.90 secs
     
     # re-read the same file much faster (it will be read from cache)
     cf_file <- iso_get_reader_example("continuous_flow_example.dxf") %>% 
         iso_read_continuous_flow()
     #> Info: preparing to read 1 data files (all will be cached)...
     #> Info: reading file 'continuous_flow_example.dxf' from cache...
    -#> Info: finished reading 1 files in 0.20 secs
    +#> Info: finished reading 1 files in 0.10 secs
     
     # turn reader caching off
     iso_turn_reader_caching_off()
    @@ -317,7 +317,7 @@ 

    iso_read_continuous_flow() #> Info: preparing to read 1 data files... #> Info: reading file 'continuous_flow_example.dxf' with '.dxf' reader -#> Info: finished reading 1 files in 2.59 secs +#> Info: finished reading 1 files in 1.97 secs # turn reader caching back on iso_turn_reader_caching_on() @@ -336,10 +336,10 @@

    parallel = TRUE ) #> Info: preparing to read 3 data files (all will be cached), setting up 3 par... -#> Info (process 3): reading file 'dual_inlet_example.caf' with '.caf' reader -#> Info (process 1): reading file 'dual_inlet_example.did' with '.did' reader -#> Info (process 2): reading file 'dual_inlet_example2.did' with '.did' reader -#> Info: finished reading 3 files in 10.14 secs

    +#> Info (process 1): reading file 'dual_inlet_example.did' with '.did' reader +#> Info (process 2): reading file 'dual_inlet_example2.did' with '.did' reader +#> Info (process 3): reading file 'dual_inlet_example.caf' with '.caf' reader +#> Info: finished reading 3 files in 10.16 secs + +
    +

    +Units

    +

    Isoreader provides a built in data type with units (iso_double_with_units) that can be used to easily keep track of units inside data frame. These units can be made explicit (=included in the column header), stripped altogher, or turned back to be implicit.

    +
    # strip all units
    +cf_file %>% 
    +  iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`)) %>% 
    +  iso_strip_units() %>% head(3)
    +#> Info: aggregating vendor data table from 1 data file(s)
    +#> # A tibble: 3 x 4
    +#>   file_id                     `Ampl 28` `rIntensity 28` `d 15N/14N`
    +#>   <chr>                           <dbl>           <dbl>       <dbl>
    +#> 1 continuous_flow_example.dxf     3024.          57524.      0.0160
    +#> 2 continuous_flow_example.dxf     3023.          57383.      0     
    +#> 3 continuous_flow_example.dxf     2074.          52732.      1.05
    +
    +# make units explicit
    +cf_file %>% 
    +  iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`)) %>% 
    +  iso_make_units_explicit() %>% head(3)
    +#> Info: aggregating vendor data table from 1 data file(s)
    +#> # A tibble: 3 x 4
    +#>   file_id                 `Ampl 28 [mV]` `rIntensity 28 [mV… `d 15N/14N [permil…
    +#>   <chr>                            <dbl>               <dbl>               <dbl>
    +#> 1 continuous_flow_exampl…          3024.              57524.              0.0160
    +#> 2 continuous_flow_exampl…          3023.              57383.              0     
    +#> 3 continuous_flow_exampl…          2074.              52732.              1.05
    +
    +# introduce new unit columns e.g. in the file info
    +cf_file %>% 
    +  iso_mutate_file_info(weight = iso_double_with_units(0.42, "mg")) %>% 
    +  iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`),
    +                            include_file_info = weight) %>%
    +  iso_make_units_explicit() %>% head(3)
    +#> Info: mutating file info for 1 data file(s)
    +#> Info: aggregating vendor data table from 1 data file(s), including file info 'weight'
    +#> # A tibble: 3 x 5
    +#>   file_id       `weight [mg]` `Ampl 28 [mV]` `rIntensity 28 [… `d 15N/14N [perm…
    +#>   <chr>                 <dbl>          <dbl>             <dbl>             <dbl>
    +#> 1 continuous_f…          0.42          3024.            57524.            0.0160
    +#> 2 continuous_f…          0.42          3023.            57383.            0     
    +#> 3 continuous_f…          0.42          2074.            52732.            1.05
    +
    +# or turn a column e.g. with custom format units in the header into implicit units
    +cf_file %>% 
    +  iso_mutate_file_info(weight.mg = 0.42) %>% 
    +  iso_get_vendor_data_table(select = c(`Ampl 28`, `rIntensity 28`, `d 15N/14N`),
    +                            include_file_info = weight.mg) %>%
    +  iso_make_units_implicit(prefix = ".", suffix = "") %>% head(3)
    +#> Info: mutating file info for 1 data file(s)
    +#> Info: aggregating vendor data table from 1 data file(s), including file info 'weight.mg'
    +#> # A tibble: 3 x 5
    +#>   file_id                        weight `Ampl 28` `rIntensity 28`   `d 15N/14N`
    +#>   <chr>                       <dbl[mg]> <dbl[mV]>      <dbl[mVs]> <dbl[permil]>
    +#> 1 continuous_flow_example.dxf      0.42  3024.040        57524.32    0.01600287
    +#> 2 continuous_flow_example.dxf      0.42  3022.789        57383.21    0.00000000
    +#> 3 continuous_flow_example.dxf      0.42  2073.872        52731.67    1.04910065
    @@ -524,6 +582,7 @@

  • Combining / subsetting isofiles
  • Dealing with file read problems
  • Re-reading files
  • +
  • Units
  • diff --git a/docs/articles/quick_start.html b/docs/articles/quick_start.html index 5151af66..46be396c 100644 --- a/docs/articles/quick_start.html +++ b/docs/articles/quick_start.html @@ -37,7 +37,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -106,7 +106,7 @@ +# save as r data storage (read back in with iso_read_dual_inlet) +iso_save(di_files, filepath = "di_save") +#> Info: exporting data from 4 iso_files into R Data Storage 'di_save.di.rds' + +# export to excel +iso_export_to_excel(di_files, filepath = "di_export") +#> Info: exporting data from 4 iso_files into Excel 'di_export.di.xlsx' +#> Info: aggregating all data from 4 data file(s) +# save as r data storage (read back in with iso_read_continuous_flow) +iso_save(cf_files, filepath = "cf_save") +#> Info: exporting data from 7 iso_files into R Data Storage 'cf_save.cf.rds' + +# export to excel +iso_export_to_excel(cf_files, filepath = "cf_export") +#> Info: exporting data from 7 iso_files into Excel 'cf_export.cf.xlsx' +#> Info: aggregating all data from 7 data file(s) +# save as r data storage (read back in with iso_read_scan) +iso_save(scan_files, filepath = "scan_save") +#> Info: exporting data from 4 iso_files into R Data Storage 'scan_save.scan.rds' + +# export to excel +iso_export_to_excel(scan_files, filepath = "scan_export") +#> Info: exporting data from 4 iso_files into Excel 'scan_export.scan.xlsx' +#> Info: aggregating all data from 4 data file(s) diff --git a/docs/articles/scan.html b/docs/articles/scan.html index bbc64ac4..92fd7e1d 100644 --- a/docs/articles/scan.html +++ b/docs/articles/scan.html @@ -37,7 +37,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -106,7 +106,7 @@ +#> Info: finished reading 4 files in 0.56 secs

    File summary

    -

    The iso_files variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the iso_get_data_summary() function.

    -
    iso_files %>% iso_get_data_summary() %>% rmarkdown::paged_table()
    +

    The scan_files variable now contains a set of isoreader objects, one for each file. Take a look at what information was retrieved from the files using the iso_get_data_summary() function.

    +
    - +
    @@ -183,11 +183,11 @@

    File Information

    Detailed file information can be aggregated for all isofiles using the iso_get_file_info() function which supports the full select syntax of the dplyr package to specify which columns are of interest (by default, all file information is retrieved).

    @@ -195,7 +195,7 @@

    Select/Rename

    File information can also be modified across an entire collection of isofiles using the iso_select_file_info() and iso_rename_file_info() functions:

    @@ -217,7 +217,7 @@

    Filter

    Any collection of isofiles can also be filtered based on the available file information using the function iso_filter_files. This function can operate on any column available in the file information and supports full dplyr syntax.

    @@ -233,20 +233,20 @@

    Mutate

    The file information in any collection of isofiles can also be mutated using the function iso_mutate_file_info. This function can introduce new columns and operate on any existing columns available in the file information (even if it does not exist in all files) and supports full dplyr syntax.

    -
    iso_files3 <- iso_files2 %>% 
    +
     
    @@ -255,7 +255,7 @@

    Resistors

    Additionally, some IRMS data files contain resistor information that are useful for downstream calculations (see e.g. section on signal conversion later in this vignette):

    -
    iso_files %>% iso_get_resistors_info() %>% rmarkdown::paged_table()
    +
     
    +
    +

    +For expert users: retrieving all data

    +

    For users familiar with the nested data frames from the tidyverse (particularly tidyr’s nest and unnest), there is an easy way to retrieve all data from the iso file objects in a single nested data frame:

    + +

    Saving collections

    Saving entire collections of isofiles for retrieval at a later point is easily done using the iso_save function which stores collections or individual isoreader file objects in the efficient R data storage format .rds (if not specified, the extension .scan.rds will be automatically appended). These saved collections can be convientiently read back using the same iso_read_scan command used for raw data files.

    - +

    Data Export

    At the moment, isoreader supports export of all data to Excel and the Feather file format (a Python/R cross-over format). Note that both export methods have similar syntax and append the appropriate file extension for each type of export file (.scan.xlsx and .scan.feather, respectively).

    - - + +
    @@ -361,6 +375,7 @@

  • Resistors
  • Raw Data
  • +
  • For expert users: retrieving all data
  • Saving collections
  • Data Export
  • diff --git a/docs/authors.html b/docs/authors.html index 396e94f8..1d74f0e3 100644 --- a/docs/authors.html +++ b/docs/authors.html @@ -77,7 +77,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/index.html b/docs/index.html index 4e4f0b4d..58554c01 100644 --- a/docs/index.html +++ b/docs/index.html @@ -37,7 +37,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/extract_data.html b/docs/reference/extract_data.html index ae07ad04..068fa5da 100644 --- a/docs/reference/extract_data.html +++ b/docs/reference/extract_data.html @@ -80,7 +80,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/extract_substring.html b/docs/reference/extract_substring.html index ad95e1b6..7e93d7d8 100644 --- a/docs/reference/extract_substring.html +++ b/docs/reference/extract_substring.html @@ -80,7 +80,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/extract_word.html b/docs/reference/extract_word.html index b9bd80b9..50d4598e 100644 --- a/docs/reference/extract_word.html +++ b/docs/reference/extract_word.html @@ -80,7 +80,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/file_readers.html b/docs/reference/file_readers.html index be0294f7..a05b943b 100644 --- a/docs/reference/file_readers.html +++ b/docs/reference/file_readers.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/index.html b/docs/reference/index.html index d3cd4ff6..873ef224 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -77,7 +77,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -308,7 +308,7 @@

    iso_get_data()

    +

    iso_get_all_data()

    Aggregate all isofiles data

    @@ -338,13 +338,13 @@

    iso_get_resistors_info()

    +

    iso_get_resistors()

    Aggregate resistors from methods info

    -

    iso_get_standards_info()

    +

    iso_get_standards()

    Aggregate standards from methods info

    diff --git a/docs/reference/iso_add_file_info.html b/docs/reference/iso_add_file_info.html index 3d3f5800..fc4a12c4 100644 --- a/docs/reference/iso_add_file_info.html +++ b/docs/reference/iso_add_file_info.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_caching.html b/docs/reference/iso_caching.html index dc8535c1..4cd4d3c6 100644 --- a/docs/reference/iso_caching.html +++ b/docs/reference/iso_caching.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_calculate_ratios.html b/docs/reference/iso_calculate_ratios.html index 4294da5b..709bacea 100644 --- a/docs/reference/iso_calculate_ratios.html +++ b/docs/reference/iso_calculate_ratios.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_cleanup_reader_cache.html b/docs/reference/iso_cleanup_reader_cache.html index caac23b1..e1e56123 100644 --- a/docs/reference/iso_cleanup_reader_cache.html +++ b/docs/reference/iso_cleanup_reader_cache.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_convert_signals.html b/docs/reference/iso_convert_signals.html index db5da448..bf5e24cd 100644 --- a/docs/reference/iso_convert_signals.html +++ b/docs/reference/iso_convert_signals.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_convert_time.html b/docs/reference/iso_convert_time.html index 2a216662..4bc57aff 100644 --- a/docs/reference/iso_convert_time.html +++ b/docs/reference/iso_convert_time.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_data_structure.html b/docs/reference/iso_data_structure.html index 89332bdd..a32b1180 100644 --- a/docs/reference/iso_data_structure.html +++ b/docs/reference/iso_data_structure.html @@ -85,7 +85,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_debug_mode.html b/docs/reference/iso_debug_mode.html index 592054d4..ea582831 100644 --- a/docs/reference/iso_debug_mode.html +++ b/docs/reference/iso_debug_mode.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_double_with_units.html b/docs/reference/iso_double_with_units.html index eb533bc7..20bc52f5 100644 --- a/docs/reference/iso_double_with_units.html +++ b/docs/reference/iso_double_with_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_expand_paths.html b/docs/reference/iso_expand_paths.html index ae7aca00..b0866960 100644 --- a/docs/reference/iso_expand_paths.html +++ b/docs/reference/iso_expand_paths.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_export_to_excel.html b/docs/reference/iso_export_to_excel.html index 0ef038fc..598560eb 100644 --- a/docs/reference/iso_export_to_excel.html +++ b/docs/reference/iso_export_to_excel.html @@ -36,7 +36,7 @@ - + @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -151,18 +151,21 @@

    Export data to Excel

    -

    This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file but they are only exported if the corresponding include_ parameter is set to TRUE and only for data types for which this type of data is available and was read (see iso_read_dual_inlet, iso_read_continuous_flow for details on read parameters). Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export.

    +

    This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file. Use the various include_... parameters to specifiy what information to include. Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export.

    iso_export_to_excel(
       iso_files,
       filepath,
    -  include_raw_data = TRUE,
    -  include_file_info = TRUE,
    -  include_method_info = TRUE,
    -  include_vendor_data_table = TRUE,
    -  include_problems = TRUE,
    +  include_file_info = everything(),
    +  include_raw_data = everything(),
    +  include_standards = !!enexpr(include_method_info),
    +  include_resistors = !!enquo(include_method_info),
    +  include_vendor_data_table = everything(),
    +  include_problems = everything(),
       with_explicit_units = FALSE,
    +  include_method_info = everything(),
    +  with_ratios = NULL,
       quiet = default(quiet)
     )
    @@ -178,28 +181,37 @@

    Arg

    the path (folder and filename) to the export file. The correct file extension is automatically added if not already in the filename, i.e. filename can be provided with or without extension.

    - include_raw_data -

    whether to include the raw data in the export (if available)

    + include_file_info +

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    - include_file_info -

    whether to include the file info in the export (if available)

    + include_raw_data +

    which columns from the raw data to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data.

    - include_method_info -

    whether to include methods infor in the export (if available)

    + include_standards +

    which columns from the standards info to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to select = file_id:reference. Set to NULL to include no standards info. +#' @param include_resistors which columns from the resistors info to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info.

    include_vendor_data_table -

    whether to include the vendor data table in the export (if available)

    +

    which columns from the vendor data table to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set parameter with_explicit_units = TRUE to make column units explicit (keep in mind that this will require specific include_vendor_data_table column selections to reflect the column names including the units). Set to NULL to include no vendor data table.

    include_problems -

    whether to include the problems table

    +

    which columns from problems to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes none of the read problems by default. Set to include_problems = everything() to include all columns.

    with_explicit_units -

    whether to include units in the column headers instead of the column data types (see iso_double_with_units)

    +

    whether to include units in the column headers of the returned data frame instead of the column data types (see iso_double_with_units). Note that any select conditions have to refer to the column names including the full units.

    + + + include_method_info +

    deprecated in favor of the more specific include_standards and include_resistors

    + + + with_ratios +

    deprecated, please use the select paramter to explicitly include or exclude ratio columns

    quiet diff --git a/docs/reference/iso_export_to_feather.html b/docs/reference/iso_export_to_feather.html index f1c53c3f..7b7af11d 100644 --- a/docs/reference/iso_export_to_feather.html +++ b/docs/reference/iso_export_to_feather.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -157,12 +157,14 @@

    Export to feather

    iso_export_to_feather(
       iso_files,
       filepath_prefix,
    -  include_raw_data = TRUE,
    -  include_file_info = TRUE,
    -  include_method_info = TRUE,
    -  include_vendor_data_table = TRUE,
    -  include_problems = TRUE,
    +  include_file_info = everything(),
    +  include_raw_data = everything(),
    +  include_standards = !!enexpr(include_method_info),
    +  include_resistors = !!enquo(include_method_info),
    +  include_vendor_data_table = everything(),
    +  include_problems = everything(),
       with_explicit_units = FALSE,
    +  include_method_info = everything(),
       quiet = default(quiet)
     )
    @@ -174,32 +176,33 @@

    Arg

    collection of iso_file objects

    - filepath_prefix -

    the path (folder and filename) prefix for the exported feather files. The correct suffix for different kinds of data and file extension is automatically added

    + include_file_info +

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    include_raw_data -

    whether to include the raw data in the export (if available)

    - - - include_file_info -

    whether to include the file info in the export (if available)

    +

    which columns from the raw data to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data.

    - include_method_info -

    whether to include methods infor in the export (if available)

    + include_standards +

    which columns from the standards info to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to select = file_id:reference. Set to NULL to include no standards info. +#' @param include_resistors which columns from the resistors info to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info.

    include_vendor_data_table -

    whether to include the vendor data table in the export (if available)

    +

    which columns from the vendor data table to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set parameter with_explicit_units = TRUE to make column units explicit (keep in mind that this will require specific include_vendor_data_table column selections to reflect the column names including the units). Set to NULL to include no vendor data table.

    include_problems -

    whether to include the problems table

    +

    which columns from problems to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes none of the read problems by default. Set to include_problems = everything() to include all columns.

    with_explicit_units -

    whether to include units in the column headers instead of the column data types (see iso_double_with_units)

    +

    whether to include units in the column headers of the returned data frame instead of the column data types (see iso_double_with_units). Note that any select conditions have to refer to the column names including the full units.

    + + + include_method_info +

    deprecated in favor of the more specific include_standards and include_resistors

    quiet diff --git a/docs/reference/iso_export_to_rda.html b/docs/reference/iso_export_to_rda.html index b68a0852..03bc24ca 100644 --- a/docs/reference/iso_export_to_rda.html +++ b/docs/reference/iso_export_to_rda.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_filter_files.html b/docs/reference/iso_filter_files.html index d64c86da..cd3f79d8 100644 --- a/docs/reference/iso_filter_files.html +++ b/docs/reference/iso_filter_files.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_filter_files_with_problems.html b/docs/reference/iso_filter_files_with_problems.html index c46917e0..82d8f4b3 100644 --- a/docs/reference/iso_filter_files_with_problems.html +++ b/docs/reference/iso_filter_files_with_problems.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_find_absolute_path_roots.html b/docs/reference/iso_find_absolute_path_roots.html index fba019d4..c5ef5afb 100644 --- a/docs/reference/iso_find_absolute_path_roots.html +++ b/docs/reference/iso_find_absolute_path_roots.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_get_all_data.html b/docs/reference/iso_get_all_data.html new file mode 100644 index 00000000..bd6664b3 --- /dev/null +++ b/docs/reference/iso_get_all_data.html @@ -0,0 +1,266 @@ + + + + + + + + +Aggregate all isofiles data — iso_get_all_data • Isoreader + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + + +
    + +
    +
    + + +
    +

    This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data iso_get_raw_data, iso_get_file_info, iso_get_vendor_data_table, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To unnest any of the specific data types (e.g. raw_data), make sure to filter first for the files that have this data type available (e.g. filter(has_raw_data)). Exclude specific types of information by setting its include... parameter to NULL (Note: for historical reasons, setting it to FALSE will also include the information).

    +
    + +
    iso_get_all_data(
    +  iso_files,
    +  include_file_info = everything(),
    +  include_raw_data = everything(),
    +  include_standards = everything(),
    +  include_resistors = everything(),
    +  include_vendor_data_table = everything(),
    +  include_problems = NULL,
    +  gather = FALSE,
    +  with_explicit_units = with_units,
    +  with_units = FALSE,
    +  with_ratios = NULL,
    +  quiet = default(quiet)
    +)
    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    iso_files

    collection of iso_file objects

    include_file_info

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    include_raw_data

    which columns from the raw data to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data.

    include_standards

    which columns from the standards info to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to select = file_id:reference. Set to NULL to include no standards info. +#' @param include_resistors which columns from the resistors info to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info.

    include_vendor_data_table

    which columns from the vendor data table to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes all columns by default. Set parameter with_explicit_units = TRUE to make column units explicit (keep in mind that this will require specific include_vendor_data_table column selections to reflect the column names including the units). Set to NULL to include no vendor data table.

    include_problems

    which columns from problems to include. Use c(...) to select multiple columns, supports all select syntax including renaming columns. Includes none of the read problems by default. Set to include_problems = everything() to include all columns.

    gather

    whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the select parameter applies to the data columns BEFORE gathering.

    with_explicit_units

    whether to include units in the column headers of the returned data frame instead of the column data types (see iso_double_with_units). Note that any select conditions have to refer to the column names including the full units.

    with_units

    this parameter has been DEPRECATED with the introduction of unit-data types (see iso_double_with_units) and will be removed in future versions of isoreader. Please use with_explicit_units instead if you really want columns to have units explicitly in the column name. Alternatively, consider working with the new implicit unit system and convert vendor data tables as needed with iso_make_units_explicit and iso_make_units_implicit.

    with_ratios

    deprecated, please use the select paramter to explicitly include or exclude ratio columns

    quiet

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    + +

    Value

    + +

    data_frame with file_ids, file_types and nested data frames for each data type (file_info, raw_data, vendor_data_table, etc.)

    +

    See also

    + + + +
    + +
    + + +
    + + +
    +

    Site built with pkgdown 1.4.1.

    +
    + +
    +
    + + + + + + + + diff --git a/docs/reference/iso_get_bgrd_data.html b/docs/reference/iso_get_bgrd_data.html index c949f4d2..a3a8305c 100644 --- a/docs/reference/iso_get_bgrd_data.html +++ b/docs/reference/iso_get_bgrd_data.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -171,7 +171,7 @@

    Arg select -

    which raw data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    +

    which data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    gather @@ -190,11 +190,11 @@

    Arg

    See also

    diff --git a/docs/reference/iso_get_data.html b/docs/reference/iso_get_data.html index 5dba5beb..b5dab9ee 100644 --- a/docs/reference/iso_get_data.html +++ b/docs/reference/iso_get_data.html @@ -6,7 +6,7 @@ -Aggregate all isofiles data — iso_get_data • Isoreader +DEPRECATED — iso_get_data • Isoreader @@ -35,8 +35,8 @@ - - + + @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -145,88 +145,23 @@
    -

    This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data iso_get_raw_data, iso_get_file_info, iso_get_vendor_data_table, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To unnest any of the specific data types (e.g. raw_data), make sure to filter first for the files that have this data type available (e.g. filter(has_raw_data)).

    +

    Please use iso_get_all_data instead.

    -
    iso_get_data(
    -  iso_files,
    -  include_file_info = everything(),
    -  include_raw_data = everything(),
    -  include_vendor_data_table = everything(),
    -  gather = FALSE,
    -  with_explicit_units = with_units,
    -  with_units = FALSE,
    -  with_ratios = FALSE,
    -  quiet = default(quiet)
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    iso_files

    collection of iso_file objects

    include_file_info

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    include_raw_data

    which columns from the raw data to include use c(...) to select multiple, supports all select syntax including renaming columns. Includes all columns by default.

    include_vendor_data_table

    which columns from the vendor data table to include - use c(...) to select multiple, supports all select syntax including renaming columns. Includes all columns by default.

    gather

    whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the select parameter applies to the data columns BEFORE gathering.

    with_explicit_units

    whether to include units in the column headers instead of the column data types (see iso_double_with_units)

    with_units

    this parameter has been DEPRECATED with the introduction of unit-data types (see iso_double_with_units) and will be removed in future versions of isoreader. Please use with_explicit_units instead if you really want columns to have units explicitly in the column name. Alternatively, consider working with the new implicit unit system and convert vendor data tables as needed with iso_make_units_explicit and iso_make_units_implicit.

    with_ratios

    whether to include ratios or just standard delta values

    quiet

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - -

    Value

    - -

    data_frame with file_ids, file_types and nested data frames for each data type (file_info, raw_data, vendor_data_table, etc.)

    -

    See also

    - - +
    iso_get_data(...)
    + +
    diff --git a/docs/reference/iso_get_data_summary.html b/docs/reference/iso_get_data_summary.html index 16c08516..25327e47 100644 --- a/docs/reference/iso_get_data_summary.html +++ b/docs/reference/iso_get_data_summary.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0
    diff --git a/docs/reference/iso_get_default_reader_parameters.html b/docs/reference/iso_get_default_reader_parameters.html index 6ef1b219..fd28796c 100644 --- a/docs/reference/iso_get_default_reader_parameters.html +++ b/docs/reference/iso_get_default_reader_parameters.html @@ -82,7 +82,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_get_file_info.html b/docs/reference/iso_get_file_info.html index fe1373ad..e031115f 100644 --- a/docs/reference/iso_get_file_info.html +++ b/docs/reference/iso_get_file_info.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -179,11 +179,11 @@

    Note

    See also

    diff --git a/docs/reference/iso_get_problems.html b/docs/reference/iso_get_problems.html index 968367b6..4ac94ac6 100644 --- a/docs/reference/iso_get_problems.html +++ b/docs/reference/iso_get_problems.html @@ -36,7 +36,7 @@ - + @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -151,10 +151,10 @@

    Retrieve parsing problems

    -

    This is identical to the readr problems function.

    +

    This function retrieves parsing problems encountered during the reading of a set of iso files.

    -
    iso_get_problems(iso_files)
    +
    iso_get_problems(iso_files, select = everything())

    Arguments

    @@ -163,6 +163,10 @@

    Arg

    + + + +
    iso_files

    collection of iso_file objects

    select

    which data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    See also

    diff --git a/docs/reference/iso_get_problems_summary.html b/docs/reference/iso_get_problems_summary.html index cd00bb47..e34d6a66 100644 --- a/docs/reference/iso_get_problems_summary.html +++ b/docs/reference/iso_get_problems_summary.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_get_raw_data.html b/docs/reference/iso_get_raw_data.html index 9a4743ad..12d44f70 100644 --- a/docs/reference/iso_get_raw_data.html +++ b/docs/reference/iso_get_raw_data.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -171,7 +171,7 @@

    Arg select -

    which raw data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    +

    which data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    gather @@ -190,18 +190,18 @@

    Arg

    See also

    diff --git a/docs/reference/iso_get_reader_example.html b/docs/reference/iso_get_reader_example.html index 2a9db6d8..62da113e 100644 --- a/docs/reference/iso_get_reader_example.html +++ b/docs/reference/iso_get_reader_example.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -190,7 +190,7 @@

    Examp #> 9 background_scan_example… scan Isodat Scan file format #> 10 full_scan_example.scn scan Isodat Scan file format #> 11 peak_shape_scan_example… scan Isodat Scan file format -#> 12 time_scan_example.scn scan Isodat Scan file format
    iso_get_reader_examples_folder()
    #> [1] "/private/var/folders/8z/hxk97xk1763cz5crb3wp7h840000gn/T/RtmpHjZNFY/temp_libpath182775933fe17/isoreader/extdata"
    +#> 12 time_scan_example.scn scan Isodat Scan file format
    iso_get_reader_examples_folder()
    #> [1] "/private/var/folders/8z/hxk97xk1763cz5crb3wp7h840000gn/T/RtmpxKpygI/temp_libpath1690d6c48813f/isoreader/extdata"
    @@ -145,54 +145,23 @@
    -

    Aggregates the resistor information recovered from the provided iso_files. This information is only available if the iso_files were read with parameter read_method_info=TRUE and only linked to specific masses if the iso_files were additionally read with parametr read_raw_data=TRUE.

    +

    Please use iso_get_resistors instead.

    -
    iso_get_resistors_info(
    -  iso_files,
    -  include_file_info = NULL,
    -  quiet = default(quiet)
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - -
    iso_files

    collection of iso_file objects

    include_file_info

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    quiet

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - -

    See also

    - - +
    iso_get_resistors_info(...)
    + +
    diff --git a/docs/reference/iso_get_standards.html b/docs/reference/iso_get_standards.html new file mode 100644 index 00000000..17e6a633 --- /dev/null +++ b/docs/reference/iso_get_standards.html @@ -0,0 +1,230 @@ + + + + + + + + +Aggregate standards from methods info — iso_get_standards • Isoreader + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + + +
    + +
    +
    + + +
    +

    Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter select to exclude/include the ratios. All standards info is only available if the iso_files were read with parameter read_method_info=TRUE.

    +
    + +
    iso_get_standards(
    +  iso_files,
    +  select = everything(),
    +  include_file_info = NULL,
    +  with_ratios = NULL,
    +  quiet = default(quiet)
    +)
    + +

    Arguments

    + + + + + + + + + + + + + + + + + + + + + + +
    iso_files

    collection of iso_file objects

    select

    which data columns to select - use c(...) to select multiple, supports all select syntax. By default, everything is included (both standards and ratios). To omit the ratios, change to select = file_id:reference.

    include_file_info

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    with_ratios

    deprecated, please use the select paramter to explicitly include or exclude ratio columns

    quiet

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    + +

    See also

    + + + +
    + +
    + + +
    + + +
    +

    Site built with pkgdown 1.4.1.

    +
    + +
    +
    + + + + + + + + diff --git a/docs/reference/iso_get_standards_info.html b/docs/reference/iso_get_standards_info.html index 764116d9..774bbc2d 100644 --- a/docs/reference/iso_get_standards_info.html +++ b/docs/reference/iso_get_standards_info.html @@ -6,7 +6,7 @@ -Aggregate standards from methods info — iso_get_standards_info • Isoreader +DEPRECATED — iso_get_standards_info • Isoreader @@ -35,8 +35,8 @@ - - + + @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0
    @@ -145,59 +145,23 @@
    -

    Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter with_ratios to exclude/include the ratios. This information is only available if the iso_files were read with parameter read_method_info=TRUE.

    +

    Please use iso_get_standards instead.

    -
    iso_get_standards_info(
    -  iso_files,
    -  with_ratios = FALSE,
    -  include_file_info = NULL,
    -  quiet = default(quiet)
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    iso_files

    collection of iso_file objects

    with_ratios

    whether to include ratios or just standard delta values

    include_file_info

    which file information to include (see iso_get_file_info). Use c(...) to select multiple, supports all select syntax including renaming columns.

    quiet

    whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to iso_turn_info_message_on and iso_turn_info_message_off

    - -

    See also

    - - +
    iso_get_standards_info(...)
    + +
    diff --git a/docs/reference/iso_get_supported_file_types.html b/docs/reference/iso_get_supported_file_types.html index 03adbf0a..42dfc028 100644 --- a/docs/reference/iso_get_supported_file_types.html +++ b/docs/reference/iso_get_supported_file_types.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0
    diff --git a/docs/reference/iso_get_units.html b/docs/reference/iso_get_units.html index 2452dd1c..9f27304b 100644 --- a/docs/reference/iso_get_units.html +++ b/docs/reference/iso_get_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_get_vendor_data_table.html b/docs/reference/iso_get_vendor_data_table.html index ad14bba6..a6a7c084 100644 --- a/docs/reference/iso_get_vendor_data_table.html +++ b/docs/reference/iso_get_vendor_data_table.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 @@ -176,7 +176,7 @@

    Arg select -

    which raw data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    +

    which data columns to select - use c(...) to select multiple, supports all select syntax. By default, all columns are selected.

    include_file_info @@ -184,7 +184,7 @@

    Arg with_explicit_units -

    whether to include units in the column headers instead of the column data types (see iso_double_with_units)

    +

    whether to include units in the column headers of the returned data frame instead of the column data types (see iso_double_with_units). Note that any select conditions have to refer to the column names including the full units.

    quiet @@ -195,12 +195,12 @@

    Arg

    See also

    +iso_get_resistors(), +iso_get_standards()

    diff --git a/docs/reference/iso_info_messages.html b/docs/reference/iso_info_messages.html index 77e25297..f13d2fee 100644 --- a/docs/reference/iso_info_messages.html +++ b/docs/reference/iso_info_messages.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_is_double_with_units.html b/docs/reference/iso_is_double_with_units.html index c96cd4e6..d25a49b1 100644 --- a/docs/reference/iso_is_double_with_units.html +++ b/docs/reference/iso_is_double_with_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_make_units_explicit.html b/docs/reference/iso_make_units_explicit.html index 338beac6..b9e2c209 100644 --- a/docs/reference/iso_make_units_explicit.html +++ b/docs/reference/iso_make_units_explicit.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_make_units_implicit.html b/docs/reference/iso_make_units_implicit.html index b8cdff44..c81470a5 100644 --- a/docs/reference/iso_make_units_implicit.html +++ b/docs/reference/iso_make_units_implicit.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_mutate_file_info.html b/docs/reference/iso_mutate_file_info.html index 72e0772e..b2e196f2 100644 --- a/docs/reference/iso_mutate_file_info.html +++ b/docs/reference/iso_mutate_file_info.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_omit_files_with_problems.html b/docs/reference/iso_omit_files_with_problems.html index 04c81ebd..ce998062 100644 --- a/docs/reference/iso_omit_files_with_problems.html +++ b/docs/reference/iso_omit_files_with_problems.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_parse_file_info.html b/docs/reference/iso_parse_file_info.html index 66e3d8a3..2a2c309a 100644 --- a/docs/reference/iso_parse_file_info.html +++ b/docs/reference/iso_parse_file_info.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_plot_continuous_flow_data.html b/docs/reference/iso_plot_continuous_flow_data.html index 6097edac..16feb40a 100644 --- a/docs/reference/iso_plot_continuous_flow_data.html +++ b/docs/reference/iso_plot_continuous_flow_data.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_plot_dual_inlet_data.html b/docs/reference/iso_plot_dual_inlet_data.html index de0a5699..bac82322 100644 --- a/docs/reference/iso_plot_dual_inlet_data.html +++ b/docs/reference/iso_plot_dual_inlet_data.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_plot_raw_data.html b/docs/reference/iso_plot_raw_data.html index 93620aa6..04b6ae5c 100644 --- a/docs/reference/iso_plot_raw_data.html +++ b/docs/reference/iso_plot_raw_data.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_printing.html b/docs/reference/iso_printing.html index e26d8fa0..b631eb3f 100644 --- a/docs/reference/iso_printing.html +++ b/docs/reference/iso_printing.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_problem_functions.html b/docs/reference/iso_problem_functions.html index d859f245..b5ff04b3 100644 --- a/docs/reference/iso_problem_functions.html +++ b/docs/reference/iso_problem_functions.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_read_continuous_flow.html b/docs/reference/iso_read_continuous_flow.html index 2da6d928..bba79d39 100644 --- a/docs/reference/iso_read_continuous_flow.html +++ b/docs/reference/iso_read_continuous_flow.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_read_dual_inlet.html b/docs/reference/iso_read_dual_inlet.html index c268c80c..bdf861e8 100644 --- a/docs/reference/iso_read_dual_inlet.html +++ b/docs/reference/iso_read_dual_inlet.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_read_files.html b/docs/reference/iso_read_files.html index 10aaeda9..f11d3f6d 100644 --- a/docs/reference/iso_read_files.html +++ b/docs/reference/iso_read_files.html @@ -81,7 +81,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_read_scan.html b/docs/reference/iso_read_scan.html index 45d7260d..2570d9ff 100644 --- a/docs/reference/iso_read_scan.html +++ b/docs/reference/iso_read_scan.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_rename_file_info.html b/docs/reference/iso_rename_file_info.html index 184eef8a..db1a9d0c 100644 --- a/docs/reference/iso_rename_file_info.html +++ b/docs/reference/iso_rename_file_info.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_reread_files.html b/docs/reference/iso_reread_files.html index 7683273d..319a40f3 100644 --- a/docs/reference/iso_reread_files.html +++ b/docs/reference/iso_reread_files.html @@ -81,7 +81,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_root_paths.html b/docs/reference/iso_root_paths.html index bb6d9f1d..60f2e33a 100644 --- a/docs/reference/iso_root_paths.html +++ b/docs/reference/iso_root_paths.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_save.html b/docs/reference/iso_save.html index 903f908e..99847199 100644 --- a/docs/reference/iso_save.html +++ b/docs/reference/iso_save.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_select_file_info.html b/docs/reference/iso_select_file_info.html index 2841f627..25fd7bec 100644 --- a/docs/reference/iso_select_file_info.html +++ b/docs/reference/iso_select_file_info.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_set_default_read_parameters.html b/docs/reference/iso_set_default_read_parameters.html index 6c5387c2..a42fbfe2 100644 --- a/docs/reference/iso_set_default_read_parameters.html +++ b/docs/reference/iso_set_default_read_parameters.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_shorten_relative_paths.html b/docs/reference/iso_shorten_relative_paths.html index aa2c5916..5bf46b48 100644 --- a/docs/reference/iso_shorten_relative_paths.html +++ b/docs/reference/iso_shorten_relative_paths.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_show_default_reader_parameters.html b/docs/reference/iso_show_default_reader_parameters.html index 1c292409..a7dd5b66 100644 --- a/docs/reference/iso_show_default_reader_parameters.html +++ b/docs/reference/iso_show_default_reader_parameters.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/iso_strip_units.html b/docs/reference/iso_strip_units.html index 4979fbc4..a0cd14e8 100644 --- a/docs/reference/iso_strip_units.html +++ b/docs/reference/iso_strip_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/isoread.html b/docs/reference/isoread.html index 79e7f169..07b29bf3 100644 --- a/docs/reference/isoread.html +++ b/docs/reference/isoread.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/isoreader-package.html b/docs/reference/isoreader-package.html index 3c33e5c1..b347b709 100644 --- a/docs/reference/isoreader-package.html +++ b/docs/reference/isoreader-package.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/print.binary_structure_map.html b/docs/reference/print.binary_structure_map.html index 4ed5423f..e7a17650 100644 --- a/docs/reference/print.binary_structure_map.html +++ b/docs/reference/print.binary_structure_map.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/read_iso_file.html b/docs/reference/read_iso_file.html index 32fb045a..cd9b55e6 100644 --- a/docs/reference/read_iso_file.html +++ b/docs/reference/read_iso_file.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/reexports.html b/docs/reference/reexports.html index bfb92b2d..c0605511 100644 --- a/docs/reference/reexports.html +++ b/docs/reference/reexports.html @@ -90,7 +90,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/set_temp.html b/docs/reference/set_temp.html index 080d058f..80873c35 100644 --- a/docs/reference/set_temp.html +++ b/docs/reference/set_temp.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/vec_arith.iso_double_with_units.html b/docs/reference/vec_arith.iso_double_with_units.html index d4fad7f5..8e2d525d 100644 --- a/docs/reference/vec_arith.iso_double_with_units.html +++ b/docs/reference/vec_arith.iso_double_with_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/vec_cast.iso_double_with_units.html b/docs/reference/vec_cast.iso_double_with_units.html index 110a13c8..302ac3cd 100644 --- a/docs/reference/vec_cast.iso_double_with_units.html +++ b/docs/reference/vec_cast.iso_double_with_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/reference/vec_ptype2.iso_double_with_units.html b/docs/reference/vec_ptype2.iso_double_with_units.html index 955685d8..6d7ee798 100644 --- a/docs/reference/vec_ptype2.iso_double_with_units.html +++ b/docs/reference/vec_ptype2.iso_double_with_units.html @@ -79,7 +79,7 @@ Isoreader - 1.0.17 + 1.1.0 diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 98b23632..e29fe6bd 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -63,6 +63,9 @@ http://isoreader.isoverse.org//reference/iso_find_absolute_path_roots.html + + http://isoreader.isoverse.org//reference/iso_get_all_data.html + http://isoreader.isoverse.org//reference/iso_get_bgrd_data.html @@ -90,9 +93,15 @@ http://isoreader.isoverse.org//reference/iso_get_reader_example.html + + http://isoreader.isoverse.org//reference/iso_get_resistors.html + http://isoreader.isoverse.org//reference/iso_get_resistors_info.html + + http://isoreader.isoverse.org//reference/iso_get_standards.html + http://isoreader.isoverse.org//reference/iso_get_standards_info.html diff --git a/man/iso_export_to_excel.Rd b/man/iso_export_to_excel.Rd index b7eb23b2..cefdc294 100644 --- a/man/iso_export_to_excel.Rd +++ b/man/iso_export_to_excel.Rd @@ -7,12 +7,15 @@ iso_export_to_excel( iso_files, filepath, - include_raw_data = TRUE, - include_file_info = TRUE, - include_method_info = TRUE, - include_vendor_data_table = TRUE, - include_problems = TRUE, + include_file_info = everything(), + include_raw_data = everything(), + include_standards = !!enexpr(include_method_info), + include_resistors = !!enquo(include_method_info), + include_vendor_data_table = everything(), + include_problems = everything(), with_explicit_units = FALSE, + include_method_info = everything(), + with_ratios = NULL, quiet = default(quiet) ) } @@ -21,17 +24,22 @@ iso_export_to_excel( \item{filepath}{the path (folder and filename) to the export file. The correct file extension is automatically added if not already in the filename, i.e. filename can be provided with or without extension.} -\item{include_raw_data}{whether to include the raw data in the export (if available)} +\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} -\item{include_file_info}{whether to include the file info in the export (if available)} +\item{include_raw_data}{which columns from the raw data to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data.} -\item{include_method_info}{whether to include methods infor in the export (if available)} +\item{include_standards}{which columns from the standards info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}. Set to NULL to include no standards info. +#' @param include_resistors which columns from the resistors info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info.} -\item{include_vendor_data_table}{whether to include the vendor data table in the export (if available)} +\item{include_vendor_data_table}{which columns from the vendor data table to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set parameter \code{with_explicit_units = TRUE} to make column units explicit (keep in mind that this will require specific \code{include_vendor_data_table} column selections to reflect the column names including the units). Set to NULL to include no vendor data table.} -\item{include_problems}{whether to include the problems table} +\item{include_problems}{which columns from problems to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes none of the read problems by default. Set to \code{include_problems = everything()} to include all columns.} -\item{with_explicit_units}{whether to include units in the column headers instead of the column data types (see \code{\link{iso_double_with_units}})} +\item{with_explicit_units}{whether to include units in the column headers of the returned data frame instead of the column data types (see \code{\link{iso_double_with_units}}). Note that any \code{select} conditions have to refer to the column names including the full units.} + +\item{include_method_info}{deprecated in favor of the more specific include_standards and include_resistors} + +\item{with_ratios}{deprecated, please use the \code{select} paramter to explicitly include or exclude ratio columns} \item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} } @@ -39,7 +47,7 @@ iso_export_to_excel( returns the iso_files object invisibly for use in pipelines } \description{ -This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file but they are only exported if the corresponding \code{include_} parameter is set to \code{TRUE} and only for data types for which this type of data is available and was read (see \code{\link{iso_read_dual_inlet}}, \code{\link{iso_read_continuous_flow}} for details on read parameters). Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export. +This function exports the passed in iso_files to Excel. The different kinds of data (raw data, file info, methods info, etc.) are exported to separate tabs within the excel file. Use the various \code{include_...} parameters to specifiy what information to include. Note that in rare instances where vectorized data columns exist in the file information (e.g. measurement_info), they are concatenated with ', ' in the excel export. } \seealso{ Other export functions: diff --git a/man/iso_export_to_feather.Rd b/man/iso_export_to_feather.Rd index 29de808c..8e1a24c4 100644 --- a/man/iso_export_to_feather.Rd +++ b/man/iso_export_to_feather.Rd @@ -7,31 +7,34 @@ iso_export_to_feather( iso_files, filepath_prefix, - include_raw_data = TRUE, - include_file_info = TRUE, - include_method_info = TRUE, - include_vendor_data_table = TRUE, - include_problems = TRUE, + include_file_info = everything(), + include_raw_data = everything(), + include_standards = !!enexpr(include_method_info), + include_resistors = !!enquo(include_method_info), + include_vendor_data_table = everything(), + include_problems = everything(), with_explicit_units = FALSE, + include_method_info = everything(), quiet = default(quiet) ) } \arguments{ \item{iso_files}{collection of iso_file objects} -\item{filepath_prefix}{the path (folder and filename) prefix for the exported feather files. The correct suffix for different kinds of data and file extension is automatically added} +\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} -\item{include_raw_data}{whether to include the raw data in the export (if available)} +\item{include_raw_data}{which columns from the raw data to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data.} -\item{include_file_info}{whether to include the file info in the export (if available)} +\item{include_standards}{which columns from the standards info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}. Set to NULL to include no standards info. +#' @param include_resistors which columns from the resistors info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info.} -\item{include_method_info}{whether to include methods infor in the export (if available)} +\item{include_vendor_data_table}{which columns from the vendor data table to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set parameter \code{with_explicit_units = TRUE} to make column units explicit (keep in mind that this will require specific \code{include_vendor_data_table} column selections to reflect the column names including the units). Set to NULL to include no vendor data table.} -\item{include_vendor_data_table}{whether to include the vendor data table in the export (if available)} +\item{include_problems}{which columns from problems to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes none of the read problems by default. Set to \code{include_problems = everything()} to include all columns.} -\item{include_problems}{whether to include the problems table} +\item{with_explicit_units}{whether to include units in the column headers of the returned data frame instead of the column data types (see \code{\link{iso_double_with_units}}). Note that any \code{select} conditions have to refer to the column names including the full units.} -\item{with_explicit_units}{whether to include units in the column headers instead of the column data types (see \code{\link{iso_double_with_units}})} +\item{include_method_info}{deprecated in favor of the more specific include_standards and include_resistors} \item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} } diff --git a/man/iso_get_all_data.Rd b/man/iso_get_all_data.Rd new file mode 100644 index 00000000..8dc8433a --- /dev/null +++ b/man/iso_get_all_data.Rd @@ -0,0 +1,61 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/aggregate_data.R +\name{iso_get_all_data} +\alias{iso_get_all_data} +\title{Aggregate all isofiles data} +\usage{ +iso_get_all_data( + iso_files, + include_file_info = everything(), + include_raw_data = everything(), + include_standards = everything(), + include_resistors = everything(), + include_vendor_data_table = everything(), + include_problems = NULL, + gather = FALSE, + with_explicit_units = with_units, + with_units = FALSE, + with_ratios = NULL, + quiet = default(quiet) +) +} +\arguments{ +\item{iso_files}{collection of iso_file objects} + +\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} + +\item{include_raw_data}{which columns from the raw data to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no raw data.} + +\item{include_standards}{which columns from the standards info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}. Set to NULL to include no standards info. +#' @param include_resistors which columns from the resistors info to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set to NULL to include no resistors info.} + +\item{include_vendor_data_table}{which columns from the vendor data table to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default. Set parameter \code{with_explicit_units = TRUE} to make column units explicit (keep in mind that this will require specific \code{include_vendor_data_table} column selections to reflect the column names including the units). Set to NULL to include no vendor data table.} + +\item{include_problems}{which columns from problems to include. Use \code{c(...)} to select multiple columns, supports all \link[dplyr]{select} syntax including renaming columns. Includes none of the read problems by default. Set to \code{include_problems = everything()} to include all columns.} + +\item{gather}{whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the \code{select} parameter applies to the data columns BEFORE gathering.} + +\item{with_explicit_units}{whether to include units in the column headers of the returned data frame instead of the column data types (see \code{\link{iso_double_with_units}}). Note that any \code{select} conditions have to refer to the column names including the full units.} + +\item{with_units}{this parameter has been DEPRECATED with the introduction of unit-data types (see \code{\link{iso_double_with_units}}) and will be removed in future versions of isoreader. Please use \code{with_explicit_units} instead if you really want columns to have units explicitly in the column name. Alternatively, consider working with the new implicit unit system and convert vendor data tables as needed with \code{\link{iso_make_units_explicit}} and \code{\link{iso_make_units_implicit}}.} + +\item{with_ratios}{deprecated, please use the \code{select} paramter to explicitly include or exclude ratio columns} + +\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} +} +\value{ +data_frame with file_ids, file_types and nested data frames for each data type (file_info, raw_data, vendor_data_table, etc.) +} +\description{ +This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data \code{\link{iso_get_raw_data}}, \code{\link{iso_get_file_info}}, \code{\link{iso_get_vendor_data_table}}, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To \code{\link[tidyr]{unnest}} any of the specific data types (e.g. \code{raw_data}), make sure to filter first for the files that have this data type available (e.g. \code{filter(has_raw_data)}). Exclude specific types of information by setting its \code{include...} parameter to \code{NULL} (Note: for historical reasons, setting it to \code{FALSE} will also include the information). +} +\seealso{ +Other data retrieval functions: +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_standards}()}, +\code{\link{iso_get_vendor_data_table}()} +} +\concept{data retrieval functions} diff --git a/man/iso_get_bgrd_data.Rd b/man/iso_get_bgrd_data.Rd index 8e574b33..a0d6698e 100644 --- a/man/iso_get_bgrd_data.Rd +++ b/man/iso_get_bgrd_data.Rd @@ -15,7 +15,7 @@ iso_get_bgrd_data( \arguments{ \item{iso_files}{collection of iso_file objects} -\item{select}{which raw data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} +\item{select}{which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} \item{gather}{whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the \code{select} parameter applies to the data columns BEFORE gathering.} @@ -28,11 +28,11 @@ Aggregate the background data from the provided iso_files. Can aggregate either } \seealso{ Other data retrieval functions: -\code{\link{iso_get_data}()}, +\code{\link{iso_get_all_data}()}, \code{\link{iso_get_file_info}()}, \code{\link{iso_get_raw_data}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_standards}()}, \code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_data.Rd b/man/iso_get_data.Rd index b456dfeb..00a6ce37 100644 --- a/man/iso_get_data.Rd +++ b/man/iso_get_data.Rd @@ -2,52 +2,10 @@ % Please edit documentation in R/aggregate_data.R \name{iso_get_data} \alias{iso_get_data} -\title{Aggregate all isofiles data} +\title{DEPRECATED} \usage{ -iso_get_data( - iso_files, - include_file_info = everything(), - include_raw_data = everything(), - include_vendor_data_table = everything(), - gather = FALSE, - with_explicit_units = with_units, - with_units = FALSE, - with_ratios = FALSE, - quiet = default(quiet) -) -} -\arguments{ -\item{iso_files}{collection of iso_file objects} - -\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} - -\item{include_raw_data}{which columns from the raw data to include use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default.} - -\item{include_vendor_data_table}{which columns from the vendor data table to include - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns. Includes all columns by default.} - -\item{gather}{whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the \code{select} parameter applies to the data columns BEFORE gathering.} - -\item{with_explicit_units}{whether to include units in the column headers instead of the column data types (see \code{\link{iso_double_with_units}})} - -\item{with_units}{this parameter has been DEPRECATED with the introduction of unit-data types (see \code{\link{iso_double_with_units}}) and will be removed in future versions of isoreader. Please use \code{with_explicit_units} instead if you really want columns to have units explicitly in the column name. Alternatively, consider working with the new implicit unit system and convert vendor data tables as needed with \code{\link{iso_make_units_explicit}} and \code{\link{iso_make_units_implicit}}.} - -\item{with_ratios}{whether to include ratios or just standard delta values} - -\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} -} -\value{ -data_frame with file_ids, file_types and nested data frames for each data type (file_info, raw_data, vendor_data_table, etc.) +iso_get_data(...) } \description{ -This function aggregates all isofiles data and returns it in a large data frame with nested columns for each type of information (file_info, raw_data, etc.). For targeted retrieval of specific data \code{\link{iso_get_raw_data}}, \code{\link{iso_get_file_info}}, \code{\link{iso_get_vendor_data_table}}, etc. are much faster and easier to work with. This function is primarily useful for downstream processing pipelines that want to carry all information along. To \code{\link[tidyr]{unnest}} any of the specific data types (e.g. \code{raw_data}), make sure to filter first for the files that have this data type available (e.g. \code{filter(has_raw_data)}). -} -\seealso{ -Other data retrieval functions: -\code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_file_info}()}, -\code{\link{iso_get_raw_data}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_standards_info}()}, -\code{\link{iso_get_vendor_data_table}()} +Please use \link{iso_get_all_data} instead. } -\concept{data retrieval functions} diff --git a/man/iso_get_file_info.Rd b/man/iso_get_file_info.Rd index c21b772b..71baa899 100644 --- a/man/iso_get_file_info.Rd +++ b/man/iso_get_file_info.Rd @@ -21,11 +21,11 @@ File info entries with multiple values remain nested multi-value (=list) columns } \seealso{ Other data retrieval functions: +\code{\link{iso_get_all_data}()}, \code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_data}()}, \code{\link{iso_get_raw_data}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_standards}()}, \code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_problems.Rd b/man/iso_get_problems.Rd index 666a15f1..94523a52 100644 --- a/man/iso_get_problems.Rd +++ b/man/iso_get_problems.Rd @@ -4,13 +4,15 @@ \alias{iso_get_problems} \title{Retrieve parsing problems} \usage{ -iso_get_problems(iso_files) +iso_get_problems(iso_files, select = everything()) } \arguments{ \item{iso_files}{collection of iso_file objects} + +\item{select}{which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} } \description{ -This is identical to the readr \code{\link[readr]{problems}} function. +This function retrieves parsing problems encountered during the reading of a set of iso files. } \seealso{ Other problem functions: diff --git a/man/iso_get_raw_data.Rd b/man/iso_get_raw_data.Rd index 80e55715..173aed32 100644 --- a/man/iso_get_raw_data.Rd +++ b/man/iso_get_raw_data.Rd @@ -15,7 +15,7 @@ iso_get_raw_data( \arguments{ \item{iso_files}{collection of iso_file objects} -\item{select}{which raw data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} +\item{select}{which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} \item{gather}{whether to gather raw data into long format (e.g. for ease of use in plotting). Not that the \code{select} parameter applies to the data columns BEFORE gathering.} @@ -28,19 +28,19 @@ Aggregate the raw ion data from the provided iso_files. Can aggregate either in } \seealso{ Other data retrieval functions: +\code{\link{iso_get_all_data}()}, \code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_data}()}, \code{\link{iso_get_file_info}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_standards}()}, \code{\link{iso_get_vendor_data_table}()} Other data retrieval functions: +\code{\link{iso_get_all_data}()}, \code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_data}()}, \code{\link{iso_get_file_info}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_standards_info}()}, +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_standards}()}, \code{\link{iso_get_vendor_data_table}()} } \concept{data retrieval functions} diff --git a/man/iso_get_resistors.Rd b/man/iso_get_resistors.Rd new file mode 100644 index 00000000..a4186540 --- /dev/null +++ b/man/iso_get_resistors.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/aggregate_data.R +\name{iso_get_resistors} +\alias{iso_get_resistors} +\title{Aggregate resistors from methods info} +\usage{ +iso_get_resistors( + iso_files, + select = everything(), + include_file_info = NULL, + quiet = default(quiet) +) +} +\arguments{ +\item{iso_files}{collection of iso_file objects} + +\item{select}{which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} + +\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} + +\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} +} +\description{ +Aggregates the resistor information recovered from the provided iso_files. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE} and only linked to specific masses if the iso_files were additionally read with parametr \code{read_raw_data=TRUE}. +} +\seealso{ +Other data retrieval functions: +\code{\link{iso_get_all_data}()}, +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_standards}()}, +\code{\link{iso_get_vendor_data_table}()} +} +\concept{data retrieval functions} diff --git a/man/iso_get_resistors_info.Rd b/man/iso_get_resistors_info.Rd index 2c3e7f5d..35cb61db 100644 --- a/man/iso_get_resistors_info.Rd +++ b/man/iso_get_resistors_info.Rd @@ -2,31 +2,10 @@ % Please edit documentation in R/aggregate_data.R \name{iso_get_resistors_info} \alias{iso_get_resistors_info} -\title{Aggregate resistors from methods info} +\title{DEPRECATED} \usage{ -iso_get_resistors_info( - iso_files, - include_file_info = NULL, - quiet = default(quiet) -) -} -\arguments{ -\item{iso_files}{collection of iso_file objects} - -\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} - -\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} +iso_get_resistors_info(...) } \description{ -Aggregates the resistor information recovered from the provided iso_files. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE} and only linked to specific masses if the iso_files were additionally read with parametr \code{read_raw_data=TRUE}. -} -\seealso{ -Other data retrieval functions: -\code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_data}()}, -\code{\link{iso_get_file_info}()}, -\code{\link{iso_get_raw_data}()}, -\code{\link{iso_get_standards_info}()}, -\code{\link{iso_get_vendor_data_table}()} +Please use \link{iso_get_resistors} instead. } -\concept{data retrieval functions} diff --git a/man/iso_get_standards.Rd b/man/iso_get_standards.Rd new file mode 100644 index 00000000..e2d8f208 --- /dev/null +++ b/man/iso_get_standards.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/aggregate_data.R +\name{iso_get_standards} +\alias{iso_get_standards} +\title{Aggregate standards from methods info} +\usage{ +iso_get_standards( + iso_files, + select = everything(), + include_file_info = NULL, + with_ratios = NULL, + quiet = default(quiet) +) +} +\arguments{ +\item{iso_files}{collection of iso_file objects} + +\item{select}{which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, everything is included (both standards and ratios). To omit the ratios, change to \code{select = file_id:reference}.} + +\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} + +\item{with_ratios}{deprecated, please use the \code{select} paramter to explicitly include or exclude ratio columns} + +\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} +} +\description{ +Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter \code{select} to exclude/include the ratios. All standards info is only available if the iso_files were read with parameter \code{read_method_info=TRUE}. +} +\seealso{ +Other data retrieval functions: +\code{\link{iso_get_all_data}()}, +\code{\link{iso_get_bgrd_data}()}, +\code{\link{iso_get_file_info}()}, +\code{\link{iso_get_raw_data}()}, +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_vendor_data_table}()} +} +\concept{data retrieval functions} diff --git a/man/iso_get_standards_info.Rd b/man/iso_get_standards_info.Rd index 539f2e8c..2bac6591 100644 --- a/man/iso_get_standards_info.Rd +++ b/man/iso_get_standards_info.Rd @@ -2,34 +2,10 @@ % Please edit documentation in R/aggregate_data.R \name{iso_get_standards_info} \alias{iso_get_standards_info} -\title{Aggregate standards from methods info} +\title{DEPRECATED} \usage{ -iso_get_standards_info( - iso_files, - with_ratios = FALSE, - include_file_info = NULL, - quiet = default(quiet) -) -} -\arguments{ -\item{iso_files}{collection of iso_file objects} - -\item{with_ratios}{whether to include ratios or just standard delta values} - -\item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} - -\item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} +iso_get_standards_info(...) } \description{ -Aggregates the isotopic standard information recovered from the provided iso_files. Can aggregate just the standards' delta values or combine the delta values with the recovered ratios (if any). Use paramter \code{with_ratios} to exclude/include the ratios. This information is only available if the iso_files were read with parameter \code{read_method_info=TRUE}. -} -\seealso{ -Other data retrieval functions: -\code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_data}()}, -\code{\link{iso_get_file_info}()}, -\code{\link{iso_get_raw_data}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_vendor_data_table}()} +Please use \link{iso_get_standards} instead. } -\concept{data retrieval functions} diff --git a/man/iso_get_vendor_data_table.Rd b/man/iso_get_vendor_data_table.Rd index 71476790..179c9d63 100644 --- a/man/iso_get_vendor_data_table.Rd +++ b/man/iso_get_vendor_data_table.Rd @@ -18,11 +18,11 @@ iso_get_vendor_data_table( \item{with_units}{this parameter has been DEPRECATED with the introduction of unit-data types (see \code{\link{iso_double_with_units}}) and will be removed in future versions of isoreader. Please use \code{with_explicit_units} instead if you really want columns to have units explicitly in the column name. Alternatively, consider working with the new implicit unit system and convert vendor data tables as needed with \code{\link{iso_make_units_explicit}} and \code{\link{iso_make_units_implicit}}.} -\item{select}{which raw data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} +\item{select}{which data columns to select - use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax. By default, all columns are selected.} \item{include_file_info}{which file information to include (see \code{\link{iso_get_file_info}}). Use \code{c(...)} to select multiple, supports all \link[dplyr]{select} syntax including renaming columns.} -\item{with_explicit_units}{whether to include units in the column headers instead of the column data types (see \code{\link{iso_double_with_units}})} +\item{with_explicit_units}{whether to include units in the column headers of the returned data frame instead of the column data types (see \code{\link{iso_double_with_units}}). Note that any \code{select} conditions have to refer to the column names including the full units.} \item{quiet}{whether to display (quiet=FALSE) or silence (quiet = TRUE) information messages. Set parameter to overwrite global defaults for this function or set global defaults with calls to \link[=iso_info_messages]{iso_turn_info_message_on} and \link[=iso_info_messages]{iso_turn_info_message_off}} } @@ -31,11 +31,11 @@ Aggregate data from the vendor-computed data table. This information is only ava } \seealso{ Other data retrieval functions: +\code{\link{iso_get_all_data}()}, \code{\link{iso_get_bgrd_data}()}, -\code{\link{iso_get_data}()}, \code{\link{iso_get_file_info}()}, \code{\link{iso_get_raw_data}()}, -\code{\link{iso_get_resistors_info}()}, -\code{\link{iso_get_standards_info}()} +\code{\link{iso_get_resistors}()}, +\code{\link{iso_get_standards}()} } \concept{data retrieval functions} From 14d9ab8dec551cae172df224c7caf9f3c27140f4 Mon Sep 17 00:00:00 2001 From: Sebastian Kopf Date: Thu, 13 Feb 2020 01:37:09 -0700 Subject: [PATCH 51/51] remove problems check for scan files issue with unix date times makes this an inconsistent test --- tests/testthat/test-export.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testthat/test-export.R b/tests/testthat/test-export.R index 124a1c10..bd63f0e2 100644 --- a/tests/testthat/test-export.R +++ b/tests/testthat/test-export.R @@ -224,8 +224,6 @@ test_that("test that export to Excel works properly", { dplyr::mutate_if(.predicate = is.numeric, .funs = signif), read_excel(str_c(filepath, ".scan.xlsx"), "resistors") %>% dplyr::mutate_if(.predicate = is.numeric, .funs = signif)) - expect_equal(iso_get_problems(scan_example) %>% select(file_id), - readxl::read_excel(str_c(filepath, ".scan.xlsx"), "problems", col_types = c("text"))) expect_true(file.remove(str_c(filepath, ".scan.xlsx")))