coordenadas npc de geom_point en ggplot2

10

¿Cómo puedo obtener las coordenadas x , y de un geom_point en un ggplot , donde el marco de referencia es la imagen trazada completa?

Puedo crear un ggplot con algunos geom_point s usando:

library(ggplot2)

my.plot <- ggplot(data.frame(x = c(0, 0.456, 1), y = c(0, 0.123, 1))) +
             geom_point(aes(x, y), color = "red")

Esto da:

ingrese la descripción de la imagen aquí

Al convertir esto en un grob , puedo extraer información adicional sobre este ggplot , como las coordenadas con respecto al panel de trazado, marcado por la flecha púrpura. Sin embargo, esto ignora el espacio ocupado por los ejes.

my.grob <- ggplotGrob(my.plot)
my.grob$grobs[[6]]$children[[3]]$x
# [1] 0.0454545454545455native 0.46native 0.954545454545454native 
my.grob$grobs[[6]]$children[[3]]$y
# [1] 0.0454545454545455native 0.157272727272727native 0.954545454545454native

¿Cómo puedo obtener los valores de las coordenadas x , y cuando comienzo a medir desde la esquina inferior izquierda de toda la imagen, marcada con la flecha verde?

Si es posible, me gustaría que la solución tenga en cuenta el tema de ggplot . Agregar un tema como + theme_void()afecta a los ejes y también cambia la ubicación de los puntos con respecto a toda la imagen trazada.

Actualización : me di cuenta de que el tamaño de fuente de los ejes cambia según el ancho y la altura de la gráfica, lo que afecta el tamaño relativo del panel de la gráfica . Por lo tanto, no será trivial proporcionar la ubicación en unidades npc sin definir el ancho y la altura del gráfico . Si es posible, proporcione la ubicación de los geom_points en función del ancho y la altura del gráfico .

LBogaardt
fuente
55
¿Por qué 2 votos a favor?
Markus
2
Sí, comente si tiene inquietudes o sugerencias.
LBogaardt
1
Tenga en cuenta que el gráfico en sí no tiene dimensiones fijas: las posiciones absolutas de los puntos no solo dependen del tamaño del dispositivo en el que imprime, sino también de la relación de sus ejes. Por lo tanto, es bastante curioso para qué lo necesitas
Tjebo
1
Con respecto a los votos negativos, tenga en cuenta que no necesito justificar mi pregunta para que sea una pregunta válida sobre SE. ¿Por qué quiero saberlo? Porque lo hago.
LBogaardt
1
este también. pero nunca obtuve una respuesta real stackoverflow.com/questions/48710478/…
Tjebo

Respuestas:

5

Cuando cambia el tamaño de un ggplot, la posición de los elementos dentro del panel no está en posiciones fijas en el espacio npc. Esto se debe a que algunos de los componentes de la trama tienen tamaños fijos, y algunos de ellos (por ejemplo, el panel) cambian las dimensiones de acuerdo con el tamaño del dispositivo.

Esto significa que cualquier solución debe tener en cuenta el tamaño del dispositivo y, si desea cambiar el tamaño de la trama, deberá ejecutar el cálculo nuevamente. Dicho esto, para la mayoría de las aplicaciones (incluida la suya, por el sonido de las cosas), esto no es un problema.

Otra dificultad es asegurarse de que está identificando los grobs correctos dentro del grob del panel, y es difícil ver cómo esto podría generalizarse fácilmente. El uso de las funciones de subconjunto de lista [[6]]y [[3]]en su ejemplo no se puede generalizar a otros gráficos.

De todos modos, esta solución funciona midiendo el tamaño y la posición del panel dentro de la tabla, y convirtiendo todos los tamaños a milímetros antes de dividirlos por las dimensiones de la trama en milímetros para convertirlos en espacio npc. He tratado de hacerlo un poco más general extrayendo el panel y los puntos por nombre en lugar de índice numérico.

library(ggplot2)
library(grid)
require(gtable)

get_x_y_values <- function(gg_plot)
{
  img_dim      <- grDevices::dev.size("cm") * 10
  gt           <- ggplot2::ggplotGrob(gg_plot)
  to_mm        <- function(x) grid::convertUnit(x, "mm", valueOnly = TRUE)
  n_panel      <- which(gt$layout$name == "panel")
  panel_pos    <- gt$layout[n_panel, ]
  panel_kids   <- gtable::gtable_filter(gt, "panel")$grobs[[1]]$children
  point_grobs  <- panel_kids[[grep("point", names(panel_kids))]]
  from_top     <- sum(to_mm(gt$heights[seq(panel_pos$t - 1)]))
  from_left    <- sum(to_mm(gt$widths[seq(panel_pos$l - 1)]))
  from_right   <- sum(to_mm(gt$widths[-seq(panel_pos$l)]))
  from_bottom  <- sum(to_mm(gt$heights[-seq(panel_pos$t)]))
  panel_height <- img_dim[2] - from_top - from_bottom
  panel_width  <- img_dim[1] - from_left - from_right
  xvals        <- as.numeric(point_grobs$x)
  yvals        <- as.numeric(point_grobs$y)
  yvals        <- yvals * panel_height + from_bottom
  xvals        <- xvals * panel_width + from_left
  data.frame(x = xvals/img_dim[1], y = yvals/img_dim[2])
}

Ahora podemos probarlo con tu ejemplo:

my.plot <- ggplot(data.frame(x = c(0, 0.456, 1), y = c(0, 0.123, 1))) +
             geom_point(aes(x, y), color = "red")

my.points <- get_x_y_values(my.plot)
my.points
#>           x         y
#> 1 0.1252647 0.1333251
#> 2 0.5004282 0.2330669
#> 3 0.9479917 0.9442339

Y podemos confirmar que estos valores son correctos trazando algunos grobs de punto sobre sus puntos rojos, utilizando nuestros valores como coordenadas npc:

my.plot
grid::grid.draw(pointsGrob(x = my.points$x, y = my.points$y, default.units = "npc"))

Creado en 2020-03-25 por el paquete reprex (v0.3.0)

Allan Cameron
fuente
1
Esta sum(to_mm(gt$heights[seq(panel_pos$t - 1)]))parte es la más extraña. ¿Podría explicar lo que está resumiendo aquí?
LBogaardt
1
@LBogaardt panel_pos$tproporciona la fila de la cuadrícula en la que se encuentra el panel (flexible), por seq(panel_pos$t -1)lo que todos los números de fila están por encima de esto y, por sum(to_mm(gt$heights[seq(panel_pos$t - 1)]))lo tanto, es la suma de las alturas de estas filas.
Allan Cameron
@LBogaardt Estaba tratando de lograr un equilibrio entre la brevedad y la claridad, pero tal vez no lo he entendido bien ...
Allan Cameron
Ah, entonces un ggplot es como una tabla, con una columna para la etiqueta y, una para la numeración del eje y, etc. Igualmente para las filas, cada una con un ancho / alto. ¿El panel es una celda en esta tabla? Entendido. Gracias Allan
LBogaardt
1
Exactamente, @LBogaardt. De hecho, ggplotGrob produce una tabla gTable o grob
Allan Cameron