可以ggplot2
用于制作所谓的 topoplot (通常用于神经科学)吗?
示例数据:
label x y signal
1 R3 0.64924459 0.91228430 2.0261520
2 R4 0.78789621 0.78234410 1.7880972
3 R5 0.93169511 0.72980685 0.9170998
4 R6 0.48406513 0.82383895 3.1933129
行代表单个电极。列x
和y
表示投影到2D空间,列signal
基本上是z轴,表示在给定电极处测量的电压。
stat_contour
无法正常工作,显然是因为网格不平等。
geom_density_2d
仅提供x
和y
的密度估算。
geom_raster
是不适合此任务的,或者我必须错误地使用它,因为它很快耗尽内存。
平滑(如右图所示)和头部轮廓(鼻子,耳朵)是不必要的。
我想避免使用Matlab并转换数据,以便它适合这个或那个工具箱......非常感谢!
最接近我的目标是通过
library(colorRamps)
ggplot(channels, aes(x, y, z = signal)) + stat_summary_2d() + scale_fill_gradientn(colours=matlab.like(20))
生成如下图像:
我已经尝试了@ alexforrence的完整数据方法,结果就是这样:
这是一个很好的开始,但有几个问题:
ggplot()
)在Intel i7 4790K上大约需要40秒,而Matlab工具箱几乎可以立即生成这些内容。我上面的'紧急解决方案'需要大约一秒钟。我收到了这些警告:
1: Removed 170235 rows containing non-finite values (stat_contour).
2: Removed 170235 rows containing non-finite values (stat_contour).
使用不同interp(xo, yo)
和stat_contour(binwidth)
值生成的两个绘图之间的比较:
如果选择低interp(xo, yo)
,则会出现锯齿状边缘,在这种情况下为xo
/ yo = seq(0, 1, length = 100)
:
答案 0 :(得分:8)
这是一个潜在的开始:
首先,我们会附上一些包裹。我正在使用akima来进行线性插值,虽然看起来EEGLAB使用某种球形插值here?(数据有点稀疏以便尝试)。
library(ggplot2)
library(akima)
library(reshape2)
接下来,阅读数据:
dat <- read.table(text = " label x y signal
1 R3 0.64924459 0.91228430 2.0261520
2 R4 0.78789621 0.78234410 1.7880972
3 R5 0.93169511 0.72980685 0.9170998
4 R6 0.48406513 0.82383895 3.1933129")
我们将插入数据,并将其粘贴在数据框中。
datmat <- interp(dat$x, dat$y, dat$signal,
xo = seq(0, 1, length = 1000),
yo = seq(0, 1, length = 1000))
datmat2 <- melt(datmat$z)
names(datmat2) <- c('x', 'y', 'value')
datmat2[,1:2] <- datmat2[,1:2]/1000 # scale it back
我将借用之前的一些答案。以下circleFun
来自Draw a circle with ggplot2。
circleFun <- function(center = c(0,0),diameter = 1, 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))
}
circledat <- circleFun(c(.5, .5), 1, npoints = 100) # center on [.5, .5]
# ignore anything outside the circle
datmat2$incircle <- (datmat2$x - .5)^2 + (datmat2$y - .5)^2 < .5^2 # mark
datmat2 <- datmat2[datmat2$incircle,]
我真的很喜欢R plot filled.contour() output in ggpplot2中轮廓图的外观,所以我们会借用那个。
ggplot(datmat2, aes(x, y, z = value)) +
geom_tile(aes(fill = value)) +
stat_contour(aes(fill = ..level..), geom = 'polygon', binwidth = 0.01) +
geom_contour(colour = 'white', alpha = 0.5) +
scale_fill_distiller(palette = "Spectral", na.value = NA) +
geom_path(data = circledat, aes(x, y, z = NULL)) +
# draw the nose (haven't drawn ears yet)
geom_line(data = data.frame(x = c(0.45, 0.5, .55), y = c(1, 1.05, 1)),
aes(x, y, z = NULL)) +
# add points for the electrodes
geom_point(data = dat, aes(x, y, z = NULL, fill = NULL),
shape = 21, colour = 'black', fill = 'white', size = 2) +
theme_bw()
通过评论中提到的改进(在extrap = TRUE
调用中设置linear = FALSE
和interp
以填充空白并分别进行样条曲线平滑,并在绘图前删除NAs),我们得到:
mgcv
可以做球形样条线。这将替换akima
(不需要包含interp()的块。)
library(mgcv)
spl1 <- gam(signal ~ s(x, y, bs = 'sos'), data = dat)
# fine grid, coarser is faster
datmat2 <- data.frame(expand.grid(x = seq(0, 1, 0.001), y = seq(0, 1, 0.001)))
resp <- predict(spl1, datmat2, type = "response")
datmat2$value <- resp