Ich versuche, eine facet_multi_col()
Funktion zu erstellen , die der facet_col()
Funktion in ähnelt ggforce
- die ein Facettenlayout mit einem Leerzeichenargument ermöglicht (das in nicht verfügbar ist)facet_wrap()
) -, jedoch über mehrere Spalten. Wie im letzten Diagramm unten (erstellt mit grid.arrange()
) möchte ich nicht, dass die Facetten notwendigerweise über Zeilen hinweg ausgerichtet werden, da die Höhen in jeder Facette basierend auf einer kategorialen y
Variablen variieren , die ich verwenden möchte.
Ich bin mit ggproto
dem Lesen der Erweiterungsanleitung überfordert . Ich denke, der beste Ansatz besteht darin, eine Layoutmatrix zu übergeben, um facet_col
festzulegen , wo Spalten für entsprechende Teilmengen der Daten aufgebrochen werden sollen, und in ggforce einen Leerzeichenparameter einzuschließen - siehe Ende der Frage.
Ein kurzes Beispiel für meine unbefriedigenden Möglichkeiten
Keine Facette
library(tidyverse)
library(gapminder)
global_tile <- ggplot(data = gapminder, mapping = aes(x = year, y = fct_rev(country), fill = lifeExp)) +
geom_tile()
global_tile
Ich möchte die Handlung nach Kontinenten aufteilen. Ich will keine so lange Figur.
facet_wrap ()
global_tile +
facet_wrap(facets = "continent", scales = "free")
facet_wrap()
hat kein Leerzeichenargument, was bedeutet, dass die Kacheln auf jedem Kontinent unterschiedlich groß sind und coord_equal()
einen Fehler auslösen
facet_col () in ggforce
library(ggforce)
global_tile +
facet_col(facets = "continent", scales = "free", space = "free", strip.position = "right") +
theme(strip.text.y = element_text(angle = 0))
Wie die Streifen an der Seite. space
Argument setzt alle Kacheln auf die gleiche Größe. Immer noch zu lang, um auf eine Seite zu passen.
grid.arrange () in gridExtra
Fügen Sie eine Datenspalte zu den Daten hinzu, für die jeder Kontinent platziert werden soll
d <- gapminder %>%
as_tibble() %>%
mutate(col = as.numeric(continent),
col = ifelse(test = continent == "Europe", yes = 2, no = col),
col = ifelse(test = continent == "Oceania", yes = 3, no = col))
head(d)
# # A tibble: 6 x 7
# country continent year lifeExp pop gdpPercap col
# <fct> <fct> <int> <dbl> <int> <dbl> <dbl>
# 1 Afghanistan Asia 1952 28.8 8425333 779. 3
# 2 Afghanistan Asia 1957 30.3 9240934 821. 3
# 3 Afghanistan Asia 1962 32.0 10267083 853. 3
# 4 Afghanistan Asia 1967 34.0 11537966 836. 3
# 5 Afghanistan Asia 1972 36.1 13079460 740. 3
# 6 Afghanistan Asia 1977 38.4 14880372 786. 3
tail(d)
# # A tibble: 6 x 7
# country continent year lifeExp pop gdpPercap col
# <fct> <fct> <int> <dbl> <int> <dbl> <dbl>
# 1 Zimbabwe Africa 1982 60.4 7636524 789. 1
# 2 Zimbabwe Africa 1987 62.4 9216418 706. 1
# 3 Zimbabwe Africa 1992 60.4 10704340 693. 1
# 4 Zimbabwe Africa 1997 46.8 11404948 792. 1
# 5 Zimbabwe Africa 2002 40.0 11926563 672. 1
# 6 Zimbabwe Africa 2007 43.5 12311143 470. 1
Verwenden Sie facet_col()
für die Darstellung für jede Spalte
g <- list()
for(i in unique(d$col)){
g[[i]] <- d %>%
filter(col == i) %>%
ggplot(mapping = aes(x = year, y = fct_rev(country), fill = lifeExp)) +
geom_tile() +
facet_col(facets = "continent", scales = "free_y", space = "free", strip.position = "right") +
theme(strip.text.y = element_text(angle = 0)) +
# aviod legends in every column
guides(fill = FALSE) +
labs(x = "", y = "")
}
Erstellen Sie eine Legende mit get_legend()
incowplot
library(cowplot)
gg <- ggplot(data = d, mapping = aes(x = year, y = country, fill = lifeExp)) +
geom_tile()
leg <- get_legend(gg)
Erstellen Sie eine Layoutmatrix mit Höhen basierend auf der Anzahl der Länder in jeder Spalte.
m <-
d %>%
group_by(col) %>%
summarise(row = n_distinct(country)) %>%
rowwise() %>%
mutate(row = paste(1:row, collapse = ",")) %>%
separate_rows(row) %>%
mutate(row = as.numeric(row),
col = col,
p = col) %>%
xtabs(formula = p ~ row + col) %>%
cbind(max(d$col) + 1) %>%
ifelse(. == 0, NA, .)
head(m)
# 1 2 3
# 1 1 2 3 4
# 2 1 2 3 4
# 3 1 2 3 4
# 4 1 2 3 4
# 5 1 2 3 4
# 6 1 2 3 4
tail(m)
# 1 2 3
# 50 1 2 NA 4
# 51 1 2 NA 4
# 52 1 2 NA 4
# 53 NA 2 NA 4
# 54 NA 2 NA 4
# 55 NA 2 NA 4
Bringen Sie g
und leg
zusammen mit grid.arrange()
ingridExtra
library(gridExtra)
grid.arrange(g[[1]], g[[2]], g[[3]], leg, layout_matrix = m, widths=c(0.32, 0.32, 0.32, 0.06))
Dies ist fast das, wonach ich suche, aber ich bin nicht zufrieden, da a) die Kacheln in verschiedenen Spalten unterschiedliche Breiten haben, da die Länge der längsten Länder- und Kontinentnamen nicht gleich ist und b) es eine Menge Code gibt, der jeweils angepasst werden muss Wenn ich eine solche Handlung machen möchte - mit anderen Daten möchte ich die Facetten nach Regionen ordnen, z. B. "Westeuropa" anstatt nach Kontinenten oder die Anzahl der Länderänderungen -, gibt es keine zentralasiatischen Länder in der gapminder
Daten enthalten.
Fortschritte beim Erstellen einer facet_multi_cols () -Funktion
Ich möchte eine Layoutmatrix an eine Facettenfunktion übergeben, wobei sich die Matrix auf jede Facette bezieht, und die Funktion könnte dann die Höhen basierend auf der Anzahl der Leerzeichen in jedem Bedienfeld ermitteln. Für das obige Beispiel wäre die Matrix:
my_layout <- matrix(c(1, NA, 2, 3, 4, 5), nrow = 2)
my_layout
# [,1] [,2] [,3]
# [1,] 1 2 4
# [2,] NA 3 5
Wie oben erwähnt, habe ich mich aus dem Code angepasst facet_col()
, um zu versuchen, eine facet_multi_col()
Funktion zu erstellen . Ich habe ein layout
Argument hinzugefügt , um eine Matrix wie my_layout
oben bereitzustellen , mit der Idee, dass beispielsweise die vierte und fünfte Ebene der Variablen, die dem facets
Argument gegeben wird, in der dritten Spalte dargestellt sind.
facet_multi_col <- function(facets, layout, scales = "fixed", space = "fixed",
shrink = TRUE, labeller = "label_value",
drop = TRUE, strip.position = 'top') {
# add space argument as in facet_col
space <- match.arg(space, c('free', 'fixed'))
facet <- facet_wrap(facets, col = col, dir = dir, scales = scales, shrink = shrink, labeller = labeller, drop = drop, strip.position = strip.position)
params <- facet$params
params <- facet$layout
params$space_free <- space == 'free'
ggproto(NULL, FacetMultiCols, shrink = shrink, params = params)
}
FacetMultiCols <- ggproto('FacetMultiCols', FacetWrap,
# from FacetCols to allow for space argument to work
draw_panels = function(self, panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) {
combined <- ggproto_parent(FacetWrap, self)$draw_panels(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params)
if (params$space_free) {
widths <- vapply(layout$PANEL, function(i) diff(ranges[[i]]$x.range), numeric(1))
panel_widths <- unit(widths, "null")
combined$widths[panel_cols(combined)$l] <- panel_widths
}
combined
}
# adapt FacetWrap layout to set position on panels following the matrix given to layout in facet_multi_col().
compute_layout = function(self, panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) {
layout <- ggproto_parent(FacetWrap, self)$compute_layout(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params)
# ???
)
Ich denke, ich muss etwas für den compute_layout
Teil schreiben , aber ich habe Mühe herauszufinden, wie das geht.
grid.arrange
obigen Beispiel ... es sei denn du meinst etwas anderes? Ich denke, die gleichen Probleme würden mit unterschiedlichen Etikettenlängen in jeder Spalte bestehen?grid.arrange
. Es ist ein sehr langer Beitrag, daher ist es schwierig, alles zu verfolgen, was Sie versucht haben. Ein bisschen hacky, aber Sie könnten versuchen, eine Monospace / näher an gleichmäßig verteilten Schriftarten für die Etiketten zu verwenden, damit deren Länge vorhersehbarer ist. Sie können auch dann Beschriftungen mit Leerzeichen auffüllen, um sicherzustellen, dass der Text näher an der gleichen Länge liegt.Antworten:
Haftungsausschluss
Ich habe noch nie eine entwickelt
facet
, aber ich fand die Frage interessant und herausfordernd genug, also habe ich es versucht. Es ist noch nicht perfekt und bei weitem nicht mit allen Feinheiten getestet, die je nach Handlung auftreten können, aber es ist ein erster Entwurf, an dem Sie arbeiten können.Idee
facet_wrap
legt die Paneele in einer Tabelle dar und jede Reihe hat eine bestimmte Höhe, die das Paneel vollständig einnimmt.gtable_add_grob
sagt:Dies könnte eine interessante Lösung sein. Ich war mir jedoch nicht sicher, wie ich das verfolgen sollte. Daher habe ich einen anderen Ansatz gewählt:
facet_wrap
alle Bedienfelder im Layout renderngtable_filter
diese Option, um die Platte einschließlich ihrer Achsen und Streifen zu greifengridExtra::arrangeGrob
diese Option, um die Bedienfelder gemäß dem übergebenen Design und der erstellten Layoutmatrix anzuordnenErgebnisse
Der vollständige Code ist etwas langwierig, kann aber unten gefunden werden. Hier sind einige Grafiken:
Ex 1 Ex 2 Ex 3 Ex 4 Ex 5
Beschränkungen
Der Code ist alles andere als narrensicher. Einige Probleme sehe ich bereits:
Code: eine Zeile pro Tick
Code: Zeilen mit unterschiedlichen Höhen
quelle
Wie in den Kommentaren vorgeschlagen, kann eine Kombination aus Cowplot und Patchwork Sie ziemlich weit bringen. Siehe meine Lösung unten.
Die Grundidee ist:
Erstellt am 06.11.2019 durch das reprex-Paket (v0.3.0)
quelle