다중 열 패싯 함수 작성


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

여기에 이미지 설명을 입력하십시오 측면의 스트립처럼. spaceargument는 모든 타일을 같은 크기로 설정합니다. 여전히 페이지에 맞추기에는 너무 깁니다.

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()in을 사용하여 범례 만들기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

가져 gleg함께 사용 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))

여기에 이미지 설명을 입력하십시오 이것은 거의 내가 한 일이지만, a) 가장 긴 국가 및 대륙 이름의 길이가 같지 않기 때문에 서로 다른 열의 타일의 너비가 다르고 b) 각 코드를 조정 해야하는 코드가 많으므로 만족스럽지 않습니다. 다른 데이터와 함께 지역별로 패싯을 정렬하고 싶습니다 (예 : 대륙이 아닌 "서유럽"또는 국가 수 변경) 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부분을 위해 무언가를 써야한다고 생각 하지만, 이것을하는 방법을 알아 내려고 고심하고 있습니다.


대신 각 대륙마다 하나씩 플롯 목록을 작성하여 cowplot 또는 patchwork와 같은 패키지 중 하나와 정렬하려고 시도 했습니까? ggproto 구축하는 것보다 쉬울 수 있습니다
카밀

@camille 나는 일종의 ... grid.arrange위 의 예에서 .. 다른 의미가 없다면? 각 열마다 다른 레이블 길이로 동일한 문제가 발생한다고 생각합니까?
gjabel

비슷한 것을 상상하고 있지만 레이아웃 패키지가 정렬보다 낫습니다 grid.arrange. 정말 긴 글이므로 시도한 모든 내용을 따르기가 어렵습니다. 약간 해 키지 만 레이블에 대해 균일 한 간격의 글꼴에 모노 스페이스 / 더 가까운 글꼴을 사용하여 길이를 더 예측할 수 있습니다. 그런 다음 빈 공간으로 레이블을 채워 텍스트가 같은 길이에 더 가깝게 만들 수 있습니다.
camille

답변:


4

기권

나는 어떤 것도 개발 한 적이 facet없지만 질문이 흥미롭고 도전적이라는 것을 알았으므로 시도해 보았습니다. 그것은 아직 완벽하지는 않으며 줄거리에 따라 발생할 수있는 모든 미묘함을 테스트하지는 않았지만 작업 할 수있는 첫 번째 초안입니다.

생각

facet_wrap테이블에 패널을 설정하고 각 행에는 패널이 완전히 차지하는 특정 높이가 있습니다. gtable_add_grob말한다 :

gtable 모델에서 grob은 항상 전체 테이블 셀을 채 웁니다. 사용자 정의를 원한다면 grob 치수를 절대 단위로 정의하거나 다른 gtable에 넣어서 grob 대신 gtable에 추가 할 수 있습니다.

이것은 흥미로운 해결책 이 될 수 있습니다. 그러나 나는 그것을 추구하는 방법을 확신하지 못했습니다. 따라서 다른 접근 방식을 취했습니다.

  1. 전달 된 레이아웃 매개 변수를 기반으로 사용자 정의 레이아웃을 만듭니다.
  2. 허락하다 facet_wrap모든 패널을 레이아웃으로 렌더링
  3. 사용하다 gtable_filter축과 스트립을 포함하여 패널을 잡는 데
  4. 레이아웃 매트릭스를 만듭니다. 나는 최소한의 행 수를 사용하고 높이 차이를 가지고 노는 두 가지 접근법을 시도했습니다. 그리고 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/3e4fb31214b5932aa0978dc6d3258dc1은 ... 그것은 정말를 던졌습니다 내가 알아낼 수없는 이상한 행동?
gjabel

데이터를 공유 (스냅 샷) 할 수 있습니까? 나는 요지를 들여다 보았지만 명백한 이유로 문제를 재현 할 수 없다.
thothal

데이터는 wpp2019 패키지에 있습니다 .. CRAN에
gjabel

아 죄송합니다. 시도해 볼 것입니다.
thothal

1
버그를 발견했습니다. 기본적으로 PANEL에 따라 레이아웃 정렬 해야합니다 . 그렇지 않으면 작동하지 않습니다. 이제 샘플이 제대로 렌더링됩니다.
thothal

1

의견에서 제안한 것처럼 cowplot과 patchwork의 조합은 상당히 멀리 갈 수 있습니다. 아래 내 솔루션을 참조하십시오.

기본 아이디어는 다음과 같습니다.

  • 먼저 행 수를 기준으로 스케일링 계수를 계산합니다.
  • 그런 다음 빈 플롯을 사용하여 계산 된 스케일링 계수로 플롯의 높이를 제한하는 일련의 단일 열 그리드를 만듭니다. (그리고 범례를 제거하십시오)
  • 그런 다음 그리드에 추가하고 범례를 추가합니다.
  • 처음에는 필 스케일의 최대 값도 계산합니다.
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]])

reprex 패키지 (v0.3.0)로 2019-11-06에 작성

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.