Можно ли заставить не встречающиеся элементы показывать в легенде ggplot? - PullRequest
4 голосов
/ 21 октября 2019

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

library(dplyr)
library(ggplot2)
square <- expand.grid(X=0:10, Y=0:10)
sq2 <- square[rep(row.names(square), 2),] %>%
  arrange(X,Y) %>%
  mutate(SPEC = rep(c('red','blue'),len=n())) %>%
  mutate(POP = ifelse(SPEC %in% 'red', X, Y)) %>%
  group_by(X,Y) %>% 
  mutate(CLR = rgb(X/10,0,Y/10)) %>% ungroup()

ggplot(sq2, aes(x=X, y=Y, fill=CLR)) + geom_tile() +
  scale_fill_identity("Species", guide="legend",
    labels=c('red','blue'), breaks=c('#FF0000','#0000FF'))

Создание этого:

X=red; Y=blue; plotted as gradient tiling

Правильно измененная версиястроит реальную карту, соответствующим образом смешивая RGB, чтобы показать пропорции видов на единицу карты. Но, учитывая это смешивание, реальные данные не обязательно включают конкретные значения, перечисленные в breaks, и в этом случае в легенде для этого вида нет записи. Если вы измените последнюю строку примера на

labels=c('red','blue','green'), breaks=c('#FF0000','#0000FF','#00FF00'))

, вы получите ту же легенду, как показано, с отображением только «красного» и «синего», поскольку в ней нет зеленого цвета. Поиск данных по каждому максимуму (видам) и присвоение их легенде возможны, но не дают хороших ключей легенды для видов, которые встречаются только в низких пропорциях. Требуется, чтобы легенда отображала идею присутствующих объектов, а не их подтвержденное присутствие - три цвета в легенде, даже если обнаружен только один вид.

Я думаю,что scale_fill_manual() или аргумент override.aes могут помочь мне в этом, но я не смог заставить работать какую-либо комбинацию.

Редактировать: Эпизод IV - Новый тупик

(Спасибо @ r2evans за исправление моего пропуска пакетов.) Я подумал, что смогу обмануть легенду, изменив еще один столбец в df в трубе обработки под названием spCLR для обозначения цвета (например, «# FF0000»), который кодирует виды каждой записи (избыточная информация, но хорошо). Теперь вызов в графическом режиме в моей реальной версии идет:

df %>% [everything] %>%
    ggplot(aes(x = X, y = Y, height = WIDTH, width = WIDTH, fill = CLR)) +
      geom_tile() +
      scale_fill_identity("Species", guide="legend",
        labels=spCODE, breaks=spCLR)

Но это дает ошибку: Error in check_breaks_labels(breaks, labels) : object 'spCLR' not found. Это кажется странным, поскольку spCLR действительно находится в df, модифицированном конвейером, и из всех значений, переданных функциям ggplot, spCODE является единственным, присутствующим в оригинальном df - так что если есть какая-то область видимостипроблема, которую я не понимаю. [Повторное редактирование - я вижу, что ни метки, ни разрывы не хотят смотреть на df $. В любом случае.]

Полагаю, (правильно?) есть какой-то способ заставить эту работу работать [?], Но это все равно не заставит легенду показывать "красный", "синий"и «зеленый» в моем игрушечном примере - о чем на самом деле и заключается мой первоначальный вопрос - потому что в нем до сих пор нет реальных зеленых данных. Итак, еще раз, нет ли способа заставить легенду ggplot2 показать вещи, о которых вы хотите поговорить, а не только те, которые присутствуют в данных?

Ответы [ 2 ]

1 голос
/ 24 октября 2019

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

ggplot(sq2, aes(x=X, y=Y)) +
  geom_tile(aes(fill = CLR)) + # move fill mapping here so new point layer doesn't inherit it
  scale_fill_identity() + # scale_*_identity has guide set to FALSE by default

  # add invisible layer with colour (not fill) mapping, within x/y coordinates within
  # same range as geom_tile layer above
  geom_point(data = . %>% 
               slice(1:3) %>% 
               # optional: list colours in the desired label order
               mutate(col = forcats::fct_inorder(c("red", "blue", "green"))),
             aes(colour = col), 
             alpha = 0) +

  # add colour scale with alpha set to 1 (overriding alpha = 0 above),
  # also make the shape square & larger to mimic the default legend keys
  # associated with fill scale
  scale_color_manual(name = "Species",
                     values = c("red" = '#FF0000', "blue" = '#0000FF', "green" = '#00FF00'),
                     guide = guide_legend(override.aes = list(alpha = 1, shape = 15, size = 5)))

plot

0 голосов
/ 29 октября 2019

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

labels=c('red','blue','green'), limits=c('#FF0000','#0000FF','#00FF00'))

, вызывая limits() вместо breaks(), и теперь мой пример и моя реальная версия работают по желанию.

Я должен сказать, что я потратил много времени, копаясь в ggplot2 reference , даже не подозревая, что limit () был правильной альтернативой breaks () - что явноупоминается на этой странице ссылок, в то время как limit () не появляется. Страница? Limit () довольно неинформативна, и я не могу найти ничего, что излагает различия между ними: когда это, а не то.

...