Как использовать генерацию нескольких цветовых шкал в ggplot для каждой переменной

Я пытаюсь создать ggplot со следующей эстетикой (см. Ниже) на относительно небольшом наборе данных (x = номер случая, y1, y2, y3 и т. д. = несколько переменных, относящихся к различным характеристикам случаев)

Case <- c("Case 1", "Case 2", "Case 3", "Case 4", "Case 5")
Age <- c(53, 46, 72, 68, 45)
Tumor_Stage <- c(1, 2, 3, 1, 2) 
Tumor_Grade <- c(3, 1, 2, 2, 1)
Smoking_Status <- c(0,1 ,1 ,0 ,1)
CD3 <- c(0,1,0,0,1)
df <- tibble(Case, Age, Tumor_Stage, Tumor_Grade, Smoking_Status, CD3)
df1 <- df %>% pivot_longer(cols = c(Age, Tumor_Stage, Tumor_Grade, Smoking_Status,CD3),
                                          names_to = "Variables")

ggplot(df1,aes(x = Case, 
                     y = Variables, 
                     col = value,
                     fill = value)) +
  geom_tile()

Я получаю следующий сюжет:

Моя попытка

НО проблема в том, что для ВСЕХ переменных создается одна шкала, и я хочу сделать другую цветовую шкалу для каждой отдельной переменной y. Как лучше всего это сделать? Большое вам спасибо за вашу помощь!

Моя цель, что-то похожее на это

Я просто застрял здесь и не знаю, как продолжить... Я видел примеры с использованием

scale_color_gradient()
  new_scale_color() +

но я не знаю, как интегрировать эту функцию для каждой переменной y

РЕДАКТИРОВАТЬ

Вот мой окончательный результат благодаря ответу Стефана!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Одним из вариантов получения индивидуальной шкалы заливки и легенды для каждой переменной с помощью ggnewscale было бы использование нескольких слоев geom_tile, по одному для каждой переменной. Для этого разделите свой фрейм данных на Variable, затем используйте, например. purrr::imap чтобы добавить отдельные слои:

library(ggplot2)
library(ggnewscale)

df1_split <- split(df1, df1$Variables)

legend_order <- rev(seq_along(df1_split))
names(legend_order) <- names(df1_split)

ggplot(df1, aes(
  x = Case,
  y = Variables
)) +
  purrr::imap(df1_split, function(x, y) {
    order <- legend_order[[y]]
    list(
      geom_tile(aes(fill = value), data = x),
      scale_fill_gradient(
        name = y,
        guide = guide_colorbar(direction = "horizontal", title.position = "top", order = order)
      ),
      new_scale_fill()
    )
  })

Второй вариант для достижения желаемого результата - создать отдельные графики для каждой переменной и склеить их вместе, используя, например. patchwork. Хотя этот подход требует дополнительных усилий, чтобы патч выглядел как один график, т. е. установка полей графика и удаление оси, одним из преимуществ является то, что легенды хорошо выровнены с категориями оси Y. И я предполагаю, что этот подход был использован для примера сюжета, который вы добавили в качестве изображения.


library(patchwork)

plot_fun <- function(x, y) {
  theme_adjust <- if (y != "Tumor_Stage") {
    theme(
      axis.line.x = element_blank(),
      axis.text.x = element_blank(),
      axis.title.x = element_blank(),
      axis.ticks.x = element_blank(),
      axis.ticks.length.x = unit(0, "pt")
    )  
  }
  
  plot_margin <- if (y == "Tumor_Stage") {
    theme(plot.margin = margin(0, 5.5, 5.5, 5.5))
  } else if (y == "Age") {
    theme(plot.margin = margin(5.5, 5.5, 0, 5.5))
  } else {
    theme(plot.margin = margin(0, 5.5, 0, 5.5))
  }
  
  ggplot(df1, aes(
    x = Case,
    y = Variables
  )) +
    geom_tile(aes(fill = value), data = x) +
    scale_fill_gradient(
          name = y,
          guide = guide_colorbar(direction = "horizontal", title.position = "top")
        ) +
    scale_y_discrete(expand = c(0, 0)) +
    theme_adjust +
    plot_margin +
    labs(y = NULL)
}

purrr::imap(df1_split, plot_fun) |> 
  wrap_plots(ncol = 1)

РЕДАКТИРОВАТЬ Относительно вашего второго вопроса. Если у вас есть сочетание категориальных и числовых переменных, я бы предложил использовать данные в широком формате. Для приведенного ниже примера я немного изменил данные примера и преобразовал Smoking_Status и Tumor_Stage в коэффициенты. По поводу шкалы заполнения. Есть вообще различный подход. Простым, но, вероятно, не самым элегантным подходом было бы создание списка шкал заполнения, то есть списка, содержащего желаемую шкалу заполнения для каждой переменной. Я также выбрал подход patchwork. Обратите внимание, что, поскольку теперь я использую широкий набор данных, больше нет необходимости разделять набор данных. Вместо этого мы должны перебрать имена столбцов.

Case <- c("Case 1", "Case 2", "Case 3", "Case 4", "Case 5")
Age <- c(53, 46, 72, 68, 45)
Tumor_Stage <- c(1, 2, 3, 1, 2)
Tumor_Grade <- c(3, 1, 2, 2, 1)
Smoking_Status <- c(0, 1, 1, 0, 1)
CD3 <- c(0, 1, 0, 0, 1)
df <- data.frame(Case, Age, Tumor_Stage, Tumor_Grade, Smoking_Status, CD3)

df$Smoking_Status <- factor(df$Smoking_Status)
df$Tumor_Stage <- factor(df$Tumor_Stage)

library(ggplot2)
library(patchwork)

cols <- c("Age", "Tumor_Stage", "Tumor_Grade", "Smoking_Status", "CD3")
cols <- sort(cols)

scale_fill <- lapply(cols, function(x) {
  if (x == "Smoking_Status") {
    scale_fill_brewer(type = "div", name = x, palette = "BrBG",
                      guide = guide_legend(direction = "horizontal", title.position = "top"))
  } else if (x == "Tumor_Stage") {
    scale_fill_brewer(type = "div", name = x, palette = "PiYG",
                      guide = guide_legend(direction = "horizontal", title.position = "top"))
  } else if (x == "Age") {
    scale_fill_gradient(name = x, low = "lightgreen", high = "darkgreen",
                        guide = guide_colorbar(direction = "horizontal", title.position = "top")
    )
  } else {
    scale_fill_gradient(name = x, 
                        guide = guide_colorbar(direction = "horizontal", title.position = "top")
    ) 
  }
})
names(scale_fill) <- cols

plot_fun <- function(x) {
  theme_adjust <- if (x != "Tumor_Stage") {
    theme(
      axis.line.x = element_blank(),
      axis.text.x = element_blank(),
      axis.title.x = element_blank(),
      axis.ticks.x = element_blank(),
      axis.ticks.length.x = unit(0, "pt")
    )  
  }
  
  plot_margin <- if (x == "Tumor_Stage") {
    theme(plot.margin = margin(0, 5.5, 5.5, 5.5))
  } else if (x == "Age") {
    theme(plot.margin = margin(5.5, 5.5, 0, 5.5))
  } else {
    theme(plot.margin = margin(0, 5.5, 0, 5.5))
  }
  
  scale_fill <- scale_fill[[x]]
  ggplot(df, aes(
    x = Case,
    y = x
  )) +
    geom_tile(aes(fill = .data[[x]])) +
    scale_fill +
    scale_y_discrete(expand = c(0, 0)) +
    theme_adjust +
    plot_margin +
    labs(y = NULL)
}

purrr::map(cols, plot_fun) |> 
  wrap_plots(ncol = 1)

Большое спасибо @stefan, я более чем благодарен за ваше удивительно структурированное предложение! У моего разочарованного «я» есть два дополнительных вопроса, если вы не возражаете. 1- Как можно изменить цветовые шкалы для каждой переменной? (как в опубликованном примере) И 2- Если я хочу ввести категориальную переменную во фрейм данных: например. CD1 <- c ("присутствует", "отсутствует", "присутствует", "отсутствует", "отсутствует"), как его можно объединить с остальными числовыми/непрерывными переменными?

Alphonse Charbel 08.01.2023 12:25

Привет Альфонсо. Я только что внес изменения и добавил один возможный подход к ответу на ваши дополнительные вопросы.

stefan 08.01.2023 13:11

Другие вопросы по теме