Как правильно использовать фасетку в ggplot geom_tile, сохраняя соотношение сторон без изменений? - PullRequest
4 голосов
/ 05 июня 2019

Я пытаюсь создать «график вероятности», предназначенный для быстрого отображения вероятности элементов по сравнению с другими элементами в таблице.

Быстрый пример:

Файл

property_data.csv для использования:

"","Country","Town","Property","Property_value"
"1","UK","London","Road_quality","Bad"
"2","UK","London","Air_quality","Very bad"
"3","UK","London","House_quality","Average"
"4","UK","London","Library_quality","Good"
"5","UK","London","Pool_quality","Average"
"6","UK","London","Park_quality","Bad"
"7","UK","London","River_quality","Very good"
"8","UK","London","Water_quality","Decent"
"9","UK","London","School_quality","Bad"
"10","UK","Liverpool","Road_quality","Bad"
"11","UK","Liverpool","Air_quality","Very bad"
"12","UK","Liverpool","House_quality","Average"
"13","UK","Liverpool","Library_quality","Good"
"14","UK","Liverpool","Pool_quality","Average"
"15","UK","Liverpool","Park_quality","Bad"
"16","UK","Liverpool","River_quality","Very good"
"17","UK","Liverpool","Water_quality","Decent"
"18","UK","Liverpool","School_quality","Bad"
"19","USA","New York","Road_quality","Bad"
"20","USA","New York","Air_quality","Very bad"
"21","USA","New York","House_quality","Average"
"22","USA","New York","Library_quality","Good"
"23","USA","New York","Pool_quality","Average"
"24","USA","New York","Park_quality","Bad"
"25","USA","New York","River_quality","Very good"
"26","USA","New York","Water_quality","Decent"
"27","USA","New York","School_quality","Bad"

Код:

prop <- read.csv('property_data.csv')

Property_col_vector <- c("NA" = "#e6194b",
                "Very bad" = "#e6194B",
                "Bad" = "#ffe119",
                "Average" = "#bfef45",
                "Decent" = "#3cb44b",
                "Good" = "#42d4f4",
                "Very good" = "#4363d8")

plot_likeliness <- function(town_property_table){
    g <- ggplot(town_property_table, aes(Property, Town)) +
      geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
      theme_classic() +
      theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
            strip.text.y = element_text(angle = 0)) +
      scale_fill_manual(values = Property_col_vector) +
      coord_fixed()
    return(g)
}

summary_town_plot <- plot_likeliness(prop)

Выход: Single_town_plot.png

Это выглядит великолепно! Теперь я создал сюжет, который выглядит красиво, потому что я использовал функциюord_fixed (), но теперь я хочу создать тот же график, ограненный Country.

Для этого я создал следующую функцию:

plot_likeliness_facetted <- function(town_property_table){
  g <- ggplot(town_property_table, aes(Property, Town)) +
    geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
          strip.text.y = element_text(angle = 0)) +
    scale_fill_manual(values = Property_col_vector) +
    facet_grid(Country ~ .,
               scale = 'free_y')
  return(g)
}

facetted_town_plot <- plot_likeliness_facetted(prop)
facetted_town_plot

Результат: facetted_town_plot.png

Однако теперь мои плитки растягиваются, и если я пытаюсь использовать '+ordins_fixed ()', я получаю ошибку:

Error: coord_fixed doesn't support free scales

Как я могу получить сюжет к фасету, но сохранить соотношение сторон? Обратите внимание, что я строю их в серии, поэтому жесткое кодирование высот графика с ручными значениями не является решением, которое мне нужно, мне нужно что-то, что динамически масштабируется с количеством значений в таблице.

Большое спасибо за любую помощь!

Редактировать: Хотя тот же вопрос задавался в несколько ином контексте в другом месте, у него было несколько ответов, но ни один из них не помечен как решающий вопрос.

Ответы [ 2 ]

7 голосов
/ 19 июня 2019

theme(aspect.ratio = 1) и space = 'free', кажется, работают.

plot_likeliness_facetted <- function(town_property_table){
  g <- ggplot(town_property_table, aes(Property, Town)) +
    geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
          strip.text.y = element_text(angle = 0), aspect.ratio = 1) +
    scale_fill_manual(values = Property_col_vector) +
    facet_grid(Country ~ .,
               scale = 'free_y', space = 'free')
  return(g)
}

enter image description here

3 голосов
/ 17 июня 2019

Это может быть не идеальный ответ, но я все равно собираюсь дать ему ответ.По сути, это будет трудно сделать с помощью базового ggplot, потому что, как вы упомянули, coord_fixed() или theme(aspect.ratio = ...) не очень хорошо работают с фасетами.

Первое решение, которое я предлагаю, этоиспользовать gtables для программной установки ширины панелей в соответствии с количеством переменных на вашей оси x:

plot_likeliness_gtable <- function(town_property_table){
  g <- ggplot(town_property_table, aes(Property, Town)) +
    geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
          strip.text.y = element_text(angle = 0)) +
    scale_fill_manual(values = Property_col_vector) +
    facet_grid(Country ~ .,
               scale = 'free_y', space = "free_y")
  # Here be the gtable bits
  gt <- ggplotGrob(g)
  # Find out where the panel is stored in the x-direction
  panel_x <- unique(gt$layout$l[grepl("panel", gt$layout$name)])[1]
  # Set that width based on the number of x-axis variables, plus 0.2 because
  # of the expand arguments in the scales
  gt$widths[panel_x] <- unit(nlevels(droplevels(town_property_table$Property)) + 0.2, "null")
  # Respect needs to be true to have 'null' units match in x- and y-direction
  gt$respect <- TRUE
  return(gt)
}

, что будет работать следующим образом:

library(grid)
x <- plot_likeliness_gtable(prop)
grid.newpage(); grid.draw(x)

И даетэтот график:

enter image description here

Все это работает достаточно хорошо, но на этом этапе, вероятно, было бы хорошо обсудить некоторые недостатки наличия gtables вместообъекты ggplot.Во-первых, вы больше не можете редактировать его с помощью ggplot, поэтому вы не можете добавить еще один + geom_myfavouriteshape() или что-то в этом роде.Вы все еще можете редактировать части графика в gtable / grid.Во-вторых, он имеет причудливый синтаксис grid.newpage(); grid.draw(), для которого нужна библиотека сетки.В-третьих, мы полагаемся на фасет ggplot, чтобы правильно установить высоту панели направления y (2.2 и 1.2 нуль-единиц в вашем примере), хотя это может быть не уместным во всех случаях.С другой стороны, вы по-прежнему определяете размеры в гибких нулевых единицах, поэтому он будет хорошо масштабироваться с любым графическим устройством, которое вы используете.

Второе решение, которое я предложу, может быть немного хакерскимна любой вкус, но это устранит первые два недостатка использования gtables.Некоторое время назад у меня были похожие проблемы со странным поведением размера панели при фасетировании, поэтому я написал эти функции для установки размеров панели.Эти функции довольно независимы от остальной части моего личного пакета для отдыха, так что вы можете просто скопировать и вставить их в свой R-сеанс.Суть этого в том, чтобы скопировать функцию рисования панели с любого графика, который вы делаете, и обернуть ее внутри новой функции, которая устанавливает размеры панели в некоторые заранее определенные числа.Он должен вызываться после любой функции фасетирования.Это будет работать так:

plot_likeliness_forcedsizes <- function(town_property_table){
  g <- ggplot(town_property_table, aes(Property, Town)) +
    geom_tile(aes(fill = Property_value, width=.9, height=.9)) +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust=0.5),
          strip.text.y = element_text(angle = 0)) +
    scale_fill_manual(values = Property_col_vector) +
    facet_grid(Country ~ .,
               scale = 'free_y', space = "free_y") +
    force_panelsizes(cols = nlevels(droplevels(town_property_table$Property)) + 0.2,
                     respect = TRUE)
  return(g)
}

myplot <- plot_likeliness_forcedsizes(prop)
myplot

enter image description here

Он все еще полагается на ggplot, корректно устанавливая высоты по оси Y, но вы можете переопределить их в пределахforce_panelsizes() если что-то пойдет не так.

Надеюсь, это помогло, удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...