Skip to content

Commit 176410e

Browse files
authored
Merge pull request #24 from slamballais/dev
0.8.1
2 parents 9562a47 + 692f53c commit 176410e

17 files changed

+394
-117
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: QDECR
22
Type: Package
33
Title: Vertex-wise statistical analysis
4-
Version: 0.8.0
4+
Version: 0.8.1
55
Authors@R: as.person(c(
66
"Ryan Muetzel <r.muetzel@erasmusmc.nl> [aut, cre]",
77
"Sander Lamballais <s.lamballaistessensohn@erasmusmc.nl> [aut]"

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ S3method(hist,vw)
55
S3method(nobs,vw_fastlm)
66
S3method(print,vw)
77
S3method(summary,vw_fastlm)
8+
export(as_mgh)
89
export(bsfbm2mgh)
10+
export(fbm_col_is_0)
911
export(fbm_col_mean)
1012
export(fbm_col_sd)
1113
export(fbm_col_sum)
14+
export(fbm_row_is_0)
1215
export(fbm_row_mean)
1316
export(fbm_row_sd)
1417
export(fbm_row_sum)
@@ -21,6 +24,7 @@ export(qdecr_load)
2124
export(qdecr_save)
2225
export(qdecr_snap)
2326
export(reload)
27+
export(save.mgh)
2428
export(stacks)
2529
export(unload)
2630
importFrom(bigstatsr,cols_along)

NEWS.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# QDECR 0.8.1
2+
3+
## Bug fixes
4+
* If the estimated smoothness is below 1, we now increase it to 1 to avoid problems down the line.
5+
* Fixed a bug in qdecr_clusters where it assumes that there is always at least 1 cluster significant.
6+
* "w-g.pct" files can now be used as a measure by specifying "qdecr_w_g.pct" (underscore instead of hyphen).
7+
* Added `fwhm` argument to `qdecr_fastlm`, which was missing before.
8+
9+
## New (minor) features
10+
* Added `cwp_thr` argument to `qdecr_fastlm` and `qdecr` to set the further cluster-wise p-value adjustment (default is 0.025 due to having 2 hemispheres, thus 0.05 / 2).
11+
* Automatically output two extra files: "significant_clusters.txt" (the output of `summary(vw, annot = TRUE)`) and "stack_names.txt" (the output of `stacks(vw)` and the corresponding stack numbers).
12+
* Modified `freeview` and `qdecr_snap`. The `mask` argument is now called `sig`. Furthermore, the ranges for the overlay colors are determined dynamically. Finally, users can now set any arguments to Freeview for manipulating surface files (see `freeview --help` on the command line).
13+
114
# QDECR 0.8.0: Momo
215

316
Version 0.8.0 is the first update after public release. It fixes a bunch of mistakes, introduces further modularization, improves the speed and also reduces the RAM load.
@@ -24,4 +37,4 @@ Version 0.7.0 is the first version that is publically released. It is also the v
2437
* Creation of the vw object and associated functions
2538
* Handling of imputed datasets (via imp2list)
2639
* Creation of all summary and plot functions
27-
* Creation of the `stack` concept
40+
* Creation of the `stack` concept

R/io_mgh.R

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ qdecr_prep_mgh <- function(input_path,
1010
files_list = list.files(input_path),
1111
mask, hemi, measure, fwhmc, target,
1212
n_cores, dir_tmp, project, backing, verbose) {
13+
measure2 <- measure
14+
if(measure2 == "w_g.pct") measure2 <- "w-g.pct"
1315
new_files <- file.path(input_path, files_list, "surf", paste(hemi, measure, fwhmc, target, "mgh", sep = "."))
1416
n <- length(new_files)
1517

@@ -115,7 +117,7 @@ load.annot <- function(input.file) {
115117
)
116118
}
117119

118-
#' Save out an MGH file from memory
120+
#' Save out an MGH file from a big matrix
119121
#'
120122
#' @param vol MGH object (as from load.mgh)
121123
#' @param fname file name to be used to save out the data
@@ -173,6 +175,13 @@ bsfbm2mgh <-function(fbm, fname, filter = NULL) {
173175
NULL
174176
}
175177

178+
#' Save out an MGH file from memory
179+
#'
180+
#' @param vol MGH object (as from load.mgh)
181+
#' @param fname file name to be used to save out the data
182+
#'
183+
#' @export
184+
#'
176185
save.mgh <-function(vol,fname) {
177186

178187
# R translation of save_mgh.m
@@ -251,4 +260,46 @@ save.mgh <-function(vol,fname) {
251260
nelts <- width * height # bytes per slice
252261
#writeBin(vol$x, fid, size = 4, endian = "big")
253262
writeBin(vol$x, fid, size = 4, endian = "big")
263+
}
264+
265+
266+
#' Create an object that is structured like an mgh object
267+
#'
268+
#' @param x the vertex-wise values
269+
#' @param v (to be added)
270+
#' @param ndim1 (to be added)
271+
#' @param ndim2 (to be added)
272+
#' @param ndim3 (to be added)
273+
#' @param nframes (to be added)
274+
#' @param type (to be added)
275+
#' @param dof (to be added)
276+
#'
277+
#' @export
278+
#'
279+
280+
as_mgh <- function(x, v = NULL, ndim1 = NULL, ndim2 = NULL, ndim3 = NULL, nframes, type, dof) {
281+
282+
if (is.vector(x)) {
283+
v <- 1L
284+
ndim1 <- as.integer(length(x))
285+
ndim2 <- 1L
286+
ndim3 <- 1L
287+
type <- 3L
288+
nframes <- 1L
289+
dof <- 0L
290+
} else {
291+
stop("as_mgh only support objects of class `vector` right now.")
292+
}
293+
294+
out <- list(x = x,
295+
v = v,
296+
ndim1 = ndim1,
297+
ndim2 = ndim2,
298+
ndim3 = ndim3,
299+
nframes = nframes,
300+
type = type,
301+
dof = dof)
302+
303+
return(out)
304+
254305
}

R/plotting.R

Lines changed: 140 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,123 @@
88
#'
99
#'@param vw The output object of a qdecr call (e.g. qdecr_fastlm)
1010
#'@param stack Either a numeric or a string for your variable (use `stacks(vw)`)
11-
#'@param mask Logical; if TRUE, only the significant clusters are shown
11+
#'@param type The type of map to plot (currently for OLS). Default is `coef`, others are `se`, `t` and `p`
12+
#'@param sig Logical; if TRUE (default), only the significant clusters are shown
13+
#'@param ... any arguments that freeview (on command line) normally takes for manipulating surface files
1214
#'@return NULL
1315
#'@export
1416

15-
freeview <- function(vw, stack = NULL, mask = TRUE) {
17+
freeview <- function(vw, stack = NULL, type = c("coef", "se", "t", "p"), sig = TRUE, ...) {
1618
if (is.null(stack)) stop("`stack` not defined. Please choose: ", paste(stacks(vw), collapse = ", "))
1719
if (length(stack) > 1) stop("More than 1 stack specified.")
1820
if(is.character(stack)) stack <- which(stacks(vw) == stack)
1921
if(is.null(stack) || stack > length(stacks(vw))) stop("specified `stack` is not present in this dataset. Please choose: ", paste(stacks(vw), collapse = ", "))
22+
type <- match.arg(type)
23+
empty_val <- if(type == "p") 1 else 0
24+
25+
read_fun <- switch(type,
26+
coef = qdecr_read_coef,
27+
se = qdecr_read_se,
28+
t = qdecr_read_t,
29+
p = qdecr_read_p)
2030

21-
temp_mgh <- qdecr_read_coef(vw, stack)
22-
if (mask) temp_mgh$x <- temp_mgh$x %MASK% qdecr_read_ocn_mask(vw, stack)
23-
if (all(temp_mgh$x == 0)) stop("Stack does not contain information (e.g. because of no significant findings), aborting plot.")
31+
temp_mgh <- read_fun(vw, stack)
32+
if (all(temp_mgh$x == empty_val)) stop("Stack does not contain information (e.g. because of no significant findings), aborting plot.")
33+
if (sig) {
34+
p_mgh <- qdecr_read_ocn_mask(vw, stack)
35+
temp_mgh$x[!p_mgh] <- empty_val
36+
if (all(temp_mgh$x == empty_val)) stop("No information in the stack passed the threshold (i.e. `p_thr` was set too strict), aborting plot.")
37+
}
38+
2439
temp_mgh_file <- paste0(vw$paths$dir_tmp2, ".stack", stack, ".temp_mgh.mgh")
2540
save.mgh(temp_mgh, temp_mgh_file)
2641
on.exit(file.remove(temp_mgh_file), add = TRUE)
2742

28-
cmdStr <- paste0("freeview --surface ", vw$paths$dir_subj, "/fsaverage/surf/", vw$input$hemi, ".inflated:overlay=", temp_mgh_file)
29-
system(cmdStr)
43+
surface <- paste0("--surface ", vw$paths$dir_subj, "/fsaverage/surf/", vw$input$hemi, ".inflated")
44+
45+
freeview_args <- list(...)
46+
freeview_args$surface <- surface
47+
freeview_args$overlay <- temp_mgh_file
48+
49+
if (is.null(freeview_args$overlay_method)) {
50+
freeview_args$overlay_method <- "linearopaque"
51+
temp_vals <- temp_mgh$x[temp_mgh$x != empty_val]
52+
freeview_args$overlay_threshold <- c(min(abs(temp_vals)), max(abs(temp_vals)))
53+
}
54+
55+
surf_cmd <- do.call(freeview_surf_cmd, freeview_args)
56+
cmd_str <- paste("freeview", surf_cmd)
57+
cat("Opening Freeview on the command line. Pop-up incoming.\nCall:", cmd_str, "\n")
58+
system(cmd_str)
3059

3160
invisible(NULL)
3261
}
3362

63+
freeview_surf_cmd <- function(surface,
64+
curvature = NULL,
65+
curvature_method = NULL,
66+
overlay = NULL,
67+
overlay_reg = NULL,
68+
overlay_method = NULL,
69+
overlay_color = NULL,
70+
overlay_threshold = NULL,
71+
overlay_frame = NULL,
72+
correlation = NULL,
73+
color = NULL,
74+
edgecolor = NULL,
75+
edgethickness = NULL,
76+
annot = NULL,
77+
annot_outline = NULL,
78+
name = NULL,
79+
offset = NULL,
80+
visible = NULL,
81+
vector = NULL,
82+
target_surf = NULL,
83+
label = NULL,
84+
label_outline = NULL,
85+
label_color = NULL,
86+
label_centroid = NULL,
87+
label_visible = NULL,
88+
spline = NULL,
89+
vertex = NULL,
90+
vertexcolor = NULL,
91+
goto = NULL,
92+
hide_in_3d = NULL,
93+
all = NULL) {
94+
string <- surface
95+
if(!is.null(curvature)) string <- paste0(string, ":curvature=", curvature)
96+
if(!is.null(curvature_method)) string <- paste0(string, ":curvature_method=", curvature_method)
97+
if(!is.null(overlay)) string <- paste0(string, ":overlay=", overlay)
98+
if(!is.null(overlay_reg)) string <- paste0(string, ":overlay_reg=", overlay_reg)
99+
if(!is.null(overlay_method)) string <- paste0(string, ":overlay_method=", overlay_method)
100+
if(!is.null(overlay_color)) string <- paste0(string, ":overlay_color=", collapse(overlay_color, collapse = ","))
101+
if(!is.null(overlay_threshold)) string <- paste0(string, ":overlay_threshold=", collapse(overlay_threshold, collapse = ","))
102+
if(!is.null(overlay_frame)) string <- paste0(string, ":overlay_frame=", overlay_frame)
103+
if(!is.null(correlation)) string <- paste0(string, ":correlation=", correlation)
104+
if(!is.null(color)) string <- paste0(string, ":color=", collapse(color, collapse = ","))
105+
if(!is.null(edgecolor)) string <- paste0(string, ":edgecolor=", collapse(edgecolor, collapse = ","))
106+
if(!is.null(edgethickness)) string <- paste0(string, ":edgethickness=", edgethickness)
107+
if(!is.null(annot)) string <- paste0(string, ":annot=", annot)
108+
if(!is.null(annot_outline)) string <- paste0(string, ":annot_outline=", annot_outline)
109+
if(!is.null(name)) string <- paste0(string, ":name=", name)
110+
if(!is.null(offset)) string <- paste0(string, ":offset=", collapse(offset, collapse = ","))
111+
if(!is.null(visible)) string <- paste0(string, ":visible=", visible)
112+
if(!is.null(vector)) string <- paste0(string, ":vector=", vector)
113+
if(!is.null(target_surf)) string <- paste0(string, ":target_surf=", target_surf)
114+
if(!is.null(label)) string <- paste0(string, ":label=", label)
115+
if(!is.null(label_outline)) string <- paste0(string, ":label_outline=", label_outline)
116+
if(!is.null(label_color)) string <- paste0(string, ":label_color=", label_color)
117+
if(!is.null(label_centroid)) string <- paste0(string, ":label_centroid=", label_centroid)
118+
if(!is.null(label_visible)) string <- paste0(string, ":label_visible=", label_visible)
119+
if(!is.null(spline)) string <- paste0(string, ":spline=", spline)
120+
if(!is.null(vertex)) string <- paste0(string, ":vertex=", vertex)
121+
if(!is.null(vertexcolor)) string <- paste0(string, ":vertexcolor=", collapse(vertexcolor, collapse = ","))
122+
if(!is.null(goto)) string <- paste0(string, ":goto=", goto)
123+
if(!is.null(hide_in_3d)) string <- paste0(string, ":hide_in_3d=", hide_in_3d)
124+
if(!is.null(all)) string <- paste0(string, ":all=", all)
125+
string
126+
}
127+
34128
#'Histograms of the vertex measures
35129
#'
36130
#'Plots a histogram of the mean vertex measure, either by vertex or by subject
@@ -64,16 +158,18 @@ hist.vw <- function(vw, qtype = c("vertex", "subject"), xlab = NULL, main = NULL
64158
#'
65159
#'@param vw The output object of a qdecr call (e.g. qdecr_fastlm)
66160
#'@param stack Either a numeric or a string for your variable (use `stacks(vw)`)
161+
#'@param type The type of map to plot (currently for OLS). Default is `coef`, others are `se`, `t` and `p`
67162
#'@param ext Extension of the image files that will be stored on disk
68163
#'@param zoom Float that determines how far the brains are zoomed in
69164
#'@param compose Logical; if TRUE, a single compiled image will be made (requires Magick++)
70165
#'@param plot_brain Logical; if TRUE, returns a graphical device with the composed images
71166
#'@param save_plot Logical; if TRUE, saves the composed image
72-
#'@param mask Logical; if TRUE, only the significant clusters are shown
167+
#'@param sig Logical; if TRUE, only the significant clusters are shown
168+
#'@param ... any arguments that freeview (on command line) normally takes for manipulating surface files
73169
#'@return NULL
74170
#'@export
75171

76-
qdecr_snap <- function(vw, stack = NULL, ext = ".tiff", zoom = 1, compose = TRUE, plot_brain = TRUE, save_plot = TRUE, mask = TRUE) {
172+
qdecr_snap <- function(vw, stack = NULL, type = c("coef", "se", "t", "p"), ext = ".tiff", zoom = 1, compose = TRUE, plot_brain = TRUE, save_plot = TRUE, sig = TRUE, ...) {
77173
if (is.null(stack)) stop("`stack` not defined. Please choose: ", paste(stacks(vw), collapse = ", "))
78174
if (length(stack) > 1) stop("More than 1 stack specified.")
79175
if(is.character(stack)) stack <- which(stacks(vw) == stack)
@@ -98,19 +194,48 @@ qdecr_snap <- function(vw, stack = NULL, ext = ".tiff", zoom = 1, compose = TRUE
98194
qsnap_e(180),
99195
qsnap(snap_names[4]),
100196
"--quit")
101-
tfile <- file.path(vw$paths$dir_tmp, "tmp_snapshot_qdecr.txt")
197+
tfile <- file.path(vw$paths$dir_tmp, "tmp_snapshot_qdecr.txt")
102198
write.table(snap_cmd, tfile, quote = F, row.names = F, col.names = F)
103199
on.exit(file.remove(tfile))
104200

105-
temp_mgh <- qdecr_read_coef(vw, stack)
106-
if (mask) temp_mgh$x <- temp_mgh$x %MASK% qdecr_read_ocn_mask(vw, stack)
107-
if (all(temp_mgh$x == 0)) stop("Stack does not contain information (e.g. because of no significant findings), aborting plot.")
201+
type <- match.arg(type)
202+
empty_val <- if(type == "p") 1 else 0
203+
204+
read_fun <- switch(type,
205+
coef = qdecr_read_coef,
206+
se = qdecr_read_se,
207+
t = qdecr_read_t,
208+
p = qdecr_read_p)
209+
210+
temp_mgh <- read_fun(vw, stack)
211+
if (all(temp_mgh$x == empty_val)) stop("Stack does not contain information (e.g. because of no significant findings), aborting plot.")
212+
if (sig) {
213+
p_mgh <- qdecr_read_ocn_mask(vw, stack)
214+
temp_mgh$x[!p_mgh] <- empty_val
215+
if (all(temp_mgh$x == empty_val)) stop("No information in the stack passed the threshold (i.e. `p_thr` was set too strict), aborting plot.")
216+
}
217+
108218
temp_mgh_file <- paste0(vw$paths$dir_tmp2, ".stack", stack, ".temp_mgh.mgh")
109219
save.mgh(temp_mgh, temp_mgh_file)
110220
on.exit(file.remove(temp_mgh_file), add = TRUE)
111221

112-
cmdStr <- paste0("freeview --surface ", vw$paths$dir_subj, "/", vw$input$target, "/surf/", vw$input$hemi, ".inflated:overlay=", temp_mgh_file, " -cmd ", tfile)
113-
system(cmdStr)
222+
surface <- paste0("--surface ", vw$paths$dir_subj, "/fsaverage/surf/", vw$input$hemi, ".inflated")
223+
224+
freeview_args <- list(...)
225+
freeview_args$surface <- surface
226+
freeview_args$overlay <- temp_mgh_file
227+
228+
if (is.null(freeview_args$overlay_method)) {
229+
freeview_args$overlay_method <- "linearopaque"
230+
temp_vals <- temp_mgh$x[temp_mgh$x != empty_val]
231+
freeview_args$overlay_threshold <- c(min(abs(temp_vals)), max(abs(temp_vals)))
232+
}
233+
234+
surf_cmd <- do.call(freeview_surf_cmd, freeview_args)
235+
cmd_str <- paste("freeview", surf_cmd, "-cmd", tfile)
236+
cat("Opening Freeview on the command line. Pop-up incoming.\nCall:", cmd_str, "\n")
237+
cat(tfile, "contains:", snap_cmd, "\n")
238+
system(cmd_str)
114239

115240
if (compose) {
116241
if (!requireNamespace("magick", quietly = TRUE)) {

R/qdecr.R

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ qdecr <- function(id,
3333
hemi = c("lh", "rh"),
3434
measure = c("thickness", "area", "area.pial", "curv",
3535
"jacobian_white", "pial", "pial_lgi", "sulc", "volume",
36-
"w-g.pct.mgh", "white.H", "white.K"),
36+
"w_g.pct", "white.H", "white.K"),
3737
fwhm = ifelse(measure == "pial_lgi", 5, 10),
3838
mcz_thr = 30,
39+
cwp_thr = 0.025,
3940
mgh = NULL,
4041
mask = NULL,
4142
mask_path = system.file("extdata", paste0(hemi, ".fsaverage.cortex.mask.mgh"), package = "QDECR"),
@@ -176,14 +177,18 @@ qdecr <- function(id,
176177
message2(paste0("Estimated smoothness is ", vw$post$fwhm_est, ", which is really high. Reduced to 30."), verbose = verbose)
177178
vw$post$fwhm_est <- 30
178179
}
180+
if(vw$post$fwhm_est < 1) {
181+
message2(paste0("Estimated smoothness is ", vw$post$fwhm_est, ", which is really low. Increased to 1."), verbose = verbose)
182+
vw$post$fwhm_est <- 1
183+
}
179184

180185
catprompt("Clusterwise correction", verbose = verbose)
181186

182187
# Run clusterwise and move all files to the right directory
183188
for ( i in seq_along(vw$stack$names)){
184189
message2("\n", verbose = verbose)
185190
runMriSurfCluster(vw$paths$final_path, vw$paths$dir_fshome, vw$input$hemi, vw$stack$p[[i]], vw$post$fwhm_est,
186-
mask_path = vw$paths$final_mask_path, verbose = verbose, mczThr = mcz_thr,
191+
mask_path = vw$paths$final_mask_path, verbose = verbose, mcz_thr = mcz_thr, cwp_thr = cwp_thr,
187192
stack = i, stack_name = vw$stack$names[i])
188193
}
189194
vw$post$final_mask <- load.mgh(vw$paths$final_mask_path)$x

0 commit comments

Comments
 (0)