topoplot in ggplot2 – 2D visualisation of e.g. EEG data

前端 未结 1 1347
长情又很酷
长情又很酷 2020-12-16 04:50

Can ggplot2 be used to produce a so-called topoplot (often used in neuroscience)?

Sample data:

   lab         


        
相关标签:
1条回答
  • 2020-12-16 05:15

    Here's a potential start:

    First, we'll attach some packages. I'm using akima to do linear interpolation, though it looks like EEGLAB uses some sort of spherical interpolation here? (the data was a little sparse to try it).

    library(ggplot2)
    library(akima)
    library(reshape2)
    

    Next, reading in the data:

    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")
    

    We'll interpolate the data, and stick that in a data frame.

    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
    

    I'm going to borrow from some previous answers. The circleFun below is from 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,]
    

    And I really liked the look of the contour plot in R plot filled.contour() output in ggpplot2, so we'll borrow that one.

    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()
    


    With improvements mentioned in the comments (setting extrap = TRUE and linear = FALSE in the interp call to fill in gaps and do a spline smoothing, respectively, and removing NAs before plotting), we get:


    mgcv can do spherical splines. This replaces akima (the chunk containing interp() isn't necessary).

    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
    

    0 讨论(0)
提交回复
热议问题