EcoTrialStructure
EcoTrialStructure provides simple and natural operations to analyze experiments in economic decision making. You can import both behavioral and physiological data.
Matlab-file import (Padoa-Schioppa lab users)
When the data have already been saved as a .mat file, the first thing to do is import the data. This package's test/data folder contains a test file that serves as a useful demo:
julia> using EcoTrialStructure
julia> cts, trs, ets = parsemat(joinpath(pkgdir(EcoTrialStructure), "test", "data", "testfile.mat"));
julia> cts
OrderedCollections.OrderedDict{Int64, CellsTrial{Float64}} with 2 entries:
3 => 63 cells with 69 timepoints
201 => 63 cells with 39 timepoints
julia> trs
OrderedCollections.OrderedDict{Int64, TrialResult} with 2 entries:
3 => TrialResult(nA=0, nB=2, leftA=true, choseA=true)
201 => TrialResult(nA=2, nB=0, leftA=false, choseA=true)
julia> ets
OrderedCollections.OrderedDict{Int64, EventTiming} with 2 entries:
3 => EventTiming(trial_start=0.0 ms, offer_on=2003.0 ms, offer_off=4820.0 m…
201 => EventTiming(trial_start=0.0 ms, offer_on=2002.0 ms, offer_off=4812.0 m…This test data file was extracted from a much larger & more complete experiment with 237 trials, of which just 2 were used for testing purposes. cts has information about the cells (see CellsTrial), trs about the offers and behavioral decisions (see TrialResult), and ets about the timing of events during each trial (see EventTiming). Each is indexed with the trial index, i.e., ets[3] extracts the event timing for trial 3.
You can extract additional information from the .mat file; an example is given by positive_cells.
Manipulating the core types
Extracting data can be done by standard Julia methods, for example:
julia> trs[3].nB
2There are also a number of convenience utilities, like isforced to detect whether a given trial presented a "forced" choice. See more examples in Simple utilities.
CellsTrial provides a number of convenience methods for extracting comparable data from different trials; see the examples in its documentation for a detailed explanation. But as an overall example, here is a demonstration based on the data above in Matlab-file import. Here, we extract dFoF data triggered at offer_on across all (two) trials, extracting 2 pre-frames and 8 post-frames:
julia> fs = FrameSeq(:offer_on, -2:7) # 0 corresponds to offer_on
FrameSeq(:offer_on, -2:7)
julia> dFoFs = [cts[trialindex][fs(et), :][2] for (trialindex, et) in ets];
julia> dFoFs[1] # trial 3
10×63 OffsetArray(::Matrix{Float64}, -2:7, 1:63) with eltype Float64 with indices -2:7×1:63:
0.146783 0.479383 0.598108 … 0.0408331 0.0357164 0.0741511
0.177113 0.481758 0.579934 0.0181103 0.029064 0.0962147
0.158358 0.318992 0.679291 0.0356008 -0.0285638 0.242723
0.0949043 0.427107 0.792304 0.105977 -0.0537317 0.26273
0.207441 0.387189 0.50655 0.0597971 -0.0613983 0.135856
0.173742 0.240543 0.49942 … 0.0809025 -0.0496011 0.129161
0.0754508 0.292467 0.44907 0.156167 0.00943657 0.143208
0.115572 0.281595 0.356081 0.0406873 0.0155088 0.122557
0.119914 0.193427 0.418782 0.100677 0.288955 0.19112
0.0501042 0.12239 0.416016 0.0648454 0.622751 0.0572375and where dFoFs[2] returns the data for trial 201.
As explanation, fs(et) "concretizes" the abstract notion of offer_on to the specific time for the trial corresponding to the events recorded in et. Frame sampling may not be precisely syncronized with behavioral events, so the frame nearest to the event time is chosen as the basepoint. Because indexing a CellsTrial with a time interval or FrameSeq returns both the time interval and the dFoF data, the final [2] selects just the dFoF data.
API reference
.mat-file parsing
EcoTrialStructure.parsemat — Functionparsemat(filename)
parsemat(readfunction, filename)Parse the trial data in filename. Optionally pass a readfunction to extract additional elements; readfunction should have syntax
function readfunction(data, args...)
moredata = data["matlab_variable_name"]
# maybe do some processing/validation
return args..., moredata
enddata is the result of MAT.matread(filename).
See also: positive_cells.
EcoTrialStructure.positive_cells — Functionpositive_cells(data, args...)Extract the vector of idx_positive_cells from the .mat file.
Core types
EcoTrialStructure.CellsTrial — TypeCellsTrial(t, dFoF)Store cellular responses for a single trial. t is the list of frame times (in units of time, see Unitful.jl) and dFoF is a matrix with one row for each time in t, and one column per cell.
Examples
julia> dFoF = [ 0.1 0.8;
-0.1 0.7;
0.2 0.6;
0.1 0.5;
0.0 0.4];
julia> ct = CellsTrial((100:100:500) * ms, dFoF)
2 cells with 5 timepointsIt's straightforward to extract the dFoF values over a particular time interval:
julia> tframes, df = ct[175ms..310ms, :]; # frames range nearest to given start..stop times
julia> tframes
200.0f0 ms..300.0f0 ms
julia> df
2×2 Matrix{Float64}:
-0.1 0.7
0.2 0.6This extracted the dFoF values recorded between 200ms and 300ms, inclusive.
In other cases, you might want a specific number of frames, starting at a particular time:
julia> tframes, df = ct[FrameSeq(175ms, 3), :]; # start nearest to 175ms, and grab 3 frames' worth
julia> tframes
200.0f0 ms..400.0f0 ms
julia> df
3×2 OffsetArray(::Matrix{Float64}, 0:2, 1:2) with eltype Float64 with indices 0:2×1:2:
-0.1 0.7
0.2 0.6
0.1 0.5EcoTrialStructure.FrameSeq — TypeFrameSeq(tstart, nframes)
FrameSeq(tstart, offsetrange)
FrameSeq(eventfield::Symbol, nframes)A sequence of nframes frames starting at the nearest timepoint to tstart. Optionally specify a range of frames offset from tstart.
julia> fs = FrameSeq(100ms, 5)
FrameSeq(100.0f0 ms, 0:4)
julia> fs = FrameSeq(100ms, -2:4)
FrameSeq(100.0f0 ms, -2:4)See CellsTrial for an example using this in indexing.
Alternatively, this can be constructed specifying a particular fieldname of EventTiming, in which case the concrete timing can be deferred until a later time based on a specific trial:
julia> fs = FrameSeq(:go, 5)
FrameSeq(:go, 0:4)
julia> et = EventTiming(0ms, 100ms, 400ms, 450ms, 837ms, 1.2s)
EventTiming(trial_start=0.0f0 ms, offer_on=100.0f0 ms, offer_off=400.0f0 ms, go=450.0f0 ms, choice=837.0f0 ms, trial_end=1200.0f0 ms)
julia> fs(et)
FrameSeq(450.0f0 ms, 0:4)EcoTrialStructure.OfferType — TypeOfferType(nA, nB, leftA::Bool)Encode the offers (nA and nB are the number of drops of A and B, respectively), and whether A was on the left.
If you have a list of OfferTypes, you can sort them. High-A trials will come before high-B trials, and left before right.
EcoTrialStructure.TrialResult — TypeTrialResult(nA, nB, leftA::Bool, choseA::Union{Bool,Missing})
TrialResult(tt::OfferType, choseA::Union{Bool,Missing})Encode the offer configuration (see OfferType), and whether the animal chose A, B, or failed to make a choice (choseA = true | false | missing, respectively).
EcoTrialStructure.EventTiming — TypeEventTiming(trial_start, offer_on, offer_off, go, choice, trial_end)Specify the timing of events during a trial. All times should be in physical units (s or ms).
Examples
julia> EventTiming(0ms, 100ms, 400ms, 450ms, 837ms, 1.2s)
EventTiming(trial_start=0.0f0 ms, offer_on=100.0f0 ms, offer_off=400.0f0 ms, go=450.0f0 ms, choice=837.0f0 ms, trial_end=1200.0f0 ms)Simple utilities
EcoTrialStructure.madechoice — Functionmadechoice(tr::TrialResult)Return true if the animal made a choice in the trial.
EcoTrialStructure.isforced — Functionisforced(tt::OfferType)
isforced(tr::TrialResult)Return true for a forced-choice trial, where either nA or nB is zero.
EcoTrialStructure.iswrong — Functioniswrong(tr::TrialResult)Return true if tr is a forced-choice trial and the animal chose the wrong option.
EcoTrialStructure.isdeferred — Functionisdeferred(fs::FrameSeq)Return true if the start time in fs is a field name of EventTiming, and hence requires concrete instantiation as fs(et) for a specific trial.
EcoTrialStructure.ncells — Functionncells(ct::CellsTrial)Return the number of cells stored in the dFoF field of ct.