ggplot2: работа с экстремальными значениями путем установки непрерывной цветовой шкалы

Я пытаюсь построить некоторые глобальные карты (растровые файлы), и у меня есть некоторые проблемы с настройкой хорошей цветовой шкалы для моих данных. Что я хотел бы сделать, так это построить свои данные с использованием расходящейся палитры (например, cm.colors), и я хотел бы центрировать «белый» цвет такой шкалы с нулевым значением, но без необходимости устанавливать симметричные значения в шкале ( т.е. одинаковое значение как отрицательное, так и положительное, т.е. limits=c(-1,1)). Кроме того, я хотел бы отображать все значения выше и/или ниже определенного значения с одним и тем же цветом.

Другими словами, если мы предположим, что моя карта имеет диапазон от -100 до 150, я хотел бы построить свою карту с расходящейся палитрой с «белым» цветом, соответствующим значению 0, и имеющим все значения, например. ниже -20 и выше 50 нанесены одним и тем же цветом, т.е. соответственно с отрицательными и положительными крайними значениями цветовой палитры.

Вот пример кода, который я использую на данный момент:

ggplot(df, aes(y=Latitude, x=Longitude)) +
  geom_raster(aes(fill=MAP)) +
  coord_equal()+ 
  theme_gray() +
  theme(panel.background = element_rect(fill = 'skyblue2', colour = 'black'), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "right",
        legend.key = element_blank()) +
  scale_fill_gradientn("MAP", limits=c(-0.5,1), colours=cm.colors(20))

person PietroB    schedule 19.06.2017    source источник


Ответы (1)


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

require(dplyr)
df %>% mutate(z2 = ifelse(z > 50, 50, ifelse(z < -20, -20, z))) %>% 
  ggplot(aes(x, y, fill = z2)) + geom_tile() + 
  scale_fill_gradient2(low = cm.colors(20)[1], high = cm.colors(20)[20])

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

df %>% mutate(z2 = cut(z, c(-Inf, seq(-20, 50, by = 10), Inf)),
              z3 = as.numeric(z2)-3) %>% 
  {ggplot(., aes(x, y, fill = z3)) + geom_tile() + 
  scale_fill_gradient2(low = cm.colors(20)[1], high = cm.colors(20)[20],
                       breaks = unique(.$z3), labels = unique(.$z2))}

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

Но я уже думал об этой задаче раньше и чувствовал себя неудовлетворенным. Предварительное усечение не оставляет приятных меток, а опция cut всегда неудобна (в частности, приходится настраивать параметры seq внутри cut и выяснять, как центрировать ячейки). Итак, я попытался определить многоразовое преобразование, которое сделает за вас усечение и перемаркировку.

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

require(scales)
trim_tails <- function(range = c(-Inf, Inf)) trans_new("trim_tails", 
                transform = function(x) {
                  force(range)
                  desired_breaks <- extended_breaks(n = 7)(x[x >= range[1] & x <= range[2]])
                  break_increment <- diff(desired_breaks)[1]
                  x[x < range[1]] <- range[1] - break_increment
                  x[x > range[2]] <- range[2] + break_increment
                  x
                },
                inverse = function(x) x,

                breaks = function(x) {
                  force(range)
                  extended_breaks(n = 7)(x)
                },
                format = function(x) {
                  force(range)
                  x[1] <- paste("<", range[1])
                  x[length(x)] <- paste(">", range[2])
                  x
                })

ggplot(df, aes(x, y, fill = z)) + geom_tile() + 
  guides(fill = guide_colorbar(label.hjust = 1)) +
  scale_fill_gradient2(low = cm.colors(20)[1], high = cm.colors(20)[20],
                       trans = trim_tails(range = c(-20,50)))

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

Также работает с легендой в рамке вместо цветовой полосы, просто используйте ... + guides(fill = guide_legend(label.hjust = 1, reverse = T)) + ...

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

person Brian    schedule 19.06.2017
comment
Отличный, полностью конкретизированный ответ Брайана. Спасибо за это. Мне это сейчас не нужно, но я вижу, что это преобразование очень удобно. - person SeldomSeenSlim; 21.06.2017