diff --git a/.Rbuildignore b/.Rbuildignore index 48fa4c9..5a5d0df 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -10,3 +10,4 @@ ^LICENSE\.md$ ^\.Rproj\.user$ ^docs$ +^derivatives$ diff --git a/.gitignore b/.gitignore index 94ece8e..f4b0c97 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ po/*~ rsconnect/ inst/doc #docs + +derivatives diff --git a/NAMESPACE b/NAMESPACE index e28ff03..09e5551 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,6 +15,7 @@ importFrom(dplyr,across) importFrom(dplyr,all_of) importFrom(grDevices,dev.off) importFrom(grDevices,jpeg) +importFrom(grDevices,png) importFrom(grDevices,rainbow) importFrom(graphics,legend) importFrom(graphics,lines) diff --git a/R/bidsify.R b/R/bidsify.R index 8afeb3c..7fee484 100644 --- a/R/bidsify.R +++ b/R/bidsify.R @@ -31,6 +31,12 @@ #' must already be installed. Defaults to FALSE. #' @param report_seed Random seed for the plots that will appear in the report. #' Defaults to 0. See [eyeris::plot()] for a more detailed description. +#' @param report_epoch_grouping_var_col String name of grouping column to use +#' for epoch-by-epoch diagnostic plots in an interactive rendered HTML report. +#' Column name must exist (i.e., be a custom grouping variable name set within +#' the metadata template of your `epoch()` call). Defaults to `"matched_event"`, +#' which all epoched dataframes have as a valid column name. To disable these +#' epoch-level diagnostic plots, set to `NULL`. #' #' @examples #' # Bleed around blink periods just long enough to remove majority of @@ -62,7 +68,8 @@ bidsify <- function(eyeris, save_all = TRUE, epochs_list = NULL, merge_epochs = FALSE, bids_dir = NULL, participant_id = NULL, session_num = NULL, task_name = NULL, run_num = NULL, save_raw = TRUE, - html_report = TRUE, pdf_report = FALSE, report_seed = 0) { + html_report = TRUE, pdf_report = FALSE, report_seed = 0, + report_epoch_grouping_var_col = "matched_event") { sub <- participant_id ses <- session_num task <- task_name @@ -221,6 +228,7 @@ bidsify <- function(eyeris, save_all = TRUE, epochs_list = NULL, for (i in seq_along(pupil_steps)) { fig_paths[i] <- file.path(figs_out, paste0("fig", i, ".jpg")) + jpeg(file.path(fig_paths[i]), width = 12, height = 7, units = "in", res = 300, pointsize = 14 @@ -237,9 +245,7 @@ bidsify <- function(eyeris, save_all = TRUE, epochs_list = NULL, jpeg(file.path(fig_paths[length(fig_paths)]), width = 12, height = 7, units = "in", res = 300, pointsize = 18 ) - plot(eyeris, steps = 1, preview_window = c(0, nrow(eyeris$timeseries))) - dev.off() fig_paths <- c( @@ -250,15 +256,84 @@ bidsify <- function(eyeris, save_all = TRUE, epochs_list = NULL, jpeg(file.path(fig_paths[length(fig_paths)]), width = 12, height = 7, units = "in", res = 300, pointsize = 18 ) - plot(eyeris, steps = length(pupil_steps), preview_window = c(0, nrow(eyeris$timeseries)) ) - dev.off() - report_output <- make_report(eyeris, report_path, fig_paths, + if (!is.null(report_epoch_grouping_var_col)) { + for (i in seq_along(epochs_to_save)) { + tryCatch( + { + check_column(epochs_to_save[[i]], report_epoch_grouping_var_col) + }, + error = function(e) { + error_handler(e, "column_doesnt_exist_in_df_error") + } + ) + + epochs_out <- file.path(figs_out, names(epochs_to_save)[i]) + check_and_create_dir(epochs_out) + + epoch_groups <- as.vector( + unique(epochs_to_save[[i]][report_epoch_grouping_var_col])[[1]] + ) + + for (group in epoch_groups) { + group_df <- epochs_to_save[[i]] + group_df <- group_df[ + group_df[[report_epoch_grouping_var_col]] == group, + ] + + for (pstep in seq_along(pupil_steps)) { + if (grepl("z", pupil_steps[pstep])) { + y_units <- "(z)" + } else { + y_units <- "(a.u.)" + } + + colors <- c("black", rainbow(length(pupil_steps) - 1)) + + y_label <- paste("pupil size", y_units) + + file_out <- file.path(epochs_out, paste0(group, "_", pstep, ".png")) + + png(file_out, + width = 3.25, + height = 2.5, + units = "in", + res = 600, + pointsize = 6 + ) + plot(group_df$timebin, group_df[[pupil_steps[pstep]]], + type = "l", xlab = "time (s)", ylab = y_label, + col = colors[pstep], + main = paste0(group, "\n", pupil_steps[pstep]) + ) + dev.off() + } + } + + epochs <- list.files(epochs_out, + full.names = FALSE, + pattern = "\\.(jpg|jpeg|png|gif)$", + ignore.case = TRUE + ) + + epochs <- file.path("source", "figures", + names(epochs_to_save)[i], epochs) + + make_gallery(eyeris, epochs, report_path, names(epochs_to_save)[i], + sub = sub, ses = ses, task = task, run = run + ) + } + } + + report_output <- make_report( + eyeris, + report_path, + fig_paths, sub = sub, ses = ses, task = task, run = run ) diff --git a/R/checks.R b/R/checks.R index 099b68e..288b865 100644 --- a/R/checks.R +++ b/R/checks.R @@ -72,6 +72,14 @@ check_baseline_inputs <- function(events, limits) { } } +check_column <- function(df, col_name) { + if (!col_name %in% colnames(df)) { + err_c <- "column_doesnt_exist_in_df_error" + err_m <- paste0("No grouping variable '", col_name, "' in the epoched df.") + stop(structure(list(message = err_m, call = match.call()), class = err_c)) + } +} + check_data <- function(eyeris, fun) { err_m <- sprintf(paste( "The provided object to `eyeris::%s()` is of type", diff --git a/R/eyeris-package.R b/R/eyeris-package.R index 2b9eec8..42cbcdc 100644 --- a/R/eyeris-package.R +++ b/R/eyeris-package.R @@ -10,6 +10,7 @@ #' @importFrom graphics text #' @importFrom grDevices dev.off #' @importFrom grDevices jpeg +#' @importFrom grDevices png #' @importFrom grDevices rainbow #' @importFrom rlang := #' @importFrom stats lm diff --git a/R/gallery.R b/R/gallery.R new file mode 100644 index 0000000..e751058 --- /dev/null +++ b/R/gallery.R @@ -0,0 +1,84 @@ +make_gallery <- function(eyeris, epochs, out, epoch_name, ...) { + params <- list(...) + + epoch_name_corrected <- sub("^epoch_", "epoch-", epoch_name) + + rmd_f <- file.path(out, paste0( + "sub-", params$sub, "_", + "run-", params$run, "_", + epoch_name_corrected, ".Rmd" + )) + + report_date <- format(Sys.time(), "%B %d, %Y | %H:%M:%OS3") + package_version <- as.character( + utils::packageVersion("eyeris") + ) + + css <- system.file( + file.path("rmarkdown", "css", "report.css"), + package = "eyeris" + ) + + sticker_path <- system.file("figures", "sticker.png", package = "eyeris") + + epoch_lightbox_html <- print_lightbox_img_html(epochs) + + content <- paste0( + "---\n", + "title: '`eyeris` epoch previewer'\n", + "date: '", report_date, "'\n", + "output:\n", + " html_document:\n", + " df_print: paged\n", + " css: '", css, "'\n", + "---\n\n", + "\n\n<img src='", sticker_path, "' class='top-right-image'>", + "\n\n---\n\n## Summary\n", + " - Subject ID: ", params$sub, "\n", + " - Session: ", params$ses, "\n", + " - Task: ", params$task, "\n", + " - Run: ", params$run, "\n", + " - BIDS Directory: ", out, "\n", + " - Source `.asc` file: ", eyeris$file, "\n", + " - [`eyeris` version](https://github.com/shawntz/eyeris): ", + package_version, "\n", + "\n\n<style type='text/css'>\n", + "@import url('http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/", + "bootstrap.min.css');\n", + "@import url('https://cdn.jsdelivr.net/npm/lightbox2/dist/css/", + "lightbox.min.css');\n</style>\n", + "<script src='https://cdn.jsdelivr.net/npm/lightbox2/dist/js/", + "lightbox.min.js'></script>\n<script>document.addEventListener(", + "'DOMContentLoaded', function() {lightbox.option({'imageFadeDuration' : 0,", + "'resizeDuration': 25,'wrapAround': false});});</script>\n\n\n", + "\n# Preprocessed Data Preview\n\n", + "\n## ", epoch_name, "\n\n", + epoch_lightbox_html, + "\n", + "\n\n---\n\n### Citation\n\n", + "```{r citation, echo=FALSE, comment=NA}\n", + "citation('eyeris')\n", + "```\n\n" + ) + + writeLines(content, con = rmd_f) + + rmarkdown::render(rmd_f, output_format = "html_document") + + unlink(rmd_f) +} + +print_lightbox_img_html <- function(images) { + html_out <- "" + + for (i in images) { + html_out <- paste0( + html_out, + '<a href="', i, '" data-lightbox="gallery" data-title="Image 1">', + '<img src="', i, + '" alt="Thumbnail 1" style="margin: 5px; width: 150px;"></a>' + ) + } + + return(html_out) +} diff --git a/R/report.R b/R/report.R index fb27b73..f779408 100644 --- a/R/report.R +++ b/R/report.R @@ -37,9 +37,15 @@ make_report <- function(eyeris, out, plots, ...) { " - Run: ", params$run, "\n", " - BIDS Directory: ", out, "\n", " - Source `.asc` file: ", eyeris$file, "\n", - " - [`eyeris` version](https://github.com/shawntschwartz/eyeris): ", + " - [`eyeris` version](https://github.com/shawntz/eyeris): ", package_version, "\n", + "\n\n<style type='text/css'>\n", + "@import url('http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/", + "bootstrap.min.css');\n", + "@import url('https://cdn.jsdelivr.net/npm/lightbox2/dist/css/", + "lightbox.min.css');\n</style>\n", + "\n## Preprocessed Data Preview\n\n", print_plots(plots), "\n", diff --git a/man/bidsify.Rd b/man/bidsify.Rd index ef853fa..02b29c0 100644 --- a/man/bidsify.Rd +++ b/man/bidsify.Rd @@ -17,7 +17,8 @@ bidsify( save_raw = TRUE, html_report = TRUE, pdf_report = FALSE, - report_seed = 0 + report_seed = 0, + report_epoch_grouping_var_col = "matched_event" ) } \arguments{ @@ -53,6 +54,13 @@ must already be installed. Defaults to FALSE.} \item{report_seed}{Random seed for the plots that will appear in the report. Defaults to 0. See \code{\link[=plot]{plot()}} for a more detailed description.} + +\item{report_epoch_grouping_var_col}{String name of grouping column to use +for epoch-by-epoch diagnostic plots in an interactive rendered HTML report. +Column name must exist (i.e., be a custom grouping variable name set within +the metadata template of your \code{epoch()} call). Defaults to \code{"matched_event"}, +which all epoched dataframes have as a valid column name. To disable these +epoch-level diagnostic plots, set to \code{NULL}.} } \description{ This method provides a structured way to save out pupil data in a BIDS-like