Sometimes I would like to use a mouse to draw a circle or squiggly shape around my plotted points to select these points specifically. Has anyone built functionality to do this yet? Perhaps something requiring Tcl/tk?
            Asked
            
        
        
            Active
            
        
            Viewed 510 times
        
    3
            
            
        - 
                    theoretically possible in ggplot but (afaik) has not been implemented. – rawr May 13 '14 at 23:38
 - 
                    expecting a single function that can work in both base and ggplot graphics is a little ambitious (not that the solution below can't potentially be improved on ...) – Ben Bolker May 13 '14 at 23:57
 - 
                    by the way, this is an interesting question, but it isn't really solving a particular programming problem -- it's more "I would like this, can someone write it for me"? – Ben Bolker May 13 '14 at 23:57
 - 
                    1I stand corrected: see `gglocator` http://cran.r-project.org/web/packages/ggmap/ggmap.pdf; apparently it was implemented via a stackoverflow thread http://stackoverflow.com/questions/9450873/locator-equivalent-in-ggplot2-for-maps?rq=1. Implementation is left as an exercise to the reader. – rawr May 14 '14 at 22:51
 - 
                    @BenBolker Can't say that I wouldn't like someone to write it for me but my attempt to write it myself runs into wall pretty quickly. Please reread the bounty where I say OR instead of AND. – cylondude May 14 '14 at 23:15
 - 
                    The problem I have is that I would like to quickly draw a squiggly shape around points that I've plotted to explore them more thoroughly. If I could create an object by drawing on my existing plots it would let me subset these points spatially. I could do this through clicking many times with gglocator/locator but it would be clunky. If there would be a way to simulate many clicks from holding mouse button and letting go like a lasso function in graphical software that would be ideal. Can you suggest an alternative method to this kind of data exploration that would work graphically? – cylondude May 14 '14 at 23:28
 - 
                    I don't think this is going to be easy -- R doesn't easily allow access to this kind of low-level mouse information. You're looking for a graphics device that allows access to a mouse handler, e.g. the [gWidgetsWWW](http://cran.at.r-project.org/web/packages/gWidgetsWWW/index.html) package (maybe??) – Ben Bolker May 15 '14 at 00:26
 - 
                    The R package `RIGHT` alluded to these capabilities https://www.youtube.com/watch?feature=player_embedded&v=thlwjYFC_yY . The selection used box boundary rather then freehand however. The package is on CRAN http://cran.r-project.org/web/packages/RIGHT/index.html . – jdharrison May 15 '14 at 01:08
 - 
                    that is not the functionality that is described in the question – rawr May 16 '14 at 02:18
 - 
                    Sorry if I was not clear in my original question but I don't see what I left out. – cylondude May 16 '14 at 16:56
 - 
                    the fact that your comments and explanations are longer than your original question should be a clue – rawr May 16 '14 at 22:00
 - 
                    Well after I put the bounty up and responded to the shotgun approach to snark from Ben Bolker, I think people chimed in with some good leads on solving my problem (thanks for the input on gglocator. I was already familiar though). So I see it as a positive step towards what I'm looking for. – cylondude May 16 '14 at 22:29
 - 
                    everything he said was accurate. this is a programming site, not a recommend me a tool site. questions like this are usually put on hold – rawr May 17 '14 at 03:44
 
1 Answers
6
            You could take advantage of locator, and then use the coordinates to put into a circle drawing function like from plotrix. Then put it into a function for ease of use:
plot(rnorm(100))
click.shape('circle', border = 'red', col = NA)

click.shape <- function(shape = c('circle', 'arrow', 'rect', 'cyl', 'line', 'poly'),
                        corners = 3L, ...) {
  shape  <- match.arg(shape)
  coords <- if (shape %in% 'poly')
    locator(as.integer(corners)) else unlist(locator(2L))
  ARROW <- function(...) {
    arrows(coords[1L], coords[3L], coords[2L], coords[4L], ...)
  }
  CIRCLE <- function(...) {
    require(plotrix)
    rad <- sqrt(((coords[2L] - coords[1L]) ^ 2) + ((coords[4L] - coords[3L]) ^ 2))
    draw.circle(coords[1L], coords[3L], radius = rad, ...)
  }
  CYL <- function(...) {
    require(plotrix)
    cylindrect(coords[1L], coords[3L], coords[2L], coords[4L], ...)
  }
  LINE <- function(...) {
    segments(coords[1L], coords[3L], coords[2L], coords[4L], ...)
  }
  POLY <- function(...) {
    polygon(coords, ...)
  }
  RECT <- function(...) {
    rect(coords[1L], coords[3L], coords[2L], coords[4L], ...)
  }
  suppressWarnings(
    switch(shape, arrow = ARROW(...), circle = CIRCLE(...), cyl = CYL(...),
           line = LINE(...), poly = POLY(...), rect = RECT(...),
           stop('Invalid shape'))
  )
}
Another option which I haven't had time lately to expand
set.seed(1618)
x <- runif(10)
y <- rnorm(10, mean = 5)
par(mfrow = c(1, 2))
plot(x, y, xlab = 'mean', ylab = 'sd')
zoomin(x, y)
## ESC to quit

code for zoomin
zoomin <- function(x, y, ...) {
  op <- par(no.readonly = TRUE)
  on.exit(par(op))
  ans <- identify(x, y, n = 1, plot = FALSE, ...)
  zoom <- function (x, y, xlim, ylim, xd, yd) {
    rxlim <- x + c(-1, 1) * (diff(range(xd)) / 20)
    rylim <- y + c(-1, 1) * (diff(range(yd)) / 20)
    par(mfrow = c(1, 2))
    plot(xd, yd, xlab = 'mean', ylab = 'sd')
    xext <- yext <- rxext <- ryext <- 0
    if (par('xaxs') == 'r') {
      xext <- diff(xlim) * 0.04
      rxext <- diff(rxlim) * 0.04
    }
    if (par('yaxs') == 'r') {
      yext <- diff(ylim) * 0.04
      ryext <- diff(rylim) * 0.04
    }
    rect(rxlim[1] - rxext, rylim[1] - ryext, rxlim[2] + rxext, rylim[2] + ryext)
    xylim <- par('usr')
    xypin <- par('pin')
    rxi0 <- xypin[1] * (xylim[2] - (rxlim[1] - rxext)) / diff(xylim[1:2])
    rxi1 <- xypin[1] * (xylim[2] - (rxlim[2] + rxext)) / diff(xylim[1:2])
    y01i <- xypin[2] * (xylim[4] - (rylim[2] + ryext)) / diff(xylim[3:4])
    y02i <- xypin[2] * ((rylim[1] - ryext) - xylim[3]) / diff(xylim[3:4])
    mu <- x
    curve(dnorm(x, mean = mu, sd = y), from = -4 * y + mu, to = 4 * y + mu, 
          xlab = paste('mean:', round(mu, 2), ', sd: ', round(y, 2)), ylab = '')
    xypin <- par('pin')
    par(xpd = NA)
    xylim <- par('usr')
    xymai <- par('mai')
    x0 <- xylim[1] - diff(xylim[1:2]) * (xymai[2] + xymai[4] + rxi0)/xypin[1]
    x1 <- xylim[1] - diff(xylim[1:2]) * (xymai[2] + xymai[4] + rxi1)/xypin[1]
    y01 <- xylim[4] - diff(xylim[3:4]) * y01i/xypin[2]
    y02 <- xylim[3] + diff(xylim[3:4]) * y02i/xypin[2]
    par(xpd = TRUE)
    xend <- xylim[1] - diff(xylim[1:2]) * xymai[2] / (2 * xypin[1])
    xprop0 <- (xylim[1] - xend) / (xylim[1] - x0)
    xprop1 <- (xylim[2] - xend) / (xylim[2] - x1)
    par(xpd = NA)
    segments(c(x0, x0, x1, x1), 
             c(y01, y02, y01, y02), 
             c(xend, xend, xend, xend), 
             c(xylim[4] - (xylim[4] - y01) * xprop0, 
               xylim[3] + (y02 - xylim[3]) * xprop0, 
               xylim[4] - (xylim[4] - y01) * xprop1, 
               xylim[3] + (y02 - xylim[3]) * xprop1))
    par(mfg = c(1, 1))
    plot(xd, yd, xlab = 'mean', ylab = 'sd')
  }
  if(length(ans)) {
    zoom(x[ans], y[ans], range(x), range(y), x, y)
    points(x[ans], y[ans], pch = 19)
    zoomin(x, y)
  }
}
        rawr
        
- 20,481
 - 4
 - 44
 - 78