एक से अधिक स्तंभ पहलू फ़ंक्शन बनाना


11

मैं एक बनाने के लिए कोशिश कर रहा हूँ facet_multi_col()समारोह, के लिए इसी तरह facet_col()के समारोह ggforceहै कि एक अंतरिक्ष तर्क के साथ एक पहलू लेआउट (जो उपलब्ध नहीं है के लिए अनुमति देता है - facet_wrap()) - लेकिन अधिक एकाधिक स्तंभों। जैसा कि नीचे दिए गए अंतिम प्लॉट में ( grid.arrange()मैं के साथ बनाया गया ) नहीं चाहता कि पंक्तियों को आवश्यक रूप से पंक्तियों में संरेखित किया जाए क्योंकि प्रत्येक पहलू में ऊँचाई एक श्रेणीगत yचर के आधार पर भिन्न होगी जिसे मैं उपयोग करना चाहता हूं।

मैं ggprotoविस्तार मार्गदर्शिका पढ़ने के साथ अपनी गहराई से खुद को अच्छी तरह से पा रहा हूं । मुझे लगता है कि सबसे अच्छा तरीका एक लेआउट मैट्रिक्स पास करना है जहां यह तय करना है कि डेटा के इसी सबसेट के लिए कॉलम को कैसे तोड़ें, और स्पेस पैरामीटर को शामिल करने के लिए facet_col ggforce में निर्माण करें - प्रश्न का अंत देखें।

मेरे असंतोषजनक विकल्पों का त्वरित चित्रण

कोई पहलू नहीं

library(tidyverse)
library(gapminder)
global_tile <- ggplot(data = gapminder, mapping = aes(x = year, y = fct_rev(country), fill = lifeExp)) +
  geom_tile()
global_tile

यहां छवि विवरण दर्ज करें मैं महाद्वीपों द्वारा साजिश को तोड़ना चाहता हूं। मुझे इतना लंबा फिगर नहीं चाहिए।

facet_wrap ()

global_tile +
  facet_wrap(facets = "continent", scales = "free")

यहां छवि विवरण दर्ज करें facet_wrap()एक अंतरिक्ष तर्क नहीं है जिसका अर्थ है कि प्रत्येक महाद्वीप में टाइलें अलग-अलग आकार हैं, coord_equal()जो एक त्रुटि का उपयोग करती हैं

ggforce में facet_col ()

library(ggforce)
global_tile +
  facet_col(facets = "continent", scales = "free", space = "free", strip.position = "right") +
  theme(strip.text.y = element_text(angle = 0)) 

यहां छवि विवरण दर्ज करें साइड पर स्ट्रिप्स की तरह। spaceतर्क सभी टाईल्स को समान आकार में सेट करता है। अभी भी एक पृष्ठ पर फिट होने के लिए बहुत लंबा है।

gridExtra में grid.arrange ()

डेटा के लिए एक कॉलम कॉलम जोड़ें जहां प्रत्येक महाद्वीप रखा जाना चाहिए

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

facet_col()प्रत्येक कॉलम के लिए प्लॉट का उपयोग करें

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

एक पौराणिक कथा का उपयोग कर बनाने get_legend()मेंcowplot

library(cowplot)
gg <- ggplot(data = d, mapping = aes(x = year, y = country, fill = lifeExp)) +
  geom_tile()
leg <- get_legend(gg)

प्रत्येक कॉलम में देशों की संख्या के आधार पर ऊँचाई के साथ एक लेआउट मैट्रिक्स बनाएँ।

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

लाओ gऔर legएक साथ उपयोग grid.arrange()मेंgridExtra

library(gridExtra)
grid.arrange(g[[1]], g[[2]], g[[3]], leg, layout_matrix = m, widths=c(0.32, 0.32, 0.32, 0.06))

यहां छवि विवरण दर्ज करें यह लगभग वही है जो मैं उसके बाद कर रहा हूं, लेकिन मैं संतुष्ट नहीं हूं क) विभिन्न स्तंभों में टाइल्स की चौड़ाई अलग-अलग है क्योंकि देश की लंबाई और महाद्वीप के नाम बराबर नहीं हैं और बी) इसके बहुत सारे कोड हैं जिन्हें प्रत्येक को ट्विक करने की आवश्यकता है समय मैं इस तरह एक भूखंड बनाना चाहता हूं - अन्य आंकड़ों के साथ मैं क्षेत्रों द्वारा पहलुओं को व्यवस्थित करना चाहता हूं, उदाहरण के लिए महाद्वीपों के बजाय "पश्चिमी यूरोप" या देशों की संख्या में परिवर्तन - कोई मध्य एशियाई देश नहीं हैं gapminder डेटा ।

एक facet_multi_cols () फ़ंक्शन बनाने के साथ प्रगति

मैं एक फंक्शनल मैट्रिक्स को एक फंक्शन फंक्शन पास करना चाहता हूँ, जहाँ मैट्रिक्स प्रत्येक फेशियल को संदर्भित करेगा, और फंक्शन तब प्रत्येक पैनल में स्पेस की संख्या के आधार पर हाइट्स का पता लगा सकता है। उपरोक्त उदाहरण के लिए मैट्रिक्स होगा:

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

जैसा कि ऊपर उल्लेख किया गया है, मैं facet_col()एक facet_multi_col()फ़ंक्शन का प्रयास करने और बनाने के लिए कोड से अनुकूलन कर रहा हूं । मैंने उपर्युक्त layoutजैसे मैट्रिक्स प्रदान करने के लिए एक तर्क जोड़ा है my_layout, इस विचार के साथ, उदाहरण के लिए, facetsतर्क को दिए गए चर के चौथे और पांचवें स्तर को तीसरे कॉलम में प्लॉट किया गया है।

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)
    # ???
)

मुझे लगता है कि मुझे compute_layoutभाग के लिए कुछ लिखने की आवश्यकता है , लेकिन मुझे यह पता लगाने के लिए संघर्ष कर रहा हूं कि यह कैसे करना है।


क्या आपने प्लॉट की सूची बनाने की कोशिश की है, प्रत्येक महाद्वीप के लिए एक, और उन्हें काउप्लॉट या पैचवर्क जैसे पैकेजों में से एक के साथ संरेखित करें? एक ggproto के निर्माण की तुलना में आसान हो सकता है
केमिली

@camille मैंने तरह से किया ... grid.arrangeऊपर के उदाहरण में .. जब तक कि आपका मतलब कुछ अलग न हो? मुझे लगता है कि प्रत्येक कॉलम में अलग-अलग लेबल लंबाई के साथ समान समस्याएं मौजूद होंगी?

मैं उसके समान कुछ कल्पना कर रहा हूं, लेकिन उन लेआउट पैकेजों को संरेखण से बेहतर मदद मिल सकती है grid.arrange। यह वास्तव में लंबी पोस्ट है इसलिए आपने जो भी कोशिश की है उसका पालन करना कठिन है। थोड़ी सी हैक की गई, लेकिन आप एक मोनोस्पेस की कोशिश कर सकते हैं / लेबलों के लिए समान रूप से दूरी वाले फ़ॉन्ट के करीब, ताकि उनकी लंबाई अधिक अनुमानित हो। आप तब भी रिक्त स्थान के साथ पैड लेबल कर सकते हैं, यह सुनिश्चित करने के लिए कि पाठ समान लंबाई के करीब है।
कैमिली

जवाबों:


4

अस्वीकरण

मैंने कभी कोई विकास नहीं किया है facet, लेकिन मुझे यह सवाल दिलचस्प और काफी चुनौती भरा लगा, इसलिए मैंने इसे आजमाया। यह अभी तक सही नहीं है और अब तक सभी सूक्ष्मताओं के साथ परीक्षण नहीं किया गया है जो आपके भूखंड के आधार पर हो सकता है, लेकिन यह एक पहला मसौदा है जिस पर आप काम कर सकते हैं।

विचार

facet_wrapएक तालिका में पैनलों को सेट करता है और प्रत्येक पंक्ति में एक निश्चित ऊंचाई होती है, जो पैनल पूरी तरह से व्याप्त होती है। gtable_add_grobकहते हैं:

गॉइट मॉडल में, ग्रब्स हमेशा पूरा टेबल सेल भरते हैं। यदि आप कस्टम औचित्य चाहते हैं, तो आपको निरपेक्ष इकाइयों में grob आयाम को परिभाषित करने की आवश्यकता हो सकती है, या इसे किसी अन्य gtable में रख सकते हैं, जिसे grob के बजाय gtable में जोड़ा जा सकता है।

यह एक दिलचस्प समाधान हो सकता है। हालांकि, मुझे यकीन नहीं था कि इसे कैसे आगे बढ़ाया जाए। इस प्रकार, मैंने एक अलग दृष्टिकोण लिया:

  1. पारित किए गए लेआउट पैरामीटर के आधार पर, एक कस्टम लेआउट बनाएं
  2. चलो facet_wrapलेआउट के लिए सभी पैनलों को प्रस्तुत करने
  3. उपयोग gtable_filterअपने कुल्हाड़ियों और स्ट्रिप्स सहित पैनल को हथियाने के लिए
  4. एक लेआउट मैट्रिक्स बनाएँ। मैंने 2 दृष्टिकोणों की कोशिश की: न्यूनतम संख्या में पंक्तियों का उपयोग करना और ऊंचाई के अंतर के साथ खेलना। और बस लगभग कई पंक्तियों को जोड़ते हुए जैसे कि y- अक्ष पर टिक होते हैं। दोनों काम से परिचित हैं, बाद वाला क्लीनर कोड बनाता है, इसलिए मैं इसका उपयोग करूंगा।
  5. gridExtra::arrangeGrobपारित डिजाइन और निर्मित लेआउट मैट्रिक्स के अनुसार पैनलों को व्यवस्थित करने के लिए उपयोग करें

परिणाम

पूर्ण कोड थोड़ा लंबा है, लेकिन नीचे पाया जा सकता है। यहाँ कुछ रेखांकन दिए गए हैं:

my_layout1 <- matrix(c(1, NA, 2, 3, 4, 5), nrow = 2)
my_layout2 <- matrix(c(1, 2, 3, 4, 5, NA), ncol = 2)

## Ex1
global_tile + facet_multi_col("continent", my_layout1, scales = "free_y", 
                              space = "free", strip.position = "top")

## Ex 2
global_tile + facet_multi_col("continent", my_layout1, scales = "free_y", 
                              space = "free", strip.position = "right")

## Ex 3 - shows that we need a minimum space for any plot 
global_tile + facet_multi_col("continent", my_layout1, scales = "free_y", 
                              space = "free", strip.position = "top", min_prop = 0)

## Ex 4
global_tile + facet_multi_col("continent", my_layout1, scales = "free_y", 
                              space = "fixed", strip.position = "right")

## Ex 5
global_tile + facet_multi_col("continent", my_layout2, scales = "free_y", 
                              space = "free")

Ex 1 Ex 2 Ex 3 Ex 4 Ex 5उदाहरण 1 उदाहरण 2 उदाहरण 3 उदाहरण 4 उदाहरण 5

प्रतिबंध

कोड मूर्ख होने से बहुत दूर है। कुछ मुद्दे जो मैं पहले से देख रहा हूँ:

  • हम (चुपचाप) मान लेते हैं कि डिज़ाइन में प्रत्येक कॉलम एक गैर NA मान से शुरू होता है (सामान्य रूप से उत्पादक कोड के लिए, पारित लेआउट को ध्यान से जांचने की आवश्यकता है (क्या आयाम फिट हैं? क्या पैनल के रूप में कई प्रविष्टियां हैं?)
  • बहुत छोटे पैनल अच्छी तरह से प्रस्तुत नहीं करते हैं, इसलिए मुझे स्ट्रिप्स की स्थिति के आधार पर ऊंचाई के लिए न्यूनतम मूल्य जोड़ना पड़ा
  • एक्सिस या स्ट्रिप्स को हिलाने या जोड़ने का प्रभाव अभी तक परीक्षण नहीं किया गया है।

कोड: एक पंक्ति प्रति टिक

## get strip and axis of a given panel
## Assumptions:
## - axis are adjacent to the panel, that is exactly +1/-1 positions to the t/b/l/r ...
## - ... unless there is a strip then it is +2/-2 
get_whole_panel <- function(panel_name,
                            table_layout) {
  target <- table_layout$layout %>%
    dplyr::filter(name == panel_name) %>%
    dplyr::select(row = t, col = l)
  stopifnot(NROW(target) == 1)
  pos <- unlist(target)
  dirs <- list(t = c(-1, 0),
               b = c(1, 0),
               l = c(0, -1),
               r = c(0, 1))
  filter_elems <- function(dir, 
                           type = c("axis", "strip")) {
    type <- match.arg(type)
    new_pos <- pos + dir
    res <- table_layout$layout %>%
      dplyr::filter(grepl(type, name),
                    l == new_pos["col"],
                    t == new_pos["row"]) %>%
      dplyr::pull(name)
    if (length(res)) res else NA
  }
  strip <- purrr::map_chr(dirs, filter_elems, type = "strip")
  strip <- strip[!is.na(strip)]
  dirs[[names(strip)]] <- 2 * dirs[[names(strip)]]
  axes  <- purrr::map_chr(dirs, filter_elems, type = "axis")
  gtable::gtable_filter(table_layout, paste(c(panel_name, axes, strip), collapse = "|"))
}


facet_multi_col <- function(facets, layout, scales = "fixed", space = "fixed",
                            shrink = TRUE, labeller = "label_value",
                            drop = TRUE, strip.position = "top", 
                            min_prop = ifelse(strip.position %in% c("top", "bottom"), 
                                              0.12, 0.1)) {
  space <- match.arg(space, c("free", "fixed"))
  if (space == "free") {
    ## if we ask for free space we need scales everywhere, so make sure they are included
    scales <- "free"
  }
  facet <- facet_wrap(facets, ncol = 1, scales = scales, shrink = shrink, 
                      labeller = labeller, drop = drop, strip.position = strip.position)
  params <- facet$params
  params$space_free <- space == "free"
  params$layout <- layout
  params$parent <- facet
  params$min_prop <- min_prop
  ggproto(NULL, FacetMultiCol, shrink = shrink, params = params)
}



render <- 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) {
    panel_names <- combined$layout$name
    panels <- lapply(panel_names[grepl("panel", panel_names)],
                     get_whole_panel,
                     table_layout = combined)

    ## remove zeroGrob panels
    zG <- sapply(panels, function(tg) all(sapply(tg$grobs, ggplot2:::is.zero)))
    panels <- panels[!zG]
    ## calculate height for each panel
    heights <- matrix(NA, NROW(params$layout), NCOL(params$layout))
    ## store the rounded range in the matrix cell corresponding to its position
    ## allow for a minimum space in dependence of the overall number of rows to
    ## render small panels well

    heights[as.matrix(layout[, c("ROW", "COL")])] <- vapply(ranges, function(r) 
      round(diff(r$y.range), 0), numeric(1))

    ## 12% should be the minimum height used by any panel if strip is on top otherwise 10%
    ## these values are empirical and can be changed
    min_height <- round(params$min_prop * max(colSums(heights, TRUE)), 0)
    heights[heights < min_height] <- min_height
    idx <- c(heights)
    idx[!is.na(idx)] <- seq_along(idx[!is.na(idx)])
    len_out <- max(colSums(heights, TRUE))
    i <- 0
    layout_matrix <- apply(heights, 2, function(col) {
      res <- unlist(lapply(col, function(n) {
        i <<- i + 1
        mark <- idx[i]
        if (is.na(n)) {
          NA
        } else {
          rep(mark, n)
        }
      }))
      len <- length(res)
      if (len < len_out) {
        res <- c(res, rep(NA, len_out - len))
      }
      res
    })

    ## set width of left axis to maximum width to align plots
    max_width <- max(do.call(grid::unit.c, lapply(panels, function(gt) gt$widths[1])))
    panels <- lapply(panels, function(p) {
      p$widths[1] <- max_width
      p
    })

    combined <- gridExtra::arrangeGrob(grobs = panels,
                            layout_matrix = layout_matrix,
                            as.table = FALSE)
    ## add name, such that find_panel can find the plotting area
    combined$layout$name <- paste("panel_", layout$LAB)
  }
  combined
}

layout <- function(data, params) {
  parent_layout <- params$parent$compute_layout(data, params)
  msg <- paste0("invalid ",
                sQuote("layout"),
                ". Falling back to ",
                sQuote("facet_wrap"),
                " layout")
  if (is.null(params$layout) ||
      !is.matrix(params$layout)) {
    warning(msg)
    parent_layout
  } else {
    ## smash layout into vector and remove NAs all done by sort
    layout <- params$layout
    panel_numbers <- sort(layout)
    if (!isTRUE(all.equal(sort(as.numeric(as.character(parent_layout$PANEL))),
                          panel_numbers))) {
      warning(msg)
      parent_layout
    } else {
      ## all good
      indices <- cbind(ROW = c(row(layout)),
                       COL = c(col(layout)),
                       PANEL = c(layout))
      indices <- indices[!is.na(indices[, "PANEL"]), ]
      ## delete row and col number from parent layout
      parent_layout$ROW <- parent_layout$COL <- NULL
      new_layout <- merge(parent_layout, 
                          indices,
                          by = "PANEL") %>%
        dplyr::arrange(PANEL)
      new_layout$PANEL <- factor(new_layout$PANEL)
      labs <- new_layout %>%
        dplyr::select(-PANEL,
                      -SCALE_X,
                      -SCALE_Y,
                      -ROW,
                      -COL) %>%
        dplyr::mutate(sep = "_") %>%
        do.call(paste, .)
      new_layout$LAB <- labs
      new_layout


    }
  }
}

FacetMultiCol <- ggproto("FacetMultiCol", FacetWrap,
                         compute_layout = layout,
                         draw_panels    = render)

कोड: विभिन्न ऊंचाइयों के साथ पंक्तियाँ

## get strip and axis of a given panel
## Assumptions:
## - axis are adjacent to the panel, that is exactly +1/-1 positions to the t/b/l/r ...
## - ... unless there is a strip then it is +2/-2 
get_whole_panel <- function(panel_name,
                            table_layout) {
  target <- table_layout$layout %>%
    dplyr::filter(name == panel_name) %>%
    dplyr::select(row = t, col = l)
  stopifnot(NROW(target) == 1)
  pos <- unlist(target)
  dirs <- list(t = c(-1, 0),
               b = c(1, 0),
               l = c(0, -1),
               r = c(0, 1))
  filter_elems <- function(dir, 
                           type = c("axis", "strip")) {
    type <- match.arg(type)
    new_pos <- pos + dir
    res <- table_layout$layout %>%
      dplyr::filter(grepl(type, name),
                    l == new_pos["col"],
                    t == new_pos["row"]) %>%
      dplyr::pull(name)
    if (length(res)) res else NA
  }
  strip <- purrr::map_chr(dirs, filter_elems, type = "strip")
  strip <- strip[!is.na(strip)]
  dirs[[names(strip)]] <- 2 * dirs[[names(strip)]]
  axes  <- purrr::map_chr(dirs, filter_elems, type = "axis")
  gtable::gtable_filter(table_layout, paste(c(panel_name, axes, strip), collapse = "|"))
}


facet_multi_col <- function(facets, layout, scales = "fixed", space = "fixed",
                            shrink = TRUE, labeller = "label_value",
                            drop = TRUE, strip.position = "top") {
  space <- match.arg(space, c("free", "fixed"))
  if (space == "free") {
    ## if we ask for free space we need scales everywhere, so make sure they are included
    scales <- "free"
  }
  facet <- facet_wrap(facets, ncol = 1, scales = scales, shrink = shrink, 
                      labeller = labeller, drop = drop, strip.position = strip.position)
  params <- facet$params
  params$space_free <- space == "free"
  params$layout <- layout
  params$parent <- facet
  ggproto(NULL, FacetMultiCol, shrink = shrink, params = params)
}



render <- 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) {
    panel_names <- combined$layout$name
    panels <- lapply(panel_names[grepl("panel", panel_names)],
                     get_whole_panel,
                     table_layout = combined)

    ## remove zeroGrob panels
    zG <- sapply(panels, function(tg) all(sapply(tg$grobs, ggplot2:::is.zero)))
    panels <- panels[!zG]

    ## calculate height for each panel
    heights <- matrix(NA, NROW(params$layout), NCOL(params$layout))
    ## need to add a minimum height as otherwise the space is too narrow
    heights[as.matrix(layout[, c("ROW", "COL")])] <- vapply(layout$PANEL, function(i) 
      max(diff(ranges[[i]]$y.range), 8), numeric(1))
    heights_cum <- sort(unique(unlist(apply(heights, 2, 
                                            function(col) cumsum(col[!is.na(col)])))))
    heights_units <- unit(c(heights_cum[1], diff(heights_cum)), "null")

    ## set width of left axis to maximum width to align plots
    max_width <- max(do.call(grid::unit.c, lapply(panels, function(gt) gt$widths[1])))
    panels <- lapply(panels, function(p) {
      p$widths[1] <- max_width
      p
    })

    mark <- 0

    ## create layout matrix
    layout_matrix <- apply(heights, 2, function(h) {
      idx <- match(cumsum(h),
              cumsum(c(heights_units)))
      idx <- idx[!is.na(idx)]
      res <- unlist(purrr::imap(idx, function(len_out, pos) {
        mark <<- mark + 1
        offset <- if (pos != 1) idx[pos - 1] else 0
          rep(mark, len_out - offset)
      }))
      len_out <- length(res)
      if (len_out < length(heights_units)) {
        res <- c(res, rep(NA, length(heights_units) - len_out)) 
      }
      res
    }) 

    combined <- gridExtra::arrangeGrob(grobs = panels,
                                layout_matrix = layout_matrix,
                                heights = heights_units,
                                as.table = FALSE)
    ## add name, such that find_panel can find the plotting area
    combined$layout$name <- paste("panel_", layout$LAB)
  }
  combined
}

layout <- function(data, params) {
  parent_layout <- params$parent$compute_layout(data, params)
  msg <- paste0("invalid ",
                sQuote("layout"),
                ". Falling back to ",
                sQuote("facet_wrap"),
                " layout")
  if (is.null(params$layout) ||
      !is.matrix(params$layout)) {
    warning(msg)
    parent_layout
  } else {
    ## smash layout into vector and remove NAs all done by sort
    layout <- params$layout
    panel_numbers <- sort(layout)
    if (!isTRUE(all.equal(sort(as.numeric(as.character(parent_layout$PANEL))),
                          panel_numbers))) {
      warning(msg)
      parent_layout
    } else {
      ## all good
      indices <- cbind(ROW = c(row(layout)),
                       COL = c(col(layout)),
                       PANEL = c(layout))
      indices <- indices[!is.na(indices[, "PANEL"]), ]
      ## delete row and col number from parent layout
      parent_layout$ROW <- parent_layout$COL <- NULL
      new_layout <- merge(parent_layout, 
                          indices,
                          by = "PANEL") %>%
        dplyr::arrange(PANEL)
      new_layout$PANEL <- factor(new_layout$PANEL)
      labs <- new_layout %>%
        dplyr::select(-PANEL,
                      -SCALE_X,
                      -SCALE_Y,
                      -ROW,
                      -COL) %>%
        dplyr::mutate(sep = "_") %>%
        do.call(paste, .)
      new_layout$LAB <- labs
      new_layout


    }
  }
}

FacetMultiCol <- ggproto("FacetMultiCol", FacetWrap,
                         compute_layout = layout,
                         draw_panels    = render)

इसके लिए बहुत धन्यवाद। मैंने कुछ अन्य आंकड़ों पर प्रयास किया है - क्षेत्रों के साथ, महाद्वीपों के बजाय (जो मैंने प्रश्न में उल्लेख किया है) ... मैंने यहां कोड डाला ... gist.github.com/gjabel/3e4fb31214b5932aa097878c6d3258dc1 ... यह कुछ वास्तव में फेंकता है अजीब व्यवहार जो मैं समझ नहीं पा रहा हूं?
गज़ब

क्या आप डेटा का (स्नैपशॉट) साझा कर सकते हैं? मैंने
जिस्ट

डेटा wpp2019 पैकेज में है .. जो कि CRAN
gjabel

आह क्षमा करें, मेरा बुरा। इसकी एक बार कोशिश करूंगा।
थोटल

1
बग मिला, मूल रूप से लेआउट को पैनल के अनुसार क्रमबद्ध किया जाना चाहिए, अन्यथा यह काम नहीं करेगा। आपका नमूना अब ठीक प्रस्तुत करना है।
थौथल

1

जैसा कि टिप्पणियों में सुझाव दिया गया है, काउपोट और पैचवर्क का संयोजन आपको काफी दूर तक पहुंचा सकता है। मेरा समाधान नीचे देखें।

मूल विचार है:

  • पंक्तियों की संख्या के आधार पर स्केलिंग फ़ैक्टर की गणना करने के लिए पहले
  • फिर सिंगल कॉलम ग्रिड की एक श्रृंखला बनाएं, जहां मैं खाली प्लॉट्स का उपयोग कैलक्लाइंड स्केलिंग फैक्टर के साथ प्लॉट्स की ऊंचाई को बाधित करने के लिए करता हूं। (और किंवदंतियों को हटा दें)
  • फिर मैं इन्हें एक ग्रिड में जोड़ता हूं और एक लीजेंड भी जोड़ता हूं।
  • शुरुआत में, मैं फिल स्केल के लिए अधिकतम गणना भी करता हूं।
library(tidyverse)
library(gapminder)
library(patchwork)
max_life <- max(gapminder$lifeExp)
generate_plot <- function(data, title){
  ggplot(data = data, mapping = aes(x = year, y = fct_rev(country), fill = lifeExp)) +
    geom_tile()+
    scale_fill_continuous(limits = c(0, max_life)) +
    ggtitle(title)
}
scale_plot <- function(plot, ratio){
  plot + theme(legend.position="none") + 
    plot_spacer() + 
    plot_layout(ncol = 1,
                heights = c(
                  ratio,
                  1-ratio
                )
    )
}
df <- gapminder %>% 
  group_by(continent) %>% 
  nest() %>% 
  ungroup() %>% 
  arrange(continent) %>% 
  mutate(
    rows = map_dbl(data, nrow),
    rel_height = (rows/max(rows)),
    plot = map2(
      data,
      continent,
      generate_plot
    ),
    spaced_plot = map2(
      plot,
      rel_height,
      scale_plot
        )
  )
wrap_plots(df$spaced_plot) + cowplot::get_legend(df$plot[[1]])

2019-11-06 को रेप्रेक्स पैकेज (v0.3.0) द्वारा बनाया गया

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.