топоплот в 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
представляют собой проекцию в двумерное пространство, а столбец 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 toolboxes удается генерировать их почти мгновенно; мой ‘экстренное решение " выше занимает около секунды.
Как вы можете видеть, верхняя и нижняя границы центральной части кажутся "разрезанными" – я не уверен, что вызывает это, но это может быть третья проблема.
-
Я получаю эти предупреждения:
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 ответ:
Вот потенциальное начало:
Сначала мы прикрепим несколько пакетов. Я использую akima для линейной интерполяции, хотя похоже, что 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 filled.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
для заполнения пробелов и сглаживания сплайнов, соответственно, и удаление 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