web-dev-qa-db-fra.com

R + ggplot: Séries chronologiques avec événements

Je suis un débutant R/ggplot. Je voudrais créer un tracé geom_line d'une série chronologique variable continue, puis ajouter une couche composée d'événements. La variable continue et ses horodatages sont stockés dans un data.frame, les événements et leurs horodatages sont stockés dans un autre data.frame.

Ce que j'aimerais vraiment, c'est quelque chose comme les graphiques sur finance.google.com. Dans ceux-ci, la série chronologique est le cours des actions et il y a des "drapeaux" pour indiquer les événements d'actualité. Je ne suis pas en train de tracer des trucs financiers, mais le type de graphique est similaire. J'essaie de tracer des visualisations des données du fichier journal. Voici un exemple de ce que je veux dire ...

google chart with events

Si cela est conseillé (?), Je voudrais utiliser des data.frames séparés pour chaque couche (une pour les observations variables continues, une autre pour les événements).

Après quelques essais et erreurs, c'est à peu près aussi proche que possible. Ici, j'utilise des exemples de données provenant d'ensembles de données fournis avec ggplot. "economics" contient des données chronologiques que j'aimerais tracer et "presidential" contient quelques événements (élections présidentielles).

library(ggplot2)
data(presidential)
data(economics)

presidential <- presidential[-(1:3),]
yrng <- range(economics$unemploy)
ymin <- yrng[1]
ymax <- yrng[1] + 0.1*(yrng[2]-yrng[1])

p2 <- ggplot()
p2 <- p2 + geom_line(mapping=aes(x=date, y=unemploy), data=economics , size=3, alpha=0.5) 
p2 <- p2 + scale_x_date("time") +  scale_y_continuous(name="unemployed [1000's]")
p2 <- p2 + geom_segment(mapping=aes(x=start,y=ymin, xend=start, yend=ymax, colour=name), data=presidential, size=2, alpha=0.5)
p2 <- p2 + geom_point(mapping=aes(x=start,y=ymax, colour=name ), data=presidential, size=3) 
p2 <- p2 + geom_text(mapping=aes(x=start, y=ymax, label=name, angle=20, hjust=-0.1, vjust=0.1),size=6, data=presidential)
p2

my attempt

Des questions:

  • C'est correct pour des événements très clairsemés, mais s'il y en a un cluster (comme cela arrive souvent dans un fichier journal), cela devient compliqué. Existe-t-il une technique que je peux utiliser pour afficher correctement un ensemble d'événements se produisant dans un court intervalle de temps? Je pensais à position_jitter, mais c'était vraiment difficile pour moi d'aller aussi loin. Google Maps empile ces "drapeaux" d'événements les uns sur les autres s'il y en a beaucoup.

  • En fait, je n'aime pas coller les données d'événement à la même échelle que l'affichage de mesure en continu. Je préférerais le mettre dans une facet_grid. Le problème est que toutes les facettes doivent provenir du même data.frame (je ne sais pas si c'est vrai). Si c'est le cas, cela ne semble pas non plus idéal (ou peut-être que j'essaie simplement d'éviter d'utiliser la refonte?)

59
Angelo

Autant que j'aime la réponse de @JD Long, je vais en mettre une qui est juste dans R/ggplot2.

L'approche consiste à créer un deuxième ensemble de données d'événements et à l'utiliser pour déterminer les positions. En commençant par ce qu'Angelo avait:

library(ggplot2)
data(presidential)
data(economics)

Extraire les données de l'événement (présidentiel) et les transformer. Calculez baseline et offset en tant que fractions des données économiques avec lesquelles elles seront tracées. Définissez le bas (ymin) sur la ligne de base. C'est là que vient la partie délicate. Nous devons pouvoir échelonner les étiquettes si elles sont trop rapprochées. Déterminez donc l'espacement entre les étiquettes adjacentes (en supposant que les événements sont triés). S'il est inférieur à un certain montant (j'ai choisi environ 4 ans pour cette échelle de données), notez que cette étiquette doit être plus élevée. Mais il doit être supérieur à celui qui le suit, utilisez donc rle pour obtenir la longueur de TRUE (c'est-à-dire qu'il doit être supérieur) et calculez un vecteur de décalage à l'aide de cela (chaque chaîne de TRUE doit compter à rebours de sa longueur à 2, les FALSEs sont juste à un décalage de 1). Utilisez-le pour déterminer le haut des barres (ymax).

events <- presidential[-(1:3),]
baseline = min(economics$unemploy)
delta = 0.05 * diff(range(economics$unemploy))
events$ymin = baseline
events$timelapse = c(diff(events$start),Inf)
events$bump = events$timelapse < 4*370 # ~4 years
offsets <- rle(events$bump)
events$offset <- unlist(mapply(function(l,v) {if(v){(l:1)+1}else{rep(1,l)}}, l=offsets$lengths, v=offsets$values, USE.NAMES=FALSE))
events$ymax <- events$ymin + events$offset * delta

Mettre cela ensemble dans un complot:

ggplot() +
    geom_line(mapping=aes(x=date, y=unemploy), data=economics , size=3, alpha=0.5) +
    geom_segment(data = events, mapping=aes(x=start, y=ymin, xend=start, yend=ymax)) +
    geom_point(data = events, mapping=aes(x=start,y=ymax), size=3) +
    geom_text(data = events, mapping=aes(x=start, y=ymax, label=name), hjust=-0.1, vjust=0.1, size=6) +
    scale_x_date("time") +  
    scale_y_continuous(name="unemployed \[1000's\]")

Vous pourriez facette, mais c'est délicat avec différentes échelles. Une autre approche consiste à composer deux graphiques. Il y a quelques manipulations supplémentaires à faire pour s'assurer que les tracés ont la même plage x, pour que les étiquettes tiennent toutes dans le tracé inférieur et pour éliminer l'axe x dans le tracé supérieur.

xrange = range(c(economics$date, events$start))

p1 <- ggplot(data=economics, mapping=aes(x=date, y=unemploy)) +
    geom_line(size=3, alpha=0.5) +
    scale_x_date("", limits=xrange) +  
    scale_y_continuous(name="unemployed [1000's]") +
    opts(axis.text.x = theme_blank(), axis.title.x = theme_blank())

ylims <- c(0, (max(events$offset)+1)*delta) + baseline
p2 <- ggplot(data = events, mapping=aes(x=start)) +
    geom_segment(mapping=aes(y=ymin, xend=start, yend=ymax)) +
    geom_point(mapping=aes(y=ymax), size=3) +
    geom_text(mapping=aes(y=ymax, label=name), hjust=-0.1, vjust=0.1, size=6) +
    scale_x_date("time", limits=xrange) +
    scale_y_continuous("", breaks=NA, limits=ylims)

#install.packages("ggExtra", repos="http://R-Forge.R-project.org")
library(ggExtra)

align.plots(p1, p2, heights=c(3,1))

36
Brian Diggs

Maintenant, j'aime ggplot autant que le prochain, mais si vous voulez créer les tableaux de types de Google Finance, pourquoi ne pas simplement le faire avec l'API graphique Google?!? Vous allez adorer ça:

install.packages("googleVis")
library(googleVis)

dates <- seq(as.Date("2011/1/1"), as.Date("2011/12/31"), "days")
happiness <- rnorm(365)^ 2
happiness[333:365] <- happiness[333:365]  * 3 + 20
Title <- NA
Annotation <- NA
df <- data.frame(dates, happiness, Title, Annotation)
df$Title[333] <- "Discovers Google Viz"
df$Annotation[333] <- "Google Viz API interface by Markus Gesmann causes acute increases in happiness."

### Everything above here is just for making up data ### 
## from here down is the actual graphics bits        ###
AnnoTimeLine  <- gvisAnnotatedTimeLine(df, datevar="dates",
                                       numvar="happiness", 
                                       titlevar="Title", annotationvar="Annotation",
                                       options=list(displayAnnotations=TRUE,
                                                    legendPosition='newRow',
                                                    width=600, height=300)
                                       )
# Display chart
plot(AnnoTimeLine) 
# Create Google Gadget
cat(createGoogleGadget(AnnoTimeLine), file="annotimeline.xml")

et il produit ce graphique fantastique:

enter image description here

83
JD Long

Plotly est un moyen simple de rendre les ggplots interactifs. Pour afficher les événements, contraignez-les en facteurs pouvant être affichés sous forme esthétique, comme la couleur.

Le résultat final est un tracé sur lequel vous pouvez faire glisser le curseur. Les graphiques affichent des données d'intérêt:

enter image description here

Voici le code pour faire le ggplot:

# load data    
data(presidential)
data(economics)

# events of interest
events <- presidential[-(1:3),]

# strip year from economics and events data frames
economics$year = as.numeric(format(economics$date, format = "%Y")) 

# use dplyr to summarise data by year
#install.packages("dplyr")
library(dplyr)
econonomics_mean <- economics %>% 
  group_by(year) %>% 
  summarise(mean_unemployment = mean(unemploy))

# add president terms to summarized data frame as a factor
president <- c(rep(NA,14), rep("Reagan", 8), rep("Bush", 4), rep("Clinton", 8), rep("Bush", 8), rep("Obama", 7))
econonomics_mean$president <- president

# create ggplot
p <- ggplot(data = econonomics_mean, aes(x = year, y = mean_unemployment)) +
  geom_point(aes(color = president)) +
  geom_line(alpha = 1/3)

Il suffit d'une seule ligne de code pour transformer le ggplot en un objet complot.

# make it interactive!
#install.packages("plotly")
library(plotly)
ggplotly(p)
3
Rich Pauloo