Как реализовать двухмерную цветовую шкалу (оттенок x яркость)?

Вот игрушка data.frame, которая иллюстрирует проблему (то есть ее самая простая версия; позже будет дополнительная складка):

df <- read.table(textConnection(
"toxin  dose    x   y
A   1   0.851   0.312
A   10  0.268   0.443
A   100 0.272   0.648
B   1   0.981   0.015
B   10  0.304   0.658
B   100 0.704   0.821
C   1   0.330   0.265
C   10  0.803   0.167
C   100 0.433   0.003
D   1   0.154   0.611
D   10  0.769   0.616
D   100 0.643   0.541
"), header = TRUE)

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

Особенно сложным аспектом этой проблемы визуализации является то, что легенда должна быть двухмерной цветной сеткой (а не одномерной цветной полосой bar) со строками соответствующие переменной toxin и столбцы, соответствующие dose (или его преобразованию).

Дополнительная морщинка, о которой я упоминал выше, заключается в том, что данные фактически включают одно контрольное наблюдение, где доза отличается от всех других (обратите внимание на строку с toxin = "Z" ниже):

df <- read.table(textConnection(
"toxin  dose    x   y
A   1   0.851   0.312
A   10  0.268   0.443
A   100 0.272   0.648
B   1   0.981   0.015
B   10  0.304   0.658
B   100 0.704   0.821
C   1   0.330   0.265
C   10  0.803   0.167
C   100 0.433   0.003
D   1   0.154   0.611
D   10  0.769   0.616
D   100 0.643   0.541
Z   0.001   0.309   0.183
"), header = TRUE)

Точка для контрольного («Z») токсина должна быть одной серой точкой. (Это нормально, если легенда двухмерной цветовой сетки не включает контрольное значение, но в этом случае должна быть хотя бы одна легенда, которая соответствующим образом идентифицирует ее точку.)

Таким образом, проблема состоит из трех частей:

  1. Представьте токсин и дозу по оттенку и яркости соответственно.
  2. Сделайте легенду в виде двухмерной цветной сетки.
  3. Легенды должны обозначать контрольную точку.

Ниже то, что мне удалось до сих пор.

Единственный способ решить первый аспект проблемы, который я могу придумать, - это выделить отдельный слой для каждого токсина и использовать градиент цвета в зависимости от дозы.

К сожалению, не существует способа указать разную шкалу градиента для каждого слоя.

Более конкретно, я сначала определяю следующее:

library(ggplot2)

hues <- RColorBrewer::brewer.pal(4, "Set1")

gradient <- function (hue_index) {
  scale_color_gradient(high = hues[hue_index],
                       low = "white",
                       trans = "log",
                       limits = c(0.1, 100),
                       breaks = c(1, 10, 100))
}

baseplot <- ggplot(mapping = aes(x = x, y = y, color = dose))

Первый слой сам по себе выглядит многообещающим:

(
 baseplot
          + geom_point(data = subset(df, toxin == "A"), size = 4)
          + gradient(1)
)

введите описание изображения здесь

Но когда я добавляю второй слой ...

(
 baseplot
          + geom_point(data = subset(df, toxin == "A"), size = 4)
          + gradient(1)
          + geom_point(data = subset(df, toxin == "B"), size = 4)
          + gradient(2)
)

... Я получаю следующее предупреждение:

Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

И, конечно же, вот сюжет, который я получаю:

введите описание изображения здесь

Мне не удалось найти способ определить разные слои, каждый со своей собственной цветовой шкалой.


person kjo    schedule 19.08.2016    source источник
comment
Ваша проблема кажется несколько похожей на этот вопрос, где обходным путем было использовать оба color и fill, чтобы получить две разные цветовые схемы.   -  person aosmith    schedule 19.08.2016
comment
Этот вопрос / ответ предлагает другое решение проблемы разных цветовых градиентов для каждой группы, хотя у него может не быть легенды.   -  person aosmith    schedule 19.08.2016


Ответы (2)


Должна ли быть сетка для легенды? Если вы хотите иметь одну легенду для токсина (цвет) и вторую легенду для дозы (альфа), вы можете использовать это (и установить свои цвета / заливки в соответствии с вашими данными)

df$dose <- factor(df$dose)

ggplot(
  df
  , aes(x = x, y = y
        , col = toxin
        , alpha = dose)
) +
  geom_point(size = 4)

введите описание изображения здесь

Если это действительно должна быть матрица для легенды, вы можете сделать матрицу самостоятельно, а затем объединить их на графике. Вы потеряете некоторую гибкость, и вам нужно будет тщательно настроить все, но в целом это должно работать (обратите внимание, что я использую минимальную тему, поскольку она кажется лучшей для легенды - очевидно, личные предпочтения):

theme_set(theme_minimal())

mainPlot <-
  ggplot(
    df
    , aes(x = x, y = y
          , col = toxin
          , alpha = dose)
  ) +
  geom_point(size = 4)

mainPlot


allLevels <-
  expand.grid(toxin = levels(df$toxin)
              , dose = levels(df$dose))

legendPlot <-
  ggplot(
    allLevels
    , aes(x = toxin, y = dose
          , col = toxin
          , alpha = dose)
  ) +
  geom_point(size = 4)

legendPlot



library(gridExtra)

grid.arrange(
  mainPlot +
    theme(legend.position = "none")
  , legendPlot +
    theme(legend.position = "none") +
    ggtitle("Legend")
  , layout_matrix =
    matrix(c(1,1,1,NA,2,NA)
           , ncol = 2)
  , widths=c(2,1)
  , heights = c(1,2,1)
  )

введите описание изображения здесь

person Mark Peterson    schedule 19.08.2016
comment
Хм. Токсин стал фактором? В противном случае хорошее приближение - person Ben Bolker; 19.08.2016
comment
Токсин начинается как фактор. Вы имеете в виду дозу? Я намеренно установил это в качестве фактора перед графиком, чтобы упростить контроль над соответствующими альфа-уровнями - если дозы не стандартизированы (или их просто больше), вероятно, было бы лучше вернуться к числовому и непрерывная шкала. (Также спасибо) - person Mark Peterson; 19.08.2016
comment
О, я вижу; OP хочет, чтобы оттенок соответствовал токсину. Это кажется плохой идеей (поскольку токсин выглядит так, как будто он действительно находится в непрерывном (логарифмическом) масштабе); Я бы предложил library(viridis); ... + scale_colour_viridis(), но это направлено на OP, а не на вас - person Ben Bolker; 19.08.2016
comment
@BenBolker: О чем ты говоришь? toxin имеет значения A, B, C, D, Z. Это примерно столько же факторов, сколько и получается. В этом нет ничего непрерывного (логарифмического) масштаба. - person kjo; 20.08.2016

Это решение является адаптацией решения, приведенного в этом ответе. Он не действительно выполняет то, о чем спрашивает вопрос (большая часть тяжелой работы с решением не выполняется ggplot2, и легенда не так ясна, как могла бы быть), но это может быть лучшим вариантом может сделать с ggplot2 для этой проблемы.

baseplot <- ggplot(data = df, mapping = aes(x = x, y = y))

palette <- function (name, indices = c(3, 5, 7)) {
  RColorBrewer::brewer.pal(9, name)[indices]
}

colors <- c(as.vector(sapply(c("Reds", "Blues", "Greens", "Purples"), palette)),
            "white")

labels <- mapply(function(toxin, dose) {
                     paste(toxin, as.character(dose), sep = " @ ")
                 },
                 df$toxin, df$dose)

(
  baseplot + geom_point(mapping = aes(color = interaction(dose, toxin)),
                        size = 4)
           + scale_color_manual(name = "toxin @ dose",
                                values = colors,
                                labels = labels)
           + guides(color = guide_legend(nrow = 5, byrow = TRUE))
)

А вот как выглядит результат:

введите описание изображения здесь

person kjo    schedule 20.08.2016
comment
См. Правку в моем ответе, где можно найти альтернативу легенде в виде матрицы. - person Mark Peterson; 22.08.2016