| Title: | Tools for Processing Eye Tracking Data |
|---|---|
| Description: | Functions for reading and pre-processing eye tracking data. |
| Authors: | Jason Geller [aut, cre], Dan Mirman [aut], Tristan Mahr [aut], Matt Winn [aut], Holger Mitterer [aut] |
| Maintainer: | Jason Geller <[email protected]> |
| License: | GPL-3 |
| Version: | 0.2.11 |
| Built: | 2026-10-27 08:10:12 UTC |
| Source: | https://github.com/dmirman/gazer |
Takes a data frame of gaze positions (or other locations), plus screen size and aoi size (or location), and computes the area of interest (AOI) for each location. Defaults assume standard four-corner design.
assign_aoi( gaze, screen_size = c(1024, 768), aoi_size = c(400, 300), aoi_loc = NULL, X = "CURRENT_FIX_X", Y = "CURRENT_FIX_Y" )assign_aoi( gaze, screen_size = c(1024, 768), aoi_size = c(400, 300), aoi_loc = NULL, X = "CURRENT_FIX_X", Y = "CURRENT_FIX_Y" )
gaze |
data frame containing positions |
screen_size |
size of the screen in pixels. Defaults to c(1024, 768) and assumes reversed vertical (i.e., (0,0) is top left). |
aoi_size |
size of AOIs in pixels. Defaults to a c(400, 300) width-height pair and assumes AOIs are in screen corners. AOIs will be coded numerically 1 to 4 in reading order (left to right, top to bottom), with 0 as center location. |
aoi_loc |
location of rectangular AOIs. Use as alternative to aoi_size for non-corner AOIs. Each AOI location should be a separate row in a data frame that has variables xmin, xmax, ymin, and ymax. Assumes reversed vertical (i.e., (0,0) is top left). AOIs will be coded numerically in row order. |
X |
name of variable containing X coordinates. Defaults to "CURRENT_FIX_X" |
Y |
name of variable containing Y coordinates. Defaults to "CURRENT_FIX_Y" |
Original gaze data frame with AOI column added. Non-AOI and off-screen gazes are marked NA.
Performs linear (sub) or divisive baseline correction relative to desired stim_onset and off_set
baseline_correction_pupil( datafile, pupil_colname = "pupil", baseline_window = NA, baseline_method = "sub", baseline_stat = "median" )baseline_correction_pupil( datafile, pupil_colname = "pupil", baseline_window = NA, baseline_method = "sub", baseline_stat = "median" )
datafile |
raw pupil data |
pupil_colname |
name of your pupil colname you want baseline corrected |
baseline_window |
user-specified threshold for baseline window. |
baseline_method |
correction method. Default is sub but can also include divisive |
baseline_stat |
statistic for the baseline value: "median" (default) or "mean". |
data frame containing baseline corrected data from event of interest
Performs linear (sub) or divisive baseline correction relative to desired stim_onset and off_set with messages.
baseline_correction_pupil_msg( datafile, pupil_colname = NULL, baseline_dur = 500, event = "event", baseline_method = "sub", baseline_stat = "median" )baseline_correction_pupil_msg( datafile, pupil_colname = NULL, baseline_dur = 500, event = "event", baseline_method = "sub", baseline_stat = "median" )
datafile |
raw pupil data |
pupil_colname |
name of your pupil colname you want baseline corrected |
baseline_dur |
duration of baseline period from stim offset |
event |
message name for stim onset |
baseline_method |
correction method. Default is sub but can also include divisive |
baseline_stat |
statistic for the baseline value: "median" (default) or "mean". |
message |
name of message column |
data frame containing baseline corrected data from event of interest
culls important behavioral data from the sample report
behave_pupil(datafile, omiterrors = TRUE, behave_colnames = NULL)behave_pupil(datafile, omiterrors = TRUE, behave_colnames = NULL)
datafile |
raw pupil data |
omiterrors |
if ture returns RTs without errors for RT analysis or dataset with all error data for error analysis |
behave_colnames |
specify variables of interest |
data frame with or withour errors
Takes a data frame list of fixations and returns a time series with the fixations expanded into time bins of specified duration.
binify_fixations( gaze, binSize = 20, keepCols = c("Subject", "TrialNumber", "Target", "T"), maxTime = NULL )binify_fixations( gaze, binSize = 20, keepCols = c("Subject", "TrialNumber", "Target", "T"), maxTime = NULL )
gaze |
a data frame of fixations, as returned by readFixationReport |
binSize |
length of bins, in ms |
keepCols |
set of columns to keep, fixation start and end times will be dropped |
maxTime |
optional parameter to set maximum trial length |
data frame containing gaze data arranged by bin
remotes::install_github("tmalsburg/saccades/saccades")).Take pupil file and detect blinks
merge blinks with sample report
Requires the saccades package (remotes::install_github("tmalsburg/saccades/saccades")).
blink_detect(samp)blink_detect(samp)
samp |
pupil data file |
merged data file with blinks detected
This function takes a dataframe and applies a simple velocity algorithm to detect blinks in the data based off velocity of pupil size changes. The algorithm has four parameters that need to be adjusted based on the particulars of the data: the amount of smoothing, the (negative) onset velocity threshold, the (positive) reversal velocity threshold, and the temporal margin
blink_velocity( df, neg_velocity_thresh = -5, pos_velocity_thresh = 5, fillback = 10, fillforward = 10, hz = 250 )blink_velocity( df, neg_velocity_thresh = -5, pos_velocity_thresh = 5, fillback = 10, fillforward = 10, hz = 250 )
df |
data frame. |
neg_velocity_thresh |
threshold for onset of blinks neg number here we use default of -5 |
fillback |
extend blinks backward |
fillforward |
extend blinks forward |
hz |
recording frequency of ET |
pos_velocity_thres |
threshold for offset of blinks neg number here we use default of 5 |
data frame containing pupil values where blinks have been linear interpolated.
calculates the median absoulte deviation (MAD) and sets the threshold to filter on.
calc_mad(max_dilation, n = 16)calc_mad(max_dilation, n = 16)
max_dilation |
pupil file returned from speed_pupil |
n |
constant used |
threhold value to filter on
Takes a data frame, name of predictor variable, and polynomial order. Creates polynomial-transformed predictor variables, adds them to the data frame and returns the result. Original data frame is unchanged, remember to assign the result.
code_poly( df = NULL, predictor = NULL, poly.order = NULL, orthogonal = TRUE, draw.poly = TRUE )code_poly( df = NULL, predictor = NULL, poly.order = NULL, orthogonal = TRUE, draw.poly = TRUE )
df |
data frame, should not contain any variables called "predictor" |
predictor |
string name of predictor variable |
poly.order |
integer order of polynomial to be created |
orthogonal |
logical value indicating whether polynomial should be orthogonal (default) or natural (aka raw) |
draw.poly |
logical value indicating whether to create a graph showing tranformed polynomial predictor values, defaults to TRUE |
Returns a data frame containing the original data and at least two new columns: "predictor".Index, and a column for each order of the polynomial-transformed predictor
WordLearnEx.gca <- code_poly(df=WordLearnEx, predictor="Block", poly.order=2) TargetFix.gca <- code_poly(df=TargetFix, predictor="Time", poly.order=2, orthogonal=FALSE)WordLearnEx.gca <- code_poly(df=WordLearnEx, predictor="Block", poly.order=2) TargetFix.gca <- code_poly(df=TargetFix, predictor="Time", poly.order=2, orthogonal=FALSE)
Fixation time course data from 8 participants with aphasia and 12 age-matched control participants.
CohortRhymeCohortRhyme
A data frame with 1600 rows and 7 variables:
subject ID, factor
subject group: Control, Broca's aphasia, Wernicke's aphasia
time (in ms) since word onset in 100ms bins
time bin number (1-20)
object type: phonologically related Competitor or Unrelated object. Target object data are omitted
type of phonological competitor: Cohort (e.g., penny - pencil) or Rhyme (e.g., carrot - parrot)
fixation proportion: proportion of correct-response trials on which the object was fixated
Mirman et al. (2011, Brain & Lang., 117:2, 53-68) http://www.danmirman.org/pdfs/Mirman_etal_2011.pdf
For each numeric variable in a data frame, computes number of observations, mean, and standard deviation, for each group, and t- and p-values for the t-test comparing the groups.
compare_groups(dat, Group)compare_groups(dat, Group)
dat |
data frame |
Group |
string name of grouping variable |
Returns a data frame with rows for each numeric variable in the input data frame (plus N, for number of observations) and columns for mean, SD, t-value, and p-value.
## Not run: compare_groups(SND, "SemNear_Cond") ## End(Not run)## Not run: compare_groups(SND, "SemNear_Cond") ## End(Not run)
A "monocular mean" averages both eyes together. If data is available in just
one eye, use the available value as the mean. If both eyes are missing, the
result is NA.
compute_monocular_mean(x1, x2)compute_monocular_mean(x1, x2)
x1 |
Numeric vector for the left pupil. |
x2 |
Numeric vector for the right pupil. |
A numeric vector of monocular mean pupil values.
Calculate percent missing data by subject and by trial
count_missing_pupil(datafile, pupil = "pupil", missingthresh = 0.2)count_missing_pupil(datafile, pupil = "pupil", missingthresh = 0.2)
pupil |
name of pupil column |
missingthresh |
user-specified threshold for missing data. set to .2 |
data frame containing missing data information
Computes Crawford-Howell t-test comparing a single case to a set of controls.
Crawford_Howell(case, control)Crawford_Howell(case, control)
case |
single case score |
control |
vector of control scores |
Returns a data frame with three values: t, df, and p.
Crawford_Howell(649, c(243, 175, 231, 164, 236, 131, 302, 303, 363, 287, 205, 297, 144, 789, 210, 245))Crawford_Howell(649, c(243, 175, 231, 164, 236, 131, 302, 303, 363, 287, 205, 297, 144, 789, 210, 245))
Aggregate baseline corrected pupillary data for a LDT task that required participants to judge the lexical status of cursive and type-print stimuli.
cursive_agg_datacursive_agg_data
A data frame with 52 rows and 3 variables
Cursive or Print
trial onset
baseline corrected pupil size
Detects blinks in a per-sample pupil time series. Starts from the
tracker's missing-data sentinel and refines each missing-sample run's
onset and offset by walking outward on the smoothed first-difference
signal – picking up the eyelid descent before the missing samples and
the recovery rise after. Adjacent blinks separated by less than
gap_ms are merged.
detect_blinks_noise( pupil, hz, smooth_ms = 10, gap_ms = 100, missing = c(0, NA) )detect_blinks_noise( pupil, hz, smooth_ms = 10, gap_ms = 100, missing = c(0, NA) )
pupil |
Numeric vector of pupil samples (one trial, one eye).
Must be in time order at the rate given by |
hz |
Sampling rate in Hz. |
smooth_ms |
Centered moving-average window applied before onset / offset refinement. Default 10 ms. |
gap_ms |
Adjacent blinks closer than this (in ms) are merged. Default 100 ms. |
missing |
Values in |
Tracker-agnostic missing handling: by default treats both NA and 0
as missing (Tobii uses 0; SR Research / EyeLink exports often use
NA). Override via missing.
Composes with dplyr exactly like gazeR::extend_blinks():
df |> group_by(subject, trial) |> mutate(blink = detect_blinks_noise(pupil, hz = 250))
If you want pupil set to NA inside blinks (gazeR-style), chain one
more line:
... |> mutate(pupil = ifelse(blink > 0, NA_real_, pupil))
Adapted to R with Dr William Paul Boyce ([email protected]); indexing bug in the original port – matching on index instead of time – was diagnosed by Dr Jason Geller.
An integer vector the same length as pupil: 0 outside
detected blinks, 1, 2, ... inside each blink. When used inside
dplyr::mutate() under a group_by(), blink ids restart at 1 in
each group.
This function will combine gaze samples into time bins.
downsample_gaze( dataframe, bin.length = 50, timevar = "time", aggvars = c("subject", "condition", "target", "trial", "object", "timebins"), type = "gaze", pupil = "baselinecorrectedp" )downsample_gaze( dataframe, bin.length = 50, timevar = "time", aggvars = c("subject", "condition", "target", "trial", "object", "timebins"), type = "gaze", pupil = "baselinecorrectedp" )
dataframe |
DataFrame containing gaze data. |
bin.length |
Length of time bins. |
timevar |
Column name representing time. |
aggvars |
Vector of variable names to group by for aggregation. |
type |
Type of data, either "pupil" for pupil data or "gaze" for gaze data. |
pupil |
column name representing pupil column |
Downsampled DataFrame.
This function will reduce the sampling fequency
downsample_pupil( dataframe, bin.length = NULL, timevar = "time", aggvars = c("subject", "script", "timebins") )downsample_pupil( dataframe, bin.length = NULL, timevar = "time", aggvars = c("subject", "script", "timebins") )
dataframe |
dataframe |
bin.length |
Length of bins to average |
timevar |
name of time variable |
aggvars |
vector of variable names to group by for aggregation |
calc dynamic pupil range (Ayasse et al., 2017; Piquado et al., 2010)) must have min pupil and max pupil size merged into df to work
dynamic_pupil(pupil_col, dmin = NA, dmax = NA)dynamic_pupil(pupil_col, dmin = NA, dmax = NA)
pupil_col |
pupil col |
dmin |
mean pupil size during the ‘bright’ condition (i.e., minimum pupil size) |
dmax |
mean pupil size during the ‘dark’ condition (i.e., max pupil size) |
return dynamic pupil size scaled to P
Extends blinks if not already done in SR Data Viewer
extend_blinks(values, fillback = 5, fillforward = 5, hz = NA)extend_blinks(values, fillback = 5, fillforward = 5, hz = NA)
values |
column with pupil values |
fillback |
NA extend backward (in ms) |
fillforward |
NA extend forward (in ms) |
hz |
sampling rate of eye tracker |
vector containing extended blinks
Take asc files and return important events Collect trial variables for your experiment. #' This code was written by Dr. Holger Mitterer
find_messages_asc( dirList, homeDir = "./", time2extract = NULL, msg2extract = NULL, vars2extract = NULL, nOfTrials = -1, binocular = FALSE )find_messages_asc( dirList, homeDir = "./", time2extract = NULL, msg2extract = NULL, vars2extract = NULL, nOfTrials = -1, binocular = FALSE )
dirList |
dir of edf files |
homeDir |
dir of files |
time2extract |
a list of strings that match message texts for which only the time should be retained. |
msg2extract |
A list of messages to extract fully based on the first word of that message output will be string variable |
vars2extract |
A list of Trial variables used by Experiment Builder that should be extracted value only, with column names set by the variable name |
nOfTrials |
number of trials to extract; -1 for all |
binocular |
set to TRUE when the recording is binocular. Message
extraction is eye-independent, so this only keeps the interface symmetric
with |
Collect trial variables for your experiment.
find_messages_edf(file_list, varnames, patterns, output_dir)find_messages_edf(file_list, varnames, patterns, output_dir)
file_list |
list of edf files |
varnames |
a vector of variable names from experiment c("TRIALID", "ACCURACY") |
patterns |
a vector of patterns to look for ("TRIALID", "!V TRIAL_VAR script") |
output_dir |
where you want files to be stored |
Fixation time course data from a spoken word-to-picture matching study with semantic distractors in the display.
FunctThemeFunctTheme
A data frame with 1530 rows and 5 variables:
subject ID, factor
time (in ms) since word onset in 50ms bins
fixation proportion (subject mean): proportion of correct-response trials on which the object was fixated
semantic relatedness condition: Function (e.g., broom - sponge) or Thematic (e.g., broom - dustpan)
object type: Target, semantically related Competitor, or Unrelated
Kalenine et al. (2012, J. Exp. Psychol. Learn. Mem. Cog., 38:5, 1274-1295) http://www.danmirman.org/pdfs/Kalenine_etal2012.pdf
Fixation time course data for participants with left hemisphere stroke from a spoken word-to-picture matching study with semantic distractors in the display.
FunctThemePtsFunctThemePts
A data frame with 8199 rows and 8 variables:
subject ID, factor
semantic relatedness condition: Function (e.g., broom - sponge) or Thematic (e.g., broom - dustpan)
object type: Target, semantically related Competitor, or Unrelated
time (in ms) relative to word onset in 50ms bins; negative numbers indicate preview period (visual display before word onset)
fixation proportion (subject mean): proportion of correct-response trials on which the object was fixated
fixation sum: number of correct-response trials on which the object was fixated (i.e., numerator for meanFix)
number of correct-response trials (i.e., denominator for meanFix)
Kalenine, Mirman, & Buxbaum (2012, Front. Hum. Neurosci., 6:106, 1-12) http://journal.frontiersin.org/Journal/10.3389/fnhum.2012.00106
Functions for reading and pre-processing eye tracking data.
Maintainer: Jason Geller [email protected]
Authors:
Jason Geller [email protected]
Dan Mirman [email protected]
Tristan Mahr [email protected]
Matt Winn [email protected]
Holger Mitterer [email protected]
Useful links:
Takes edf files and converts them to asc This code was written by Dr. Holger Mitterer
generate_edf2asc_bat(homeDir, subDirs, deleteExisting = F)generate_edf2asc_bat(homeDir, subDirs, deleteExisting = F)
homeDir |
dir of files |
subDirs |
end location of files |
deleteExisting |
overwrite file - set to false |
Use fixation data to calculate two diagnostics of gaze track quality: non-fixation time and out-of-bounds proportion.
get_gaze_diagnostics(gaze, screen_size = c(1024, 768))get_gaze_diagnostics(gaze, screen_size = c(1024, 768))
gaze |
fixation list from fixation report |
screen_size |
size of the screen in pixels. Defaults to c(1024, 768) |
data frame containing gaze diagnostics
Takes a model fit with lmer and (optionally) an estimation method, returns a data frame containing fixed effect coefficient estimates, SE, t-values, and p-values. Can use normal approximation, Kenward-Roger approximation for degrees of freedom, or both. If Kenward-Roger approximation is used, result also contains approximated degrees of freedom.
get_pvalues(model, method = "normal")get_pvalues(model, method = "normal")
model |
a merMod model object fit by lmer |
method |
p-value estimation method, defaults to "normal", can also be "KR" or "all" |
data frame containing fixed effect estimates, SE, t-values, (degrees of freedom if appropriate) and p-values.
require(lme4) model <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) get_pvalues(model) get_pvalues(model, "KR")require(lme4) model <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) get_pvalues(model) get_pvalues(model, "KR")
Takes a model and name of a random effect. Returns a data frame containing those random effect estimates arranged and ready for quantifying individual effect sizes.
get_ranef(model = NULL, ranef_name = NULL)get_ranef(model = NULL, ranef_name = NULL)
model |
(g)lmerMod object |
ranef_name |
string name of random effect (right of pipe in (g)lmer call) |
Returns a data frame containing the random effect estimates
## Not run: # FunctTheme example model FunctTheme.gca <- code_poly(FunctTheme, predictor = "Time", poly.order = 2, draw.poly = FALSE) m.FT <- lme4::lmer(meanFix ~ (poly1 + poly2) * Object * Condition + (poly1 + poly2 | Subject) + (poly1 + poly2 | Subject:Object:Condition), data = FunctTheme.gca, REML = FALSE) re <- get_ranef(m.FT, "Subject:Object:Condition") ## End(Not run)## Not run: # FunctTheme example model FunctTheme.gca <- code_poly(FunctTheme, predictor = "Time", poly.order = 2, draw.poly = FALSE) m.FT <- lme4::lmer(meanFix ~ (poly1 + poly2) * Object * Condition + (poly1 + poly2 | Subject) + (poly1 + poly2 | Subject:Object:Condition), data = FunctTheme.gca, REML = FALSE) re <- get_ranef(m.FT, "Subject:Object:Condition") ## End(Not run)
Calculate trackloss and remove trials and subjects with excessive trackloss
get_trackloss(gaze, screen_size = c(1024, 768), missingthresh = 0.2)get_trackloss(gaze, screen_size = c(1024, 768), missingthresh = 0.2)
gaze |
fixation list from fixation report |
screen_size |
size of the screen in pixels. Defaults to c(1024, 768) |
missingthresh |
threshold to throw out trials and subjs |
data frame containing gaze df with trackloss by subjs and trial. Trials and Subjs with excessive trackloss removed
Perfroms hanning smoothing on pupil data
hanning_filter(pupil, degree = 11)hanning_filter(pupil, degree = 11)
pupil |
column with pupil values |
degree |
default of 11. |
vector containing hanning filtered pupil data
Turns pupil sizes with blinks NA and linearly interpolates values
interpolate_pupil( datafile, pupil, subject, trial, extendblinks = FALSE, maxgap = Inf, type = c("linear", "cubic"), hz = NA )interpolate_pupil( datafile, pupil, subject, trial, extendblinks = FALSE, maxgap = Inf, type = c("linear", "cubic"), hz = NA )
datafile |
data frame. |
extendblinks |
blinks already extended in data frame |
maxgap |
max number of NAs to interpolate. |
type |
type of interpolation (linear or cubic) |
hz |
recording frequency of ET |
data frame containing interpolated data
A wrapper function to make setting legend position easier when using ggplot.
legend_positioning(pos, edge_color = "black", legend_fill = "white")legend_positioning(pos, edge_color = "black", legend_fill = "white")
pos |
Legend position as a two-element vector |
edge_color |
Legend edge color, defaults to black |
legend_fill |
Legend fill color, defaults to white |
library(ggplot2) p <- ggplot(TargetFix, aes(Time, meanFix, color = Condition)) + stat_summary(fun = mean, geom = "line") p + legend_positioning(c(0,1)) #put legend in upper left corner p + legend_positioning(c(1,0)) #put legend in lower right cornerlibrary(ggplot2) p <- ggplot(TargetFix, aes(Time, meanFix, color = Condition)) + stat_summary(fun = mean, geom = "line") p + legend_positioning(c(0,1)) #put legend in upper left corner p + legend_positioning(c(1,0)) #put legend in lower right corner
This function performs low pass filtering
lowpass_filter(pd, order, filter_to_use, samplerate = NA)lowpass_filter(pd, order, filter_to_use, samplerate = NA)
pd |
pupil column. |
order |
order butter filter |
filter_to_use |
hz to low pass filter |
samplerate |
sampling rate of the eye tracker in Hz |
vector containing low pass filtered pupil data
This will make sure your data is in the right format for processing. This package is designed to deal with data at it comes out of the eyetracker in a long-form csv style format. Thus data input here would be a long dataframe, wherein each row is a single frame collected by the eyetracker.
x, y, and pupil may each name a single column or several. A single
column is renamed to gazeR's canonical name (x, y, or pupil); when you
pass more than one column (e.g. left and right eye) the original column names
are kept as-is. Either way the selected columns are moved to the front of the
data frame and every other column is kept, in its original order, after them.
make_gazer( data, subject = "subject", trial = "trial", time = "time", x = "x", y = "y", pupil = NULL )make_gazer( data, subject = "subject", trial = "trial", time = "time", x = "x", y = "y", pupil = NULL )
data |
a raw, long form dataframe organised by subject, trial, and time. |
subject |
column name indicating subject ID |
trial |
column name indicating trial ID. This should be unique for participants |
time |
column name indicating time column (should be numeric) |
x |
gaze x column(s). One name is renamed to |
y |
gaze y column(s); see |
pupil |
pupil column(s). One name is renamed to |
A dataframe ready to use in gazer
Take asc files and extract relevant data Merges eye data (from parse_asc) with message data (from find_messages_asc) This code was written by Dr. Holger Mitterer
merge_asc_files(dirList, homeDir = "./", dataType = c("fixation", "sample"))merge_asc_files(dirList, homeDir = "./", dataType = c("fixation", "sample"))
dirList |
list if asc files |
homeDir |
directory of edf files |
dataType |
type of eye data being merged. Both left-join message data
onto eye data, so every eye row is kept (one row per eye row, like
parse_asc). "fixation" (default) joins by trial only, so every message
row for a trial is attached to every eye row for that trial. "sample"
joins by trial and time, so message columns are filled in only where a
message landed on that exact timestamp and |
Merge eye files from a ufolder if filetype is from sr renames column variables and makes them lower case Places time in ms if edf files just reads them in
merge_gazer_files( file_list, blink_colname = "blink", pupil_colname = "pupil", filetype = "sr" )merge_gazer_files( file_list, blink_colname = "blink", pupil_colname = "pupil", filetype = "sr" )
file_list |
path to .xls files |
blink_colname |
name of your blink colname:AVERAGE_IN_BLINK, LEFT_, #RIGHT |
pupil_colname |
name of your pupil colname:AVERAGE_IN_Pupil, LEFT_, #RIGHT |
filetype |
if reading raw edf file use 'edf' if using sr files use type 'sr' |
data frame containing pupil data
Merge pupil files from a folder Renames column variables and makes them lower case Places time in ms
merge_pupil(file_list, blink_colname = "", pupil_colname = "", filetype = "sr")merge_pupil(file_list, blink_colname = "", pupil_colname = "", filetype = "sr")
file_list |
path to .xls files |
blink_colname |
name of your blink colname:AVERAGE_IN_BLINK, LEFT_, #RIGHT |
pupil_colname |
name of your pupil colname:AVERAGE_IN_Pupil, LEFT_, #RIGHT |
filetype |
if reading raw edf file use 'edf' if using sr files use type 'sr' |
data frame containing pupil data
Loads gaze samples and event messages from two parallel folders, attaches
each message to the nearest gaze sample by system_time_stamp (as-of
merge), assigns trial numbers from onset / offset markers, and rebases
time to be trial-relative. The result is one row per gaze sample, with
event-marker columns set sparsely on the samples that absorbed an event.
merge_tobii_titta( gaze, msg, onset_prefix = "onset_", offset_prefix = "offset_" )merge_tobii_titta( gaze, msg, onset_prefix = "onset_", offset_prefix = "offset_" )
gaze |
A directory of gaze CSVs, or a character vector of gaze file paths. |
msg |
A directory of message CSVs, or a character vector of msg
file paths. Paired with |
onset_prefix |
Message prefix marking trial onsets. Required.
Default |
offset_prefix |
Message prefix marking trial offsets. Pass |
merge_tobii_titta()
reads the split CSVs that the current Titta build emits.
Files are paired by basename. A subject's gaze and msg file must share
the same filename minus extension, e.g. gaze/sub01.csv <->
msg/sub01.csv. The basename (without extension) becomes subject.
Trials are identified by message prefixes. Three modes are supported, picked automatically from the data:
Bracketed (default): onset_<label> starts a trial, offset_<label>
ends it. Samples between an offset and the next onset are dropped.
Onset-only: pass offset_prefix = NULL, or simply have no rows
matching the offset prefix. Each onset runs until the next onset.
Custom prefixes: override onset_prefix / offset_prefix for
any other convention (start_<n>, startfix_<n>, etc.).
The text following the onset prefix is kept as event_label and
broadcast across every sample in that trial – it can be a stim filename,
a trial number, a condition tag, whatever your protocol uses.
Because gaze is sampled on a fixed cadence (30 / 120 / 300 Hz on Tobii
trackers) but messages fire asynchronously, a message timestamp almost
never exactly equals a sample timestamp. Each message is therefore
merged onto the gaze sample whose timestamp is closest, found
vectorized via findInterval(). match_offset_us
(matched sample ts - msg ts) is exposed for diagnostics; on a 30 Hz
tracker this is at most ~16 ms (half the inter-sample gap), and well
below 1 ms on a 300 Hz tracker.
Raw left_pupil_diameter, right_pupil_diameter,
left_eye_openness_diameter, and right_eye_openness_diameter (if
present) pass through unchanged. Average them downstream if you want a
single signal – gazeR::compute_monocular_mean() or rowMeans(..., na.rm = TRUE).
time is in milliseconds relative to each trial's first sample (the one
the onset message merged onto), matching gazeR's per-trial convention.
A tibble with one row per gaze sample inside a trial window.
Leading columns: subject, system_time_stamp, time, msg,
message, event, event_label, match_offset_us, trial. The
raw Tobii sample columns (gaze x/y, pupil diameter, eye openness,
validity flags, etc.) follow.
## Not run: out <- merge_tobii_titta( gaze = "data/raw/gaze", msg = "data/raw/msg" ) # Onset-only recording with custom prefix out <- merge_tobii_titta( gaze = "data/raw/gaze", msg = "data/raw/msg", onset_prefix = "startfix_", offset_prefix = NULL ) ## End(Not run)## Not run: out <- merge_tobii_titta( gaze = "data/raw/gaze", msg = "data/raw/msg" ) # Onset-only recording with custom prefix out <- merge_tobii_titta( gaze = "data/raw/gaze", msg = "data/raw/msg", onset_prefix = "startfix_", offset_prefix = NULL ) ## End(Not run)
Creates a moving average using a specified averageing window if n is 5, then it uses the target value, 2 preceding and 2 following
moving_average_pupil(x, n = 5, centered = TRUE)moving_average_pupil(x, n = 5, centered = TRUE)
x |
data |
n |
sed for moving window |
centered |
whether moving average window should be centered (TRUE) or trailing (FALSE) |
return sum divided by count
Picture naming data from individuals with aphasia undergoing a treatment trial. Each participant was tested on a 175-item picture naming test (Philadelphia Naming Test) multiple times over the course of the treatment and responses were coded.
NamingRecoveryNamingRecovery
A data frame with 115 rows and 9 variables:
subject ID, factor
participant's aphasia subtype: Anomic, Conduction, or Wernicke's aphasia
test iteration, 0 is the baseline at the start of treatment
proportion correct naming responses
proportion semantic errors (cat - "dog")
proportion mixed errors (cat - "rat")
proportion formal errors (cat - "mat")
proportion unrelated errors (cat - "log")
proportion nonword errors (cat - "lat")
Moss Aphasia Psycholinguistics Database <mappd.org>
Centers the sample index or time stamp of your time series to a reference marker, such that 0 always coincides with the onset of a particular event.
onset_pupil(time, marker, event)onset_pupil(time, marker, event)
time |
Numeric vector of sample indices or time stamps corresponding to a single time series. |
marker |
Vector containing markers of trial events. |
event |
Label in |
Numeric vector that zeroes time to the trial onset. In the
event that no onset marker is found, a warning is issued and a vector of
NAs is returned.
Take asc files and extract relevant data Does not merge data with messages for fixation data, obtains sample report and puts time in ms This code was written by Dr. Holger Mitterer
parse_asc( dirList, homeDir = "./", overwriteBlinks = FALSE, cutPreview = 0, binocular = FALSE )parse_asc( dirList, homeDir = "./", overwriteBlinks = FALSE, cutPreview = 0, binocular = FALSE )
dirList |
list if asc files |
homeDir |
directory of edf files |
overwriteBlinks |
set to false |
cutPreview |
number of ms to cut from the start of each trial (default 0) |
binocular |
set to TRUE to return both left and right eye samples (x/y/pupil for each eye plus per-eye blink flags); FALSE (default) returns a single (monocular) eye. |
Take EDF files and return data that is in format usable for gazeR for pupil merges the sample data with the message data (merges on nearest values (millisecond diff between SR and this function) puts time in ms adds subject variable column cleans up the column names creates pupil column that is ambigious as to whether you sampled from left eye, right eye, or both (takes the average) For fixation data, obtains sample report and puts time in ms
parse_edf(file_list, output_dir, type = "pupil")parse_edf(file_list, output_dir, type = "pupil")
file_list |
directory to edf files |
type |
include whether you want to parse edf pupil data (pupil) or vwp (fixations) |
output.dir |
directory to save new cleaned files |
Take EDF files and return message events to extract behav data creates pupil column that is ambigious to whether you sampled from left eye, right eye, or both (takes the average)
parse_edf_messages(file_list, output.dir)parse_edf_messages(file_list, output.dir)
file_list |
directory to edf files |
output.dir |
directory to save new cleaned files |
Reads gaze.csv, 3d_eye_states.csv, events.csv, and gaze_surface.csv from a subject folder,
attaches the nearest (non-recording) event to gaze rows (within a lag), defines
trial boundaries, removes blank trials, and resets time to 0 within each trial.
parse_pl( subject_dir, subject_id = basename(normalizePath(subject_dir)), aoi = FALSE, max_event_lag_ms = 100, start_mode = c("any", "exact"), start_messages = NULL )parse_pl( subject_dir, subject_id = basename(normalizePath(subject_dir)), aoi = FALSE, max_event_lag_ms = 100, start_mode = c("any", "exact"), start_messages = NULL )
subject_dir |
Path to a single subject directory containing Pupil Labs CSVs. |
subject_id |
Character ID for the subject. Defaults to folder name. |
aoi |
Logical; if TRUE, use surface (AOI) stream as well. |
max_event_lag_ms |
Maximum absolute difference (ms) allowed when snapping an event to the nearest gaze row. Defaults to 100. |
start_mode |
Trial start rule: |
start_messages |
Character vector of trial-start messages when
|
Tibble: subject, trial, time, x, y, pupil, blink, message, start_message_first.
Recursively finds immediate subfolders, runs read_pl_subject on each,
writes per-subject CSVs and a combined CSV, and invisibly returns the combined tibble.
process_all_subjects_PL( root_dir, output_dir = file.path(root_dir, "processed"), ... )process_all_subjects_PL( root_dir, output_dir = file.path(root_dir, "processed"), ... )
root_dir |
Directory containing one folder per subject. |
output_dir |
Directory for outputs. Defaults to |
... |
Extra arguments forwarded to |
Invisibly returns a combined tibble of all processed subjects.
## Not run: process_all_subjects("/path/Timeseries", start_mode = "exact", start_messages = c("trial-started-light", "start-dark")) ## End(Not run)## Not run: process_all_subjects("/path/Timeseries", start_mode = "exact", start_messages = c("trial-started-light", "start-dark")) ## End(Not run)
A fast, single-file reader for EyeLink .asc files (convert .edf with
SR Research's edf2asc first). The file is parsed in a few vectorised
passes, so even large recordings take seconds. Unlike parse_asc() and
find_messages_asc(), it needs no folder-per-subject layout and writes
nothing to disk: it returns one long data frame, one row per sample, ready
for the pupil functions (extend_blinks(), smooth_interpolate_pupil(),
baseline_correction_pupil_msg(), ...), or the shiny gazeR app
(https://github.com/jgeller112/shiny-gazeR).
read_asc( file, eye = c("average", "left", "right", "both"), subject = NULL, trial_start = "TRIALID", trial_end = "TRIAL_RESULT", keep_messages = FALSE )read_asc( file, eye = c("average", "left", "right", "both"), subject = NULL, trial_start = "TRIALID", trial_end = "TRIAL_RESULT", keep_messages = FALSE )
file |
path to an |
eye |
which eye to use for binocular recordings: |
subject |
subject label. Defaults to the file name without extension. |
trial_start, trial_end
|
message text (first word) marking the start and end of a trial. |
keep_messages |
if |
What it does:
Trials run from each TRIALID message to the matching
TRIAL_RESULT message (both configurable). Samples outside a trial are
dropped. time is in ms relative to the start of recording in that trial
(the !MODE RECORD message, or TRIALID if there is none), matching
parse_asc(). The raw tracker clock is kept in tracker_time.
Messages are attached to the first sample at or after the message
time, in a message column. Experiment Builder offsets
(MSG <time> <offset> <text>) are applied (time − offset). Tracker
housekeeping messages (RECCFG, GAZE_COORDS, !V DRAW_LIST, ...) are
left out of the sample data but kept in the full message table.
Trial variables written by Experiment Builder (!V TRIAL_VAR name value) become one column per variable, with types guessed by
utils::type.convert(). These are usually your condition columns.
Blinks come from the tracker's SBLINK/EBLINK events (blink is
1 during a blink). Pupil values of 0 and missing gaze (.) become NA.
Binocular data are handled with eye: "average", "left",
"right", or "both" (per-eye columns plus the average). With
"average" and "both", pupil, x and y are the mean of the two
eyes, computed only where both eyes are valid (otherwise NA), and
blink is 1 if either eye is in a blink.
A tibble with one row per sample:
subject: participant label (file name without extension, or subject).
trial: trial number (one per TRIALID).
time: ms from the start of recording in the trial (!MODE RECORD).
pupil: pupil size in tracker units (area or diameter, see
asc_info); NA when missing. For eye = "average"/"both", the mean
of both eyes where both are valid.
x, y: gaze position in screen pixels; NA when missing. Averaged
like pupil.
blink: 1 during a blink, 0 otherwise. For eye = "average"/"both",
1 if either eye is in a blink.
message: event message(s) attached to this sample, otherwise NA.
tracker_time: the tracker's own clock (ms).
With eye = "both": pupil_left, pupil_right, x_left, y_left,
x_right, y_right, blink_left, blink_right, each for one eye.
One column per Experiment Builder trial variable.
Attribute "asc_info" holds the sampling rate (hz), recorded eyes,
pupil type (area or diameter) and the eye used.
## Not run: d <- read_asc("data/PN001.asc", eye = "left") attr(d, "asc_info")$hz ## End(Not run)## Not run: d <- read_asc("data/PN001.asc", eye = "left") attr(d, "asc_info")$hz ## End(Not run)
Reads eye-tracking recordings stored in the BIDS format for physiological
recordings (BEP020): *_recording-<label>_physio.tsv.gz files (no header)
with JSON sidecars describing their columns, plus optional
*_physioevents.tsv.gz files with messages and events. Returns one long
data frame, one row per sample, in the same layout as read_asc().
read_bids_eyetrack( path, eye = c("average", "left", "right", "both"), trial_start = NULL )read_bids_eyetrack( path, eye = c("average", "left", "right", "both"), trial_start = NULL )
path |
a BIDS dataset folder (searched recursively), or a vector of
|
eye |
which eye to use for binocular data: |
trial_start |
optional character vector of messages that start a trial (matched against the start of each message). |
Participants: subject is built from the sub-, ses-, task- and
run- entities of each file name (e.g. sub-01_task-rest_run-1).
Columns: timestamp, x_coordinate, y_coordinate and pupil_size
become time, x, y and pupil; other columns are kept. Timestamps
are converted to ms from the Units of timestamp in the sidecar (s,
ms, us or ns, so Pupil Labs and Tobii conversions work too), or,
without units, from the step between samples and SamplingFrequency.
Eyes: recordings of the left and right eye (e.g. recording-eye1
and recording-eye2, identified by RecordedEye in the sidecar) are
merged by timestamp. eye works as in read_asc(): "average" (mean
of both eyes where both are valid; blink is 1 if either eye blinks),
"left", "right", or "both" (per-eye columns plus the average).
Events: rows of *_physioevents.tsv.gz with a message are attached
to the first sample at or after their onset; rows with
trial_type == "blink" (or blink == 1) mark blink for their duration.
Trials: a BIDS recording is continuous, so by default each run is one
trial. With trial_start, a new trial starts at each message matching
one of these strings, and time is ms from that message; samples before
the first one are dropped.
A tibble with subject, trial, time, pupil, x, y,
blink, message, tracker_time, sample_rate, eyes_recorded and
any extra columns. Attribute "bids_info" has one row per subject with
sample_rate, eyes_recorded, manufacturer and pupil_units from the
sidecars, and blink_events (whether any blink events were found).
## Not run: d <- read_bids_eyetrack("my_bids_dataset", eye = "left") d <- read_bids_eyetrack("my_bids_dataset", trial_start = "TRIALID") ## End(Not run)## Not run: d <- read_bids_eyetrack("my_bids_dataset", eye = "left") d <- read_bids_eyetrack("my_bids_dataset", trial_start = "TRIALID") ## End(Not run)
Reads in a fixation report created by EyeLink's EyeDataViewer application
read_fixation_report( filename, screen_size = c(1024, 768), name_changes = c(Subject = "RECORDING_SESSION_LABEL", ACC = "StimSlide.ACC", RT = "StimSlide.RT", TargetLoc = "CorrectPort"), plot_fix_scatter = TRUE, pages = 1 )read_fixation_report( filename, screen_size = c(1024, 768), name_changes = c(Subject = "RECORDING_SESSION_LABEL", ACC = "StimSlide.ACC", RT = "StimSlide.RT", TargetLoc = "CorrectPort"), plot_fix_scatter = TRUE, pages = 1 )
filename |
string containing full filename of fixation report |
screen_size |
size of the screen in pixels. Used to compute track quality diagnostics. Defaults to c(1024, 768) |
name_changes |
set of variable name pairs for improving variable names. Should be formatted as named character vector with new names as values and old names as names; see plyr::rename() |
plot_fix_scatter |
logical indicating whether a scatterplot of fixations should be created |
page |
outputs a plots 4 x 3 and displays the first page. Change number to see other pages if you have more Ps |
gaze data frame containing fixation report
Brings each participant's data to target_hz, working within each
subject and trial:
resample_gaze( x, target_hz, hz = NULL, pupil_cols = c("pupil", "pupil_left", "pupil_right"), gaze_cols = c("x", "y", "x_left", "y_left", "x_right", "y_right"), blink_cols = c("blink", "blink_left", "blink_right"), event_cols = "message", interpolate = TRUE )resample_gaze( x, target_hz, hz = NULL, pupil_cols = c("pupil", "pupil_left", "pupil_right"), gaze_cols = c("x", "y", "x_left", "y_left", "x_right", "y_right"), blink_cols = c("blink", "blink_left", "blink_right"), event_cols = "message", interpolate = TRUE )
x |
data frame with |
target_hz |
target sampling rate in Hz. |
hz |
recorded sampling rate: a single number, a named vector (names =
subjects), or |
pupil_cols, gaze_cols
|
pupil and gaze columns. Columns not in |
blink_cols |
blink-flag columns (1 = in a blink). Columns not in |
event_cols |
event/message columns. Columns not in |
interpolate |
when upsampling, interpolate the new samples' pupil and
gaze values (default |
Downsampling (recorded rate above target_hz): consecutive samples
are averaged into bins of 1000 / target_hz ms, which avoids aliasing.
Pupil and gaze values are the bin mean, and are NA if any sample in the
bin is missing (pupil values <= 0 count as missing). Blink columns are
1 if any sample in the bin was in a blink. Event columns keep every
message in the bin (joined with "; "). Other columns take the bin's
first value. time becomes the start of the bin.
Upsampling (recorded rate below target_hz): new samples are added
with upsample_gaze(). With interpolate = TRUE, their pupil and gaze
values are linearly interpolated from the recorded samples either side,
and left NA if either neighbour is missing (e.g. during a blink).
Same rate: each trial is put on an evenly spaced grid, inserting any
samples the tracker dropped as NA (never interpolated), ready for the
usual blink and interpolation steps.
Inserted samples are flagged in a resampled column. Upsampling and the
uniform grid need a whole-millisecond step (e.g. 1000, 500, 250 Hz).
x at target_hz, sorted by subject, trial and time, with a
logical resampled column (TRUE for samples added by upsampling or
gap filling).
upsample_gaze(), downsample_pupil() and downsample_gaze()
(which average into analysis time bins rather than resample).
x <- data.frame(subject = "s1", trial = 1, time = 0:9, pupil = c(1000:1003, NA, 1005:1009), message = NA) x$message[3] <- "stim" resample_gaze(x, target_hz = 500) # 1000 -> 500 Hzx <- data.frame(subject = "s1", trial = 1, time = 0:9, pupil = c(1000:1003, NA, 1005:1009), message = NA) x$message[3] <- "stim" resample_gaze(x, target_hz = 500) # 1000 -> 500 Hz
Smooths the pupil trace before linear or cubic interpolation.
smooth_interpolate_pupil( datafile, pupil = "pupil", extendpupil = "extendpupil", extendblinks = FALSE, step.first = "smooth", filter = "moving", maxgap = Inf, type = NULL, hz = NA, n = NA )smooth_interpolate_pupil( datafile, pupil = "pupil", extendpupil = "extendpupil", extendblinks = FALSE, step.first = "smooth", filter = "moving", maxgap = Inf, type = NULL, hz = NA, n = NA )
datafile |
data frame. |
pupil |
name of pupil column |
extendpupil |
name of pupil col that was extended |
extendblinks |
blinks already extended in data frame |
step.first |
whether you want to smooth/interpolate or interpolate/smooth |
filter |
use either moving and hann filter |
maxgap |
max number of NAs to interpolate. |
type |
type of interpolation (linear or cubic) |
hz |
recording frequency of ET |
n |
moving average window |
df containing interpolated data
Calculates max absoulte dilation speed as suggested in Kret & Sjak-Shie (in press) calculates the median absoulte deviation (MAD) and sets the threshold to filter on.
speed_pupil(pup, time)speed_pupil(pup, time)
pup |
column listing pupil sizes |
time |
column in data file listing time |
data with pupil speeds for each time point
Time course of target object fixation during a spoken word-to-picture matching (i.e., "visual world paradigm").
TargetFixTargetFix
A data frame with 300 rows and 7 variables
subject ID, factor
time (in ms) relative to word onset in 50ms bins, 300-1000
rescaling of Time variable for easier model-fitting, 1-15
word frequency condition: High (e.g., torch, frog) or Low (e.g., class, horse)
fixation proportion (subject mean): proportion of correct-response trials on which the object was fixated
fixation sum: number of correct-response trials on which the object was fixated (i.e., numerator for meanFix)
number of correct-response trials (i.e., denominator for meanFix)
Increase the sampling frequency to target_hz Hz by inserting additional rows.
Missing values in gaze and pupil data will be preserved for later interpolation.
upsample_gaze( x, pupil_cols = c("Pupil_Diameter"), gaze_cols = c("x_pred_normalised", "y_pred_normalised"), target_hz = 1000, upsample_pupil = TRUE, event_cols = c("message") )upsample_gaze( x, pupil_cols = c("Pupil_Diameter"), gaze_cols = c("x_pred_normalised", "y_pred_normalised"), target_hz = 1000, upsample_pupil = TRUE, event_cols = c("message") )
x |
A dataframe containing gaze and pupil data with columns: |
pupil_cols |
Character vector of pupil diameter column names. |
gaze_cols |
Character vector of gaze position column names. |
target_hz |
Target sampling frequency (default is 1000 Hz). |
upsample_pupil |
Logical; if |
event_cols |
Character vector of event/message columns that should stay
empty on inserted rows (default |
Original rows are returned unchanged. Each inserted row takes its other
columns (e.g. condition, trial variables) from the original sample before
it, except pupil_cols/gaze_cols (left NA for interpolation) and
event_cols (left NA, so each event message still appears once).
A dataframe with up-sampled time points and an up_sampled column
(TRUE for inserted rows, FALSE for original ones).
Accuracy in 2-alternative forced-choice word learning experiment. On each trial, participants see two complex geometric shapes, hear a novel word (e.g., "pibu"), and have to guess which object corresponds to the word. They receive feedback and gradually learn which object goes with which word. There are 6 trials in each of the 10 training blocks. Transitional probability of the novel words (i.e., p("bu"|"pi")) was experimentally manipulated (between-subjects) during an exposure phase immediately preceding the word learning phase.
WordLearnExWordLearnEx
A data frame with 560 rows and 4 variables
subject id, N=56
transitional probability of words, manipulated between-subjects
training block (1-10), each block consists of 6 trials
proportion correct responses during the block, chance = 0.5
Partial data from Mirman et al. (2008, Cognition, 108:1, 271-280). http://www.danmirman.org/pdfs/MirmanMagnusonGrafEstesDixon2008.pdf