-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpreprocess-import-fieldtrip.R
270 lines (233 loc) · 7.67 KB
/
preprocess-import-fieldtrip.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#' @author Zhengjia Wang
#' @date July 09, 2023
#' @license Apache-2.0
#'
#' @title Imports FieldTrip to RAVE (trial as blocks)
#' @description
#' Imports and convert FieldTrip to RAVE, each "trial" (in FieldTrip)
#' is treated as session/block/run in RAVE (RAVE does not process raw
#' data at trial level)
#'
#' @param file_path File path to the FieldTrip data (Matlab, tested with
#' Matlab >= v7.3)
#' @param project_name RAVE project name to use (you can name it with your task)
#' @param subject_code Subject code to use
#' @param data_name the data name in FieldTrip data (depending on how you save
#' data, default is `NULL`, which uses the largest dataset in Matlab file)
#' @param block_prefix RAVE block prefix (you can name it with your
#' `{session}_{task}`)
#' @param do_import whether to import the data into RAVE with given project
#' and subject; default is true
#' @param launch_rave whether to launch RAVE at the end; default is true if
#' running in an interactive R session
#'
#' @examples
#'
#' # load the script
#' import_ft <- raveio::load_snippet("preprocess-import-fieldtrip", local = FALSE)
#'
#' # print documentation
#' print(import_ft)
#'
#' # run script
#' file_path = "sub-DM1006_ses-intraop_task-lombard_ft-raw.mat"
#' # BIDS_info <- raveio:::analyze_bids_fname(file_path)
#'
#' import_ft(
#' file_path = file_path,
#' # # Default using BIDS sub+ses+task information
#' # project_name = BIDS_info$task,
#' # subject_code = BIDS_info$sub,
#' # block_prefix = sprintf("%s_%s", BIDS_info$ses, BIDS_info$task),
#' do_import = TRUE,
#' launch_rave = TRUE
#' )
#'
#'
#' END OF DOC
NULL
# ---- variables ---------------------------------------------------------------
# DIPSAUS DEBUG START
#
# project_name <- "lombard"
# subject_code <- "DM1006"
# file_path <- "./sub-DM1006_ses-intraop_task-lombard_ft-raw.mat"
# data_name <- NULL
# block_prefix <- "intraop_lombard"
#
# launch_rave <- TRUE
# ---- code body ---------------------------------------------------------------
`%?<-%` <- dipsaus::`%?<-%`
# normalize the path
file_path <- normalizePath(file_path, winslash = "/")
# Get BIDS information from the file name and set default (if not specified)
BIDS_info <- raveio:::analyze_bids_fname(file_path)
project_name %?<-% BIDS_info$task
subject_code %?<-% BIDS_info$sub
block_prefix %?<-% sprintf("%s_%s", BIDS_info$ses, BIDS_info$task)
# Default
data_name %?<-% NULL
do_import %?<-% TRUE
launch_rave %?<-% TRUE
# The following function has been incorporated into latest raveio
# re-implement for max backward compatibility
# Make sure scipy and mat73 has been installed
message("Checking Python environment")
rpymat::ensure_rpymat()
py_packages <- rpymat::list_pkgs()
req_modules <- c("scipy", "mat73")
req_modules <- req_modules[!req_modules %in% py_packages$package]
if(length(req_modules)) {
rpymat::add_packages(req_modules)
}
sio <- rpymat::import("scipy.io", convert = FALSE)
mat73 <- rpymat::import("mat73", convert = FALSE)
# If you see `ERROR:root:ERROR: MATLAB type not supported: table, (uint32)`, it's fine
message("Don't panic if you see the following message: `MATLAB type not supported: table, (uint32)`. You are fine!!!")
# raw_data <- raveio::read_mat(file_path, engine = "py")
raw_data <- tryCatch({
# <= 7.3
sio$loadmat(file_path)
}, error = function(e) {
mat73$loadmat(file_path)
})
# convert to R object
raw_data <- rpymat::py_to_r(raw_data)
if(!length(data_name)) {
nms <- names(raw_data)
size <- sapply(nms, function(nm) {
object.size(raw_data[[nm]])
})
data_name <- nms[which.max(size)][[1]]
}
data_list <- raw_data[[data_name]]
# Configuration
# Number of channels
channel_counts <- data_list$hdr$nChans
# Number of runs (RAVE blocks), stored as trials
block_counts <- data_list$hdr$nTrials
# Sampling frequency
fsample <- data_list$fsample
# collect channel information
channel_table <- data.frame(Electrode = seq_len(channel_counts))
tryCatch({
col <- unlist(data_list$label)
if(length(col) == channel_counts) {
channel_table$Label <- col
}
col <- unlist(data_list$hdr$chantype)
if(length(col) == channel_counts) {
channel_table$Type <- col
}
col <- unlist(data_list$hdr$chanunit)
if(length(col) == channel_counts) {
channel_table$Unit <- col
}
}, error = function(...) {})
# Convert to RAVE raw data format
progress <- dipsaus::progress2("Converting to RAVE",
shiny_auto_close = TRUE,
max = channel_counts * block_counts)
save_block <- function(block_num) {
# DIPSAUS DEBUG START
# # quick debug using shortcut to set variables
# block_num <- 1
# channel_num <- 1
# chn <- 1
# get block name and create path
block_name <- sprintf("%s_%03d", block_prefix, block_num)
block_path <- file.path(raveio::raveio_getopt("raw_data_dir"), subject_code, block_name)
# create
block_path <- raveio::dir_create2(block_path)
# get block data
block_data <- data_list$trial[[ block_num ]]
if(!is.matrix(block_data)) {
block_data <- array(block_data, dim = c(channel_counts, length(block_data) / channel_counts))
} else {
if(nrow(block_data) < channel_counts) {
stop(sprintf("Number of channel [%s] in data is smaller than the expected [%s] in block [%s]",
nrow(block_data), channel_counts, block_name))
}
}
lapply(seq_len(channel_counts), function(chn) {
progress$inc(sprintf("%s - %s", block_name, chn))
channel_data <- block_data[chn, ]
channel_data[!is.finite(channel_data)] <- 0
channel_file <- file.path(block_path, sprintf("chan_%s.h5", chn))
# save data
raveio::save_h5(
x = channel_data,
file = channel_file,
name = "data",
level = 7,
replace = TRUE,
new_file = FALSE,
ctype = "numeric",
quiet = TRUE
)
# save channel information
info <- as.list(channel_table[chn, ])
raveio::save_h5(
x = jsonlite::toJSON(info, auto_unbox = TRUE),
file = channel_file,
name = "info",
level = 7,
replace = TRUE,
ctype = "character",
quiet = TRUE
)
})
# save time-point information
time <- data_list$time[[ block_num ]]
if(length(time) && is.numeric(time)) {
raveio::save_h5(
x = time,
file = file.path(block_path, "time.h5"),
name = "data",
level = 7,
replace = TRUE,
new_file = FALSE,
ctype = "numeric",
quiet = TRUE
)
}
return()
}
lapply(seq_len(block_counts), save_block)
# We don't need large data anymore
rm(raw_data, data_list); gc()
# --- Conversion is done, imporing into RAVE
if( do_import ) {
# load into RAVE using RAVE pipeline
pipeline <- raveio::pipeline("import_lfp_native")
# set pipeline
pipeline$set_settings(
skip_validation = FALSE,
import_setup__subject_code = subject_code,
import_setup__project_name = project_name,
import_channels__unit = "NA",
import_channels__sample_rate = fsample,
import_channels__electrodes = seq_len(channel_counts),
import_channels__electrode_file = "auto",
import_blocks__session_block = sprintf("%s_%03d", block_prefix, seq_len(block_counts)),
import_blocks__format = ".mat/.h5 file per electrode per block",
force_import = TRUE
)
pipeline$run(scheduler = "none", type = "smart")
if(ncol(channel_table) > 1) {
subject <- pipeline$read("subject")
electrodes <- subject$get_electrode_table()
for(nm in names(channel_table)[-1]) {
electrodes[[nm]] <- channel_table[[nm]]
}
raveio::save_meta2(
data = electrodes,
meta_type = "electrodes",
project_name = subject$project_name,
subject_code = subject$subject_code
)
}
}
if(interactive() && launch_rave) {
rave::start_rave2()
}