diff --git a/DESCRIPTION b/DESCRIPTION index ce664a2..0486223 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -4,7 +4,7 @@ Title: eyeris Version: 0.0.0.9000 Authors@R: person( - given = c("Shawn", "T."), + given = "Shawn", family = "Schwartz", role = c("aut", "cre"), email = "stschwartz@stanford.edu", diff --git a/Makefile b/Makefile index 3e963e9..4a09a32 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # default target -all: uninstall getdeps build install roxygenize readme clean +all: uninstall getdeps build install roxygenize readme ghpages clean # debugging target debug: uninstall build install clean @@ -49,6 +49,18 @@ readme: Rscript -e "devtools::build_readme()" @echo "[ OK ] - README update completed!\n" +# pkgdown website preview +website: + @echo "[ INFO ] - building eyeris pkgdown docs website preview..." + Rscript -e "pkgdown::build_site()" + @echo "[ OK ] - pkgdown website preview build completed!\n" + +# pkgdown github pages website (jekyll) +ghpages: + @echo "[ INFO ] - building eyeris pkgdown docs website for github pages..." + Rscript -e "pkgdown::build_site_github_pages(clean = TRUE, install = FALSE, new_process = FALSE)" + @echo "[ OK ] - pkgdown website build for github pages completed!\n" + # clean build directory clean: @echo "[ INFO ] - cleaning eyeris package build directory..." diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..ae49a9e --- /dev/null +++ b/NEWS.md @@ -0,0 +1,11 @@ +# eyeris 0.0.0.9000 + +--- +`November 23rd, 2024` + +## Major Changes +- change one +- change two + +## Bug Fixes +- fix one diff --git a/R/bidsify.R b/R/bidsify.R index e59afab..3e1dc99 100644 --- a/R/bidsify.R +++ b/R/bidsify.R @@ -36,7 +36,7 @@ #' # Bleed around blink periods just long enough to remove majority of #' # deflections due to eyelid movements #' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> #' eyeris::detransient() |> diff --git a/R/deblink.R b/R/deblink.R index a8cdd5a..75f3c61 100644 --- a/R/deblink.R +++ b/R/deblink.R @@ -13,18 +13,18 @@ #' indicating different numbers of milliseconds pad forward/backward around each #' missing sample, in the format `c(backward, forward)`. #' -#' @return An `eyeris` object with a new column: `pupil_deblink`. +#' @return An `eyeris` object with a new column: `pupil_raw_{...}_deblink`. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> -#' eyeris::deblink(extend = 40) # 40 ms in both directions -#' } +#' eyeris::deblink(extend = 40) |> # 40 ms in both directions +#' plot(seed = 0) #' -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> -#' eyeris::deblink(extend = c(40, 50)) # 40 ms backward, 50 ms forward +#' eyeris::deblink(extend = c(40, 50)) |> # 40 ms backward, 50 ms forward +#' plot(seed = 0) #' #' @export deblink <- function(eyeris, extend = 40) { diff --git a/R/detransient.R b/R/detransient.R index ed8b2c5..ffeb6f0 100644 --- a/R/detransient.R +++ b/R/detransient.R @@ -11,15 +11,14 @@ #' threshold. #' #' @return An `eyeris` object with a new column in `timeseries`: -#' `pupil_detransient`. +#' `pupil_raw_{...}_detransient`. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> -#' eyeris::detransient() -#' } +#' eyeris::detransient() |> +#' plot(seed = 0) #' #' @export detransient <- function(eyeris, n = 16) { @@ -42,10 +41,14 @@ detransient_pupil <- function(x, prev_op, n) { speed <- function(x, y) { delta <- diff(x) / diff(y) - pupil <- abs(cbind(c(NA, delta), c(delta, NA))) - pupil <- apply(pupil, 1, max, na.rm = TRUE) - pupil <- ifelse(pupil == -Inf, NA, pupil) + pupil <- apply(pupil, 1, function(row) { + if (all(is.na(row))) { + return(NA) # return NA for all-NA rows + } else { + return(max(row, na.rm = TRUE)) # only compute max for valid rows + } + }) return(pupil) } diff --git a/R/detrend.R b/R/detrend.R index cbda42e..02ea51b 100644 --- a/R/detrend.R +++ b/R/detrend.R @@ -6,18 +6,17 @@ #' @param eyeris An object of class `eyeris` dervived from [eyeris::load()]. #' #' @return An `eyeris` object with two new columns in `timeseries`: -#' `detrend_fitted_betas`, and `pupil_detrend`. +#' `detrend_fitted_betas`, and `pupil_raw_{...}_detrend`. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> #' eyeris::detransient() |> #' eyeris::interpolate() |> #' eyeris::lpfilt(plot_freqz = TRUE) |> -#' eyeris::detrend() -#' } +#' eyeris::detrend() |> +#' plot(seed = 0) #' #' @export detrend <- function(eyeris) { diff --git a/R/epoch.R b/R/epoch.R index 9b31284..336b276 100644 --- a/R/epoch.R +++ b/R/epoch.R @@ -80,7 +80,8 @@ #' (`epoch_`). #' #' @examples -#' eye_preproc <- system.file("extdata", "assocret.asc", package = "eyeris") |> +#' \dontrun{ +#' eye_preproc <- system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> #' eyeris::detransient() |> @@ -88,11 +89,89 @@ #' eyeris::lpfilt(plot_freqz = TRUE) |> #' eyeris::zscore() #' +#' # example 1: select 1 second before/after matched event message "PROBE*" #' eye_preproc |> #' eyeris::epoch(events = "PROBE*", limits = c(-1, 1)) #' +#' # example 2: select all samples between each trial #' eye_preproc |> -#' eyeris::epoch(events = "TRIALID {trial}") # all samples between each trial +#' eyeris::epoch(events = "TRIALID {trial}") +#' +#' # example 3: grab the 1 second following probe onset +#' eye_preproc |> +#' eyeris::epoch( +#' events = "PROBE_START_{trial}", +#' limits = c(0, 1) +#' ) +#' +#' # example 4: 2 seconds prior to and 1 second after probe onset +#' eye_preproc |> +#' eyeris::epoch( +#' events = "PROBE_START_{trial}", +#' limits = c(-2, 1), +#' label = "prePostProbe" # custom epoch label name +#' ) +#' +#' # example 5: manual start/end event pairs +#' # note: here, the `msg` column of each data frame is optional +#' eye_prepoc |> +#' eyeris::epoch( +#' events = list( +#' data.frame(time = c(11243355), msg = c("TRIALID 0")), # start events +#' data.frame(time = c(11245956), msg = c("RESPONSE_0")) # end events +#' ) +#' ) +#' +#' # example 6: manual start/end event pairs +#' # note: set `msg` to NA if you only want to pass in start/end timestamps +#' eye_preproc |> +#' eyeris::epoch( +#' events = list( +#' data.frame(time = c(11243355), msg = NA), # start events +#' data.frame(time = c(11245956), msg = NA) # end events +#' ) +#' ) +#' +#' ## examples with baseline arguments enabled +#' +#' # example 7: use mean of 1-s preceding "PROBE_START" (i.e. "DELAY_STOP") +#' # to perform subtractive baselining of the 1-s PROBE epochs. +#' eye_preproc |> +#' eyeris::epoch( +#' events = "PROBE_START_{trial}", +#' limits = c(0, 1), # grab 0 seconds prior to and 1 second post PROBE event +#' label = "prePostProbe", # custom epoch label name +#' calc_baseline = TRUE, +#' apply_baseline = TRUE, +#' baseline_type = "sub", # "sub"tractive baseline calculation is default +#' baseline_events = "DELAY_STOP_*", +#' baseline_period = c(-1, 0) +#' ) +#' +#' # example 8: use mean of time period between set start/end event messages +#' # (i.e. between "DELAY_START" and "DELAY_STOP"). In this case, the +#' # `baseline_period` argument will be ignored since both a "start" and "end" +#' # message string are provided to the `baseline_events` argument. +#' eye_preproc |> +#' eyeris::epoch( +#' events = "PROBE_START_{trial}", +#' limits = c(0, 1), # grab 0 seconds prior to and 1 second post PROBE event +#' label = "prePostProbe", # custom epoch label name +#' calc_baseline = TRUE, +#' apply_baseline = TRUE, +#' baseline_type = "sub", # "sub"tractive baseline calculation is default +#' baseline_events = c("DELAY_START_*", +#' "DELAY_STOP_*") +#' ) +#' +#' # example 9: additional (potentially helpful) example +#' start_events <- data.frame(time = c(11243355, 11247588), +#' msg = c("TRIALID 0", "TRIALID 1")) +#' end_events <- data.frame(time = c(11245956, 11250506), +#' msg = c("RESPONSE_0", "RESPONSE_1")) +#' eye_prepoc |> +#' eyeris::epoch(events = list(start_events, end_events)) +#' } #' #' @export epoch <- function(eyeris, events, limits = NULL, label = NULL, diff --git a/R/glassbox.R b/R/glassbox.R index c6cee8a..599a278 100644 --- a/R/glassbox.R +++ b/R/glassbox.R @@ -55,23 +55,32 @@ #' @param ... Additional arguments to override the default, prescribed settings. #' #' @examples -#' \dontrun{ -#' demo_data <- system.file("extdata", "assocret.asc", package = "eyeris") +#' demo_data <- system.file("extdata", "memory.asc", package = "eyeris") #' #' # (1) examples using the default prescribed parameters and pipeline recipe +#' #' ## (a) run an automated pipeline with no real-time inspection of parameters #' output <- eyeris::glassbox(demo_data) +#' plot( +#' output, +#' steps = c(1, 5), +#' preview_window = c(0, nrow(output$timeseries)), +#' seed = 0 +#' ) #' #' ## (b) run a interactive workflow (with confirmation prompts after each step) -#' output <- eyeris::glassbox(demo_data, confirm = TRUE) +#' # output <- eyeris::glassbox(demo_data, confirm = TRUE, seed = 0) +#' #' #' # (2) examples overriding the default parameters -#' output <- eyeris::glassbox(demo_data, -#' confirm = TRUE, +#' output <- eyeris::glassbox( +#' demo_data, +#' confirm = FALSE, # TRUE if you want to visualize each step in real-time #' deblink = list(extend = 40), #' lpfilt = list(plot_freqz = FALSE) #' ) -#' } +#' +#' plot(output, seed = 0) #' #' @export glassbox <- function(file, confirm = FALSE, detrend_data = FALSE, diff --git a/R/interpolate.R b/R/interpolate.R index d94b0cd..0dd5def 100644 --- a/R/interpolate.R +++ b/R/interpolate.R @@ -8,20 +8,18 @@ #' values on either end, respectively, using the "rule = 2" argument in the #' `approx()` function. #' -#' #' @param eyeris An object of class `eyeris` dervived from [eyeris::load()]. #' #' @return An `eyeris` object with a new column in `timeseries`: -#' `pupil_interpolate`. +#' `pupil_raw_{...}_interpolate`. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> #' eyeris::detransient() |> -#' eyeris::interpolate() -#' } +#' eyeris::interpolate() |> +#' plot(seed = 0) #' #' @export interpolate <- function(eyeris) { @@ -38,8 +36,10 @@ interpolate_pupil <- function(x, prev_op) { prev_pupil <- x[[prev_op]] } - interp_pupil <- zoo::na.approx(prev_pupil, - na.rm = FALSE, maxgap = Inf, + interp_pupil <- zoo::na.approx( + prev_pupil, + na.rm = FALSE, + maxgap = Inf, rule = 2 ) diff --git a/R/load.R b/R/load.R index fa4a966..f9dfd8d 100644 --- a/R/load.R +++ b/R/load.R @@ -21,10 +21,8 @@ #' @seealso [eyelinker::read.asc()] which this function wraps. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() -#' } #' #' @export load_asc <- function(file) { diff --git a/R/lpfilt.R b/R/lpfilt.R index c570608..6b84e94 100644 --- a/R/lpfilt.R +++ b/R/lpfilt.R @@ -11,17 +11,17 @@ #' @param rs Required minimal attenuation within stopband in dB. #' @param plot_freqz Boolean flag for displaying filter frequency response. #' -#' @return An `eyeris` object with a new column in `timeseries`: `pupil_lpfilt`. +#' @return An `eyeris` object with a new column in `timeseries`: +#' `pupil_raw_{...}_lpfilt`. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> #' eyeris::detransient() |> #' eyeris::interpolate() |> -#' eyeris::lpfilt(plot_freqz = TRUE) -#' } +#' eyeris::lpfilt(plot_freqz = TRUE) |> +#' plot(seed = 0) #' #' @export lpfilt <- function(eyeris, wp = 4, ws = 8, diff --git a/R/plot.R b/R/plot.R index a7747a6..216c83d 100644 --- a/R/plot.R +++ b/R/plot.R @@ -37,11 +37,11 @@ #' #' @examples #' \dontrun{ -#' # using the default 10000 to 20000 ms time subset -#' plot(eyeris_data) +#' # example 1: using the default 10000 to 20000 ms time subset +#' plot(your_eyeris_data_output_here) #' -#' # using a custom time subset (i.e., 1 to 500 ms) -#' plot(eyeris_data, preview_window = c(1, 500)) +#' # example 2: using a custom time subset (i.e., 1 to 500 ms) +#' plot(your_eyeris_data_output_here, preview_window = c(1, 500)) #' } #' #' @rdname plot.eyeris diff --git a/R/sticker.data.R b/R/sticker.data.R index 1d89b13..454dfbc 100644 --- a/R/sticker.data.R +++ b/R/sticker.data.R @@ -2,5 +2,5 @@ #' #' @format A png image file that is rendered into the README and html reports. #' -#' @keywords eyeris, sticker, logo, hex +#' @keywords internal "sticker" diff --git a/R/zscore.R b/R/zscore.R index 7c25894..866e001 100644 --- a/R/zscore.R +++ b/R/zscore.R @@ -1,13 +1,13 @@ -#' Z-Score pupil timeseries data +#' z-score pupil timeseries data #' #' The intended use of this method is to scale the arbitrary units of the pupil #' size timeseries to have a mean of `0` and a standard deviation of `1`. This #' is accomplished by mean centering the data points and then dividing them by #' their standard deviation (i.e., z-scoring the data, similar to -#' [base::scale()]). Z-scoring the pupil data is helpful for trial-level and -#' between-subjects analyses where arbitrary units of pupil size recorded by the -#' tracker do not scale across participants, and therefore make analyses that -#' depend on data from more than one participant difficult to interpret. +#' [base::scale()]). Opting to z-score your pupil data helps with trial-level +#' and between-subjects analyses where arbitrary units of pupil size recorded by +#' the tracker do not scale across participants, and therefore make analyses +#' that depend on data from more than one participant difficult to interpret. #' #' @details #' In general, it is common to z-score pupil data within any given @@ -34,22 +34,20 @@ #' on the entire pupil timeseries (before epoching the data), and then split and #' take the mean of the z-scored timeseries as a function of condition variable. #' -#' #' @param eyeris An object of class `eyeris` dervived from [eyeris::load()]. #' -#' @return A numeric vector giving number of characters (code points) in each -#' element of the character vector. Missing string have missing length. +#' @return An `eyeris` object with a new column in `timeseries`: +#' `pupil_raw_{...}_z`. #' #' @examples -#' \dontrun{ -#' system.file("extdata", "assocret.asc", package = "eyeris") |> +#' system.file("extdata", "memory.asc", package = "eyeris") |> #' eyeris::load_asc() |> #' eyeris::deblink(extend = 50) |> #' eyeris::detransient() |> #' eyeris::interpolate() |> #' eyeris::lpfilt(plot_freqz = TRUE) |> -#' eyeris::zscore() -#' } +#' eyeris::zscore() |> +#' plot(seed = 0) #' #' @export zscore <- function(eyeris) { diff --git a/README.Rmd b/README.Rmd index 42a06ab..a6327ee 100644 --- a/README.Rmd +++ b/README.Rmd @@ -9,17 +9,16 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", - out.width = "100%", - fig.cap = "README FIGURE" + out.width = "100%" ) ``` -# `eyeris`: create and run flexible and reproducible pupillometry preprocessing pipelines in R +# `eyeris`: flexible reproducible pupil preprocessing pipelines in R - - + + @@ -55,29 +54,32 @@ to flexibly construct and compare different pipelines. We hope you enjoy! -shawn ```{r glassbox-example, fig.dpi=300} -set.seed(1) +set.seed(32) library(eyeris) -demo_data <- system.file("extdata", "assocret.asc", package = "eyeris") +demo_data <- system.file("extdata", "memory.asc", package = "eyeris") -eyeris_preproc <- glassbox(demo_data, detrend_data = F, lpfilt = list(plot_freqz = T)) +eyeris_preproc <- glassbox( + demo_data, + detrend_data = F, + lpfilt = list(plot_freqz = T) +) ``` ### step-wise correction of pupillary signal - ```{r glassbox-plot, echo=TRUE, fig.width=12, fig.height=5, fig.dpi=300} plot(eyeris_preproc) ``` -### final pre-post correction of pupillary signal (raw -> preprocessed) - +### final pre-post correction of pupillary signal (raw ➡ preprocessed) ```{r timeseries-plot, echo=TRUE, fig.width=8, fig.height=5, fig.dpi=300} plot(eyeris_preproc, steps = c(1, 5), preview_window = c(0, nrow(eyeris_preproc$timeseries))) ``` -# Comments, suggestions, questions, issues +--- +# Comments, suggestions, questions, issues? Please use the issues tab (https://github.com/shawntz/eyeris/issues) to make note of any bugs, comments, suggestions, feedback, etc... all are welcomed and appreciated, thanks! diff --git a/README.md b/README.md index 27df0cb..7443e8b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# `eyeris`: create and run flexible and reproducible pupillometry preprocessing pipelines in R +# `eyeris`: flexible reproducible pupil preprocessing pipelines in R - - + + @@ -45,32 +45,27 @@ compare different pipelines. We hope you enjoy! -shawn ``` r -set.seed(1) +set.seed(32) library(eyeris) -demo_data <- system.file("extdata", "assocret.asc", package = "eyeris") +demo_data <- system.file("extdata", "memory.asc", package = "eyeris") -eyeris_preproc <- glassbox(demo_data, detrend_data = F, lpfilt = list(plot_freqz = T)) +eyeris_preproc <- glassbox( + demo_data, + detrend_data = F, + lpfilt = list(plot_freqz = T) +) #> ✔ [ OK ] - Running eyeris::load_asc() #> ✔ [ OK ] - Running eyeris::deblink() #> ✔ [ OK ] - Running eyeris::detransient() #> ✔ [ OK ] - Running eyeris::interpolate() -#> ! [ INFO ] - No NAs detected in pupil data for interpolation... Skipping! #> ✔ [ OK ] - Running eyeris::lpfilt() #> ✔ [ OK ] - Skipping eyeris::detrend() #> ✔ [ OK ] - Running eyeris::zscore() ``` -