Sto cercando di creare una facet_multi_col()
funzione, simile alla facet_col()
funzione in ggforce
- che consente un layout di sfaccettatura con un argomento spaziale (che non è disponibile in facet_wrap()
) - ma su più colonne. Come nell'ultima trama di seguito (creata con grid.arrange()
), non voglio che le sfaccettature si allineino necessariamente tra le righe poiché le altezze di ciascuna sfaccettatura variano in base a una y
variabile categoriale che desidero utilizzare.
Mi sto trovando ben oltre la mia profondità ggproto
dopo aver letto la guida di estensione . Penso che l'approccio migliore sia quello di passare una matrice di layout per dettare dove spezzare le colonne per i corrispondenti sottoinsiemi di dati e costruire facet_col
in ggforce per includere un parametro spaziale - vedere la fine della domanda.
Una rapida illustrazione delle mie opzioni insoddisfacenti
Nessuna sfaccettatura
library(tidyverse)
library(gapminder)
global_tile <- ggplot(data = gapminder, mapping = aes(x = year, y = fct_rev(country), fill = lifeExp)) +
geom_tile()
global_tile
Voglio scomporre la trama per continenti. Non voglio una figura così lunga.
facet_wrap ()
global_tile +
facet_wrap(facets = "continent", scales = "free")
facet_wrap()
non ha un argomento spaziale, il che significa che le tessere hanno dimensioni diverse in ogni continente, utilizzando coord_equal()
un errore
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))
Come le strisce sul lato. space
L'argomento imposta tutte le tessere sulla stessa dimensione. Ancora troppo tempo per adattarsi a una pagina.
grid.arrange () in gridExtra
Aggiungi una colonna di colonna ai dati per dove dovrebbe essere posizionato ogni continente
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
Utilizzare facet_col()
per la trama per ogni colonna
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 = "")
}
Crea una legenda usando get_legend()
incowplot
library(cowplot)
gg <- ggplot(data = d, mapping = aes(x = year, y = country, fill = lifeExp)) +
geom_tile()
leg <- get_legend(gg)
Crea una matrice di layout con altezze in base al numero di paesi in ciascuna colonna.
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
Riunisci g
e leg
usa insieme 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))
Questo è quasi ciò che sto cercando, ma non sono soddisfatto come a) le tessere in colonne diverse hanno larghezze diverse in quanto la lunghezza dei nomi dei paesi e dei continenti più lunghi non è uguale eb) è un sacco di codice che deve essere modificato ciascuno volta che voglio fare una trama come questa - con altri dati voglio organizzare le sfaccettature per regioni, ad esempio "Europa occidentale" piuttosto che continenti o cambiamenti del numero di paesi - non ci sono paesi dell'Asia centrale nei gapminder
dati.
Progressi con la creazione di una funzione facet_multi_cols ()
Voglio passare una matrice di layout a una funzione di sfaccettatura, in cui la matrice si riferirebbe a ogni sfaccettatura e la funzione potrebbe quindi capire le altezze in base al numero di spazi in ciascun pannello. Per l'esempio sopra la matrice sarebbe:
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
Come accennato in precedenza, mi sono adattato dal codice facet_col()
per provare a creare una facet_multi_col()
funzione. Ho aggiunto un layout
argomento per fornire una matrice come my_layout
sopra, con l'idea che, per esempio, il quarto e il quinto livello della variabile data facets
all'argomento siano tracciati nella terza colonna.
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)
# ???
)
Penso di aver bisogno di scrivere qualcosa per la compute_layout
parte, ma sto lottando per capire come farlo.
grid.arrange
nell'esempio sopra ... a meno che tu non intenda qualcosa di diverso? Penso che esistano gli stessi problemi con diverse lunghezze di etichetta in ogni colonna?
grid.arrange
. È un post davvero lungo, quindi è difficile seguire tutto quello che hai provato. Un po 'confuso, ma potresti provare un carattere monospaziale / più vicino al carattere uniformemente distanziato per le etichette in modo che le loro lunghezze siano più prevedibili. Potresti anche riempire le etichette con spazi vuoti per assicurarti che il testo sia più vicino alla stessa lunghezza.