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 |
keys_values
154 |
a character vector of containing some "key: value"
155 | strings.
156 |
157 |
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.
a character vector with lines of the form "key: value", or a
153 | list of vectors of colon-separated text
154 |
155 |
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".
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 |
eprime_log
154 |
a character vector containing the lines of text from Eprime
155 | txt file
156 |
157 |
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(...).
the whitelisted or blacklisted values of the attribute
159 |
160 |
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.
the whitelisted or blacklisted values for Eprime.Level
157 |
158 |
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.
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.
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.
Either the full or relative path to an Eprime .txt file
149 |
150 |
151 |
remove_clock
152 |
Whether to exclude the Clock.Information XML entries.
153 | Enabled by default.
154 |
155 |
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.
'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.
an EprimeFrame object, or a FrameList object (a list of
149 | EprimeFrames)
150 |
151 |
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 |
168 |
171 |
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 |
--------------------------------------------------------------------------------