współrzędne npc geom_point w ggplot2

10

Jak mogę uzyskać współrzędne x , y punktu geom w ggplocie , gdzie ramką odniesienia jest cały wykreślony obraz?

Mogę utworzyć ggplot z niektórymi punktami geom za pomocą:

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

To daje:

wprowadź opis zdjęcia tutaj

Konwertując to na grob , mogę wyciągnąć dodatkowe informacje o tym ggplocie , takie jak współrzędne w odniesieniu do panelu wykresu, oznaczone fioletową strzałką. Ignoruje to jednak przestrzeń zajmowaną przez osie.

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

Jak mogę uzyskać wartości współrzędnych x , y, kiedy zacznę mierzyć od lewego dolnego rogu całego obrazu, oznaczonego zieloną strzałką?

Jeśli to możliwe, chciałbym roztwór do uwzględnienia motyw z ggplot . Dodanie motywu podobnego + theme_void()wpływa na osie, a także przesuwa położenie punktów w stosunku do całego drukowanego obrazu.

Aktualizacja : Zrozumiałem, że rozmiar czcionki osi zmienia się w zależności od szerokości i wysokości wykresu, wpływając na względny rozmiar panelu wykresu . Dlatego podanie lokalizacji w jednostkach NPC nie będzie trywialne bez zdefiniowania szerokości i wysokości wykresu . Jeśli to możliwe, podaj położenie punktów geom w zależności od szerokości i wysokości wykresu .

LBogaardt
źródło
5
Dlaczego 2 opinie negatywne?
markus
2
Tak, proszę o komentarz, jeśli masz wątpliwości lub sugestie.
LBogaardt
1
Weź pod uwagę, że sam wykres nie ma ustalonych wymiarów - bezwzględne położenia punktów zależą nie tylko od wielkości urządzenia, na którym drukujesz, ale także od stosunku twoich osi. Dlatego dość ciekawe, do czego potrzebujesz
Tjebo
1
Jeśli chodzi o opinie negatywne, zauważ, że nie muszę uzasadniać mojego pytania, aby było to ważne pytanie dotyczące SE. Dlaczego chcę wiedzieć? Ponieważ ja tak.
LBogaardt
1
To też. ale nigdy nie otrzymałem prawdziwej odpowiedzi stackoverflow.com/questions/48710478/...
Tjebo

Odpowiedzi:

5

Kiedy zmieniasz rozmiar ggplot, pozycje elementów w panelu nie znajdują się w ustalonych pozycjach w przestrzeni NPC. Wynika to z faktu, że niektóre elementy działki mają ustalone rozmiary, a niektóre z nich (na przykład panel) zmieniają wymiary zgodnie z rozmiarem urządzenia.

Oznacza to, że każde rozwiązanie musi uwzględniać rozmiar urządzenia, a jeśli chcesz zmienić rozmiar działki, musisz ponownie uruchomić obliczenia. To powiedziawszy, dla większości aplikacji (w tym twojej, odgłosów rzeczy), to nie jest problem.

Inną trudnością jest upewnienie się, że identyfikujesz poprawne wyniki w panelu, i trudno jest zrozumieć, jak można to łatwo uogólnić. Korzystanie z listy funkcji podzbioru [[6]]i [[3]]w przykładzie nie jest uogólnić na inne działki.

W każdym razie to rozwiązanie działa poprzez pomiar rozmiaru i położenia panelu w gtable oraz przekształcenie wszystkich rozmiarów na milimetry przed podzieleniem przez wymiary wykresu w milimetrach w celu konwersji na przestrzeń npc. Próbowałem uczynić to nieco bardziej ogólnym, wyodrębniając panel i punkty według nazwy, a nie indeksu numerycznego.

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])
}

Teraz możemy przetestować to na twoim przykładzie:

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

I możemy potwierdzić, że te wartości są poprawne, wykreślając niektóre punkty grobu nad twoimi czerwonymi punktami, używając naszych wartości jako współrzędnych npc:

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

Utworzono 2020-03-25 przez pakiet reprezentx (v0.3.0)

Allan Cameron
źródło
1
Ta sum(to_mm(gt$heights[seq(panel_pos$t - 1)]))część jest najdziwniejsza. Czy możesz wyjaśnić, co tutaj podsumowujesz?
LBogaardt
1
@LBogaardt panel_pos$tpodaje rząd siatki, w której znajduje się (elastyczny) panel, podobnie jak seq(panel_pos$t -1)wszystkie numery rzędów powyżej, a zatem sum(to_mm(gt$heights[seq(panel_pos$t - 1)]))suma wysokości tych rzędów.
Allan Cameron
@LBogaardt Próbowałem znaleźć równowagę między zwięzłością i jasnością, ale być może nie do końca mam rację ...
Allan Cameron
Ach, więc wykres gg jest jak tabela, z jedną kolumną dla etykiety y, jedną dla numeracji osi y itp. Podobnie dla rzędów, każdy o szerokości / wysokości. Panel ma jedną komórkę w tej tabeli? Rozumiem. Dzięki Allan.
LBogaardt
1
Dokładnie, @LBogaardt. W rzeczywistości ggplotGrob produkuje tabelę gTable lub tabelę grob
Allan Cameron