62 votes

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

Je suis un débutant en R/ggplot. Je voudrais créer un graphique géom_ligne d'une série temporelle à 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 je voudrais vraiment J'aimerais faire quelque chose comme les graphiques sur finance.google.com. Dans ces graphiques, la série temporelle est le prix des actions et il y a des "drapeaux" pour indiquer les nouveaux événements. Je ne suis pas en train de tracer des trucs financiers, mais le type de graphique est similaire. J'essaie de tracer des visualisations de données de fichiers journaux. 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 (un pour les observations de variables continues, un autre pour les événements).

Après quelques essais et erreurs, je n'ai pas pu faire mieux. Ici, j'utilise des exemples de données provenant de jeux de données fournis avec ggplot. Le jeu "economics" contient des données de séries temporelles que j'aimerais tracer et le jeu "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

Questions :

  • Cela ne pose pas de problème pour les événements très rares, mais s'il y a un groupe d'événements (comme c'est souvent le cas dans un fichier journal), cela devient désordonné. Existe-t-il une technique permettant d'afficher proprement un ensemble d'événements survenant dans un court intervalle de temps ? Je pensais à position_jitter, mais j'ai eu beaucoup de mal à aller aussi loin. google charts 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 dans la même échelle que l'affichage des mesures continues. Je préférerais les mettre dans une grille de facettes. Le problème est que les facettes doivent toutes 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 juste d'éviter d'utiliser reshape ?).

7 votes

Intrigue intéressante : ne vous attendez pas à trouver un emploi après l'arrivée au pouvoir d'un président républicain !

0 votes

C'était juste les données les plus pratiques et les plus disponibles à utiliser comme exemple - mais oui, ça fait réfléchir :-)

85voto

JD Long Points 20477

J'aime bien ggplot, mais si vous voulez faire des graphiques de type Google Finance, pourquoi ne pas le faire avec l'API graphique de 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 ça donne ce fantastique graphique :

enter image description here

12 votes

Vous avez senti le bonheur augmenter, n'est-ce pas ? Vous voyez, les graphiques ne mentent pas ! :)

0 votes

Prédiction : Vous allez avoir une sérieuse augmentation de votre réputation grâce à cette démo.

41voto

Brian Diggs Points 22433

Bien 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 sur les événements et à l'utiliser pour déterminer les positions. En commençant par ce que @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 la valeur inférieure ( ymin ) à la ligne de base. C'est ici qu'intervient la partie délicate. Nous devons être en mesure de décaler les étiquettes si elles sont trop proches les unes des autres. Déterminez donc l'espacement entre les étiquettes adjacentes (en supposant que les événements sont triés). S'il est inférieur à une certaine valeur (j'ai choisi environ 4 ans pour cette échelle de données), alors il faut noter que cette étiquette doit être plus haute. Mais il doit être plus haut que celui qui le suit, donc utilisez rle pour obtenir la longueur de TRUE (c'est-à-dire qu'il doit être plus élevé) et calcule un vecteur de décalage en l'utilisant (chaque chaîne de TRUE doit compter à rebours de sa longueur jusqu'à 2, le FALSE sont juste à un décalage de 1). Utilisez ceci 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

En mettant tout ça ensemble dans une intrigue :

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 faire des facettes, mais c'est délicat avec les différentes échelles. Une autre approche consiste à composer deux graphes. Il faut faire quelques manipulations supplémentaires pour s'assurer que les graphiques ont la même plage d'abscisses, pour que les étiquettes tiennent toutes dans le graphique inférieur et pour éliminer l'axe des abscisses dans le graphique 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))

3 votes

Woohoo ! entre vous et @JDLong, j'ai appris un très bon kung fu R aujourd'hui !

0 votes

Très utile, merci @Brian Diggs. Un peu déprécié. Voici une version mise à jour du code : pastebin.com/sVAACtQe (j'ai dû jouer avec les marges, c'est fastidieux - n'hésitez pas à faire des copier-coller, naturellement).

4voto

Rich Pauloo Points 2577

Plotly est un moyen facile de rendre les ggplots interactifs. Pour afficher les événements, il faut les convertir en facteurs qui peuvent être affichés de manière esthétique, comme la couleur.

Le résultat final est un graphique sur lequel vous pouvez faire glisser le curseur. Les graphiques affichent les données qui vous intéressent :

enter image description here

Voici le code pour réaliser 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 ligne de code pour transformer le ggplot en un objet plotly.

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

0 votes

Wow, c'est très joli. Merci.

2voto

Victor Espinoza Points 78

Considérant que vous tracez des séries chronologiques et des informations qualitatives, la plupart des livres économiques utilisent la zone de traçage pour indiquer un changement structurel ou un événement sur les données, je recommande donc d'utiliser quelque chose comme ceci :

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

ggplot() +
  geom_rect(aes(xmin = start,
                xmax = end,
                ymin = 0, ymax = Inf,
                fill = name),
            data = presidential,
            show.legend = F) +
  geom_text(aes(x = start+500,
                y = 2000,
                label = name,
                angle = 90),
            data = presidential) +
  geom_line(aes(x = date, y = unemploy),
            data= economics) +
  scale_fill_brewer(palette = "Blues") +
  labs(x = "time", y = "unemploy")

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X