Given a ggplot of, for example, points, how would you find out the row of data that a given point corresponded to?
A sample plot:
library(ggplot2)
(p <- ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
facet_wrap(~ gear)
)
We can get the grobs that contain points with grid.ls
+ grid.get
.
grob_names <- grid.ls(print = FALSE)$name
point_grob_names <- grob_names[grepl("point", grob_names)]
point_grobs <- lapply(point_grob_names, grid.get)
This last variable contains details of the x-y coordinates, and pointsize, etc. (try unclass(point_grobs[[1]])
), but it isn't obvious how I get the row of data in mtcars
that each point corresponds to.
To answer kohske's question about why am I doing this, I'm using gridSVG
to create an interactive scatterplot. When you roll the mouse over a point, I want to display contextual information. In the mtcars example, I could show a tooltip with the name of the car or other values from that row of the data frame.
My hacky idea so far is to include an id
column as an invisible text label:
mtcars$id <- seq_len(nrow(mtcars))
p + geom_text(aes(label = id), colour = NA)
Then traverse the tree of grobs from the point grob to the text grob, and display the row of the dataset indexed by the label.
This is fiddly and not very generalisable. If there's a way to store the id
value within the point grob, it would be much cleaner.
First off a grob is just short for “grid graphical object” from the low-level graphics package grid; Think of it as a set of instructions for create a graphical object (i.e. a plot). The graphics library underneath all of ggplot2's graphical elements are really composed of grob's because ggplot2 uses grid underneath.
coarse, rough. uncouth, rude, crude.
This script generate a SVG file wherein you can interactively annotate the points.
library(ggplot2)
library(gridSVG)
geom_point2 <- function (...) GeomPoint2$new(...)
GeomPoint2 <- proto(GeomPoint, {
objname <- "point2"
draw <- function(., data, scales, coordinates, na.rm = FALSE, ...) {
data <- remove_missing(data, na.rm, c("x", "y", "size", "shape"),
name = "geom_point")
if (empty(data))
return(zeroGrob())
name <- paste(.$my_name(), data$PANEL[1], sep = ".")
with(coordinates$transform(data, scales), ggname(name,
pointsGrob(x, y, size = unit(size, "mm"), pch = shape,
gp = gpar(col = alpha(colour, alpha), fill = fill, label = label,
fontsize = size * .pt))))
}}
)
p <- ggplot(mtcars, aes(mpg, wt, label = rownames(mtcars))) + geom_point2() + facet_wrap(~ gear)
print(p)
grob_names <- grid.ls(print = FALSE)$name
point_grob_names <- sort(grob_names[grepl("point", grob_names)])
point_grobs_labels <- lapply(point_grob_names, function(x) grid.get(x)$gp$label)
library(rjson)
jlabel <- toJSON(point_grobs_labels)
grid.text("value", 0.05, 0.05, just = c(0, 0), name = "text_place", gp = gpar(col = "red"))
script <- '
var txt = null;
function f() {
var id = this.id.match(/geom_point2.([0-9]+)\\.points.*\\.([0-9]+)$/);
txt.textContent = label[id[1]-1][id[2]-1];
}
window.addEventListener("load",function(){
var es = document.getElementsByTagName("circle");
for (i=0; i<es.length; ++i) es[i].addEventListener("mouseover", f, false);
txt = (document.getElementById("text_place").getElementsByTagName("tspan"))[0];
},false);
'
grid.script(script = script)
grid.script(script = paste("var label = ", jlabel))
gridToSVG()
Do you know some places I can upload SVG file?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With