একাধিক কলাম ফেইস ফাংশন তৈরি করা হচ্ছে


11

আমি একটি তৈরি করার চেষ্টা করছি facet_multi_col()ফাংশন, অনুরূপ facet_col()ফাংশন ggforceযে একটি স্থান যুক্তি সঙ্গে একটি পল বিন্যাস (যা পাওয়া যায় না জন্য করতে পারবেন - facet_wrap()) - কিন্তু ওভার একাধিক কলাম। নীচের শেষ চক্রান্তের মতো (এর সাথে তৈরি grid.arrange()) আমি চাই না যে প্রয়োজনীয় দিকগুলি সারিগুলি জুড়ে সারিবদ্ধভাবে সংযুক্ত করা হোক কারণ প্রতিটি ফ্যাক্টের উচ্চতাটি yআমি ব্যবহার করতে চাই এমন একটি শ্রেণিবদ্ধ ভেরিয়েবলের ভিত্তিতে পরিবর্তিত হবে ।

ggprotoএক্সটেনশন গাইডটি পড়ে আমি নিজের গভীরতার বাইরে নিজেকে খুঁজে পাচ্ছি । আমি মনে করি যে facet_col উপাত্তের সাথে সম্পর্কিত সাবটেক্টগুলির জন্য কলামগুলি কোথায় ভাঙ্গতে হবে এবং যেখানে কোনও স্পেস প্যারামিটার অন্তর্ভুক্ত করার জন্য জিজিফোর্স তৈরি করা উচিত - সেখানে প্রশ্নের শেষটি দেখুন সর্বোত্তম পন্থা layout

আমার অসন্তুষ্ট বিকল্পগুলির একটি দ্রুত চিত্রণ

কোন রূপ নেই

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()ত্রুটি ছুঁড়ে ব্যবহার করে

জিজিফোর্সে 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)) 

এখানে চিত্র বর্ণনা লিখুন পাশের স্ট্রিপগুলির মতো Like spaceআর্গুমেন্ট সমস্ত টাইল একই আকারে সেট করে। একটি পৃষ্ঠায় ফিট করার জন্য এখনও অনেক দীর্ঘ।

গ্রিডএক্সট্রায় গ্রিড.আরঞ্জ ()

যেখানে প্রতিটি মহাদেশ স্থাপন করা উচিত সেখানে ডেটাতে একটি কলাম কলাম যুক্ত করুন

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

এখানে চিত্র বর্ণনা লিখুন এটি প্রায় আমার পরে, তবে আমি ক হিসাবে সন্তুষ্ট নই) দীর্ঘতম দেশ এবং মহাদেশের নামগুলির দৈর্ঘ্য সমান নয় এবং খ) বিভিন্ন কলামের টাইলগুলির বিভিন্ন প্রস্থ রয়েছে এবং এর প্রতিটি কোডই প্রতিটি টুইট করার দরকার রয়েছে code সময় আমি এই জাতীয় একটি প্লট তৈরি করতে চাই - অন্যান্য ডেটা সহ আমি অঞ্চলগুলি অনুসারে দিকগুলি সাজিয়ে তুলতে চাই, যেমন "পশ্চিম ইউরোপ" মহাদেশগুলি বা দেশের সংখ্যা পরিবর্তনের পরিবর্তে - gapminderতথ্যে কোনও মধ্য এশীয় দেশ নেই ।

একটি ফেসট_মલ્ટি_কুল () ফাংশন তৈরির সাথে অগ্রগতি

আমি একটি লেআউট ম্যাট্রিক্সকে একটি রূপের ফাংশনে পাস করতে চাই, যেখানে ম্যাট্রিক্স প্রতিটি দিককে বোঝায় এবং ফাংশনটি তখন প্রতিটি প্যানেলে ফাঁকের সংখ্যার উপর ভিত্তি করে উচ্চতাগুলি নির্ধারণ করতে পারে। উপরের উদাহরণের জন্য ম্যাট্রিক্সটি হ'ল:

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উপরের উদাহরণে .. যদি না আপনি অন্যরকম কিছু বোঝেন ? আমি মনে করি প্রতিটি কলামে বিভিন্ন লেবেলের দৈর্ঘ্যের সাথে একই সমস্যা থাকবে?
gjabel

আমি এর অনুরূপ কিছু কল্পনা করছি, তবে এই লেআউট প্যাকেজগুলি এর চেয়ে ভাল প্রান্তিককরণে সহায়তা করতে পারে grid.arrange। এটি একটি দীর্ঘ পোস্ট তাই আপনার চেষ্টা করা সমস্ত কিছু অনুসরণ করা শক্ত। কিছুটা হ্যাকি, তবে আপনি লেবেলগুলির জন্য একত্রে ব্যবধানযুক্ত ফন্টের কাছাকাছি একটি মনোস্পেস / চেষ্টা করতে পারেন যাতে তাদের দৈর্ঘ্য আরও অনুমানযোগ্য। এমনকি পাঠ্যটি একই দৈর্ঘ্যের নিকটে রয়েছে কিনা তা নিশ্চিত করার জন্য আপনি ফাঁকা ফাঁকা জায়গাগুলির সাথে লেবেলগুলি প্যাড করতে পারেন।
ক্যামিল

উত্তর:


4

দাবি পরিত্যাগী

আমি কখনই কোনও বিকাশ করতে facetপারি নি, তবে আমি প্রশ্নটি আকর্ষণীয় এবং একটি চ্যালেঞ্জের পক্ষে যথেষ্ট পেয়েছি, তাই আমি এটি চেষ্টা করে দেখেছি। এটি এখনও নিখুঁত নয় এবং আপনার চক্রান্তের উপর নির্ভর করে ঘটতে পারে এমন সমস্ত সূক্ষ্মতার সাথে এখনও পরীক্ষা করা হয়নি, তবে এটি একটি প্রথম খসড়া যা থেকে আপনি কাজ করতে পারেন।

ধারণা

facet_wrapএকটি সারণীতে প্যানেলগুলি সেট করে এবং প্রতিটি সারিতে একটি নির্দিষ্ট উচ্চতা থাকে যা প্যানেল পুরোপুরি দখল করে। gtable_add_grobবলেছেন:

গেটেবল মডেলটিতে, গ্রুবগুলি সর্বদা সম্পূর্ণ টেবিল ঘর পূরণ করে। আপনি যদি কাস্টম ন্যায়সঙ্গততা চান তবে আপনার খাঁটি মাত্রাকে পরম ইউনিটগুলিতে সংজ্ঞায়িত করতে হতে পারে, বা এটি অন্য একটি জিটিবেলে স্থাপন করা যেতে পারে যা পরে গ্রাবের পরিবর্তে gtable এ যুক্ত করা যায়।

এটি একটি আকর্ষণীয় সমাধান হতে পারে। তবে কীভাবে তা অনুসরণ করব তা নিশ্চিত ছিলাম না। সুতরাং, আমি একটি ভিন্ন পদ্ধতির গ্রহণ করেছি:

  1. পাস করা লেআউট প্যারামিটারের উপর ভিত্তি করে একটি কাস্টম বিন্যাস তৈরি করুন
  2. দিন facet_wrapলেআউটে wrt সব প্যানেল রেন্ডার
  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")

প্রাক্তন 1 প্রাক্তন 2 প্রাক্তন 3 প্রাক্তন 4 প্র 5উদাহরণ 1 উদাহরণ 2 উদাহরণ 3 উদাহরণ 4 উদাহরণ 5

বিধিনিষেধ

কোডটি নির্বোধ থেকে দূরে। আমি ইতিমধ্যে কিছু সমস্যা দেখছি:

  • আমরা (নিঃশব্দে) ধরে নিই যে ডিজাইনের প্রতিটি কলামটি নন এনএ মান দিয়ে শুরু হয় (সাধারণভাবে একটি উত্পাদনশীল কোডের জন্য, পাস লেআউটটি সাবধানে পরীক্ষা করা দরকার (মাত্রাগুলি ফিট হয়? প্যানেলের মতো এতগুলি এন্ট্রি আছে কি? ইত্যাদি))
  • খুব ছোট প্যানেলগুলি ভাল রেন্ডার করে না, সুতরাং স্ট্রিপের অবস্থানের উপর নির্ভর করে আমাকে উচ্চতার জন্য ন্যূনতম মান যোগ করতে হয়েছিল
  • অক্ষ বা স্ট্রিপগুলি সরানো বা যুক্ত করার প্রভাবটি এখনও পরীক্ষা করা হয়নি।

কোড: টিক প্রতি এক সারি

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

আপনি কি ডেটা (একটি স্ন্যাপশট) ভাগ করতে পারেন? আমি সংক্ষিপ্তসারটি দেখলাম, তবে সুস্পষ্ট কারণে সমস্যাটি পুনরুত্পাদন করতে পারি না ...
থোথাল

ডেটা 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) দ্বারা নির্মিত

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.