Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mattansb/epp-tb
EPP-TB: The ERP Post-Processing Tool Box
https://github.com/mattansb/epp-tb
eeg eeglab epp-tb erp ersp itc matlab plotting topo-plots wavelet
Last synced: 3 months ago
JSON representation
EPP-TB: The ERP Post-Processing Tool Box
- Host: GitHub
- URL: https://github.com/mattansb/epp-tb
- Owner: mattansb
- License: gpl-3.0
- Created: 2018-01-11T10:06:02.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2020-12-20T18:09:53.000Z (about 4 years ago)
- Last Synced: 2024-10-11T18:23:36.891Z (3 months ago)
- Topics: eeg, eeglab, epp-tb, erp, ersp, itc, matlab, plotting, topo-plots, wavelet
- Language: MATLAB
- Homepage:
- Size: 51.4 MB
- Stars: 7
- Watchers: 2
- Forks: 1
- Open Issues: 3
-
Metadata Files:
- Readme: README.Rmd
- License: LICENSE
Awesome Lists containing this project
README
---
output: github_document
---# EPP-TB: The ERP Post-Processing Tool Box
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE,fig.path = "doc/")library(tidyverse)
library(magrittr)
library(scales)
library(patchwork)```
Once completing pre-processing in eeglab/erplab/Net Station, you're ready to get to the fun stuff: plotting and measuring with EPP-TB!
This package is aimed at simple (readable), concise (one function per action) and reproducible code writing.
Additionally, a major component is the ability to export data and plots to be used and further manipulated elsewhere (say, `ggplot2`?).All user-end functions start with `epp_*`.
## Getting Started
### Prerequisites
To use EPP-TB you will need:
* Matlab (2015a+).
* [eeglab](https://sccn.ucsd.edu/eeglab/index.php) (14.X.X) for importing `.set` files, and plotting topos.
* R (for plotting in R)
+ [`tidyverse`](https://github.com/tidyverse/tidyverse) + [`purrr`](https://github.com/tidyverse/purrr).
+ [`scales`](https://github.com/hadley/scales) for plotting in R.
+ [`mgcv`](https://cran.r-project.org/package=mgcv) for plotting topo plots.### Installing
You can install the package by [downloading](https://github.com/mattansb/EPP-TB/releases) and adding the EPP-TB folder (and sub-folders) to your Matlab paths.
## Importing Data into an EPP structure
Three import methods are currently supported:
- `epp_loadeeglab` - Import multiple `.set` files from eeglab (supports wavelet analysis based on [Mike X. Cohen's great book and code](http://www.mikexcohen.com/)).
- `epp_loaderplab` - Import from [erplab](http://www.erpinfo.org/erplab.html).
- `epp_loadegimat` - Import multiple `.mat` files exported from Net Station (EGI).The resulting structure array has `length(struct)` equal to the number of conditions, and contains the following fields, per condition:
- **Condition**: the name of the conditions.
- **timeLine**: a vector of time points.
- **IDs**: a table with two variables - _**ID**_ and _**nTrials**_ (the number of trials a ERP has been averaged across).
- **Data**: a `channels` *x* `time points` *x* `Subjects` matrix for ERP data.If a wavelet analysis has been preformed, the Data field is replaced with:
- **ersp** and **itc**: `channels` *x* `frequencies` *x* `time points` *x* `Subjects` matrices.
- **Freqs**: a vector of frequencies used.
### Data Reduction / Reshaping
These functions can be used to compute new conditions or manipulate existing data:
- Merge 2 or more conditions with `epp_combineconds()`.
- Compute differences between 2 conditions with `epp_diffwave()`.
- Compute LRP with `epp_LRP()`
- Compute global field potentials with `epp_GFP()`.
- Collapse TF data to frequency waveforms with `epp_reshapeTF()`.
- Make grand-average ERP/ERSP/ITC with `epp_makegrands()` (useful for plotting large data sets).### Working with ID data
- Add data to `study.IDs` from a table with `epp_appendID()`.
- Retain subjects that have data in all specified conditions with `epp_matchsubjects()`.
- Select data from specific subjects by some variable in `study.IDs` with `epp_filter_by()`.
- Pull data from `study.IDs` with `epp_extractIDs()`.## Plotting
> For all `epp_plot*` functions, which allow plotting data from `epp` structures, there is an accompanying general `p_*` function, that allows plotting from other data structures (with a little bit of wrangling).
### ERP Plots
#### Grand averages
Grand averages can be plotted by specifying the conditions and channel indices (averaged across) to plot:```Matlab
conds = {'Crr','L5'};
channel_inds = [5 6 11 12];
epp_plotgrands(study,conds,channel_inds)
``````{r grand_plot}
grand_data <- read_csv("doc/grand_plot_data.csv") %>% # load the data file
as.tbl() %>%
mutate_at(.vars = c("Time","mean","sd"), .funs = as.numeric) %>%
mutate(Condition = as.factor(Condition),
N = as.integer(N),
se = sd/sqrt(N)) %>%
separate(Condition,c('Group','Level'))grand_data %>%
ggplot(aes(x = Time, y = mean,
color = Level, fill = Level,
group = Level)) +
geom_vline(xintercept = 0) +
geom_hline(yintercept = 0) +
geom_line(size = 1) +
scale_x_continuous(expand = c(0, 0)) +
scale_color_manual(values = c('blue','red')) +
labs(x = "Time", y = expression(paste(mu,"V"))) +
theme_minimal() + theme(legend.position="bottom")```
You can also plot error envelopes:
```Matlab
epp_plotgrands(study,conds,channel_inds,'errorType','SE')
% can also be set to 'SD' or 'CIXX'
% (with XX replaced with any percent: 'CI95','CI80', etc...).
``````{r grand_plot_se}
grand_data %>%
ggplot(aes(x = Time, y = mean,
color = Level, fill = Level,
group = Level)) +
geom_vline(xintercept = 0) +
geom_hline(yintercept = 0) +
geom_ribbon(aes(ymin = mean-se, ymax = mean+se),
alpha = 0.25, color = NA) +
geom_line(size = 1) +
scale_x_continuous(expand = c(0, 0)) +
scale_color_manual(values = c('blue','red')) +
scale_fill_manual(values = c('blue','red')) +
labs(x = "Time", y = expression(paste(mu,"V"))) +
theme_minimal()+ theme(legend.position="bottom")
```#### Topo Plots
Topo plotting is dependent on eeglab functions. Additionally, you will need to provide a eeglab-like chanlocs structure to these functions, with `length(chanlocs)==size(study.Data,1)`.
```Matlab
times = [185];
epp_plottopo(study,chanlocs,conds,channel_inds,times)
```
```{r topo_plot}raw.data <- read.csv("doc/topo_plot_data.csv") %>%
as.tbl() %>%
mutate(Condition = as.factor(Condition),
# convert Theta and Radius to (x,y)
radianTheta = pi/180*Theta,
x = Radius*sin(radianTheta),
y = Radius*cos(radianTheta),
r = sqrt(x^2 + y^2))expand.topo <- function(data, gridRes = NULL) {
library(mgcv)
if (is.null(gridRes))
gridRes <- data[[1]] %$% unique(Channel) %>% {length(.)/2}
res <- list()
for (c in 1:length(data)) {
LL <- data[[c]]
minx <- min(LL$x)*1.1
miny <- min(LL$y)*1.1
maxx <- max(LL$x)*1.1
maxy <- max(LL$y)*1.1
tmp.GAM <- expand.grid(x = seq(minx*1.05,maxx*1.05,length = gridRes),
y = seq(miny*1.05,maxy*1.05,length = gridRes)) %>%
data.frame()
tmp.GAM$Amp <- LL %>%
gam(Amp ~ s(x, y, bs = 'ts'), data = .) %>%
predict(tmp.GAM, type = "response")
res[[c]] <- tmp.GAM %>%
filter(sqrt(x^2 + y^2) <= max(diff(range(x)),diff(range(y)))/2)
rm(tmp.GAM)
}
return(res)
}circleFun <- function(diameter = 1, center = c(0,0), npoints = 100) {
r <- diameter / 2
tt <- seq(0,2*pi,length.out = npoints)
xx <- center[1] + r * cos(tt)
yy <- center[2] + r * sin(tt)
return(data.frame(x = xx, y = yy))
}# Topo theme
theme_topo <- function(base_size = 12) {
theme_bw(base_size = base_size) %+replace%
theme(rect = element_blank(),
line = element_blank(),
axis.text = element_blank(),
axis.title = element_blank()
)
}chanlocs <- raw.data %>%
select(Channel, x, y) %>%
distinct()diam <- chanlocs %$%
max(diff(range(x)),diff(range(y))) # can be adjusted
head <- list()
head$shape <- circleFun(diam*0.75)
head$mask <- circleFun(diam*1.15)
head$nose <- data.frame(x = c(-.075,0,.075),
y = c(-.005,.075,-.005) + max(head$shape$y))topo.data <- raw.data %>%
group_by(Condition,TimePnt) %>%
select(Condition,TimePnt,Channel,x,y,Amp) %>%
nest() %>%
mutate(data = expand.topo(data)) %>%
unnest() %>%
separate(Condition,c('Group','Level'))ggplot(topo.data,aes(x, y)) +
## The Topos
geom_raster(aes(fill = Amp))+
facet_grid(~Level) +
## The Head
geom_path(data = head$mask, color = "white",size = 4) +
geom_path(data = head$nose, size = 1.5) +
geom_path(data = head$shape, size = 1.5) +
## The Channels
geom_point(data = chanlocs) +
## Design
scale_fill_distiller(palette = "RdYlBu", direction = -1,
limits = c(-2,2), oob = squish) +
labs(fill = "Amplitude") +
theme_topo() +
coord_equal()```
#### Butterfly and Trace Plots
Butterfly plots can be used to plot the mean ERP for each subject individually.
```Matlab
epp_plotbutterfly(study,conds,channel_inds)
``````{r butterfly_plot}
butterfly_data <- read_csv("doc/butterfly_plot_data.csv") %>% # load the data file
group_by(Condition,Time) %>%
mutate(meanAmp = mean(amp)) %>%
ungroup() %>%
separate(Condition,c('Group','Level'))butterfly_data %>%
ggplot(aes(x = Time, y = amp,
color = Level,
group = ID)) +
facet_wrap(~Level) +
geom_vline(xintercept = 0) +
geom_hline(yintercept = 0) +
geom_line(alpha = 0.8) +
geom_line(aes(y = meanAmp), color = "black", size = 1) +
scale_x_continuous(expand = c(0, 0)) +
scale_color_manual(values = c('blue','red')) +
labs(x = "Time", y = expression(paste(mu,"V"))) +
theme_minimal()+ theme(legend.position="none")```
Trace plots are similar to butterfly plots, but the mean activation (across subjects) is plotted for each channel separately.
```Matlab
channel_inds = []; % if left blank, all channels are plotted.
epp_plotbutterfly(study,conds,channel_inds,'trace',true)
```
```{r trace_plot}trace_data <- read_csv("doc/trace_plot_data.csv") %>%
separate(Condition,c('Group','Level'))trace_data %>%
ggplot(aes(x = Time, y = amp,
color = Level,
group = ID)) +
facet_wrap(~Level) +
geom_vline(xintercept = 0) +
geom_hline(yintercept = 0) +
geom_line(alpha = 0.8) +
scale_x_continuous(expand = c(0, 0)) +
scale_color_manual(values = c('blue','red')) +
labs(x = "Time", y = expression(paste(mu,"V"))) +
theme_minimal()+ theme(legend.position="none")```
#### Channel Plots
Similar to trace plots, channel plots give a picture of what is happening at each channel (like `eeglab`'s `plottopo`). These come in two flavors:
- Topo Plots - channel data is plotted in 2-d space, like a topo-plot.
- Grid Plots - channel data is plotted on a simple grid.Channel topo plots are created with the following Matlab call:
```Matlab
channel_inds = []; % if left blank, all channels are plotted.
epp_plotchannels(study,conds,electrodes,'chanlocs',chanlocs)
```Grid plots are called using the same call, without providing `chanlocs`.
### TF Plots
#### Time-Frequency Plot
Time Frequency plots plot both ersp and itc:
```Matlab
epp_plotTF(study,conds,channel_inds)
``````{r TFplot}
df <- read_csv("doc/TFplot_data.csv") %>%
separate(Condition,'Level')# Plot --------------------------------------------------------------------
# Use geom_raster to plot y-axis scale as is
ersp_plot <- ggplot(df,aes(Time, Frequencies, fill = ersp)) + # or fill = itc
geom_raster(interpolate = T) +
facet_grid(.~Level) +
scale_y_log10(breaks = c(1,4,8,12,30), expand = c(0, 0)) + # if Frequencies are log-spaced
scale_x_continuous(breaks = seq(-200,700,by = 200), expand = c(0, 0)) +
scale_fill_gradientn(colors = c('black','red','yellow','white'),
limits = c(0,2.5),oob=squish) +
theme_minimal() +
labs(fill = 'Power (dB)')itc_plot <- ggplot(df,aes(Time, Frequencies, fill = itc)) + # or fill = itc
geom_raster(interpolate = T) +
facet_grid(.~Level) +
scale_y_log10(breaks = c(1,4,8,12,30), expand = c(0, 0)) + # if Frequencies are log-spaced
scale_x_continuous(breaks = seq(-200,700,by = 200), expand = c(0, 0)) +
scale_fill_gradientn(colors = c('black','red','yellow','white'),
limits = c(0,0.4),oob=squish) +
theme_minimal() +
labs(fill = 'ITC')ersp_plot/itc_plot
```
#### Other Plots
All the listed above plotting methods also support TF data. For example:
```Matlab
times = [160 410];
bands = [4 8; 8 12];
% a matrix of frequencies, with each row containing a range of frequencies
% to plot (1st column is lower limit, 2nd column is upper limit of band).
epp_plottopo(study,chanlocs,conds,channel_inds,times,...
'freqs',bands, 'type', 'ersp')
```
```{r TFtopo}
raw.data <- read.csv("doc/TFtopo_data.csv") %>%
as.tbl() %>%
mutate(Condition = as.factor(Condition),
# convert Theta and Radius to (x,y)
radianTheta = pi/180*Theta,
x = Radius*sin(radianTheta),
y = Radius*cos(radianTheta),
r = sqrt(x^2 + y^2))expand.topo <- function(data, gridRes = NULL) {
if (is.null(gridRes))
gridRes <- data %$% unique(Channel) %>% {length(.)/2}
range_x <- range(data$x)*1.15
range_y <- range(data$y)*1.15
tmp.GAM <- expand.grid(x = seq(range_x[1],range_x[2],length = gridRes),
y = seq(range_y[1],range_y[2],length = gridRes)) %>%
data.frame()
tmp.GAM$Amp <- gam(Amp ~ s(x, y, bs = 'ts'), data = data) %>%
predict(tmp.GAM, type = "response")
tmp.GAM %>%
filter(sqrt(x^2 + y^2) <= max(diff(range(x)),diff(range(y)))/2)
}circleFun <- function(diameter = 1, center = c(0,0), npoints = 100) {
r <- diameter / 2
tt <- seq(0,2*pi,length.out = npoints)
xx <- center[1] + r * cos(tt)
yy <- center[2] + r * sin(tt)
return(data.frame(x = xx, y = yy))
}# Topo theme
theme_topo <- function(base_size = 12) {
theme_bw(base_size = base_size) %+replace%
theme(rect = element_blank(),
line = element_blank(),
axis.text = element_blank(),
axis.title = element_blank()
)
}# Channel Data
chanlocs <- raw.data %>%
select(Channel, x, y) %>%
distinct()# Head Shapes
diam <- chanlocs %>%
select(x,y) %>%
map_dbl(~diff(range(.))) %>% max()
head <- list(
shape = circleFun(diam*0.75),
mask = circleFun(diam*1.15),
nose = data.frame(x = c(-.075,0,.075),
y = c(-.005,.075,-.005) + diam*0.375)
)# Topo Data
topo.data <- raw.data %>%
select(Condition,TimePnt,Channel,x,y,Amp) %>%
separate(Condition,c('Level','Group','Band')) %>%
mutate(Band = factor(Band, labels = c('Theta (4-8Hz)','Gamma (8-12Hz)'))) %>%
group_by(Level,Band) %>%
nest() %>%
mutate(data = map(data,expand.topo)) %>%
unnest()# Plot
ggplot(topo.data,aes(x, y)) +
geom_raster(aes(fill = Amp))+
facet_grid(Band~Level) +
geom_path(data = head$mask, color = "white",size = 4) +
geom_path(data = head$nose, size = 1.5) +
geom_path(data = head$shape, size = 1.5) +
scale_fill_gradientn(colors = c('white','cyan','blue','black','red','yellow','white'),
limits = c(-2,2), oob = squish) +
labs(fill = "Power (dB)") +
theme_topo() +
coord_equal()```
### Exporting to R
All plots can be exported to R and plotted with [`ggplot2`](https://github.com/tidyverse/ggplot2) by setting `'R',true` in any of the plotting function (this is how the plots in this README were made). This produces two time-stamped files:
- A data file (`*_data.csv`)
- A code file (`*_code.R`), to plot said data using `ggplot2`.## Measuring
Measuring can be done via the `epp_get*` functions. These are wrapper functions for the various `m_*` functions (with one functions per method), **which should not be called directly** (unless you know what you're doing).
Saving results always produces a 2-sheet `.xlsx` file, with the second sheet containing the parameters used in measuring - this is to insure reproducibility of results. Thus, if you have the output `.xlsx` file, you'll never find yourself asking "what was the time window we used last time?".
### ERP Amplitude
The function `epp_getAmp` implements the methods for measuring amplitudes described in chapter 9 of [Steven J. Luck's intro to ERP book](https://mitpress.mit.edu/books/introduction-event-related-potential-technique-0).
- (Local) Peak amplitude
- Point amplitude
- Mean amplitude
- Integral
- Area### ERP Latency
The function `epp_getLat` implements the methods for measuring latencies described in chapter 9 of [Steven J. Luck's intro to ERP book](https://mitpress.mit.edu/books/introduction-event-related-potential-technique-0), as well as those described in [Kiesel et al.'s Jakknife paper](http://onlinelibrary.wiley.com/doi/10.1111/j.1469-8986.2007.00618.x/abstract).
- (Local) Peak latency
- Relative criterion
- Fractional area
- Baseline deviation
- Absolute criterion### TF Power
The function `epp_getTF` measure the *mean* ersp / itc from selected channels, within a specified time-window,separately for specified frequency bands (as shown above, for `epp_plottopoTF`).
## Authors
- **Mattan S. Ben-Shachar** [aut, cre].
- **Rachel Rac** [ctb].
- **Michael Shmueli** [ctb].## Acknowledgments
- **Mike X. Cohen** - who's [code](http://mikexcohen.com/lectures.html) was implemented in [`f_WaveletConv.m`](https://github.com/mattansb/EPP-TB/blob/master/supportfuncs/f_WaveletConv.m).
- **Matt Craddock** - who's [code](https://craddm.github.io/blog/2017/02/25/EEG-topography) was implemented in [`epp_plottopo.R`](https://github.com/mattansb/EPP-TB/blob/master/plot/epp_plottopo.R).
- **Winston Chang** - who's [code](http://www.cookbook-r.com/Graphs/Plotting_means_and_error_bars_(ggplot2)/) for computing within-subject (and mixed model) CI's was implemented in [`epp_plotgrands.m`](https://github.com/mattansb/EPP-TB/blob/master/plot/epp_plotgrands.m).