/
mne.r
170 lines (159 loc) · 6.33 KB
/
mne.r
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
library(tidyverse)
library(reticulate)
mne <- NULL
.onLoad <- function(libname, pkgname) {
# push MNE into global environment
mne <<- reticulate::import("mne", delay_load = TRUE)
cat(
sprintf(
"Importing MNE version=%s, path='%s'\n", mne$`__version__`,
mne$`__path__`))
assign("mne", mne, .GlobalEnv)
}
#' Get data from MNE into R data.frame
#'
#' \code{get_data_frame} helps importing MNE data structures
#'
#' The code will call the \code{.to_data_frame} method of the MNE
#' data container and returns a dataframe readily usable in R. Note
#' that the type definitions below refer to Python types. Please see
#' the reticulate documentation to learn about R-to-Python
#' \href{https://rstudio.github.io/reticulate/articles/calling_python.
#' html}{conversion rules}. Note that this function requires, next to
#' MNE, a working [Pandas](https://pandas.pydata.org) installation.
#' For background information on exporting MNE objects
#' to dataframes, consider the designated \href{https://martinos.org/
#' mne/dev/auto_tutorials/plot_epochs_to_data_frame.html}{MNE
#' tutorial}.
#'
#' @param inst An instance of MNE data containsers, e.g,
#' \code{mne$Epochs}, \code{mne$io$Raw}, \code{mne$Evoked}.
#' @param picks A zero-indexed integer array, a string, list, slice or
#' None.
#' @param index The columns to be uesed as pandas index. tuple of str
#' or None.
#' @param scaling_time Scaling to be applied to time units. Float.
#' @param scalings Scaling to be applied to the channels picked.
#' @param copy Whether to make a copy of the data.
#' @param start If it is a Raw object, this defines a starting index
#' for creating the dataframe from a slice. The times will be
#' interpolated from the index and the sampling rate of the
#' signal. Int or None.
#' @param stop If it is a Raw object, this defines a stop index for
#' creating the dataframe from a slice. The times will be
#' interpolated from the index and the sampling rate of the
#' signal. Int or None.
#' @param long_format If True, the dataframe is returned in long
#' format where each row is one observation of the signal at a
#' unique coordinate of channels, time points, epochs and
#' conditions. The number of factors depends on the data
#' container. For convenience, a ch_type column is added when
#' using this option that will facilitate subsetting the
#' resulting dataframe.
#' If False, mne-r is making sure that the channel names
#' are cleaned from white spaces to esnure that the R-formulas
#' will work. In the case that epochs are passed, the pandas
#' muliti-index is unpacked and the columns "condition",
#' "epochs", and "time" are prepended. Unliker the
#' MNE-Python function, the default is True.
#' @return Returns a data.frame. The layout depends on the options
#' (e.g. \code{long_format}) and the type of instance
#' (e.g. Epochs vs Raw).
#' @export
#' @examples
#' library(mne)
#' fname <- paste(mne$datasets$testing$data_path(),
#' "MEG", "sample", "sample_audvis_trunc_raw.fif",
#' sep = "/")
#' raw <- mne$io$read_raw_fif(fname, preload = T)
#' raw_df <- get_data_frame(raw)
#' print(head(raw_df))
get_data_frame <- function(inst, picks = NULL, index = NULL,
scaling_time = 1e3, scalings = NULL,
copy = T, start = NULL, stop = NULL,
long_format = T) {
# handle MNE python version
inspect <- reticulate::import("inspect")
to_df_args <- inspect$getargspec(inst$to_data_frame)$args
.args <- list(picks = picks, index = index,
scaling_time = 1e3, scalings = scalings,
copy = copy, start = start, stop = stop,
long_format = long_format)
if ("long_format" %in% to_df_args & long_format) {
out <- do.call(inst$to_data_frame, .args)
} else if (!("long_format" %in% to_df_args) & long_format) {
.args$long_format <- NULL
.args$inst <- inst
out <- do.call(get_long_format, .args)
} else {
out <- do.call(inst$to_data_frame, .args)
cat("Deleting whitespace in channel names ...")
colnames(out) <- colnames(out) %>%
gsub(" ", "", x = ., fixed = T)
if ("mne.epochs.BaseEpochs" %in% class(inst)) {
mindex <- attr(
out, "pandas.index")$values %>% reticulate::py_to_r(.)
out <- data.frame(
condition = sapply(mindex, "[[", 1) %>% as.factor(),
epoch = sapply(mindex, "[[", 2) %>% as.factor(),
time= sapply(mindex, "[[", 3) %>% as.numeric(),
out)
}
}
return(out)
}
get_long_format <- function(inst, picks, index, scaling_time,
scalings, copy, start, stop){
.args <- as.list(match.call())[-1]
.args$inst <- NULL
out <- do.call(inst$to_data_frame, .args)
# Order used in MNE
# Raw + evoked
# c('time', 'channel', 'observation', 'ch_type')
# Epochs
# c('condition', "epoch", 'time', 'channel', 'observation',ś
# 'ch_type')
# common steps
ch_idx <- 0L:(inst$info["nchan"] - 1L)
ch_type <- sapply(
ch_idx, function(.) mne$io$pick$channel_type(inst$info, .)) %>%
as.factor()
if ("mne.epochs.BaseEpochs" %in% class(inst)) {
observation <- out %>%
as.matrix() %>%
t() %>%
matrix(nrow = prod(out %>% dim(.)))
channel <- out %>% colnames() %>% rep(., times = dim(out)[1])
mindex <- attr(
out, "pandas.index")$values %>% reticulate::py_to_r(.)
condition <- sapply(mindex, "[[", 1)
epoch <- sapply(mindex, "[[", 2)
time <- sapply(mindex, "[[", 3)
# we mostly trust base-R grid expansion magic ...
out_df <- data.frame(
condition = condition,
epoch = epoch %>% as.factor() %>% rep(each = dim(out)[2]),
time = time %>% as.numeric() %>% rep(each = dim(out)[2]),
channel = channel,
observation = observation,
ch_type = ch_type
)
} else {
observation <- as.vector(t(out))
channel <- out %>% colnames()
time <- out %>%
rownames() %>%
as.numeric() %>%
rep(each = length(channel))
# we trust base-R grid expansion magic ...
out_df <- data.frame(
time = time,
channel = channel,
observation = observation,
ch_type = ch_type
)
}
# XXX source estimate class is not yet handled.
# Add error or support.
return(out_df)
}