Топоплот в ggplot2 - 2D визуализация, например, данных ЭЭГ
Можно ggplot2
использоваться для производства так называемого топоплота (часто используется в нейробиологии)?
Пример данных:
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 и преобразования данных так, чтобы они соответствовали тому или иному набору инструментов... Большое спасибо!
Обновление (26 января 2016 г.)
Самое близкое, что я смог достичь своей цели - это через
library(colorRamps)
ggplot(channels, aes(x, y, z = signal)) + stat_summary_2d() + scale_fill_gradientn(colours=matlab.like(20))
который производит изображение как это:
Обновление 2 (27 января 2016 г.)
Я попробовал подход @alexforrence с полными данными, и вот результат:
Это отличное начало, но есть пара вопросов:
- Последний звонок (
ggplot()
) занимает около 40 секунд на Intel i7 4790K, в то время как наборы инструментов Matlab удается генерировать их практически мгновенно; мое "экстренное решение" выше занимает около секунды. - Как вы можете видеть, верхняя и нижняя границы центральной части выглядят "нарезанными" - я не уверен, что является причиной этого, но это может быть третьей проблемой.
Я получаю эти предупреждения:
1: Removed 170235 rows containing non-finite values (stat_contour). 2: Removed 170235 rows containing non-finite values (stat_contour).
Обновление 3 (27 января 2016 г.)
Сравнение двух участков, полученных с разными interp(xo, yo)
а также stat_contour(binwidth)
ценности:
Рваные края, если кто-то выбирает низкий interp(xo, yo)
, в этом случае xo
/ yo = seq(0, 1, length = 100)
:
1 ответ
Вот потенциальное начало:
Сначала мы приложим несколько пакетов. Я использую Акиму для линейной интерполяции, хотя похоже, что EEGLAB использует здесь какую-то сферическую интерполяцию ? (данные были немного редкими, чтобы попробовать это).
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
ниже - отрисовать круг с помощью 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 fill.contour() в 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
вызов, чтобы заполнить пробелы и сделать сплайн-сглаживание, соответственно, и удаление NA перед построением), мы получаем:
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