├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ └── R-CMD-check.yaml ├── .gitignore ├── .travis.yml ├── CRAN-SUBMISSION ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── extract.R ├── filter.R ├── frames.R ├── load.R ├── patterns.R ├── preview.R ├── rprime-package.R ├── to_data_frame.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── docs ├── LICENSE-text.html ├── articles │ ├── index.html │ ├── multiple-files.html │ ├── parsing-summary.html │ └── quick-start-demo.html ├── authors.html ├── docsearch.css ├── docsearch.js ├── index.html ├── link.svg ├── pkgdown.css ├── pkgdown.js ├── pkgdown.yml └── reference │ ├── EprimeFrame.html │ ├── FrameList.html │ ├── as.EprimeFrame.html │ ├── as.FrameList.html │ ├── extract_chunks.html │ ├── filter_in.html │ ├── index.html │ ├── keep_levels.html │ ├── list_functions.html │ ├── listify.html │ ├── preview_eprime.html │ ├── read_eprime.html │ ├── rprime.html │ └── to_data_frame.html ├── man ├── EprimeFrame.Rd ├── FrameList.Rd ├── as.EprimeFrame.Rd ├── as.FrameList.Rd ├── extract_chunks.Rd ├── filter_in.Rd ├── keep_levels.Rd ├── list_functions.Rd ├── listify.Rd ├── preview_eprime.Rd ├── read_eprime.Rd ├── rprime.Rd └── to_data_frame.Rd ├── rprime.Rproj ├── tests ├── test-all.R └── testthat │ ├── data │ ├── Blending_001L00XS4.txt │ ├── Coartic_Block1_001P00XS1.txt │ ├── MINP_001L00XS1.txt │ ├── MP_Block1_001P00XA1.txt │ ├── NonWordRep_001X00XS1.txt │ └── not_an_eprime_file.txt │ ├── test-eprime-frame.R │ ├── test-filter.R │ ├── test-load.R │ └── test-print.R └── vignettes ├── data ├── Blending_001L00XS4.txt ├── MINP_001L00XS1.txt ├── MP_Block1_001P00XA1.txt ├── SAILS │ ├── SAILS_001X00XS1.txt │ └── SAILS_002X00XS1.txt └── not_an_eprime_file.txt ├── multiple-files.Rmd ├── parsing-summary.Rmd └── quick-start-demo.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.travis\.yml$ 4 | ^README\.Rmd$ 5 | ^README\.md$ 6 | ^README-.*\.png$ 7 | ^cran-comments\.md$ 8 | tests/testthat/data/NonWordRep_001X00XS1.txt 9 | ^deploy-pages.sh$ 10 | ^_pkgdown\.yml$ 11 | ^docs$ 12 | ^pkgdown$ 13 | ^codecov\.yml$ 14 | ^LICENSE$ 15 | ^CRAN-RELEASE$ 16 | ^doc$ 17 | ^Meta$ 18 | ^\.github$ 19 | ^CRAN-SUBMISSION$ 20 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | 8 | name: R-CMD-check.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | R-CMD-check: 14 | runs-on: ${{ matrix.config.os }} 15 | 16 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | - {os: macos-latest, r: 'release'} 23 | - {os: windows-latest, r: 'release'} 24 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 25 | - {os: ubuntu-latest, r: 'release'} 26 | - {os: ubuntu-latest, r: 'oldrel-1'} 27 | 28 | env: 29 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 30 | R_KEEP_PKG_SOURCE: yes 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - uses: r-lib/actions/setup-pandoc@v2 36 | 37 | - uses: r-lib/actions/setup-r@v2 38 | with: 39 | r-version: ${{ matrix.config.r }} 40 | http-user-agent: ${{ matrix.config.http-user-agent }} 41 | use-public-rspm: true 42 | 43 | - uses: r-lib/actions/setup-r-dependencies@v2 44 | with: 45 | extra-packages: any::rcmdcheck 46 | needs: check 47 | 48 | - uses: r-lib/actions/check-r-package@v2 49 | with: 50 | upload-snapshots: true 51 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | 4 | # Example code in package build process 5 | *-Ex.R 6 | 7 | # R data files from past sessions 8 | .Rdata 9 | .Rproj.user 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | cache: packages 5 | 6 | after_success: 7 | - Rscript -e 'covr::coveralls()' 8 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 0.1.3 2 | Date: 2025-04-25 19:53:57 UTC 3 | SHA: 158886a9fe0b7866e876aeb9da3a04b05cf25f3c 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rprime 2 | Title: Functions for Working with 'Eprime' Text Files 3 | Version: 0.1.3 4 | Authors@R: 5 | person(given = "Tristan", 6 | family = "Mahr", 7 | role = c("aut", "cre"), 8 | email = "tristan.mahr@wisc.edu", 9 | comment = c(ORCID = "0000-0002-8890-5116")) 10 | Description: 'Eprime' is a set of programs for administering 11 | psychological experiments by computer. This package provides functions 12 | for loading, parsing, filtering and exporting data in the text files 13 | produced by 'Eprime' experiments. 14 | License: GPL-2 15 | URL: https://github.com/tjmahr/rprime 16 | BugReports: https://github.com/tjmahr/rprime/issues 17 | Depends: 18 | R (>= 3.0.1) 19 | Imports: 20 | assertthat, 21 | plyr, 22 | stringi, 23 | stringr (>= 1.0.0), 24 | tools, 25 | utils 26 | Suggests: 27 | knitr, 28 | readr, 29 | rmarkdown, 30 | testthat 31 | VignetteBuilder: 32 | knitr 33 | Encoding: UTF-8 34 | RoxygenNote: 7.3.2 35 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(EprimeFrame,EprimeChunk) 4 | S3method(EprimeFrame,character) 5 | S3method(FrameList,character) 6 | S3method(FrameList,list) 7 | S3method(print,EprimeFrame) 8 | S3method(print,FrameList) 9 | S3method(to_data_frame,EprimeFrame) 10 | S3method(to_data_frame,FrameList) 11 | S3method(to_data_frame,default) 12 | export(EprimeFrame) 13 | export(FrameList) 14 | export(as.EprimeFrame) 15 | export(as.FrameList) 16 | export(drop_levels) 17 | export(extract_chunks) 18 | export(filter_in) 19 | export(filter_out) 20 | export(keep_levels) 21 | export(listify) 22 | export(preview_eprime) 23 | export(preview_frames) 24 | export(preview_levels) 25 | export(read_eprime) 26 | export(to_data_frame) 27 | import(assertthat) 28 | import(plyr) 29 | import(stringr) 30 | importFrom(utils,head) 31 | importFrom(utils,str) 32 | importFrom(utils,tail) 33 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # rprime 0.1.3 2 | 3 | * Fix link in a documentation. 4 | 5 | # rprime 0.1.2 6 | 7 | * Fixed package for compatibility with stringi 1.5.3. 8 | * Added a `NEWS.md` file to track changes to the package. 9 | -------------------------------------------------------------------------------- /R/extract.R: -------------------------------------------------------------------------------- 1 | 2 | #' Extract log-frames from an Eprime log file 3 | #' 4 | #' Almost all of the information in an Eprime file comes in chunks of text 5 | #' bracketed by the lines \code{*** LogFrame Start ***} and \code{*** LogFrame 6 | #' End ***}. The exception is the header information which is bracketed by 7 | #' \code{*** Header Start ***} and \code{*** Header End ***}. 8 | #' 9 | #' \code{extract_chunks} extracts the bracketed text, storing each log-frame of 10 | #' text in a list. The lists also include some additional lines of text as 11 | #' metadata: \code{Eprime.FrameNumber} and \code{Eprime.Basename} (the name of 12 | #' the source file). The header log-frame also gets dummy lines: 13 | #' \code{Procedure: Header} and \code{Running: Header}. 14 | #' 15 | #' These character vectors of colon-separated lines are converted into proper 16 | #' lists by \code{FrameList(...)}. 17 | #' 18 | #' @param eprime_log a character vector containing the lines of text from Eprime 19 | #' txt file 20 | #' @return a list of character vectors, where each vector contains the lines of 21 | #' a log-frame 22 | #' @export 23 | extract_chunks <- function(eprime_log) { 24 | parsed <- parse_chunks(eprime_log) 25 | basename <- ifelse(!has_attr(eprime_log, "basename"), NA, 26 | attr(eprime_log, "basename")) 27 | parsed <- parsed %>% 28 | update_header %>% 29 | insert_frame_numbers %>% 30 | insert_basename(basename) 31 | lapply(parsed, as.EprimeChunk) 32 | } 33 | 34 | as.EprimeChunk <- function(x) { 35 | class(x) <- c("EprimeChunk", class(x)) 36 | x 37 | } 38 | 39 | # Add "Running", "Procedure" lines to the header log-frame 40 | update_header <- function(chunked) { 41 | if (has_header(chunked)) { 42 | header_position <- Position(is_header, chunked) 43 | header <- chunked[[header_position]] 44 | row_run <- new_line("Running", "Header") 45 | row_prc <- new_line("Procedure", "Header") 46 | header <- insert_line(header, c(row_run, row_prc)) 47 | chunked[[header_position]] <- header 48 | } 49 | chunked 50 | } 51 | 52 | # Add "Eprime.FrameNumber" lines to every frame 53 | insert_frame_numbers <- function(chunked) { 54 | rows <- new_line(rprime_cols$frame, seq_along(chunked)) 55 | Map(insert_line, chunked, rows) 56 | } 57 | 58 | # Add "Eprime.Basename" lines to every log-frame 59 | insert_basename <- function(chunked, basename) { 60 | rows <- new_line(rprime_cols$basename, basename) 61 | Map(insert_line, chunked, rows) 62 | } 63 | 64 | # Insert a line in the second-to-last position in a log-frame 65 | insert_line <- function(xs, ys) { 66 | c(but_last(xs), ys, last(xs)) 67 | } 68 | 69 | #' Extract the lines of text from each log-frame 70 | #' 71 | #' @param eprime_log a character vector of lines from an Eprime log file 72 | #' @return a list of character vectors, where each vector contains the lines of 73 | #' a log-frame 74 | #' @noRd 75 | parse_chunks <- function(eprime_log) { 76 | # Find all the texts between log-frame boundaries 77 | starts <- str_which(eprime_log, patterns$bracket_start) 78 | ends <- str_which(eprime_log, patterns$bracket_end) 79 | ranges <- make_ranges(starts, ends, eprime_log) 80 | 81 | pull_lines <- function(lines) eprime_log[lines] 82 | frames <- lapply(ranges, pull_lines) 83 | frames 84 | } 85 | 86 | 87 | #' Check Eprime log-frame line ranges 88 | #' 89 | #' @param starts the line numbers of the log-frame start lines 90 | #' @param ends the line numbers of the log-frame end lines 91 | #' @param eprime_log a character vector of lines from an Eprime log file 92 | #' @return a list of sequences. Each list contains the line numbers contained by 93 | #' an Eprime log-frame. If there is a log frame without an end-line, that 94 | #' partial frame is excluded and its contents are previed in a warning 95 | #' message. 96 | #' @noRd 97 | make_ranges <- function(starts, ends, eprime_log) { 98 | # There should be the same number of starts and ends 99 | min_chunks <- min(length(starts), length(ends)) 100 | old_starts <- starts 101 | starts <- starts[seq_len(min_chunks)] 102 | ends <- ends[seq_len(min_chunks)] 103 | 104 | # Warn if there is an incomplete frame (more old_starts than ends) 105 | bad_lines <- setdiff(old_starts, starts) 106 | if (!length_zero(bad_lines)) { 107 | last_bad_line <- max(bad_lines) 108 | 109 | # Give a preview of the incomplete chunk in the warning 110 | last_preview_line <- min(last_bad_line + 10, length(eprime_log)) 111 | sample_range <- seq(last_bad_line, last_preview_line) 112 | warning_header <- paste0("Incomplete Log Frame found on line ", bad_lines) 113 | lines <- paste0(c(warning_header, eprime_log[sample_range]), collapse = "\n") 114 | warning(lines) 115 | } 116 | 117 | # Each start should come before its corresponding end 118 | well_ordered_pairs <- starts < ends 119 | starts <- starts[well_ordered_pairs] 120 | ends <- ends[well_ordered_pairs] 121 | 122 | Map(seq, starts, ends) 123 | } 124 | 125 | -------------------------------------------------------------------------------- /R/filter.R: -------------------------------------------------------------------------------- 1 | # Make a filtering predicate 2 | make_filter <- function(key, values) { 3 | assert_that(length(key) == 1) 4 | function(frame_list) { 5 | # Convert NULLs to NA 6 | plucked <- pluck_apply(key, frame_list) 7 | plucked[sapply(plucked, is.null)] <- NA 8 | is.element(unlist(plucked), values) 9 | } 10 | } 11 | 12 | 13 | 14 | #' Filter levels in or out of a FrameList based on attribute values 15 | #' 16 | #' @param frame_list a list of \code{EprimeFrame} objects 17 | #' @param key the name of the attribute to filter in or out 18 | #' @param values the whitelisted or blacklisted values of the attribute 19 | #' @return for \code{filter_in}, only log-frames where \code{key} is one of the 20 | #' \code{values} are kept. for \code{filter_out}, log-frames where \code{key} 21 | #' is one of the \code{values} are omitted. 22 | #' @export 23 | filter_in <- function(frame_list, key, values) { 24 | has_key_value <- make_filter(key, values) 25 | as.FrameList(frame_list[has_key_value(frame_list)]) 26 | } 27 | 28 | #' @rdname filter_in 29 | #' @export 30 | filter_out <- function(frame_list, key, values) { 31 | lacks_key_value <- Negate(make_filter(key, values)) 32 | as.FrameList(frame_list[lacks_key_value(frame_list)]) 33 | } 34 | 35 | #' Filter levels in or out of a FrameList based on Eprime.Level values 36 | #' 37 | #' These functions are shortcuts for calls to \code{filter_in} or 38 | #' \code{filter_out}. 39 | #' 40 | #' Note that the meaning of Eprime.Level value in a log-frame ultimately is 41 | #' equal to one plus the number of tabs before each line in the log-frame. 42 | #' 43 | #' @inheritParams filter_in 44 | #' @param level_numbers the whitelisted or blacklisted values for Eprime.Level 45 | #' @return for \code{keep_levels}, only log-frames where the level matches one 46 | #' of the \code{level_numbers} are kept. for \code{keep_levels}, log-frames 47 | #' where the level matches one of the \code{level_numbers} are omitted. 48 | #' @export 49 | keep_levels <- function(frame_list, level_numbers) { 50 | filter_in(frame_list, rprime_cols$level, as.character(level_numbers)) 51 | } 52 | 53 | #' @rdname keep_levels 54 | #' @export 55 | drop_levels <- function(frame_list, level_numbers) { 56 | filter_out(frame_list, rprime_cols$level, as.character(level_numbers)) 57 | } 58 | -------------------------------------------------------------------------------- /R/frames.R: -------------------------------------------------------------------------------- 1 | #' Convert lines from an Eprime file into EprimeFrame objects 2 | #' 3 | #' Convert character vectors of implicit key-value pairs (e.g., \code{c("key1: 4 | #' value1", "key2: value2")}), into lists of explicit key-value pairs, 5 | #' \code{list(key1 = "value1", key2 = "value2")}. 6 | #' 7 | #' @details During the conversion, if \code{Running: x}, then the 8 | #' \code{x.Sample} and \code{x.Cycle} lines are simplified into \code{Sample} 9 | #' and \code{Cycle} lines. The \code{x: value} line is recoded as 10 | #' \code{Eprime.LevelName: x_value}. The purpose of this tidying is to force 11 | #' the same set of key names (eventually, column names) onto frames with 12 | #' different values for "Running". 13 | #' 14 | #' @param x a character vector with lines of the form \code{"key: value"}, or a 15 | #' list of vectors of colon-separated text 16 | #' @return When passed a list of character vectors of \code{"key: value"} lines, 17 | #' a FrameList object (a list of EprimeFrames) is returned. when passed a 18 | #' single vector vector of \code{"key: value"} lines, a single EprimeFrame 19 | #' object is returned inside of a FrameList object. 20 | #' @export 21 | #' @examples 22 | #' lines <- c("\t*** LogFrame Start ***", 23 | #' "\tProcedure: FamTask", 24 | #' "\titem1: bear", 25 | #' "\titem2: chair", 26 | #' "\tCorrectResponse: bear", 27 | #' "\tImageSide: Left", 28 | #' "\tDuration: 885", 29 | #' "\tFamiliarization: 1", 30 | #' "\tFamInforcer: 1", 31 | #' "\tReinforcerImage: Bicycle1", 32 | #' "\tFamiliarization.Cycle: 1", 33 | #' "\tFamiliarization.Sample: 1", 34 | #' "\tRunning: Familiarization", 35 | #' "\tFamTarget.RESP: ", 36 | #' "\tCorrect: True", 37 | #' "\t*** LogFrame End ***") 38 | #' # List of 1 39 | #' # $ :List of 17 40 | #' # ..$ Eprime.Level : num 2 41 | #' # ..$ Eprime.LevelName : chr "Familiarization_1" 42 | #' # ..$ Eprime.Basename : chr "NA" 43 | #' # ..$ Eprime.FrameNumber: chr "1" 44 | #' # ..$ Procedure : chr "FamTask" 45 | #' # ..$ Running : chr "Familiarization" 46 | #' # ..$ item1 : chr "bear" 47 | #' # ..$ item2 : chr "chair" 48 | #' # ..$ CorrectResponse : chr "bear" 49 | #' # ..$ ImageSide : chr "Left" 50 | #' # ..$ Duration : chr "885" 51 | #' # ..$ FamInforcer : chr "1" 52 | #' # ..$ ReinforcerImage : chr "Bicycle1" 53 | #' # ..$ Cycle : chr "1" 54 | #' # ..$ Sample : chr "1" 55 | #' # ..$ FamTarget.RESP : chr "" 56 | #' # ..$ Correct : chr "True" 57 | #' # ..- attr(*, "class")= chr [1:2] "EprimeFrame" "list" 58 | #' # - attr(*, "class")= chr [1:2] "list" "FrameList" 59 | FrameList <- function(x) UseMethod("FrameList") 60 | 61 | #' @export 62 | FrameList.character <- function(x) { 63 | FrameList.list(extract_chunks(x)) 64 | } 65 | 66 | #' @export 67 | FrameList.list <- function(x) { 68 | assert_that(is_list_of(x, "EprimeChunk") | is_list_of(x, "character")) 69 | as.FrameList(lapply(x, EprimeFrame)) 70 | } 71 | 72 | 73 | 74 | #' Create an EprimeFrame object 75 | #' 76 | #' This constructor function converts a character vector into an 77 | #' \code{EprimeFrame} object, which is just a list with some special metadata 78 | #' values. Strings with the format \code{"key: value"} are parsed into \code{key 79 | #' = value} list items (via \code{listify}). 80 | #' 81 | #' @param keys_values a character vector of containing some \code{"key: value"} 82 | #' strings. 83 | #' @return a list with the class \code{EprimeFrame} and with special 84 | #' \code{Eprime.} metadata, \code{Running} and \code{Procedure} values, all 85 | #' set to NA by default. 86 | #' @export 87 | #' @examples 88 | #' # Default metadata values 89 | #' lines <- c( 90 | #' "key: value", 91 | #' "question: answer", 92 | #' "garbage text") 93 | #' 94 | #' EprimeFrame(lines) 95 | #' # List of 8 96 | #' # $ Eprime.Level : num 1 97 | #' # $ Eprime.LevelName : logi NA 98 | #' # $ Eprime.Basename : logi NA 99 | #' # $ Eprime.FrameNumber: logi NA 100 | #' # $ Procedure : logi NA 101 | #' # $ Running : logi NA 102 | #' # $ key : chr "value" 103 | #' # $ question : chr "answer" 104 | #' 105 | #' # Normalize [Running] related lines 106 | #' keys_values <- c( 107 | #' "Running: Demo", 108 | #' "Demo: ExampleCode", 109 | #' "Demo.Cycle: 1", 110 | #' "Demo.Sample: 1", 111 | #' "Key: Value") 112 | #' 113 | #' EprimeFrame(keys_values) 114 | #' # List of 9 115 | #' # $ Eprime.Level : num 1 116 | #' # $ Eprime.LevelName : chr "Demo_ExampleCode" 117 | #' # $ Eprime.Basename : logi NA 118 | #' # $ Eprime.FrameNumber: logi NA 119 | #' # $ Procedure : logi NA 120 | #' # $ Running : chr "Demo" 121 | #' # $ Cycle : chr "1" 122 | #' # $ Sample : chr "1" 123 | #' # $ Key : chr "Value" 124 | EprimeFrame <- function(keys_values) UseMethod("EprimeFrame") 125 | 126 | #' @export 127 | EprimeFrame.EprimeChunk <- function(keys_values = character(0)) { 128 | EprimeFrame.character(keys_values) 129 | } 130 | 131 | #' @export 132 | EprimeFrame.character <- function(keys_values = character(0)) { 133 | keys_values <- merge_lists(listify(keys_values), count_tabs(keys_values)) 134 | frame <- as.EprimeFrame(keys_values) 135 | tidy(frame) 136 | } 137 | 138 | 139 | 140 | #' Convert a list of EprimeFrames into a FrameList object 141 | #' @param xs a list of EprimeFrames 142 | #' @return the original list as a \code{FrameList} object 143 | #' @export 144 | as.FrameList <- function(xs) { 145 | assert_that(is_list_of(xs, "list"), is_list_of(xs, "EprimeFrame")) 146 | class(xs) <- unique(c(class(xs), "FrameList", "list")) 147 | xs 148 | } 149 | 150 | #' Convert a list into an EprimeFrame object 151 | #' @param xs a list 152 | #' @return the original list as an \code{EprimeFrame} object (along with dummy 153 | #' Eprime metadata fields) 154 | #' @export 155 | as.EprimeFrame <- function(xs) { 156 | assert_that(is.list(xs)) 157 | with_defaults <- merge_lists(default_metadata, xs) 158 | structure(with_defaults, class = c("EprimeFrame", "list")) 159 | } 160 | 161 | 162 | 163 | #' @export 164 | print.FrameList <- function(...) str(...) 165 | 166 | #' @export 167 | print.EprimeFrame <- function(...) str(...) 168 | 169 | 170 | 171 | #' Clean up `Running`-related attributes 172 | #' 173 | #' A fresh EprimeFrame object has the structure: 174 | #' 175 | #' \code{ 176 | #' Eprime.LevelName: NA 177 | #' Eprime.Level: [Level] 178 | #' Running: [Key] 179 | #' [Key]: [Value] 180 | #' [Key].Cycle: [Cycle] 181 | #' [Key].Sample: [Sample] 182 | #' } 183 | #' 184 | #' These \code{[Key]} values make it harder to merge together data-frames later 185 | #' on, since each unique \code{[Key]} gets its own column name. Therefore, we 186 | #' normalize these field names early on. The end result has the structure: 187 | #' 188 | #' \code{ 189 | #' Eprime.LevelName: [Key]_[Value] 190 | #' Eprime.Level: [Level] 191 | #' Running: [Key] 192 | #' Cycle: [Cycle] 193 | #' Sample: [Sample] 194 | #' } 195 | #' 196 | #' @noRd 197 | tidy <- function(x) { 198 | eprime_frame <- x 199 | level_depth <- eprime_frame[[rprime_cols$level]] 200 | level_label <- eprime_frame[[rprime_cols$running]] 201 | level_name <- eprime_frame[[rprime_cols$level_name]] 202 | 203 | # Tidy if Running field is used and level name is still NA 204 | has_level_label <- !is.na(level_label) 205 | needs_level_name <- is.na(level_name) 206 | 207 | if (needs_level_name & has_level_label) { 208 | # Store [Key]_[Value] as "Eprime.LevelName" 209 | level_index <- eprime_frame[[level_label]] 210 | level_name <- paste0(level_label, "_", level_index) 211 | new_list <- structure(as.list(level_name), names = rprime_cols$level_name) 212 | 213 | # Remove "[Key]: [Value]" item and then remove "[Key]." from names 214 | eprime_frame[level_label] <- NULL 215 | key_dot <- paste0(level_label, "\\.") 216 | names(eprime_frame) <- str_replace(names(eprime_frame), key_dot, "") 217 | eprime_frame <- merge_lists(eprime_frame, new_list) 218 | } 219 | 220 | class(eprime_frame) <- class(x) 221 | eprime_frame 222 | } 223 | 224 | 225 | #' Convert a vector of colon-separated text lines into a list of named elements 226 | #' 227 | #' @details Some minor cleaning of the input is performed: 228 | #' \itemize{ 229 | #' \item Lines without a colon-space separator \code{": "} are filtered out. 230 | #' \item Once the strings are split at the separator, white-space on the 231 | #' left and right sides of each half-string is omitted.} 232 | #' @param colon_sep_xs a character vector with lines of the form 233 | #' \code{"key: value"} 234 | #' @return a named list of the values in the colon-separated lines. 235 | #' \code{"key: value"} yields \code{list(key = "value")} 236 | #' @export 237 | listify <- function(colon_sep_xs) { 238 | colon_sep_xs <- Filter(is_row, colon_sep_xs) 239 | splits <- str_split_fixed(colon_sep_xs, pattern = ": ", 2) 240 | # Trim after splitting so "X: " lines are correctly parsed 241 | splits <- apply(splits, 2, str_trim) 242 | # apply reduces single row matrix into a vector 243 | splits <- if (!is.matrix(splits)) matrix(splits, ncol = 2) else splits 244 | structure(as.list(splits[, 2]), names = splits[, 1]) 245 | } 246 | 247 | 248 | # Infer level of nesting (for a log-frame) by counting tabs 249 | # 250 | # The number of tabs before the "key: value" information in a log-frame tells 251 | # where the frame is nested in the experiment's structure. 252 | count_tabs <- function(colon_sep_xs) { 253 | colon_sep_xs <- ifelse(length_zero(colon_sep_xs), "", colon_sep_xs) 254 | # Add one because there is no level 0 255 | level <- str_count(first(colon_sep_xs), "\\t") + 1 256 | structure(list(level), names = rprime_cols$level) 257 | } 258 | 259 | -------------------------------------------------------------------------------- /R/load.R: -------------------------------------------------------------------------------- 1 | 2 | #' Read in a text file generated by Eprime 3 | #' 4 | #' @param filename Either the full or relative path to an Eprime .txt file 5 | #' @param remove_clock Whether to exclude the Clock.Information XML entries. 6 | #' Enabled by default. 7 | #' @return Each line of the file is stored and returned in a character vector. 8 | #' 9 | #' @details The encoding on an Eprime txt file should be UCS-2 Little Endian, 10 | #' but sometimes this is not the case. We delegate the fussy encoding details 11 | #' to the \code{stringi::str_read_lines} function. 12 | #' 13 | #' If the file is not an Eprime txt--that is, if it is missing the lines 14 | #' \code{*** Header Start ***} and \code{*** Header End ***}--a warning is 15 | #' raised and the lines of text are replaced by a dummy header. 16 | #' @export 17 | read_eprime <- function(filename, remove_clock = TRUE) { 18 | basename <- tools::file_path_sans_ext(basename(filename)) 19 | 20 | raw <- stringi::stri_read_raw(filename) 21 | top_enc <- stringi::stri_enc_detect(raw)[[1]][1, 1] 22 | eprime_log <- stringi::stri_read_lines(filename, encoding = top_enc) 23 | 24 | 25 | if (!has_header(eprime_log)) { 26 | warning(filename, " is not an Eprime txt file. Dummy text will be used instead.") 27 | eprime_log <- c("*** Header Start ***", "*** Header End ***") 28 | } 29 | 30 | 31 | if (remove_clock) { 32 | clock_lines <- str_detect(eprime_log, "Clock.Information: ") 33 | eprime_log <- eprime_log[!clock_lines] 34 | } 35 | 36 | attr(eprime_log, "basename") <- basename 37 | class(eprime_log) <- c("EprimeLines", "character") 38 | eprime_log 39 | } 40 | -------------------------------------------------------------------------------- /R/patterns.R: -------------------------------------------------------------------------------- 1 | patterns <- list( 2 | bracket = "\\*{3} (.*) (Start|End) \\*{3}", 3 | bracket_start = "\\*{3} (.*) Start \\*{3}", 4 | bracket_end = "\\*{3} (.*) End \\*{3}", 5 | header_start = "^\\*{3} Header Start \\*{3}$", 6 | header_end = "^\\*{3} Header End \\*{3}$", 7 | footer_start = "^\\*{3} LogFrame Start \\*{3}$", 8 | footer_end = "^\\*{3} LogFrame End \\*{3}$", 9 | row = ".+: .*" 10 | ) 11 | 12 | # Names of the metadata fields. 13 | rprime_cols <- list( 14 | level = "Eprime.Level", 15 | level_name = "Eprime.LevelName", 16 | basename = "Eprime.Basename", 17 | frame = "Eprime.FrameNumber", 18 | procedure = "Procedure", 19 | running = "Running" 20 | ) 21 | 22 | # Make a list of the metadata fields initialized to NAs 23 | default_metadata <- structure( 24 | .Data = as.list(rep(NA, length(rprime_cols))), 25 | names = unlist(unname(rprime_cols))) 26 | 27 | str_which <- function(string, pattern) which(str_detect(string, pattern)) 28 | new_line <- function(key, value) sprintf("%s: %s", key, value) 29 | is_header <- function(xs) any(str_detect(xs, patterns$header_start)) 30 | is_row <- function(xs) any(str_detect(xs, patterns$row)) 31 | has_header <- function(xs) any(sapply(xs, is_header)) 32 | 33 | -------------------------------------------------------------------------------- /R/preview.R: -------------------------------------------------------------------------------- 1 | #' Preview the levels in a parsed Eprime file 2 | #' 3 | #' @details \code{preview_levels} prints out the unique combinations of 4 | #' Eprime.Level number, Procedure, and Running in the frame list. 5 | #' \code{preview_frames} prints out example frame from each of the unique 6 | #' levels. \code{preview_eprime} does both. 7 | #' 8 | #' @param frame_list a FrameList (a list of EprimeFrames) 9 | #' @return Nothing. Preview text is printed to the console. 10 | #' @export 11 | preview_eprime <- function(frame_list) { 12 | preview_levels(frame_list) 13 | preview_frames(frame_list) 14 | invisible(NULL) 15 | } 16 | 17 | #' @rdname preview_eprime 18 | #' @export 19 | preview_levels <- function(frame_list) { 20 | prep <- preview_prep(frame_list) 21 | cat("Level Counts: \n") 22 | print(prep$row_counts, row.names = FALSE) 23 | invisible(NULL) 24 | } 25 | 26 | #' @rdname preview_eprime 27 | #' @export 28 | preview_frames <- function(frame_list) { 29 | prep <- preview_prep(frame_list) 30 | 31 | for(chunk_num in seq_along(prep$unique_frames)) { 32 | curr_row <- prep$unique_rows[chunk_num, ] 33 | curr_chunk <- prep$unique_frames[[chunk_num]] 34 | 35 | cat("\n") 36 | print(curr_row, row.names = FALSE) 37 | str(curr_chunk) 38 | } 39 | 40 | invisible(NULL) 41 | } 42 | 43 | preview_prep <- function(frame_list) { 44 | keys <- c("Eprime.Level", "Running", "Procedure") 45 | main_cols <- pick_apply(keys, frame_list) 46 | full_table <- to_data_frame(main_cols)[keys] 47 | 48 | unique_rows <- unique(full_table) 49 | unique_frames <- as.FrameList(frame_list[as.numeric(row.names(unique_rows))]) 50 | 51 | # Include frequency count 52 | row_counts <- plyr::count(full_table) 53 | row_counts <- plyr::join(unique_rows, row_counts, by = keys) 54 | 55 | list(row_counts = row_counts, unique_rows = unique_rows, 56 | unique_frames = unique_frames) 57 | } 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /R/rprime-package.R: -------------------------------------------------------------------------------- 1 | #' Functions for dealing with Eprime txt files 2 | #' 3 | #' @name rprime 4 | #' @docType package 5 | #' @import stringr 6 | #' @import plyr 7 | #' @import assertthat 8 | #' @importFrom utils str tail head 9 | #' @keywords internal 10 | "_PACKAGE" 11 | 12 | # The following block is used by usethis to automatically manage 13 | # roxygen namespace tags. Modify with care! 14 | ## usethis namespace: start 15 | ## usethis namespace: end 16 | NULL 17 | -------------------------------------------------------------------------------- /R/to_data_frame.R: -------------------------------------------------------------------------------- 1 | #' Convert Eprime Frames into data-frames 2 | #' 3 | #' @details Individual EprimeFrames are converted to a data-frame using 4 | #' `as.data.frame()`. (Strings are not converted to factors.) 5 | #' 6 | #' Each of the individual data-frames are then `rbind()`-ed together, with 7 | #' missing columns being filled with NA. 8 | #' 9 | #' @param x an EprimeFrame object, or a FrameList object (a list of 10 | #' EprimeFrames) 11 | #' @return all of the EprimeFrames combined into a single data frame. 12 | #' @export 13 | #' @seealso [plyr::rbind.fill()] 14 | #' @md 15 | to_data_frame <- function(x) { 16 | UseMethod("to_data_frame") 17 | } 18 | 19 | #' @export 20 | to_data_frame.default <- function(x) { 21 | data_frames <- lapply(x, as.data.frame.list, stringsAsFactors = FALSE) 22 | rbind.fill(data_frames) 23 | } 24 | 25 | #' @export 26 | to_data_frame.FrameList <- function(x) { 27 | data_frames <- lapply(x, to_data_frame) 28 | rbind.fill(data_frames) 29 | } 30 | 31 | #' @export 32 | to_data_frame.EprimeFrame <- function(x) { 33 | as.data.frame.list(x, stringsAsFactors = FALSE) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | first <- function(...) head(..., n = 1) 2 | but_last <- function(...) head(..., n = -1) 3 | last <- function(...) tail(..., n = 1) 4 | 5 | length_zero <- function(x) length(x) == 0 6 | length_one <- function(x) length(x) == 1 7 | 8 | is_list_of <- function(xs, classes) { 9 | assert_that(is.list(xs)) 10 | all(vapply(xs, function(x) inherits(x, classes), logical(1))) 11 | } 12 | 13 | merge_lists <- function(x, y) { 14 | x[names(y)] <- y 15 | x 16 | } 17 | 18 | #' Higher-order functions for dealing with lists 19 | #' 20 | #' These functions were inspired by underscore.js. 21 | #' 22 | #' @name list_functions 23 | #' @param key the name of a value in a list 24 | #' @param keys a character vector of names in a list 25 | #' @param xss a list of lists 26 | #' @return \code{pluck} returns an unnamed value and \code{pluck_apply} returns 27 | #' a list of unnamed values. \code{pick} returns a simplified version of the 28 | #' original list. \code{pick_apply} returns a list of simplified lists. 29 | #' 30 | #' @details \itemize{ \item \code{pluck}: Pluck a named value from a list \item 31 | #' \code{pick}: Simplify a list by picking out whitelisted names} 32 | #' 33 | #' The simple versions of \code{pluck} and \code{pick} are curried functions, 34 | #' meaning that they return a function which can be applied to a list. See the 35 | #' syntax in the usage section. 36 | #' @keywords internal 37 | NULL 38 | 39 | #' @rdname list_functions 40 | pluck <- function(key) { 41 | function(xs) xs[[key]] 42 | } 43 | 44 | #' @rdname list_functions 45 | pluck_apply <- function(key, xss) { 46 | assert_that(is_list_of(xss, "list")) 47 | lapply(xss, pluck(key)) 48 | } 49 | 50 | #' @rdname list_functions 51 | pick <- function(keys) { 52 | function(xs) { 53 | classes <- class(xs) 54 | xs <- xs[is.element(names(xs), keys)] 55 | class(xs) <- classes 56 | xs 57 | } 58 | } 59 | 60 | #' @rdname list_functions 61 | pick_apply <- function(keys, xss) { 62 | assert_that(is_list_of(xss, "list")) 63 | classes <- class(xss) 64 | xss <- lapply(xss, pick(keys)) 65 | class(xss) <- classes 66 | xss 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | md_document: 4 | variant: markdown_github 5 | --- 6 | 7 | ```{r, echo = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "README-" 12 | ) 13 | ``` 14 | 15 | 16 | 17 | rprime 18 | =============================================================================== 19 | 20 | 21 | [![R-CMD-check](https://github.com/tjmahr/rprime/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/tjmahr/rprime/actions/workflows/R-CMD-check.yaml) 22 | [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/rprime)](https://CRAN.R-project.org/package=rprime) 23 | 24 | 25 | ```{r, child="vignettes/quick-start-demo.Rmd"} 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | destination: docs 2 | reference: 3 | - title: Most important functions 4 | desc: ~ 5 | contents: 6 | - '`read_eprime`' 7 | - '`FrameList`' 8 | - '`filter_in`' 9 | - '`keep_levels`' 10 | - '`to_data_frame`' 11 | - title: Supporting functions 12 | desc: These help with the parsing of the text file. 13 | contents: 14 | - '`listify`' 15 | - '`extract_chunks`' 16 | - '`preview_eprime`' 17 | - title: Objects 18 | desc: These objects represent chunks of data. 19 | contents: 20 | - '`EprimeFrame`' 21 | - '`as.EprimeFrame`' 22 | - '`as.FrameList`' 23 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | 2 | ## Submission 3 | 4 | This submission fixes CRAN notes related to documentation and Lazydata. 5 | 6 | 0 errors | 0 warnings | 0 notes 7 | -------------------------------------------------------------------------------- /docs/articles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Articles • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 | 123 | 124 | 125 | 126 |
127 | 128 |
129 |
130 | 133 | 134 |
135 |

All vignettes

136 |

137 | 138 |
139 |
Working with multiple files
140 |
141 |
Parsing summary
142 |
143 |
Quick start demonstration
144 |
145 |
146 |
147 |
148 |
149 | 150 | 151 | 161 |
162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Authors • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 | 123 | 124 | 125 | 126 |
127 | 128 |
129 |
130 | 133 | 134 |
    135 |
  • 136 |

    Tristan Mahr. Author, maintainer. 137 |

    138 |
  • 139 |
140 | 141 |
142 | 143 |
144 | 145 | 146 | 147 | 157 |
158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | position: relative; 22 | } 23 | 24 | body > .container { 25 | display: flex; 26 | height: 100%; 27 | flex-direction: column; 28 | } 29 | 30 | body > .container .row { 31 | flex: 1 0 auto; 32 | } 33 | 34 | footer { 35 | margin-top: 45px; 36 | padding: 35px 0 36px; 37 | border-top: 1px solid #e5e5e5; 38 | color: #666; 39 | display: flex; 40 | flex-shrink: 0; 41 | } 42 | footer p { 43 | margin-bottom: 0; 44 | } 45 | footer div { 46 | flex: 1; 47 | } 48 | footer .pkgdown { 49 | text-align: right; 50 | } 51 | footer p { 52 | margin-bottom: 0; 53 | } 54 | 55 | img.icon { 56 | float: right; 57 | } 58 | 59 | img { 60 | max-width: 100%; 61 | } 62 | 63 | /* Fix bug in bootstrap (only seen in firefox) */ 64 | summary { 65 | display: list-item; 66 | } 67 | 68 | /* Typographic tweaking ---------------------------------*/ 69 | 70 | .contents .page-header { 71 | margin-top: calc(-60px + 1em); 72 | } 73 | 74 | dd { 75 | margin-left: 3em; 76 | } 77 | 78 | /* Section anchors ---------------------------------*/ 79 | 80 | a.anchor { 81 | margin-left: -30px; 82 | display:inline-block; 83 | width: 30px; 84 | height: 30px; 85 | visibility: hidden; 86 | 87 | background-image: url(./link.svg); 88 | background-repeat: no-repeat; 89 | background-size: 20px 20px; 90 | background-position: center center; 91 | } 92 | 93 | .hasAnchor:hover a.anchor { 94 | visibility: visible; 95 | } 96 | 97 | @media (max-width: 767px) { 98 | .hasAnchor:hover a.anchor { 99 | visibility: hidden; 100 | } 101 | } 102 | 103 | 104 | /* Fixes for fixed navbar --------------------------*/ 105 | 106 | .contents h1, .contents h2, .contents h3, .contents h4 { 107 | padding-top: 60px; 108 | margin-top: -40px; 109 | } 110 | 111 | /* Navbar submenu --------------------------*/ 112 | 113 | .dropdown-submenu { 114 | position: relative; 115 | } 116 | 117 | .dropdown-submenu>.dropdown-menu { 118 | top: 0; 119 | left: 100%; 120 | margin-top: -6px; 121 | margin-left: -1px; 122 | border-radius: 0 6px 6px 6px; 123 | } 124 | 125 | .dropdown-submenu:hover>.dropdown-menu { 126 | display: block; 127 | } 128 | 129 | .dropdown-submenu>a:after { 130 | display: block; 131 | content: " "; 132 | float: right; 133 | width: 0; 134 | height: 0; 135 | border-color: transparent; 136 | border-style: solid; 137 | border-width: 5px 0 5px 5px; 138 | border-left-color: #cccccc; 139 | margin-top: 5px; 140 | margin-right: -10px; 141 | } 142 | 143 | .dropdown-submenu:hover>a:after { 144 | border-left-color: #ffffff; 145 | } 146 | 147 | .dropdown-submenu.pull-left { 148 | float: none; 149 | } 150 | 151 | .dropdown-submenu.pull-left>.dropdown-menu { 152 | left: -100%; 153 | margin-left: 10px; 154 | border-radius: 6px 0 6px 6px; 155 | } 156 | 157 | /* Sidebar --------------------------*/ 158 | 159 | #pkgdown-sidebar { 160 | margin-top: 30px; 161 | position: -webkit-sticky; 162 | position: sticky; 163 | top: 70px; 164 | } 165 | 166 | #pkgdown-sidebar h2 { 167 | font-size: 1.5em; 168 | margin-top: 1em; 169 | } 170 | 171 | #pkgdown-sidebar h2:first-child { 172 | margin-top: 0; 173 | } 174 | 175 | #pkgdown-sidebar .list-unstyled li { 176 | margin-bottom: 0.5em; 177 | } 178 | 179 | /* bootstrap-toc tweaks ------------------------------------------------------*/ 180 | 181 | /* All levels of nav */ 182 | 183 | nav[data-toggle='toc'] .nav > li > a { 184 | padding: 4px 20px 4px 6px; 185 | font-size: 1.5rem; 186 | font-weight: 400; 187 | color: inherit; 188 | } 189 | 190 | nav[data-toggle='toc'] .nav > li > a:hover, 191 | nav[data-toggle='toc'] .nav > li > a:focus { 192 | padding-left: 5px; 193 | color: inherit; 194 | border-left: 1px solid #878787; 195 | } 196 | 197 | nav[data-toggle='toc'] .nav > .active > a, 198 | nav[data-toggle='toc'] .nav > .active:hover > a, 199 | nav[data-toggle='toc'] .nav > .active:focus > a { 200 | padding-left: 5px; 201 | font-size: 1.5rem; 202 | font-weight: 400; 203 | color: inherit; 204 | border-left: 2px solid #878787; 205 | } 206 | 207 | /* Nav: second level (shown on .active) */ 208 | 209 | nav[data-toggle='toc'] .nav .nav { 210 | display: none; /* Hide by default, but at >768px, show it */ 211 | padding-bottom: 10px; 212 | } 213 | 214 | nav[data-toggle='toc'] .nav .nav > li > a { 215 | padding-left: 16px; 216 | font-size: 1.35rem; 217 | } 218 | 219 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 220 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 221 | padding-left: 15px; 222 | } 223 | 224 | nav[data-toggle='toc'] .nav .nav > .active > a, 225 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 226 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 227 | padding-left: 15px; 228 | font-weight: 500; 229 | font-size: 1.35rem; 230 | } 231 | 232 | /* orcid ------------------------------------------------------------------- */ 233 | 234 | .orcid { 235 | font-size: 16px; 236 | color: #A6CE39; 237 | /* margins are required by official ORCID trademark and display guidelines */ 238 | margin-left:4px; 239 | margin-right:4px; 240 | vertical-align: middle; 241 | } 242 | 243 | /* Reference index & topics ----------------------------------------------- */ 244 | 245 | .ref-index th {font-weight: normal;} 246 | 247 | .ref-index td {vertical-align: top; min-width: 100px} 248 | .ref-index .icon {width: 40px;} 249 | .ref-index .alias {width: 40%;} 250 | .ref-index-icons .alias {width: calc(40% - 40px);} 251 | .ref-index .title {width: 60%;} 252 | 253 | .ref-arguments th {text-align: right; padding-right: 10px;} 254 | .ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px} 255 | .ref-arguments .name {width: 20%;} 256 | .ref-arguments .desc {width: 80%;} 257 | 258 | /* Nice scrolling for wide elements --------------------------------------- */ 259 | 260 | table { 261 | display: block; 262 | overflow: auto; 263 | } 264 | 265 | /* Syntax highlighting ---------------------------------------------------- */ 266 | 267 | pre { 268 | word-wrap: normal; 269 | word-break: normal; 270 | border: 1px solid #eee; 271 | } 272 | 273 | pre, code { 274 | background-color: #f8f8f8; 275 | color: #333; 276 | } 277 | 278 | pre code { 279 | overflow: auto; 280 | word-wrap: normal; 281 | white-space: pre; 282 | } 283 | 284 | pre .img { 285 | margin: 5px 0; 286 | } 287 | 288 | pre .img img { 289 | background-color: #fff; 290 | display: block; 291 | height: auto; 292 | } 293 | 294 | code a, pre a { 295 | color: #375f84; 296 | } 297 | 298 | a.sourceLine:hover { 299 | text-decoration: none; 300 | } 301 | 302 | .fl {color: #1514b5;} 303 | .fu {color: #000000;} /* function */ 304 | .ch,.st {color: #036a07;} /* string */ 305 | .kw {color: #264D66;} /* keyword */ 306 | .co {color: #888888;} /* comment */ 307 | 308 | .message { color: black; font-weight: bolder;} 309 | .error { color: orange; font-weight: bolder;} 310 | .warning { color: #6A0366; font-weight: bolder;} 311 | 312 | /* Clipboard --------------------------*/ 313 | 314 | .hasCopyButton { 315 | position: relative; 316 | } 317 | 318 | .btn-copy-ex { 319 | position: absolute; 320 | right: 0; 321 | top: 0; 322 | visibility: hidden; 323 | } 324 | 325 | .hasCopyButton:hover button.btn-copy-ex { 326 | visibility: visible; 327 | } 328 | 329 | /* headroom.js ------------------------ */ 330 | 331 | .headroom { 332 | will-change: transform; 333 | transition: transform 200ms linear; 334 | } 335 | .headroom--pinned { 336 | transform: translateY(0%); 337 | } 338 | .headroom--unpinned { 339 | transform: translateY(-100%); 340 | } 341 | 342 | /* mark.js ----------------------------*/ 343 | 344 | mark { 345 | background-color: rgba(255, 255, 51, 0.5); 346 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 347 | padding: 1px; 348 | } 349 | 350 | /* vertical spacing after htmlwidgets */ 351 | .html-widget { 352 | margin-bottom: 10px; 353 | } 354 | 355 | /* fontawesome ------------------------ */ 356 | 357 | .fab { 358 | font-family: "Font Awesome 5 Brands" !important; 359 | } 360 | 361 | /* don't display links in code chunks when printing */ 362 | /* source: https://stackoverflow.com/a/10781533 */ 363 | @media print { 364 | code a:link:after, code a:visited:after { 365 | content: ""; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $(".examples, div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent; 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 2.7.2 2 | pkgdown: 1.6.1 3 | pkgdown_sha: ~ 4 | articles: 5 | multiple-files: multiple-files.html 6 | parsing-summary: parsing-summary.html 7 | quick-start-demo: quick-start-demo.html 8 | last_built: 2020-09-24T11:00Z 9 | 10 | -------------------------------------------------------------------------------- /docs/reference/EprimeFrame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Create an EprimeFrame object — EprimeFrame • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
    66 |
    67 | 127 | 128 | 129 | 130 |
    131 | 132 |
    133 |
    134 | 139 | 140 |
    141 |

    This constructor function converts a character vector into an 142 | EprimeFrame object, which is just a list with some special metadata 143 | values. Strings with the format "key: value" are parsed into key 144 | = value list items (via listify).

    145 |
    146 | 147 |
    EprimeFrame(keys_values)
    148 | 149 |

    Arguments

    150 | 151 | 152 | 153 | 154 | 156 | 157 |
    keys_values

    a character vector of containing some "key: value" 155 | strings.

    158 | 159 |

    Value

    160 | 161 |

    a list with the class EprimeFrame and with special 162 | Eprime. metadata, Running and Procedure values, all 163 | set to NA by default.

    164 | 165 |

    Examples

    166 |
    # Default metadata values 167 | lines <- c( 168 | "key: value", 169 | "question: answer", 170 | "garbage text") 171 | 172 | EprimeFrame(lines) 173 |
    #> List of 8 174 | #> $ Eprime.Level : num 1 175 | #> $ Eprime.LevelName : logi NA 176 | #> $ Eprime.Basename : logi NA 177 | #> $ Eprime.FrameNumber: logi NA 178 | #> $ Procedure : logi NA 179 | #> $ Running : logi NA 180 | #> $ key : chr "value" 181 | #> $ question : chr "answer" 182 | #> - attr(*, "class")= chr [1:2] "EprimeFrame" "list"
    # List of 8 183 | # $ Eprime.Level : num 1 184 | # $ Eprime.LevelName : logi NA 185 | # $ Eprime.Basename : logi NA 186 | # $ Eprime.FrameNumber: logi NA 187 | # $ Procedure : logi NA 188 | # $ Running : logi NA 189 | # $ key : chr "value" 190 | # $ question : chr "answer" 191 | 192 | # Normalize [Running] related lines 193 | keys_values <- c( 194 | "Running: Demo", 195 | "Demo: ExampleCode", 196 | "Demo.Cycle: 1", 197 | "Demo.Sample: 1", 198 | "Key: Value") 199 | 200 | EprimeFrame(keys_values) 201 |
    #> List of 9 202 | #> $ Eprime.Level : num 1 203 | #> $ Eprime.LevelName : chr "Demo_ExampleCode" 204 | #> $ Eprime.Basename : logi NA 205 | #> $ Eprime.FrameNumber: logi NA 206 | #> $ Procedure : logi NA 207 | #> $ Running : chr "Demo" 208 | #> $ Cycle : chr "1" 209 | #> $ Sample : chr "1" 210 | #> $ Key : chr "Value" 211 | #> - attr(*, "class")= chr [1:2] "EprimeFrame" "list"
    # List of 9 212 | # $ Eprime.Level : num 1 213 | # $ Eprime.LevelName : chr "Demo_ExampleCode" 214 | # $ Eprime.Basename : logi NA 215 | # $ Eprime.FrameNumber: logi NA 216 | # $ Procedure : logi NA 217 | # $ Running : chr "Demo" 218 | # $ Cycle : chr "1" 219 | # $ Sample : chr "1" 220 | # $ Key : chr "Value" 221 |
    222 |
    223 | 228 |
    229 | 230 | 231 |
    232 | 235 | 236 |
    237 |

    Site built with pkgdown 1.6.1.

    238 |
    239 | 240 |
    241 |
    242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /docs/reference/FrameList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Convert lines from an Eprime file into EprimeFrame objects — FrameList • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 126 | 127 | 128 | 129 |
    130 | 131 |
    132 |
    133 | 138 | 139 |
    140 |

    Convert character vectors of implicit key-value pairs (e.g., c("key1: 141 | value1", "key2: value2")), into lists of explicit key-value pairs, 142 | list(key1 = "value1", key2 = "value2").

    143 |
    144 | 145 |
    FrameList(x)
    146 | 147 |

    Arguments

    148 | 149 | 150 | 151 | 152 | 154 | 155 |
    x

    a character vector with lines of the form "key: value", or a 153 | list of vectors of colon-separated text

    156 | 157 |

    Value

    158 | 159 |

    When passed a list of character vectors of "key: value" lines, 160 | a FrameList object (a list of EprimeFrames) is returned. when passed a 161 | single vector vector of "key: value" lines, a single EprimeFrame 162 | object is returned inside of a FrameList object.

    163 |

    Details

    164 | 165 |

    During the conversion, if Running: x, then the 166 | x.Sample and x.Cycle lines are simplified into Sample 167 | and Cycle lines. The x: value line is recoded as 168 | Eprime.LevelName: x_value. The purpose of this tidying is to force 169 | the same set of key names (eventually, column names) onto frames with 170 | different values for "Running".

    171 | 172 |

    Examples

    173 |
    lines <- c("\t*** LogFrame Start ***", 174 | "\tProcedure: FamTask", 175 | "\titem1: bear", 176 | "\titem2: chair", 177 | "\tCorrectResponse: bear", 178 | "\tImageSide: Left", 179 | "\tDuration: 885", 180 | "\tFamiliarization: 1", 181 | "\tFamInforcer: 1", 182 | "\tReinforcerImage: Bicycle1", 183 | "\tFamiliarization.Cycle: 1", 184 | "\tFamiliarization.Sample: 1", 185 | "\tRunning: Familiarization", 186 | "\tFamTarget.RESP: ", 187 | "\tCorrect: True", 188 | "\t*** LogFrame End ***") 189 | # List of 1 190 | # $ :List of 17 191 | # ..$ Eprime.Level : num 2 192 | # ..$ Eprime.LevelName : chr "Familiarization_1" 193 | # ..$ Eprime.Basename : chr "NA" 194 | # ..$ Eprime.FrameNumber: chr "1" 195 | # ..$ Procedure : chr "FamTask" 196 | # ..$ Running : chr "Familiarization" 197 | # ..$ item1 : chr "bear" 198 | # ..$ item2 : chr "chair" 199 | # ..$ CorrectResponse : chr "bear" 200 | # ..$ ImageSide : chr "Left" 201 | # ..$ Duration : chr "885" 202 | # ..$ FamInforcer : chr "1" 203 | # ..$ ReinforcerImage : chr "Bicycle1" 204 | # ..$ Cycle : chr "1" 205 | # ..$ Sample : chr "1" 206 | # ..$ FamTarget.RESP : chr "" 207 | # ..$ Correct : chr "True" 208 | # ..- attr(*, "class")= chr [1:2] "EprimeFrame" "list" 209 | # - attr(*, "class")= chr [1:2] "list" "FrameList" 210 |
    211 |
    212 | 217 |
    218 | 219 | 220 |
    221 | 224 | 225 |
    226 |

    Site built with pkgdown 1.6.1.

    227 |
    228 | 229 |
    230 |
    231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /docs/reference/as.EprimeFrame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Convert a list into an EprimeFrame object — as.EprimeFrame • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Convert a list into an EprimeFrame object

    139 |
    140 | 141 |
    as.EprimeFrame(xs)
    142 | 143 |

    Arguments

    144 | 145 | 146 | 147 | 148 | 149 | 150 |
    xs

    a list

    151 | 152 |

    Value

    153 | 154 |

    the original list as an EprimeFrame object (along with dummy 155 | Eprime metadata fields)

    156 | 157 |
    158 | 163 |
    164 | 165 | 166 |
    167 | 170 | 171 |
    172 |

    Site built with pkgdown 1.6.1.

    173 |
    174 | 175 |
    176 |
    177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/reference/as.FrameList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Convert a list of EprimeFrames into a FrameList object — as.FrameList • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Convert a list of EprimeFrames into a FrameList object

    139 |
    140 | 141 |
    as.FrameList(xs)
    142 | 143 |

    Arguments

    144 | 145 | 146 | 147 | 148 | 149 | 150 |
    xs

    a list of EprimeFrames

    151 | 152 |

    Value

    153 | 154 |

    the original list as a FrameList object

    155 | 156 |
    157 | 162 |
    163 | 164 | 165 |
    166 | 169 | 170 |
    171 |

    Site built with pkgdown 1.6.1.

    172 |
    173 | 174 |
    175 |
    176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /docs/reference/extract_chunks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Extract log-frames from an Eprime log file — extract_chunks • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
    66 |
    67 | 127 | 128 | 129 | 130 |
    131 | 132 |
    133 |
    134 | 139 | 140 |
    141 |

    Almost all of the information in an Eprime file comes in chunks of text 142 | bracketed by the lines *** LogFrame Start *** and *** LogFrame 143 | End ***. The exception is the header information which is bracketed by 144 | *** Header Start *** and *** Header End ***.

    145 |
    146 | 147 |
    extract_chunks(eprime_log)
    148 | 149 |

    Arguments

    150 | 151 | 152 | 153 | 154 | 156 | 157 |
    eprime_log

    a character vector containing the lines of text from Eprime 155 | txt file

    158 | 159 |

    Value

    160 | 161 |

    a list of character vectors, where each vector contains the lines of 162 | a log-frame

    163 |

    Details

    164 | 165 |

    extract_chunks extracts the bracketed text, storing each log-frame of 166 | text in a list. The lists also include some additional lines of text as 167 | metadata: Eprime.FrameNumber and Eprime.Basename (the name of 168 | the source file). The header log-frame also gets dummy lines: 169 | Procedure: Header and Running: Header.

    170 |

    These character vectors of colon-separated lines are converted into proper 171 | lists by FrameList(...).

    172 | 173 |
    174 | 179 |
    180 | 181 | 182 |
    183 | 186 | 187 |
    188 |

    Site built with pkgdown 1.6.1.

    189 |
    190 | 191 |
    192 |
    193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/reference/filter_in.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Filter levels in or out of a FrameList based on attribute values — filter_in • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Filter levels in or out of a FrameList based on attribute values

    139 |
    140 | 141 |
    filter_in(frame_list, key, values)
    142 | 
    143 | filter_out(frame_list, key, values)
    144 | 145 |

    Arguments

    146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
    frame_list

    a list of EprimeFrame objects

    key

    the name of the attribute to filter in or out

    values

    the whitelisted or blacklisted values of the attribute

    161 | 162 |

    Value

    163 | 164 |

    for filter_in, only log-frames where key is one of the 165 | values are kept. for filter_out, log-frames where key 166 | is one of the values are omitted.

    167 | 168 |
    169 | 174 |
    175 | 176 | 177 |
    178 | 181 | 182 |
    183 |

    Site built with pkgdown 1.6.1.

    184 |
    185 | 186 |
    187 |
    188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/reference/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Function reference • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 123 | 124 | 125 | 126 |
    127 | 128 |
    129 |
    130 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 159 | 160 | 161 | 162 | 165 | 166 | 167 | 168 | 171 | 172 | 173 | 174 | 177 | 178 | 179 | 180 | 183 | 184 | 185 | 186 | 187 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 202 | 203 | 204 | 205 | 208 | 209 | 210 | 211 | 214 | 215 | 216 | 217 | 218 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 233 | 234 | 235 | 236 | 239 | 240 | 241 | 242 | 245 | 246 | 247 | 248 |
    145 |

    Most important functions

    146 |

    147 |
    157 |

    read_eprime()

    158 |

    Read in a text file generated by Eprime

    163 |

    FrameList()

    164 |

    Convert lines from an Eprime file into EprimeFrame objects

    169 |

    filter_in() filter_out()

    170 |

    Filter levels in or out of a FrameList based on attribute values

    175 |

    keep_levels() drop_levels()

    176 |

    Filter levels in or out of a FrameList based on Eprime.Level values

    181 |

    to_data_frame()

    182 |

    Convert Eprime Frames into data-frames

    188 |

    Supporting functions

    189 |

    These help with the parsing of the text file.

    190 |
    200 |

    listify()

    201 |

    Convert a vector of colon-separated text lines into a list of named elements

    206 |

    extract_chunks()

    207 |

    Extract log-frames from an Eprime log file

    212 |

    preview_eprime() preview_levels() preview_frames()

    213 |

    Preview the levels in a parsed Eprime file

    219 |

    Objects

    220 |

    These objects represent chunks of data.

    221 |
    231 |

    EprimeFrame()

    232 |

    Create an EprimeFrame object

    237 |

    as.EprimeFrame()

    238 |

    Convert a list into an EprimeFrame object

    243 |

    as.FrameList()

    244 |

    Convert a list of EprimeFrames into a FrameList object

    249 |
    250 | 251 | 256 |
    257 | 258 | 259 |
    260 | 263 | 264 |
    265 |

    Site built with pkgdown 1.6.1.

    266 |
    267 | 268 |
    269 |
    270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /docs/reference/keep_levels.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Filter levels in or out of a FrameList based on Eprime.Level values — keep_levels • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
    64 |
    65 | 125 | 126 | 127 | 128 |
    129 | 130 |
    131 |
    132 | 137 | 138 |
    139 |

    These functions are shortcuts for calls to filter_in or 140 | filter_out.

    141 |
    142 | 143 |
    keep_levels(frame_list, level_numbers)
    144 | 
    145 | drop_levels(frame_list, level_numbers)
    146 | 147 |

    Arguments

    148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |
    frame_list

    a list of EprimeFrame objects

    level_numbers

    the whitelisted or blacklisted values for Eprime.Level

    159 | 160 |

    Value

    161 | 162 |

    for keep_levels, only log-frames where the level matches one 163 | of the level_numbers are kept. for keep_levels, log-frames 164 | where the level matches one of the level_numbers are omitted.

    165 |

    Details

    166 | 167 |

    Note that the meaning of Eprime.Level value in a log-frame ultimately is 168 | equal to one plus the number of tabs before each line in the log-frame.

    169 | 170 |
    171 | 176 |
    177 | 178 | 179 |
    180 | 183 | 184 |
    185 |

    Site built with pkgdown 1.6.1.

    186 |
    187 | 188 |
    189 |
    190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /docs/reference/list_functions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Higher-order functions for dealing with lists — list_functions • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    These functions were inspired by underscore.js.

    139 |
    140 | 141 |
    pluck(key)
    142 | 
    143 | pluck_apply(key, xss)
    144 | 
    145 | pick(keys)
    146 | 
    147 | pick_apply(keys, xss)
    148 | 149 |

    Arguments

    150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
    key

    the name of a value in a list

    xss

    a list of lists

    keys

    a character vector of names in a list

    165 | 166 |

    Value

    167 | 168 |

    pluck returns an unnamed value and pluck_apply returns 169 | a list of unnamed values. pick returns a simplified version of the 170 | original list. pick_apply returns a list of simplified lists.

    171 |

    Details

    172 | 173 | 174 |
      175 |
    • pluck: Pluck a named value from a list

    • 176 |
    • pick: Simplify a list by picking out whitelisted names

    • 177 |
    178 | 179 |

    The simple versions of pluck and pick are curried functions, 180 | meaning that they return a function which can be applied to a list. See the 181 | syntax in the usage section.

    182 | 183 |
    184 | 189 |
    190 | 191 | 192 |
    193 | 196 | 197 |
    198 |

    Site built with pkgdown 1.6.1.

    199 |
    200 | 201 |
    202 |
    203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /docs/reference/listify.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Convert a vector of colon-separated text lines into a list of named elements — listify • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Convert a vector of colon-separated text lines into a list of named elements

    139 |
    140 | 141 |
    listify(colon_sep_xs)
    142 | 143 |

    Arguments

    144 | 145 | 146 | 147 | 148 | 150 | 151 |
    colon_sep_xs

    a character vector with lines of the form 149 | "key: value"

    152 | 153 |

    Value

    154 | 155 |

    a named list of the values in the colon-separated lines. 156 | "key: value" yields list(key = "value")

    157 |

    Details

    158 | 159 |

    Some minor cleaning of the input is performed:

      160 |
    • Lines without a colon-space separator ": " are filtered out.

    • 161 |
    • Once the strings are split at the separator, white-space on the 162 | left and right sides of each half-string is omitted.

    • 163 |
    164 | 165 | 166 |
    167 | 172 |
    173 | 174 | 175 |
    176 | 179 | 180 |
    181 |

    Site built with pkgdown 1.6.1.

    182 |
    183 | 184 |
    185 |
    186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /docs/reference/preview_eprime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Preview the levels in a parsed Eprime file — preview_eprime • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Preview the levels in a parsed Eprime file

    139 |
    140 | 141 |
    preview_eprime(frame_list)
    142 | 
    143 | preview_levels(frame_list)
    144 | 
    145 | preview_frames(frame_list)
    146 | 147 |

    Arguments

    148 | 149 | 150 | 151 | 152 | 153 | 154 |
    frame_list

    a FrameList (a list of EprimeFrames)

    155 | 156 |

    Value

    157 | 158 |

    Nothing. Preview text is printed to the console.

    159 |

    Details

    160 | 161 |

    preview_levels prints out the unique combinations of 162 | Eprime.Level number, Procedure, and Running in the frame list. 163 | preview_frames prints out example frame from each of the unique 164 | levels. preview_eprime does both.

    165 | 166 |
    167 | 172 |
    173 | 174 | 175 |
    176 | 179 | 180 |
    181 |

    Site built with pkgdown 1.6.1.

    182 |
    183 | 184 |
    185 |
    186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /docs/reference/read_eprime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Read in a text file generated by Eprime — read_eprime • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Read in a text file generated by Eprime

    139 |
    140 | 141 |
    read_eprime(filename, remove_clock = TRUE)
    142 | 143 |

    Arguments

    144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 154 | 155 |
    filename

    Either the full or relative path to an Eprime .txt file

    remove_clock

    Whether to exclude the Clock.Information XML entries. 153 | Enabled by default.

    156 | 157 |

    Value

    158 | 159 |

    Each line of the file is stored and returned in a character vector.

    160 |

    Details

    161 | 162 |

    The encoding on an Eprime txt file should be UCS-2 Little Endian, 163 | but sometimes this is not the case. We delegate the fussy encoding details 164 | to the stringi::str_read_lines function.

    165 |

    If the file is not an Eprime txt--that is, if it is missing the lines 166 | *** Header Start *** and *** Header End ***--a warning is 167 | raised and the lines of text are replaced by a dummy header.

    168 | 169 |
    170 | 175 |
    176 | 177 | 178 |
    179 | 182 | 183 |
    184 |

    Site built with pkgdown 1.6.1.

    185 |
    186 | 187 |
    188 |
    189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /docs/reference/rprime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Functions for dealing with Eprime txt files — rprime • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
    66 |
    67 | 127 | 128 | 129 | 130 |
    131 | 132 |
    133 |
    134 | 139 | 140 |
    141 |

    'Eprime' is a set of programs for administering 142 | psychological experiments by computer. This package provides functions 143 | for loading, parsing, filtering and exporting data in the text files 144 | produced by 'Eprime' experiments.

    145 |
    146 | 147 | 148 | 149 |

    See also

    150 | 151 |

    Useful links:

    155 | 156 |
    157 |

    Author

    158 | 159 |

    Maintainer: Tristan Mahr tristan.mahr@wisc.edu (ORCID)

    160 | 161 |
    162 | 167 |
    168 | 169 | 170 |
    171 | 174 | 175 |
    176 |

    Site built with pkgdown 1.6.1.

    177 |
    178 | 179 |
    180 |
    181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /docs/reference/to_data_frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Convert Eprime Frames into data-frames — to_data_frame • rprime 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 124 | 125 | 126 | 127 |
    128 | 129 |
    130 |
    131 | 136 | 137 |
    138 |

    Convert Eprime Frames into data-frames

    139 |
    140 | 141 |
    to_data_frame(x)
    142 | 143 |

    Arguments

    144 | 145 | 146 | 147 | 148 | 150 | 151 |
    x

    an EprimeFrame object, or a FrameList object (a list of 149 | EprimeFrames)

    152 | 153 |

    Value

    154 | 155 |

    all of the EprimeFrames combined into a single data frame.

    156 |

    Details

    157 | 158 |

    Individual EprimeFrames are converted to a data-frame using 159 | as.data.frame. (Strings are not converted to factors.)

    160 |

    Each of the individual data-frames are then rbinded together, with 161 | missing columns being filled with NA.

    162 |

    See also

    163 | 164 |

    rbind.fill

    165 | 166 |
    167 | 172 |
    173 | 174 | 175 | 185 |
    186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /man/EprimeFrame.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/frames.R 3 | \name{EprimeFrame} 4 | \alias{EprimeFrame} 5 | \title{Create an EprimeFrame object} 6 | \usage{ 7 | EprimeFrame(keys_values) 8 | } 9 | \arguments{ 10 | \item{keys_values}{a character vector of containing some \code{"key: value"} 11 | strings.} 12 | } 13 | \value{ 14 | a list with the class \code{EprimeFrame} and with special 15 | \code{Eprime.} metadata, \code{Running} and \code{Procedure} values, all 16 | set to NA by default. 17 | } 18 | \description{ 19 | This constructor function converts a character vector into an 20 | \code{EprimeFrame} object, which is just a list with some special metadata 21 | values. Strings with the format \code{"key: value"} are parsed into \code{key 22 | = value} list items (via \code{listify}). 23 | } 24 | \examples{ 25 | # Default metadata values 26 | lines <- c( 27 | "key: value", 28 | "question: answer", 29 | "garbage text") 30 | 31 | EprimeFrame(lines) 32 | # List of 8 33 | # $ Eprime.Level : num 1 34 | # $ Eprime.LevelName : logi NA 35 | # $ Eprime.Basename : logi NA 36 | # $ Eprime.FrameNumber: logi NA 37 | # $ Procedure : logi NA 38 | # $ Running : logi NA 39 | # $ key : chr "value" 40 | # $ question : chr "answer" 41 | 42 | # Normalize [Running] related lines 43 | keys_values <- c( 44 | "Running: Demo", 45 | "Demo: ExampleCode", 46 | "Demo.Cycle: 1", 47 | "Demo.Sample: 1", 48 | "Key: Value") 49 | 50 | EprimeFrame(keys_values) 51 | # List of 9 52 | # $ Eprime.Level : num 1 53 | # $ Eprime.LevelName : chr "Demo_ExampleCode" 54 | # $ Eprime.Basename : logi NA 55 | # $ Eprime.FrameNumber: logi NA 56 | # $ Procedure : logi NA 57 | # $ Running : chr "Demo" 58 | # $ Cycle : chr "1" 59 | # $ Sample : chr "1" 60 | # $ Key : chr "Value" 61 | } 62 | -------------------------------------------------------------------------------- /man/FrameList.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/frames.R 3 | \name{FrameList} 4 | \alias{FrameList} 5 | \title{Convert lines from an Eprime file into EprimeFrame objects} 6 | \usage{ 7 | FrameList(x) 8 | } 9 | \arguments{ 10 | \item{x}{a character vector with lines of the form \code{"key: value"}, or a 11 | list of vectors of colon-separated text} 12 | } 13 | \value{ 14 | When passed a list of character vectors of \code{"key: value"} lines, 15 | a FrameList object (a list of EprimeFrames) is returned. when passed a 16 | single vector vector of \code{"key: value"} lines, a single EprimeFrame 17 | object is returned inside of a FrameList object. 18 | } 19 | \description{ 20 | Convert character vectors of implicit key-value pairs (e.g., \code{c("key1: 21 | value1", "key2: value2")}), into lists of explicit key-value pairs, 22 | \code{list(key1 = "value1", key2 = "value2")}. 23 | } 24 | \details{ 25 | During the conversion, if \code{Running: x}, then the 26 | \code{x.Sample} and \code{x.Cycle} lines are simplified into \code{Sample} 27 | and \code{Cycle} lines. The \code{x: value} line is recoded as 28 | \code{Eprime.LevelName: x_value}. The purpose of this tidying is to force 29 | the same set of key names (eventually, column names) onto frames with 30 | different values for "Running". 31 | } 32 | \examples{ 33 | lines <- c("\t*** LogFrame Start ***", 34 | "\tProcedure: FamTask", 35 | "\titem1: bear", 36 | "\titem2: chair", 37 | "\tCorrectResponse: bear", 38 | "\tImageSide: Left", 39 | "\tDuration: 885", 40 | "\tFamiliarization: 1", 41 | "\tFamInforcer: 1", 42 | "\tReinforcerImage: Bicycle1", 43 | "\tFamiliarization.Cycle: 1", 44 | "\tFamiliarization.Sample: 1", 45 | "\tRunning: Familiarization", 46 | "\tFamTarget.RESP: ", 47 | "\tCorrect: True", 48 | "\t*** LogFrame End ***") 49 | # List of 1 50 | # $ :List of 17 51 | # ..$ Eprime.Level : num 2 52 | # ..$ Eprime.LevelName : chr "Familiarization_1" 53 | # ..$ Eprime.Basename : chr "NA" 54 | # ..$ Eprime.FrameNumber: chr "1" 55 | # ..$ Procedure : chr "FamTask" 56 | # ..$ Running : chr "Familiarization" 57 | # ..$ item1 : chr "bear" 58 | # ..$ item2 : chr "chair" 59 | # ..$ CorrectResponse : chr "bear" 60 | # ..$ ImageSide : chr "Left" 61 | # ..$ Duration : chr "885" 62 | # ..$ FamInforcer : chr "1" 63 | # ..$ ReinforcerImage : chr "Bicycle1" 64 | # ..$ Cycle : chr "1" 65 | # ..$ Sample : chr "1" 66 | # ..$ FamTarget.RESP : chr "" 67 | # ..$ Correct : chr "True" 68 | # ..- attr(*, "class")= chr [1:2] "EprimeFrame" "list" 69 | # - attr(*, "class")= chr [1:2] "list" "FrameList" 70 | } 71 | -------------------------------------------------------------------------------- /man/as.EprimeFrame.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/frames.R 3 | \name{as.EprimeFrame} 4 | \alias{as.EprimeFrame} 5 | \title{Convert a list into an EprimeFrame object} 6 | \usage{ 7 | as.EprimeFrame(xs) 8 | } 9 | \arguments{ 10 | \item{xs}{a list} 11 | } 12 | \value{ 13 | the original list as an \code{EprimeFrame} object (along with dummy 14 | Eprime metadata fields) 15 | } 16 | \description{ 17 | Convert a list into an EprimeFrame object 18 | } 19 | -------------------------------------------------------------------------------- /man/as.FrameList.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/frames.R 3 | \name{as.FrameList} 4 | \alias{as.FrameList} 5 | \title{Convert a list of EprimeFrames into a FrameList object} 6 | \usage{ 7 | as.FrameList(xs) 8 | } 9 | \arguments{ 10 | \item{xs}{a list of EprimeFrames} 11 | } 12 | \value{ 13 | the original list as a \code{FrameList} object 14 | } 15 | \description{ 16 | Convert a list of EprimeFrames into a FrameList object 17 | } 18 | -------------------------------------------------------------------------------- /man/extract_chunks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extract.R 3 | \name{extract_chunks} 4 | \alias{extract_chunks} 5 | \title{Extract log-frames from an Eprime log file} 6 | \usage{ 7 | extract_chunks(eprime_log) 8 | } 9 | \arguments{ 10 | \item{eprime_log}{a character vector containing the lines of text from Eprime 11 | txt file} 12 | } 13 | \value{ 14 | a list of character vectors, where each vector contains the lines of 15 | a log-frame 16 | } 17 | \description{ 18 | Almost all of the information in an Eprime file comes in chunks of text 19 | bracketed by the lines \code{*** LogFrame Start ***} and \code{*** LogFrame 20 | End ***}. The exception is the header information which is bracketed by 21 | \code{*** Header Start ***} and \code{*** Header End ***}. 22 | } 23 | \details{ 24 | \code{extract_chunks} extracts the bracketed text, storing each log-frame of 25 | text in a list. The lists also include some additional lines of text as 26 | metadata: \code{Eprime.FrameNumber} and \code{Eprime.Basename} (the name of 27 | the source file). The header log-frame also gets dummy lines: 28 | \code{Procedure: Header} and \code{Running: Header}. 29 | 30 | These character vectors of colon-separated lines are converted into proper 31 | lists by \code{FrameList(...)}. 32 | } 33 | -------------------------------------------------------------------------------- /man/filter_in.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filter.R 3 | \name{filter_in} 4 | \alias{filter_in} 5 | \alias{filter_out} 6 | \title{Filter levels in or out of a FrameList based on attribute values} 7 | \usage{ 8 | filter_in(frame_list, key, values) 9 | 10 | filter_out(frame_list, key, values) 11 | } 12 | \arguments{ 13 | \item{frame_list}{a list of \code{EprimeFrame} objects} 14 | 15 | \item{key}{the name of the attribute to filter in or out} 16 | 17 | \item{values}{the whitelisted or blacklisted values of the attribute} 18 | } 19 | \value{ 20 | for \code{filter_in}, only log-frames where \code{key} is one of the 21 | \code{values} are kept. for \code{filter_out}, log-frames where \code{key} 22 | is one of the \code{values} are omitted. 23 | } 24 | \description{ 25 | Filter levels in or out of a FrameList based on attribute values 26 | } 27 | -------------------------------------------------------------------------------- /man/keep_levels.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filter.R 3 | \name{keep_levels} 4 | \alias{keep_levels} 5 | \alias{drop_levels} 6 | \title{Filter levels in or out of a FrameList based on Eprime.Level values} 7 | \usage{ 8 | keep_levels(frame_list, level_numbers) 9 | 10 | drop_levels(frame_list, level_numbers) 11 | } 12 | \arguments{ 13 | \item{frame_list}{a list of \code{EprimeFrame} objects} 14 | 15 | \item{level_numbers}{the whitelisted or blacklisted values for Eprime.Level} 16 | } 17 | \value{ 18 | for \code{keep_levels}, only log-frames where the level matches one 19 | of the \code{level_numbers} are kept. for \code{keep_levels}, log-frames 20 | where the level matches one of the \code{level_numbers} are omitted. 21 | } 22 | \description{ 23 | These functions are shortcuts for calls to \code{filter_in} or 24 | \code{filter_out}. 25 | } 26 | \details{ 27 | Note that the meaning of Eprime.Level value in a log-frame ultimately is 28 | equal to one plus the number of tabs before each line in the log-frame. 29 | } 30 | -------------------------------------------------------------------------------- /man/list_functions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{list_functions} 4 | \alias{list_functions} 5 | \alias{pluck} 6 | \alias{pluck_apply} 7 | \alias{pick} 8 | \alias{pick_apply} 9 | \title{Higher-order functions for dealing with lists} 10 | \usage{ 11 | pluck(key) 12 | 13 | pluck_apply(key, xss) 14 | 15 | pick(keys) 16 | 17 | pick_apply(keys, xss) 18 | } 19 | \arguments{ 20 | \item{key}{the name of a value in a list} 21 | 22 | \item{xss}{a list of lists} 23 | 24 | \item{keys}{a character vector of names in a list} 25 | } 26 | \value{ 27 | \code{pluck} returns an unnamed value and \code{pluck_apply} returns 28 | a list of unnamed values. \code{pick} returns a simplified version of the 29 | original list. \code{pick_apply} returns a list of simplified lists. 30 | } 31 | \description{ 32 | These functions were inspired by underscore.js. 33 | } 34 | \details{ 35 | \itemize{ \item \code{pluck}: Pluck a named value from a list \item 36 | \code{pick}: Simplify a list by picking out whitelisted names} 37 | 38 | The simple versions of \code{pluck} and \code{pick} are curried functions, 39 | meaning that they return a function which can be applied to a list. See the 40 | syntax in the usage section. 41 | } 42 | \keyword{internal} 43 | -------------------------------------------------------------------------------- /man/listify.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/frames.R 3 | \name{listify} 4 | \alias{listify} 5 | \title{Convert a vector of colon-separated text lines into a list of named elements} 6 | \usage{ 7 | listify(colon_sep_xs) 8 | } 9 | \arguments{ 10 | \item{colon_sep_xs}{a character vector with lines of the form 11 | \code{"key: value"}} 12 | } 13 | \value{ 14 | a named list of the values in the colon-separated lines. 15 | \code{"key: value"} yields \code{list(key = "value")} 16 | } 17 | \description{ 18 | Convert a vector of colon-separated text lines into a list of named elements 19 | } 20 | \details{ 21 | Some minor cleaning of the input is performed: 22 | \itemize{ 23 | \item Lines without a colon-space separator \code{": "} are filtered out. 24 | \item Once the strings are split at the separator, white-space on the 25 | left and right sides of each half-string is omitted.} 26 | } 27 | -------------------------------------------------------------------------------- /man/preview_eprime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/preview.R 3 | \name{preview_eprime} 4 | \alias{preview_eprime} 5 | \alias{preview_levels} 6 | \alias{preview_frames} 7 | \title{Preview the levels in a parsed Eprime file} 8 | \usage{ 9 | preview_eprime(frame_list) 10 | 11 | preview_levels(frame_list) 12 | 13 | preview_frames(frame_list) 14 | } 15 | \arguments{ 16 | \item{frame_list}{a FrameList (a list of EprimeFrames)} 17 | } 18 | \value{ 19 | Nothing. Preview text is printed to the console. 20 | } 21 | \description{ 22 | Preview the levels in a parsed Eprime file 23 | } 24 | \details{ 25 | \code{preview_levels} prints out the unique combinations of 26 | Eprime.Level number, Procedure, and Running in the frame list. 27 | \code{preview_frames} prints out example frame from each of the unique 28 | levels. \code{preview_eprime} does both. 29 | } 30 | -------------------------------------------------------------------------------- /man/read_eprime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/load.R 3 | \name{read_eprime} 4 | \alias{read_eprime} 5 | \title{Read in a text file generated by Eprime} 6 | \usage{ 7 | read_eprime(filename, remove_clock = TRUE) 8 | } 9 | \arguments{ 10 | \item{filename}{Either the full or relative path to an Eprime .txt file} 11 | 12 | \item{remove_clock}{Whether to exclude the Clock.Information XML entries. 13 | Enabled by default.} 14 | } 15 | \value{ 16 | Each line of the file is stored and returned in a character vector. 17 | } 18 | \description{ 19 | Read in a text file generated by Eprime 20 | } 21 | \details{ 22 | The encoding on an Eprime txt file should be UCS-2 Little Endian, 23 | but sometimes this is not the case. We delegate the fussy encoding details 24 | to the \code{stringi::str_read_lines} function. 25 | 26 | If the file is not an Eprime txt--that is, if it is missing the lines 27 | \code{*** Header Start ***} and \code{*** Header End ***}--a warning is 28 | raised and the lines of text are replaced by a dummy header. 29 | } 30 | -------------------------------------------------------------------------------- /man/rprime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rprime-package.R 3 | \docType{package} 4 | \name{rprime} 5 | \alias{rprime-package} 6 | \alias{rprime} 7 | \title{Functions for dealing with Eprime txt files} 8 | \description{ 9 | 'Eprime' is a set of programs for administering psychological experiments by computer. This package provides functions for loading, parsing, filtering and exporting data in the text files produced by 'Eprime' experiments. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/tjmahr/rprime} 15 | \item Report bugs at \url{https://github.com/tjmahr/rprime/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Tristan Mahr \email{tristan.mahr@wisc.edu} (\href{https://orcid.org/0000-0002-8890-5116}{ORCID}) 21 | 22 | } 23 | \keyword{internal} 24 | -------------------------------------------------------------------------------- /man/to_data_frame.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/to_data_frame.R 3 | \name{to_data_frame} 4 | \alias{to_data_frame} 5 | \title{Convert Eprime Frames into data-frames} 6 | \usage{ 7 | to_data_frame(x) 8 | } 9 | \arguments{ 10 | \item{x}{an EprimeFrame object, or a FrameList object (a list of 11 | EprimeFrames)} 12 | } 13 | \value{ 14 | all of the EprimeFrames combined into a single data frame. 15 | } 16 | \description{ 17 | Convert Eprime Frames into data-frames 18 | } 19 | \details{ 20 | Individual EprimeFrames are converted to a data-frame using 21 | \code{as.data.frame()}. (Strings are not converted to factors.) 22 | 23 | Each of the individual data-frames are then \code{rbind()}-ed together, with 24 | missing columns being filled with NA. 25 | } 26 | \seealso{ 27 | \code{\link[plyr:rbind.fill]{plyr::rbind.fill()}} 28 | } 29 | -------------------------------------------------------------------------------- /rprime.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: f6d7629a-394d-4e43-b0bd-f5ff388a5d0e 3 | 4 | RestoreWorkspace: Default 5 | SaveWorkspace: Default 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: knitr 14 | LaTeX: XeLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageCheckArgs: --as-cran 23 | PackageRoxygenize: rd,collate,namespace 24 | -------------------------------------------------------------------------------- /tests/test-all.R: -------------------------------------------------------------------------------- 1 | library("testthat") 2 | test_check("rprime") 3 | -------------------------------------------------------------------------------- /tests/testthat/data/Coartic_Block1_001P00XS1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/tests/testthat/data/Coartic_Block1_001P00XS1.txt -------------------------------------------------------------------------------- /tests/testthat/data/MINP_001L00XS1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/tests/testthat/data/MINP_001L00XS1.txt -------------------------------------------------------------------------------- /tests/testthat/data/MP_Block1_001P00XA1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/tests/testthat/data/MP_Block1_001P00XA1.txt -------------------------------------------------------------------------------- /tests/testthat/data/NonWordRep_001X00XS1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/tests/testthat/data/NonWordRep_001X00XS1.txt -------------------------------------------------------------------------------- /tests/testthat/data/not_an_eprime_file.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla a semper quam. Cras tincidunt, turpis a congue imperdiet, arcu metus consectetur risus, sed bibendum dolor ipsum sed magna. 2 | 3 | Pellentesque vitae purus sed est elementum pharetra. Proin egestas dui fringilla, sodales mi vitae, molestie mauris. 4 | 5 | Nulla facilisi. Maecenas non sapien eget nisl placerat pharetra. Sed id augue enim. -------------------------------------------------------------------------------- /tests/testthat/test-eprime-frame.R: -------------------------------------------------------------------------------- 1 | ## Test functions that create lists from character vectors of colon-separated 2 | ## values. 3 | 4 | # expect_identical lists to have the same order 5 | expect_nearly_identical <- function(x, y, ...) { 6 | expect_identical(sort_names(x), sort_names(y), ...) 7 | } 8 | sort_names <- function(x) x[sort(names(x))] 9 | 10 | 11 | context("EprimeFrame") 12 | 13 | test_that("Default values", { 14 | # Empty EprimeFrame 15 | default_list <- structure(list( 16 | Eprime.LevelName = NA, 17 | Eprime.Level = NA, 18 | Eprime.Basename = NA, 19 | Eprime.FrameNumber = NA, 20 | Procedure = NA, 21 | Running = NA), class = c("EprimeFrame", "list")) 22 | 23 | expect_nearly_identical(as.EprimeFrame(list()), default_list) 24 | 25 | # It's not possible to have an NA Eprime.Level when constructing from a 26 | # character vector 27 | default_character <- default_list 28 | default_character$Eprime.Level <- 1 29 | expect_nearly_identical(EprimeFrame(character()), default_character) 30 | }) 31 | 32 | test_that("Simple construction", { 33 | test_values <- c( 34 | "key: value", 35 | "question: answer", 36 | "garbage text") 37 | 38 | expected_list <- structure(list( 39 | Eprime.LevelName = NA, 40 | Eprime.Level = 1, 41 | Eprime.Basename = NA, 42 | Eprime.FrameNumber = NA, 43 | Procedure = NA, 44 | Running = NA, 45 | key = "value", 46 | question = "answer"), class = c("EprimeFrame", "list")) 47 | 48 | expect_nearly_identical(EprimeFrame(test_values), expected_list) 49 | }) 50 | 51 | test_that("Running-related values are normalized", { 52 | keys_values <- c( 53 | "Running: Demo", 54 | "Demo: ExampleCode", 55 | "Demo.Cycle: 1", 56 | "Demo.Sample: 1", 57 | "Key: Value") 58 | 59 | normalized_running <- structure(list( 60 | Eprime.Level = 1, 61 | Eprime.LevelName = "Demo_ExampleCode", 62 | Eprime.Basename = NA, 63 | Eprime.FrameNumber = NA, 64 | Procedure = NA, 65 | Running = "Demo", 66 | Cycle = "1", 67 | Sample = "1", 68 | Key = "Value"), class = c("EprimeFrame", "list")) 69 | 70 | expect_nearly_identical(EprimeFrame(keys_values), normalized_running) 71 | }) 72 | 73 | 74 | test_that("listify handles typical data", { 75 | example_lines <- c( 76 | "\t*** LogFrame Start ***", 77 | "\tProcedure: PracticeProc", 78 | "\tStimulus1: toothbrush", 79 | "\t*** LogFrame End ***") 80 | 81 | expected_example <- list( 82 | Procedure = "PracticeProc", 83 | Stimulus1 = "toothbrush") 84 | 85 | expect_identical(listify(example_lines), expected_example) 86 | }) 87 | 88 | test_that("listify handles garbage values", { 89 | garbage_lines <- c("", NA, 90 | "\t*** LogFrame Start ***", 91 | "*** LogFrame End ***") 92 | 93 | usable_lines <- c( 94 | "Item: 1", 95 | "\tWhiteSpaceItem: Stuff ", 96 | "AnotherItem: Two Colons: Yes", 97 | "NoValue: ", 98 | "Last Item: Whatever") 99 | 100 | expected <- list( 101 | Item = "1", 102 | WhiteSpaceItem = "Stuff", 103 | AnotherItem = "Two Colons: Yes", 104 | NoValue = "", 105 | `Last Item` = "Whatever") 106 | expect_identical(listify(c(garbage_lines, usable_lines)), expected) 107 | 108 | }) 109 | -------------------------------------------------------------------------------- /tests/testthat/test-filter.R: -------------------------------------------------------------------------------- 1 | # Test files 2 | good_file <- "data/MINP_001L00XS1.txt" 3 | 4 | context("Filtering functions") 5 | 6 | test_that("filter in/out", { 7 | eprime_log <- read_eprime(good_file) 8 | chunked <- FrameList(eprime_log) 9 | 10 | just_header <- filter_in(chunked, "Running", "Header") 11 | header_frame <- just_header[[1]] 12 | header_df <- to_data_frame(just_header) 13 | 14 | # There should just be one frame when we filter to just the header 15 | expect_equal(length(just_header), 1) 16 | expect_equal(nrow(header_df), 1) 17 | expect_equal(header_frame$Running, "Header") 18 | 19 | # Filter down to just the Familiarization trials 20 | level_2 <- filter_in(chunked, "Eprime.Level", 2) 21 | just_practice <- filter_out(level_2, "Running", "Test") 22 | practice_df <- to_data_frame(just_practice) 23 | level_2_df <- to_data_frame(level_2) 24 | 25 | # There are six familiarization trials 26 | expect_equal(length(just_practice), 6) 27 | expect_equal(nrow(practice_df), 6) 28 | expect_equal(unique(practice_df$Running), "Familiarization") 29 | 30 | # Filtering with multiple values 31 | keys_peas <- filter_in(level_2, "CorrectResponse", c("keys", "peas")) 32 | keys_peas_df <- to_data_frame(keys_peas) 33 | # Test against subset 34 | keys_peas_rows <- nrow(subset(level_2_df, CorrectResponse %in% c("keys", "peas"))) 35 | expect_equal(nrow(keys_peas_df), keys_peas_rows) 36 | }) 37 | 38 | test_that("keep/drop levels", { 39 | eprime_log <- read_eprime(good_file) 40 | chunked <- FrameList(eprime_log) 41 | df_full <- to_data_frame(chunked) 42 | 43 | just_level_1 <- keep_levels(chunked, 1) 44 | df_level_1 <- to_data_frame(just_level_1) 45 | 46 | just_level_2 <- keep_levels(chunked, 2) 47 | df_level_2 <- to_data_frame(just_level_2) 48 | 49 | # Levels 1 and 2 partition the trials 50 | expect_equal(nrow(df_level_1) + nrow(df_level_2), nrow(df_full)) 51 | 52 | # Dropping level 2 and keeping level 1 should return the same frames. Test by 53 | # comparing the dfs (need to omit NAs first to allow comparison.) 54 | not_level_2 <- drop_levels(chunked, 2) 55 | df_not_level_2 <- to_data_frame(not_level_2) 56 | 57 | remove_na_columns <- function(df) Filter(function(x) !any(is.na(x)), df) 58 | df_level_1 <- remove_na_columns(df_level_1) 59 | df_not_level_2 <- remove_na_columns(df_not_level_2) 60 | expect_equal(df_not_level_2, df_level_1) 61 | 62 | }) 63 | -------------------------------------------------------------------------------- /tests/testthat/test-load.R: -------------------------------------------------------------------------------- 1 | # Helpers 2 | first <- function(...) head(..., n = 1) 3 | last <- function(...) tail(..., n = 1) 4 | first_line_header <- function(x) first(x) == "*** Header Start ***" 5 | count_blanks <- function(xs) sum(stringr::str_detect(xs, "^$")) 6 | 7 | # Test files 8 | good_file <- "data/MINP_001L00XS1.txt" 9 | bad_encoding <- "data/Blending_001L00XS4.txt" 10 | crashed_experiment <- "data/MP_Block1_001P00XA1.txt" 11 | no_footer <- "data/Coartic_Block1_001P00XS1.txt" 12 | not_an_eprime_file <- "data/not_an_eprime_file.txt" 13 | 14 | 15 | context("Reading standard files") 16 | 17 | test_that("Load well-formed data", { 18 | eprime_log <- read_eprime(good_file, remove_clock = FALSE) 19 | expect_equal(first(eprime_log), "*** Header Start ***") 20 | expect_equal(eprime_log[29], "\t*** LogFrame Start ***") 21 | expect_equal(last(eprime_log), "*** LogFrame End ***") 22 | 23 | # Removing the clock removes 2 lines, one of which is in the header 24 | no_clock <- read_eprime(good_file, remove_clock = TRUE) 25 | expect_equal(no_clock[28], "\t*** LogFrame Start ***") 26 | expect_equal(length(no_clock) + 2, length(eprime_log)) 27 | }) 28 | 29 | 30 | context("Reading non-standard files") 31 | 32 | test_that("load file with unexpected encoding", { 33 | # no warnings 34 | warnings <- evaluate_promise(read_eprime(bad_encoding))$warnings 35 | expect_equal(length(warnings), 0) 36 | 37 | # first line header and no blank lines 38 | bad_encoding_lines <- read_eprime(bad_encoding) 39 | expect_true(first_line_header(bad_encoding_lines)) 40 | expect_lt(count_blanks(bad_encoding_lines), 1) 41 | }) 42 | 43 | test_that("load file from a crashed experiment", { 44 | # no warnings 45 | warnings <- evaluate_promise(read_eprime(crashed_experiment))$warnings 46 | expect_equal(length(warnings), 0) 47 | 48 | # first line header and no blank lines 49 | crashed_experiment_lines <- read_eprime(crashed_experiment) 50 | expect_true(first_line_header(crashed_experiment_lines)) 51 | expect_lt(count_blanks(crashed_experiment_lines), 1) 52 | }) 53 | 54 | test_that("non-eprime file raises warning", { 55 | expect_warning(read_eprime(not_an_eprime_file), "not an Eprime txt file") 56 | }) 57 | 58 | test_that("non-eprime file uses dummy text", { 59 | # first line header 60 | not_an_eprime_file_lines <- suppressWarnings(read_eprime(not_an_eprime_file)) 61 | expect_true(first_line_header(not_an_eprime_file_lines)) 62 | }) 63 | -------------------------------------------------------------------------------- /tests/testthat/test-print.R: -------------------------------------------------------------------------------- 1 | # Test files 2 | good_file <- "data/Blending_001L00XS4.txt" 3 | 4 | context("Printing and previewing data") 5 | 6 | test_that("preview levels", { 7 | eprime_log <- read_eprime(good_file) 8 | chunked <- FrameList(eprime_log) 9 | output <- capture.output(preview_levels(chunked)) 10 | 11 | # This file has 5 levels 12 | expect_equal(length(output), 5 + 2) 13 | expect_equal(output[1], "Level Counts: ") 14 | }) 15 | 16 | test_that("preview frames", { 17 | eprime_log <- read_eprime(good_file) 18 | chunked <- FrameList(eprime_log) 19 | output <- capture.output(preview_frames(chunked)) 20 | 21 | # First chunk is the Header 22 | expect_true(output[2] == " Eprime.Level Running Procedure") 23 | expect_true(output[3] == " 1 Header Header") 24 | }) 25 | 26 | test_that("preview eprime combines level and frame previews", { 27 | eprime_log <- read_eprime(good_file) 28 | chunked <- FrameList(eprime_log) 29 | output <- capture.output(preview_eprime(chunked)) 30 | output_levels <- capture.output(preview_levels(chunked)) 31 | output_frames <- capture.output(preview_frames(chunked)) 32 | 33 | expect_true(all(output == c(output_levels, output_frames))) 34 | }) 35 | 36 | 37 | test_that("print an EprimeFrame", { 38 | eprime_log <- read_eprime(good_file) 39 | chunked <- FrameList(eprime_log) 40 | 41 | # Print the frame 42 | frame <- chunked[[1]] 43 | printed_frame <- capture.output(frame) 44 | 45 | # Check specific lines in the print function 46 | proc_line <- ' $ Procedure : chr "Header"' 47 | classes <- ' - attr(*, "class")= chr [1:2] "EprimeFrame" "list"' 48 | expect_equal(printed_frame[5 + 1], proc_line) 49 | expect_equal(printed_frame[22 + 2], classes) 50 | }) 51 | 52 | test_that("print a FrameList", { 53 | eprime_log <- read_eprime(good_file) 54 | chunked <- FrameList(eprime_log) 55 | 56 | # Print the frame 57 | printed_list <- capture.output(chunked) 58 | nlines <- length(printed_list) 59 | 60 | # Check specific lines in the print function 61 | l1 <- "List of 25" 62 | l2 <- " $ :List of 22" 63 | last_1 <- ' ..- attr(*, "class")= chr [1:2] "EprimeFrame" "list"' 64 | last_2 <- ' - attr(*, "class")= chr [1:2] "list" "FrameList"' 65 | 66 | expect_equal(printed_list[1], l1) 67 | expect_equal(printed_list[2], l2) 68 | expect_equal(printed_list[nlines - 1], last_1) 69 | expect_equal(printed_list[nlines], last_2) 70 | }) 71 | 72 | -------------------------------------------------------------------------------- /vignettes/data/MINP_001L00XS1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/vignettes/data/MINP_001L00XS1.txt -------------------------------------------------------------------------------- /vignettes/data/MP_Block1_001P00XA1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/vignettes/data/MP_Block1_001P00XA1.txt -------------------------------------------------------------------------------- /vignettes/data/SAILS/SAILS_001X00XS1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/vignettes/data/SAILS/SAILS_001X00XS1.txt -------------------------------------------------------------------------------- /vignettes/data/SAILS/SAILS_002X00XS1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjmahr/rprime/c26f2a445f7da3910cb1d12bc1a31cc8a462543c/vignettes/data/SAILS/SAILS_002X00XS1.txt -------------------------------------------------------------------------------- /vignettes/data/not_an_eprime_file.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla a semper quam. Cras tincidunt, turpis a congue imperdiet, arcu metus consectetur risus, sed bibendum dolor ipsum sed magna. 2 | 3 | Pellentesque vitae purus sed est elementum pharetra. Proin egestas dui fringilla, sodales mi vitae, molestie mauris. 4 | 5 | Nulla facilisi. Maecenas non sapien eget nisl placerat pharetra. Sed id augue enim. -------------------------------------------------------------------------------- /vignettes/multiple-files.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Working with multiple files" 3 | author: "Tristan Mahr" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Working with multiple files} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | \usepackage[utf8]{inputenc} 10 | --- 11 | 12 | ```{r, echo = FALSE, message = FALSE} 13 | library("rprime") 14 | library("knitr") 15 | opts_chunk$set( 16 | comment = "#>", 17 | error = FALSE, 18 | tidy = FALSE, 19 | collapse = TRUE) 20 | ``` 21 | 22 | Suppose we want to load all the Eprime files in a directory and combine the 23 | results in dataframe. 24 | 25 | My strategy in this scenario is to figure out what I need to do for a single 26 | file and then wrap those steps in a function that takes a filepath to a txt 27 | file and returns a dataframe. After some exploration and interactive 28 | programming, I come up with the following function. 29 | 30 | ```{r} 31 | library("plyr") 32 | reduce_sails <- function(sails_path) { 33 | sails_lines <- read_eprime(sails_path) 34 | sails_frames <- FrameList(sails_lines) 35 | 36 | # Trials occur at level 3 37 | sails_frames <- keep_levels(sails_frames, 3) 38 | sails <- to_data_frame(sails_frames) 39 | 40 | # Tidy up 41 | to_pick <- c("Eprime.Basename", "Running", "Module", "Sound", 42 | "Sample", "Correct", "Response") 43 | sails <- sails[to_pick] 44 | running_map <- c(TrialLists = "Trial", PracticeBlock = "Practice") 45 | sails$Running <- running_map[sails$Running] 46 | 47 | # Renumber trials in the practice and experimental blocks separately. 48 | # Numerically code correct response. 49 | sails <- ddply(sails, .(Running), mutate, 50 | TrialNumber = seq(from = 1, to = length(Running)), 51 | CorrectResponse = ifelse(Correct == Response, 1, 0)) 52 | sails$Sample <- NULL 53 | 54 | # Optionally, one might save the processed file via: 55 | # csv <- paste0(file_path_sans_ext(sails_path), ".csv") 56 | # write.csv(sails, csv, row.names = FALSE) 57 | sails 58 | } 59 | ``` 60 | 61 | Here's a preview of what the function returns when given a filepath. 62 | 63 | ```{r} 64 | head(reduce_sails("data/SAILS/SAILS_001X00XS1.txt")) 65 | ``` 66 | 67 | Now that the function works on one file, I can use `ldply` to apply the 68 | function to several files, returning results in a single dataframe. (For 69 | `dplyr`, I would `lapply` the function to each path to get a list of 70 | dataframes, then use `bind_rows` to combine into a single dataframe.) 71 | 72 | ```{r} 73 | sails_paths <- list.files("data/SAILS/", pattern = ".txt", full.names = TRUE) 74 | sails_paths 75 | ensemble <- ldply(sails_paths, reduce_sails) 76 | ``` 77 | 78 | Finally, with all of the subjects' data contained in a single dataframe, I can 79 | use `ddply` plus `summarise` and compute summary scores at different levels of 80 | aggregation within each subject. 81 | 82 | ```{r} 83 | # Score trials within subjects 84 | overall <- ddply(ensemble, .(Eprime.Basename, Running), summarise, 85 | Score = sum(CorrectResponse), 86 | PropCorrect = Score / length(CorrectResponse)) 87 | overall 88 | 89 | # Score modules within subjects 90 | modules <- ddply(ensemble, .(Eprime.Basename, Running, Module), summarise, 91 | Score = sum(CorrectResponse), 92 | PropCorrect = mean(CorrectResponse)) 93 | modules 94 | ``` 95 | -------------------------------------------------------------------------------- /vignettes/parsing-summary.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Parsing summary" 3 | author: "Tristan Mahr" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Parsing summary} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | \usepackage[utf8]{inputenc} 10 | --- 11 | 12 | ```{r, echo = FALSE, message = FALSE} 13 | library("rprime") 14 | library("knitr") 15 | opts_chunk$set( 16 | comment = "#>", 17 | error = FALSE, 18 | tidy = FALSE, 19 | collapse = TRUE) 20 | ``` 21 | 22 | Eprime, or more officially E-Prime® 2.0, is a set of programs by [Psychology 23 | Software Tools, Inc.](https://www.pstnet.com/) used for running psychological 24 | experiments. Data from experiment sessions are saved into two files: 1) a 25 | proprietary binary `.edat2` file, and 2) a plain-text `.txt` file. (File formats 26 | used by Eprime are documented on the site at this URL: 27 | `https://support.pstnet.com/hc/en-us/articles/229354727-INFO-E-Prime-file-extensions-18091`.) 28 | The rprime package provides a set of functions for parsing these `.txt` files 29 | inside R. 30 | 31 | This vignette documents how rprime parses an Eprime text file and the changes 32 | it makes during that process. **See the quick-start vignette for examples of 33 | how to use this package.** 34 | 35 | **Disclaimer**: I write this documentation as someone who has spent little time 36 | creating and designing experiments in Eprime but has spent a lot of time 37 | parsing Eprime text files in R. Therefore, this vignette is not documentation 38 | for Eprime software or its data format. Instead, this vignette just outlines 39 | this package's approach to extracting data from Eprime text files. 40 | 41 | 42 | ## Background 43 | 44 | An Eprime `txt` file is a series of **frames**. Each frame is a list of 45 | key-value pairs. Frames are bracketed by special start and stop lines. 46 | 47 | Each file begins with a special **Header** frame. The header is set off by the 48 | brackets `*** Header Start***` and `*** Header End***`. The fields in the 49 | header describe the basic settings of the experiment. A header frame looks like 50 | something this (the several-hundred character-long `Clock.Information` line has 51 | been omitted): 52 | 53 | ``` 54 | *** Header Start *** 55 | VersionPersist: 1 56 | LevelName: Session 57 | LevelName: Block 58 | LevelName: Trial 59 | LevelName: SubTrial 60 | LevelName: LogLevel5 61 | LevelName: LogLevel6 62 | LevelName: LogLevel7 63 | LevelName: LogLevel8 64 | LevelName: LogLevel9 65 | LevelName: LogLevel10 66 | Experiment: SAE_MinimalPairDiscrim 67 | SessionDate: 07-10-2013 68 | SessionTime: 10:46:22 69 | SessionTimeUtc: 3:46:22 PM 70 | Dialect: Yes 71 | Subject: 001 72 | ExpTyp: L 73 | Age: 00 74 | Gender: X 75 | Session: 1 76 | RandomSeed: -261296366 77 | Group: 1 78 | Display.RefreshRate: 60.003 79 | *** Header End *** 80 | ``` 81 | 82 | The other frames in the text file are **LogFrames**. These record data about 83 | the individual procedures or trials are executed during the experiment. The 84 | following lines immediately follow the header from the previous example: 85 | 86 | ``` 87 | Level: 2 88 | *** LogFrame Start *** 89 | Procedure: FamTask 90 | item1: bear 91 | item2: chair 92 | CorrectResponse: bear 93 | ImageSide: Left 94 | Duration: 885 95 | Familiarization: 1 96 | FamInforcer: 1 97 | ReinforcerImage: Bicycle1 98 | Familiarization.Cycle: 1 99 | Familiarization.Sample: 1 100 | Running: Familiarization 101 | FamTarget.RESP: 102 | Correct: True 103 | *** LogFrame End *** 104 | ``` 105 | 106 | Trials and procedures can be **nested** inside of higher-order sections of the 107 | experiment. For example, practice trials may be nested inside of a practice 108 | block of an experiment. The level of nesting in the above frame is indicated by 109 | the number of tabs before each line as well as the `Level` line. The only Level 110 | 1 frames in an experiment appear to the header frame and the final frame of an 111 | experiment (which largely duplicates the header frame). 112 | 113 | Some **special fields** appear in every log-frame. These fields are 114 | `Procedure`, `Running`, `[Running].Sample`, `[Running].Cycle` and `[Running]` 115 | where `[Running]` is the value of the running field. In the previous example, 116 | these were: 117 | 118 | ``` 119 | Procedure: FamTask 120 | Familiarization.Cycle: 1 121 | Familiarization.Sample: 1 122 | Running: Familiarization 123 | Familiarization: 1 124 | ``` 125 | 126 | When trials are presented in a random order, the `[Running].Sample` field 127 | records the sequential trial number. 128 | 129 | 130 | ## Strategy 131 | 132 | The basic strategy for parsing the Eprime file: 133 | 134 | 1. Read in the Eprime file. 135 | 2. Create `EprimeFrame` objects. 136 | a. Extract the frames. 137 | b. Create key-value pairs inside the frame by splitting text lines at `:` 138 | characters. 139 | c. Add some helpful metadata inside each frame. 140 | d. Bundle up the key-value pairs into an R `list` object with an added 141 | `EprimeFrame` class. 142 | 3. Bundle the frames from an experiment together inside of a `FrameList` 143 | object. 144 | 145 | Each of these steps is handled by some lower level functions. **In practice, 146 | only two high-level functions are necessary to transform from a text file to a 147 | list of `Eprime.Frame objects`: `read_eprime` and `FrameList`.** Here's how 148 | these two functions can be used to get us the header frame from a text file. 149 | 150 | ```{r} 151 | library("rprime") 152 | eprime_lists <- FrameList(read_eprime("data/MINP_001L00XS1.txt")) 153 | eprime_lists[[1]] 154 | ``` 155 | 156 | ### File reading 157 | 158 | Lines from the text file are read into R and stored into a character vector. 159 | Here are the first few lines from the example file: 160 | 161 | ```{r} 162 | exp_lines <- read_eprime("data/MINP_001L00XS1.txt") 163 | head(exp_lines) 164 | ``` 165 | 166 | Some things to note about file reading. By default, the enormous 167 | `Clock.Information` lines are omitted. Also, if the file is not an Eprime 168 | `.txt` file, a **dummy header** is created so that the file may be treated like 169 | any other like Eprime. The user is warned as this happens. 170 | 171 | ```{r} 172 | bad_lines <- read_eprime("data/not_an_eprime_file.txt") 173 | head(bad_lines) 174 | ``` 175 | 176 | I chose to use a dummy header instead of raising an error so that code which 177 | loads multiple files at once will not fail outright when it encounters a bad 178 | file. 179 | 180 | 181 | ### Chunking 182 | 183 | The next step in parsing the file is to extract all the frames. This is 184 | accomplished by pulling out lines of text that fall between a pair of 185 | bracketing lines. When there is no closing bracket, as when an experiment is 186 | aborted, a warning is raised and the partial frame is skipped. 187 | 188 | ```{r} 189 | # Experiment aborted on trial 3 190 | aborted <- FrameList(read_eprime("data/MP_Block1_001P00XA1.txt")) 191 | ``` 192 | 193 | The low-level function for chunking the lines of text is `extract_chunks`. 194 | During the chunking, some metadata is inserted into the frames as additional 195 | `Key: Value` lines. These fields are: 196 | 197 | 1. `Eprime.FrameNumber`, the number of the log frame in the file 198 | 2. `Eprime.Basename`, the `basename` of the source file 199 | 3. The header frame also gets the lines `Procedure: Header` and 200 | `Running: Header`. 201 | 202 | The result of chunking is a list of character vectors. Here's how the second 203 | frame looks after chunking. 204 | 205 | ```{r} 206 | chunks <- extract_chunks(exp_lines) 207 | chunks[[2]] 208 | ``` 209 | 210 | ### Building lists from "Key: Value" strings 211 | 212 | The data from each log-frame have been stored as character vector in a list. 213 | Next, we convert each vector of `"Key: Value"` strings into a list of named 214 | elements. `EprimeFrame` carries out this task. 215 | 216 | During this stage, the special fields (noted above) are parsed. The original 217 | form of the special fields is: 218 | 219 | ``` 220 | Running: [Key] 221 | [Key]: [Value] 222 | [Key].Cycle: [Cycle] 223 | [Key].Sample: [Sample] 224 | ``` 225 | 226 | These `[Key]` values make it harder to merge together data-frames later on, 227 | since each unique `[Key]` gets its own column name. Therefore, we normalize 228 | these fields like so: 229 | 230 | 1. `[Key]: [Value]` line is deleted and stored in the `Eprime.LevelName` field 231 | instead as `"[Key]_[Value]"`. 232 | 2. `[Key].Sample` and `[Key].Cycle` are renamed 233 | to just `Cycle` and `Sample`. 234 | 235 | One additional field is added, `Eprime.Level`, to record the depth of nesting 236 | (equal to the number of tabs plus one). 237 | 238 | Here is how the second frame appears after this stage of parsing: 239 | 240 | ```{r} 241 | EprimeFrame(chunks[[2]]) 242 | ``` 243 | -------------------------------------------------------------------------------- /vignettes/quick-start-demo.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Quick start demonstration" 3 | author: "Tristan Mahr" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Quick start demonstration} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | \usepackage[utf8]{inputenc} 10 | --- 11 | 12 | ```{r, echo = FALSE, message = FALSE} 13 | library("rprime") 14 | library("knitr") 15 | opts_chunk$set( 16 | comment = "#>", 17 | error = FALSE, 18 | tidy = FALSE, 19 | collapse = TRUE) 20 | options(str = strOptions(vec.len = 2)) 21 | ``` 22 | 23 | **rprime** is an R package for parsing `.txt` generated by E-Prime, a program 24 | for running psychological experiments. 25 | 26 | 27 | Overview 28 | ------------------------------------------------------------------------------- 29 | 30 | The main workflow for rprime involves: 31 | 32 | 1. `read_eprime`: reliably read in data from an Eprime log (`.txt`) file. 33 | 2. `FrameList`: extract the text in each `"LogFrame"` in the file, storing each 34 | log-frame as an R list. 35 | 3. `preview_levels`, `preview_eprime`: explore the structure of the parsed 36 | data. 37 | 4. `keep_levels`, `drop_levels`, `filter_in`, `filter_out`: select and filter 38 | particular levels from the txt-file. 39 | 5. `to_data_frame`: make a data-frame from the parsed data. 40 | 41 | 42 | Installation 43 | ------------------------------------------------------------------------------- 44 | 45 | To get the current, released version from CRAN: 46 | 47 | ```{r, eval = FALSE} 48 | install.packages("rprime") 49 | ``` 50 | 51 | 52 | Examples 53 | ------------------------------------------------------------------------------- 54 | 55 | ### Getting data into R 56 | 57 | Load the file with `read_eprime` and parse its contents with `FrameList`. 58 | 59 | ```{r} 60 | library("rprime") 61 | # Read in an Eprime text file 62 | experiment_lines <- read_eprime("data/SAILS/SAILS_001X00XS1.txt") 63 | 64 | # Extract and parse the log-frames from the file 65 | experiment_data <- FrameList(experiment_lines) 66 | ``` 67 | 68 | ### Exploring 69 | 70 | In the text file, frames were distinguished by the procedure they are running as 71 | well as the their level of nesting. Get an overview of the different types of 72 | frames with `preview_levels`. 73 | 74 | ```{r} 75 | # There are six different kinds of frames in this file 76 | preview_levels(experiment_data) 77 | ``` 78 | 79 | Get a preview of the data in each kind of frame with `preview_frames`. 80 | 81 | ```{r} 82 | preview_frames(experiment_data) 83 | ``` 84 | 85 | `preview_eprime` (not demonstrated here) does both kinds of previews (levels 86 | and frames). 87 | 88 | ### Filtering 89 | 90 | Use `keep_levels` and `drop_levels` to filter frames according to nesting level. 91 | 92 | ```{r} 93 | # Filter (out) by depth of nesting 94 | not_level_1 <- drop_levels(experiment_data, 1) 95 | preview_levels(not_level_1) 96 | 97 | # Filter (in) by depth of nesting 98 | just_level_3 <- keep_levels(experiment_data, 3) 99 | preview_levels(just_level_3) 100 | ``` 101 | 102 | Use `filter_in` and `filter_out` to filter frames using attribute values. Use 103 | repeated filtering statements to drill down into the list of frames. 104 | 105 | ```{r} 106 | # Filter (out) by attribute values 107 | no_header <- filter_out(experiment_data, "Running", values = "Header") 108 | preview_levels(no_header) 109 | 110 | # Filter (in) by attribute values 111 | not_practice <- filter_in(experiment_data, "Running", "TrialLists") 112 | preview_levels(not_practice) 113 | 114 | # Drill down further into the trials by filtering again 115 | sue_trials <- filter_in(not_practice, "Module", "SUE") 116 | preview_eprime(sue_trials) 117 | ``` 118 | 119 | ### Exporting 120 | 121 | Convert to a dataframe with `to_dataframe`. Attribute names in the log-frames 122 | become column names in the dataframe. 123 | 124 | ```{r} 125 | # Export to dataframe 126 | sue_trials_df <- to_data_frame(sue_trials) 127 | str(sue_trials_df) 128 | 129 | # Don't need every column 130 | columns_to_keep <- c("Eprime.Basename", "Module", "Sample", 131 | "Correct", "Response") 132 | sue_trials_df <- sue_trials_df[columns_to_keep] 133 | head(sue_trials_df) 134 | ``` 135 | 136 | **Note**: rprime thinks that all the values in the final dataframe are 137 | character values. You can use `type_convert` in the readr package to correct 138 | the column types: 139 | 140 | ```{r} 141 | # Right now the sample numbers are stored as character values 142 | str(sue_trials_df) 143 | 144 | library("readr") 145 | sue_trials_df <- type_convert(sue_trials_df) 146 | # Now, they are stored as integers... 147 | str(sue_trials_df) 148 | ``` 149 | --------------------------------------------------------------------------------