Forword: Fornisco una risposta ragionevolmente soddisfacente alla mia domanda. Capisco che questa è una pratica accettabile. Naturalmente la mia speranza è di invitare suggerimenti e miglioramenti.Area di riempimento tra due linee, con alta/bassa e date
Il mio scopo è tracciare due serie temporali (memorizzate in un dataframe con le date memorizzate come classe 'Data') e per riempire l'area tra i punti dati con due colori diversi a seconda che uno sia sopra l'altro. Per esempio, per tracciare un indice di Obbligazioni e un indice di Azioni, e per riempire l'area in rosso quando l'indice azionario è sopra l'indice obbligazionario, e per riempire l'area in blu altrimenti.
Ho usato ggplot2
per questo scopo, perché sono abbastanza familiare con il pacchetto (autore: Hadley Wickham), ma sentitevi liberi di suggerire altri approcci. Ho scritto una funzione personalizzata basata sulla funzione geom_ribbon()
del pacchetto ggplot2
. All'inizio ho dovuto affrontare problemi relativi alla mia mancanza di esperienza nella gestione della funzione geom_ribbon()
e degli oggetti della classe 'Date'
. La funzione sotto rappresenta il mio sforzo per risolvere questi problemi, quasi sicuramente è rotonda, inutilmente complicata, maldestra, ecc. Quindi la mia domanda è: Si prega di suggerire miglioramenti e/o approcci alternativi. In definitiva, sarebbe bello avere una funzione generale disponibile qui.
dati:
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')
funzione personalizzata:
## 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)
}
Usage:
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())
Risultato:
Mentre ci sono domande e risposte su StackOverflow legate, non ho trovato uno che era sufficientemente dettagliata per il mio scopo. Ecco una selezione di scambi utili:
- create-geom-ribbon-for-min-max-range: fa una domanda simile, ma fornisce meno dettagli di quelli che stavo cercando.
- possible-bug-in-geom-ribbon: strettamente correlati, ma mancano passaggi intermedi su come calcolare il massimo/minimo.
- fill-region-between-two-loess-smoothed-lines-in-r-with-ggplot: strettamente correlato, ma si concentra sulle linee loess. Eccellente.
- ggplot-colouring-areas-between-density-lines-according-to-relative-position: strettamente correlato, ma focalizzato sulle densità. Questo post mi ha ispirato molto.
La funzione non è molto versatile. Per esempio, se trasformo i dati nella chiamata in '' ggplot() '', questo non verrà rilevato, diciamo se scrivo '' ggplot (df, aes (x = Date, y = value/100,. ..) '' Questo è solo un problema – PatrickT
dovresti mettere la risposta nella sezione di risposta anche se stai rispondendo alla tua stessa domanda – rawr
@rawr, ci ho pensato, ma ho pensato che la mia domanda sarebbe stata più facile capire se io anche postato un'immagine del risultato voluto, quindi ho anche aggiunto il codice ... – PatrickT