Заполните область между двумя линиями, с максимумом / минимумом и датами
Предисловие: я даю достаточно удовлетворительный ответ на свой вопрос. Я понимаю, что это приемлемая практика. Естественно, я надеюсь пригласить предложения и улучшения.
Моя цель состоит в том, чтобы построить два временных ряда (хранящихся в кадре данных с датами, хранящимися в классе «Дата») и заполнить область между точками данных двумя разными цветами в зависимости от того, находится ли один над другим. Например, чтобы построить индекс Облигаций и индекс Акций, и заполнить область красным, когда Фондовый индекс выше индекса облигаций, и заполнить область синим цветом в противном случае.
я использовалggplot2
для этого, потому что я достаточно знаком с пакетом (автор: Хэдли Уикхем), но не стесняйтесь предлагать другие подходы. Я написал пользовательскую функцию, основанную наgeom_ribbon()
функцияggplot2
пакет. Вначале я столкнулся с проблемами, связанными с отсутствием у меня опыта вgeom_ribbon()
функция и объекты класса'Date'
, Приведенная ниже функция представляет мои усилия по решению этих проблем, почти наверняка это обходные пути, излишне сложные, неуклюжие и т. Д. Итак, мой вопрос:Пожалуйста, предложите улучшения и / или альтернативные подходы, В конечном счете, было бы замечательно, чтобы здесь была доступна функция общего назначения.
Данные:
set.seed(123456789)
df <- data.frame(
Date = seq.Date(as.Date("1950-01-01"), by = "1 month", length.out = 12*10),
Stocks = 100 + c(0, cumsum(runif(12*10-1, -30, 30))),
Bonds = 100 + c(0, cumsum(runif(12*10-1, -5, 5))))
library('reshape2')
df <- melt(df, id.vars = 'Date')
Пользовательская функция:
## Function to plot geom_ribbon for class Date
geom_ribbon_date <- function(data, group, N = 1000) {
# convert column of class Date to numeric
x_Date <- as.numeric(data[, which(sapply(data, class) == "Date")])
# append numeric date to dataframe
data$Date.numeric <- x_Date
# ensure fill grid is as fine as data grid
N <- max(N, length(x_Date))
# generate a grid for fill
seq_x_Date <- seq(min(x_Date), max(x_Date), length.out = N)
# ensure the grouping variable is a factor
group <- factor(group)
# create a dataframe of min and max
area <- Map(function(z) {
d <- data[group == z,];
approxfun(d$Date.numeric, d$value)(seq_x_Date);
}, levels(group))
# create a categorical variable for the max
maxcat <- apply(do.call('cbind', area), 1, which.max)
# output a dataframe with x, ymin, ymax, is. max 'dummy', and group
df <- data.frame(x = seq_x_Date,
ymin = do.call('pmin', area),
ymax = do.call('pmax', area),
is.max = levels(group)[maxcat],
group = cumsum(c(1, diff(maxcat) != 0))
)
# convert back numeric dates to column of class Date
df$x <- as.Date(df$x, origin = "1970-01-01")
# create and return the geom_ribbon
gr <- geom_ribbon(data = df, aes(x, ymin = ymin, ymax = ymax, fill = is.max, group = group), inherit.aes = FALSE)
return(gr)
}
Использование:
ggplot(data = df, aes(x = Date, y = value, group = variable, colour = variable)) +
geom_ribbon_date(data = df, group = df$variable) +
theme_bw() +
xlab(NULL) +
ylab(NULL) +
ggtitle("Bonds Versus Stocks (Fake Data!)") +
scale_fill_manual('is.max', breaks = c('Stocks', 'Bonds'),
values = c('darkblue','darkred')) +
theme(legend.position = 'right', legend.direction = 'vertical') +
theme(legend.title = element_blank()) +
theme(legend.key = element_blank())
Результат:
Хотя есть вопросы и ответы по stackoverflow, я не нашел достаточно подробного для моих целей. Вот выбор полезных обменов:
создание-геая-ленты-для-мин-макс-диапазон: Задает аналогичный вопрос, но предоставляет меньше деталей, чем я искал.возможно, ошибка-в-геой-лента: Тесно связаны, но промежуточные шаги по вычислению макс / мин отсутствуют.заполнить область между ними два-лессовые сглаженный линий-в-р-с-ggplot: Тесно связаны, но фокусируются на лессовых линиях. Отлично.ggplot-раскраска-область-между плотностью линий-по-отношению к-положение : Тесно связаны, но сосредоточены на плотности. Этот пост очень вдохновил меня.