Я хочу воспроизвести анимированный рисунок Эда Хокинса об изменении климата в R
с gganimate
. Фигура называется климатическая спираль. В то время как статическая ggplot
фигура показывает правильный порядок строк по годам (самые последние данные вверху), анимированный график с transition_reveal()
приводит к неправильному порядку строк.
Вот воспроизводимый пример кода с синтетическими данными:
library(tidyverse)
library(lubridate)
library(gganimate)
library(RColorBrewer)
# Create monthly data from 1950 to 2020 (and a component for rising values with time)
df <- tibble(year = rep(1950:2020, each = 12),
month = rep(month.abb, 2020-1950+1)) %>%
mutate(date = dmy(paste("01",month,year)),
value = rnorm(n(), 0, 2) + row_number()*0.005) %>%
with_groups(year, mutate, value_yr = mean(value))
temp <- df %>%
ggplot(aes(x = month(date, label=T), y = value, color = value_yr)) +
geom_line(size = 0.6, aes(group = year)) +
geom_hline(yintercept = 0, color = "white") +
geom_hline(yintercept = c(-4,4), color = c("skyblue3","red1"), size = 0.2) +
geom_vline(xintercept = 1:12, color = "white", size = 0.2) +
annotate("label", x = 12.5, y = c(-4,0,4), label = c("-4°C","0°C","+4°C"),
color = c("skyblue3","white","red1"), size = 2.5, fill = "#464950",
label.size = NA, label.padding = unit(0.1, "lines"),) +
geom_point(x = 1, y = -11, size = 15, color = "#464950") +
geom_label(aes(x = 1, y = -11, label = year),
color = "white", size = 4,
fill = "#464950", label.size = NA) +
coord_polar(start = 0) +
scale_color_gradientn(colors = rev(brewer.pal(n=11, name = "RdBu")),
limits = range(df$value_yr)) +
labs(x = "", y = "") +
theme_bw() +
theme(panel.background = element_blank(),
panel.border = element_blank(),
panel.grid.major = element_blank(),
plot.background=element_rect(fill = "#464950", color = "#464950"),
axis.text.x = element_text(margin = margin(t = -20, unit = "pt"),
color = "white"),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
legend.position = "none")
Теперь мы можем либо сохранить график в формате PNG, либо анимировать и сохранить в формате GIF:
ggsave(temp, filename = "test.png", width = 5, height = 5, dpi = 320)
# Animate by date:
anim <- temp +
transition_reveal(date) +
ease_aes('linear')
output <- animate(anim, nframes = 100, end_pause = 30,
height = 5, width = 5, units = "in", res = 300)
anim_save("test.gif", output)
Посмотрим на результаты!
На первый взгляд результаты кажутся одинаковыми, однако на деталях видны различия (например, выделенная синяя линия).
В этом примере кода с синтетическими данными различия незначительны. Но с реальными данными цифры выглядят совсем иначе, так как многие красные линии (недавние точки данных с высокими температурами) исчезают на заднем плане. Итак, как сохранить заказ в transition_reveal()
по дате? Любая помощь приветствуется, большое спасибо!
Это не ответ как таковой. Вот почему. Вам придется сказать мне, что вы предпочитаете, учитывая эту информацию, чтобы я дал вам решение.
Я попробовал несколько вещей, каждая из которых, как я была уверена, сработает, но не сработала. Итак, я хотел посмотреть, что происходит в ggplot
. Моя догадка оказалась верной. Ваши данные в порядке value_yr
в png
, а не year
.
Я повторяю этот вопрос в конце:
Either you can put the animation in order of
value_yr
or you can put the color inggplot
in order by year. Which would you prefer?
Откуда я знаю? Я извлек назначенные цвета в объекте.
tellMe <- ggplot_build(temp)$data[[1]]
head(tellMe)
# colour x y group PANEL flipped_aes size linetype alpha
# 1 #1E60A4 1 -1.75990067 1 1 FALSE 0.6 1 NA
# 2 #1E60A4 2 -0.08968196 1 1 FALSE 0.6 1 NA
# 3 #1E60A4 3 -0.69657130 1 1 FALSE 0.6 1 NA
# 4 #1E60A4 4 -0.10777727 1 1 FALSE 0.6 1 NA
# 5 #1E60A4 5 1.57710505 1 1 FALSE 0.6 1 NA
# 6 #1E60A4 6 1.63277369 1 1 FALSE 0.6 1 NA
gimme <- tellMe %>% group_by(group) %>%
summarise(color = unique(colour)) %>%
print(n = 100) # there are less than 100, I just want them all
head(gimme)
# # A tibble: 6 × 2
# group color
# <int> <chr>
# 1 1 #1E60A4
# 2 2 #114781
# 3 3 #175290
# 4 4 #053061
# 5 5 #1C5C9E
# 6 6 #3E8BBF
Для меня это указывало на то, что цвета не были в групповом порядке, поэтому я хотел увидеть цвета, чтобы визуализировать порядок.
Я использовал эту функцию. Я знаю, что это из демо, но не помню из какого. Я искал, чтобы включить это сюда, но не нашел.
# this is from a demo (not sure which one anymore!
showCols <- function(cl=colors(), bg = "lightgrey",
cex = .75, rot = 20) {
m <- ceiling(sqrt(n <-length(cl)))
length(cl) <- m*m; cm <- matrix(cl, m)
require("grid")
grid.newpage(); vp <- viewport(w = .92, h = .92)
grid.rect(gp=gpar(fill=bg))
grid.text(cm, x = col(cm)/m, y = rev(row(cm))/m, rot = rot,
vp=vp, gp=gpar(cex = cex, col = cm))
}
showCols(gimme$color)
Верхний левый цвет — это самый старый год, значение под ним — следующий год и так далее. Самый последний год — это нижнее значение в крайнем правом столбце.
df %>% group_by(yr) %>% summarise(value_yr = unique(value_yr))
# they are in 'value_yr' order in ggplot, not year
# # A tibble: 71 × 2
# yr value_yr
# <int> <dbl>
# 1 1950 0.0380
# 2 1951 -0.215
# 3 1952 -0.101
# 4 1953 -0.459
# 5 1954 -0.00130
# 6 1955 0.559
# 7 1956 -0.457
# 8 1957 -0.251
# 9 1958 1.10
# 10 1959 0.282
# # … with 61 more rows
Either you can put the animation in order of
value_yr
or you can put the color inggplot
in order by year. Which would you prefer?
Вы не будете использовать transition_reveal
для группировки и перехода по одному и тому же элементу. К сожалению, я не могу сказать вам, почему, но, похоже, он застрял на 1958 году!
Чтобы этот gif слева соответствовал ggplot
png справа:
Во-первых, я изменил вызовы на ggplot
и geom_line
.
ggplot(aes(x = month(date, label = T), y = value,
group = yr, color = yr)) +
geom_line(size = .6)
Затем я попытался использовать transition_reveal
, но заметил, что последующие годы были наслоены на под других лет. Я не могу объяснить это странное поведение. Когда я бежал showCol
после смены temp,
цвета были в порядке. Это исключило то, о чем я думал изначально.
Я изменил объект anim
, используя transition_manual
, чтобы задать порядок слоев графика.
anim <- temp +
transition_manual(yr, cumulative = T) +
ease_aes('linear')
Вот и все. Теперь слои совпадают.
Что касается того, сработало бы это до того, как вы изменили назначение цвета: исходный сюжет с ручными переходами года слева, ggplot
png справа:
Похоже, это тоже сработало бы. Итак, мое первоначальное растянутое объяснение было далеко не таким полезным, как я думал, но, по крайней мере, теперь у вас есть рабочее решение. (Вздох.)
Просто чтобы убедиться, что я правильно понял: вы хотите, чтобы оба изображения выглядели как GIF-изображение, которое у вас есть в вашем вопросе? Или вы просите, чтобы цвета были в порядке по годам?
Цвет должен быть задан value_yr
, который является среднегодовой температурой на конечном графике (синий = низкая годовая температура, красный = высокая годовая температура). Тем не менее, я сначала подумал, что GIF был неправильным, и на самом деле я все еще так думаю (хотя вы показали, что PNG также был испорчен). Давайте проведем простой тест с color = year
и всего 10 годами: посмотреть результат здесь. Например, красная линия для 2010 года находится не сверху, а позади других линий для более ранних лет. Вот почему gganimate ведет себя не так, как я ожидал. Спасибо тебе за твое терпение :)
Вау, да. Ты прав. Я обновил свой ответ решением, которое работает (и сохранил там свою застенчивую неправильность). Обновления добавлены внизу.
Ого, наконец решил проблему! Большое спасибо, что поделились своими навыками! Я думаю написать gganimate
вопрос на Github, так как другие тоже могут столкнуться с этой проблемой.
Это отличная идея. Я уверен, что другие сталкивались с этой проблемой, многие из которых не знали, что они столкнулись!
Большое спасибо за то, что нашли время и за очень информативный ответ. Я уже многому научился благодаря вашему подходу! Прежде всего, я не знал, что ggplot упорядочивает элементы по эстетике
color
. На самом деле я надеялся правильно отсортировать строки с эстетикойgroup
вgeom_line()
и порядком самого фрейма данных. Что я, наконец, хотел бы видеть, так это сортировку по дате (последний год сверху) как в статическом PNG, так и в анимированном GIF. Я очень ценю вашу помощь.