67 votes

Compréhension des dates et traçage d'un histogramme avec ggplot2 en R

Question principale

Je rencontre des problèmes pour comprendre pourquoi la gestion des dates, des libellés et des interruptions ne fonctionne pas comme je l'aurais attendu dans R lorsque j'essaie de faire un histogramme avec ggplot2.

Je recherche :

  • Un histogramme de la fréquence de mes dates
  • Des repères centrés sous les barres correspondantes
  • Des libellés de date au format %Y-b
  • Des limites appropriées ; espace vide minimisé entre le bord de l'espace de la grille et les barres les plus extérieures

J'ai mis mes données en ligne sur pastebin pour que vous puissiez reproduire ceci. J'ai créé plusieurs colonnes car je n'étais pas sûr de la meilleure façon de le faire :

> dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)
> head(dates)
       YM       Date Year Month
1 2008-Apr 2008-04-01 2008     4
2 2009-Apr 2009-04-01 2009     4
3 2009-Apr 2009-04-01 2009     4
4 2009-Apr 2009-04-01 2009     4
5 2009-Apr 2009-04-01 2009     4
6 2009-Apr 2009-04-01 2009     4

Voici ce que j'ai essayé :

library(ggplot2)
library(scales)
dates$converted <- as.Date(dates$Date, format="%Y-%m-%d")

ggplot(dates, aes(x=converted)) + geom_histogram()
+      opts(axis.text.x = theme_text(angle=90))

Cela donne ce graphique. Je voulais un format %Y-%b, donc j'ai cherché autour et essayé ce qui suit, basé sur ce SO :

ggplot(dates, aes(x=converted)) + geom_histogram()
+    scale_x_date(labels=date_format("%Y-%b"),
+    breaks = "1 month")
+    opts(axis.text.x = theme_text(angle=90))

stat_bin: binwidth defaulted to range/30. Use 'binwidth = x' to adjust this.

Cela me donne ce graphique

  • Format correct de l'axe x
  • La distribution de fréquence a changé de forme (problème de binwidth?)
  • Les repères ne semblent pas centrés sous les barres
  • Les limites ont également changé

J'ai travaillé sur l'exemple dans la documentation de ggplot2 dans la section scale_x_date et geom_line() semble casser, libeller et centrer correctement les repères quand je l'utilise avec les mêmes données sur l'axe x. Je ne comprends pas pourquoi l'histogramme est différent.


Mises à jour basées sur les réponses d'edgester et gauden

Je pensais initialement que la réponse de gauden m'avait aidé à résoudre mon problème, mais je suis maintenant perplexe après avoir examiné de plus près. Notez les différences entre les graphiques résultants des deux réponses après le code.

Supposez pour les deux :

library(ggplot2)
library(scales)
dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)

D'après la réponse de @edgester ci-dessous, j'ai pu faire ce qui suit :

freqs <- aggregate(dates$Date, by=list(dates$Date), FUN=length)
freqs$names <- as.Date(freqs$Group.1, format="%Y-%m-%d")

ggplot(freqs, aes(x=names, y=x)) + geom_bar(stat="identity") +
       scale_x_date(breaks="1 month", labels=date_format("%Y-%b"),
                    limits=c(as.Date("2008-04-30"),as.Date("2012-04-01"))) +
       ylab("Fréquence") + xlab("Année et mois") +
       theme_bw() + opts(axis.text.x = theme_text(angle=90))

Voici ma tentative basée sur la réponse de gauden :

dates$Date <- as.Date(dates$Date)
ggplot(dates, aes(x=Date)) + geom_histogram(binwidth=30, colour="white") +
       scale_x_date(labels = date_format("%Y-%b"),
                    breaks = seq(min(dates$Date)-5, max(dates$Date)+5, 30),
                    limits = c(as.Date("2008-05-01"), as.Date("2012-04-01"))) +
       ylab("Fréquence") + xlab("Année et mois") +
       theme_bw() + opts(axis.text.x = theme_text(angle=90))

Graphique basé sur l'approche de edgester :

edgester-plot

Graphique basé sur l'approche de gauden :

gauden-plot

Remarquez ce qui suit :

  • des lacunes dans le graphique de gauden pour 2009-Dec et 2010-Mar; table(dates$Date) révèle qu'il y a 19 instances de 2009-12-01 et 26 instances de 2010-03-01 dans les données
  • le graphique de edgester commence en avril 2008 et se termine en mai 2012. C'est correct en fonction d'une valeur minimale dans les données du 1er avril 2008 et d'une date maximale du 1er mai 2012. Pour une raison quelconque, le graphique de gauden commence en mars 2008 et se termine toujours en mai 2012. Après avoir compté les bacs et lu le long des libellés des mois, je ne peux vraiment pas comprendre quel graphique a un excès ou manque un bac de l'histogramme!

Des idées sur les différences ici ? la méthode de edgester pour créer un comptage séparé


Références connexes

À titre de remarque, voici d'autres sources d'informations sur les dates et ggplot2 pour les passants à la recherche d'aide :

  • Commencé ici sur learnr.wordpress, un blog R populaire. Il indiquait que je devais obtenir mes données au format POSIXct, ce que je pense maintenant est faux et a gaspillé mon temps.
  • Un autre article de learnr recrée une série temporelle dans ggplot2, mais n'était pas vraiment applicable à ma situation.
  • r-bloggers a un article sur ceci, mais il semble obsolète. L'option simple format= n'a pas fonctionné pour moi.
  • Cette question SO joue avec les interruptions et les libellés. J'ai essayé de traiter mon vecteur Date comme continu et je ne pense pas que cela a très bien fonctionné. On dirait qu'il superposait le même texte de libellé encore et encore donc les lettres avaient l'air un peu étranges. La distribution est assez correcte mais il y a des interruptions étranges. Ma tentative basée sur la réponse acceptée était comme ça (résultat ici).

0 votes

Vérifiez le package lubridate.

0 votes

@gsk3 J'en avais entendu parler, bien que je pense que cela aide avec la mise en forme, les intervalles, l'incrémentation, et ainsi de suite. Penses-tu que mon problème réside dans quelque chose que lubridate pourrait résoudre? Je pensais que c'était lié à la syntaxe d'utilisation correcte de ggplot2.

0 votes

Je ne comprends pas votre question. Avez-vous essayé de poser une question et ensuite d'y répondre dans le même message ? Si c'est le cas, veuillez reformuler votre question en tant que question, puis y répondre vous-même. (Cela est activement encouragé sur SO.)

36voto

gauden Points 4926

METTRE À JOUR

Version 2: Utilisation de la classe Date

J'ai mis à jour l'exemple pour démontrer l'alignement des étiquettes et le paramétrage des limites sur le graphique. Je démontre également que as.Date fonctionne effectivement lorsqu'il est utilisé de manière cohérente (c'est probablement une meilleure option pour vos données que mon exemple précédent).

Le Graphique Cible v2

histogramme basé sur les dates

Le Code v2

Et voici le code (quelque peu excessivement) commenté :

library("ggplot2")
library("scales")

dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)
dates$Date <- as.Date(dates$Date)

# convertir la Date en son équivalent numérique
# Notez que les Dates sont stockées en interne sous forme de nombre de jours,
# il est donc facile de faire la conversion mentalement d'un format à l'autre
dates$num <- as.numeric(dates$Date)

bin <- 60 # utilisé pour agréger les données et aligner les étiquettes

p <- ggplot(dates, aes(num, ..count..))
p <- p + geom_histogram(binwidth = bin, colour="white")

# Les données numériques sont traitées comme des dates,
# les intervalles sont définis en fonction de la largeur du bin,
# et un ensemble d'étiquettes est généré et ajusté pour s'aligner avec les barres
p <- p + scale_x_date(breaks = seq(min(dates$num)-20, # changer le terme -20 selon votre préférence
                                   max(dates$num), 
                                   bin),
                      labels = date_format("%Y-%b"),
                      limits = c(as.Date("2009-01-01"), 
                                 as.Date("2011-12-01")))

# à partir d'ici, formatez à votre aise
p <- p + theme_bw() + xlab(NULL) + opts(axis.text.x  = theme_text(angle=45,
                                                                  hjust = 1,
                                                                  vjust = 1))
p

Version 1: Utilisation de POSIXct

J'essaie une solution qui effectue tout dans ggplot2, dessinant sans agrégation et définissant les limites sur l'axe des x entre le début de 2009 et la fin de 2011.

Le Graphique Cible v1

graphique avec limites définies dans ggplot2

Le Code v1

library("ggplot2")
library("scales")

dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)
dates$Date <- as.POSIXct(dates$Date)

p <- ggplot(dates, aes(Date, ..count..)) + 
    geom_histogram() +
    theme_bw() + xlab(NULL) +
    scale_x_datetime(breaks = date_breaks("3 months"),
                     labels = date_format("%Y-%b"),
                     limits = c(as.POSIXct("2009-01-01"), 
                                as.POSIXct("2011-12-01")) )

p

Bien sûr, il est possible de jouer avec les options d'étiquetage sur l'axe, mais cela permet de conclure le traçage avec une routine courte et propre dans le package de traçage.

0 votes

Merci pour cela. Quelques questions. 1) Même après avoir lu la documentation, je ne comprends pas la différence entre date et datetime. 2) Pourquoi les vecteurs as.POSIXct fonctionnent-ils mais pas as.Date? 3) De même, pourquoi ne pas définir des limites avec c(as.Date(), as.Date()) fonctionne mais as.POSIXct oui? Merci!

0 votes

J'ai joué avec cela et il semble que cela souffre du fait que les étiquettes / pauses ne sont pas alignées avec les barres également. Toutes les entrées sont simplement des mois, donc essentiellement c'est discret. Lorsque j'utilise une forme quelconque de scale_x_date (ou datetime), j'obtiens un commentaire de largeur de bin manquant et mes ticks / étiquettes ne se superposent pas aux barres. Comment pourrait-on faire cela?

0 votes

@Hendy J'ai mis à jour l'intrigue avec un nouvel exemple, en utilisant le format Date et en tirant parti du fait que les Dates sont stockées en interne comme le nombre de jours depuis le 1er Janvier 1970. Le nombre de jours correspond à votre structure de données et permet (a) une transformation facile dans le graphique (b) un alignement parfait des étiquettes sur l'axe (c) une conversion intuitive aller-retour pour le regroupement, le réglage des limites de l'axe et l'étiquetage. J'espère que cela vous aidera.

7voto

Je sais que c'est une vieille question, mais pour quiconque y accède en 2021 (ou plus tard), cela peut être fait beaucoup plus facilement en utilisant l'argument breaks= pour geom_histogram() et en créant une petite fonction raccourci pour créer la séquence requise.

dates <- read.csv("http://pastebin.com/raw.php?i=sDzXKFxJ", sep=",", header=T)

dates$Date <- lubridate::ymd(dates$Date)

by_month <- function(x,n=1){
  seq(min(x,na.rm=T),max(x,na.rm=T),by=paste0(n," mois"))
}

ggplot(dates,aes(Date)) +
  geom_histogram(breaks = by_month(dates$Date)) +
  scale_x_date(labels = scales::date_format("%Y-%b"),
               breaks = by_month(dates$Date,2)) + 
  theme(axis.text.x = element_text(angle=90))

histogramme

1 votes

Je me demande juste: est-il possible d'une manière ou d'une autre d'éliminer la partie "dates$" des deux références de dates$Date? J'ai essayé mais j'ai échoué.

1 votes

@wint3rschlaefer, vous pourriez entourer le tout avec with(), donc quelque chose comme with(dates,...), où vous remplacez les ... par la commande ggplot ci-dessus et supprimez le dates$

5voto

edgester Points 158

Je pense que la chose clé est que vous devez effectuer le calcul de fréquence en dehors de ggplot. Utilisez aggregate() avec geom_bar(stat="identity") pour obtenir un histogramme sans les facteurs réordonnés. Voici un exemple de code :

require(ggplot2)

# scales est utilisé avec ggplot et ajoute les fonctions scale* nécessaires
require(scales)

# nécessite la fonction month() pour le tracé supplémentaire
require(lubridate)

# données originales
#df<-read.csv("http://pastebin.com/download.php?i=sDzXKFxJ", header=TRUE)

# données simulées
years=sample(seq(2008,2012),681,replace=TRUE,prob=c(0.0176211453744493,0.302496328928047,0.323054331864905,0.237885462555066,0.118942731277533))
months=sample(seq(1,12),681,replace=TRUE)
my.dates=as.Date(paste(years,months,01,sep="-"))
df=data.frame(YM=strftime(my.dates, format="%Y-%b"),Date=my.dates,Year=years,Month=months)
# fin de la création de données simulées

# trier la liste juste pour l'aspect visuel. Cela ne fait aucune différence dans les résultats finaux
df=df[do.call(order, df[c("Date")]), ]

# ajouter une colonne factice pour plus de clarté dans le traitement
df$Count=1

# calculons nous-mêmes les fréquences
freqs=aggregate(Count ~ Year + Month, data=df, FUN=length)

# reconstruire la colonne Date pour que ggplot fonctionne
freqs$Date=as.Date(paste(freqs$Year,freqs$Month,"01",sep="-"))

# J'ai défini les pauses pour 2 mois pour réduire le désordre
g<-ggplot(data=freqs,aes(x=Date,y=Count))+ geom_bar(stat="identity") + scale_x_date(labels=date_format("%Y-%b"),breaks="2 months") + theme_bw() + opts(axis.text.x = theme_text(angle=90))
print(g)

# ne pas écraser le graphique précédent
dev.new()

# juste pour le plaisir, voici une vue facettée par année
# Ajoutez le facteur Month.name pour que les choses fonctionnent. month() garde les niveaux de facteur dans l'ordre
freqs$Month.name=month(freqs$Date,label=TRUE, abbr=TRUE)
g2<-ggplot(data=freqs,aes(x=Month.name,y=Count))+ geom_bar(stat="identity") + facet_grid(Year~.) + theme_bw()
print(g2)

0 votes

Je viens de voir ceci. Je prévois de le traiter... mais il semble que cela aurait été beaucoup plus facile d'utiliser simplement les données que j'ai déjà fournies. Y a-t-il une raison pour laquelle vous n'avez pas fait ça? Il contient à la fois un ensemble de valeurs %Y-%b et %Y-%m-%d qui auraient dû le rendre possible à utiliser?

0 votes

Voir la section mise à jour dans ma question. J'ai pu appliquer votre utilisation de l'agrégation pour faire exactement ce que je veux faire. Jetez un coup d'œil; Je pense que vous n'avez pas besoin de votre vecteur df$Count ou de certaines autres choses que vous avez faites pour obtenir un résultat utilisable. Maintenant, j'aimerais juste savoir comment définir des limites en fonction des plages de dates. Je n'avais également pas besoin de lubridate.

0 votes

J'ai inclus des données factices pour la postérité. La question StackOverflow peut rester alors que l'entrée pastebin disparaît. Dans ce cas, ma réponse fonctionnera toujours telle qu'elle est écrite. Vous avez raison, lubridate n'est nécessaire que pour le deuxième graphique, pas le premier.

0voto

Marco Guado Points 9

Le graphique d'erreur sous le titre "Graphique basé sur l'approche de Gauden" est dû au paramètre binwidth : ... + Geom_histogram (binwidth = 30, color = "white") + ... Si nous changeons la valeur de 30 à une valeur inférieure à 20, comme 10, vous obtiendrez toutes les fréquences.

En statistiques, les valeurs sont plus importantes que la présentation ; il vaut mieux avoir un graphique simple qu'une image très jolie mais avec des erreurs.

0 votes

Je ne suis pas sûr de savoir comment interpréter ta leçon de statistiques... est-ce que mon graphique est inexact d'une manière ou d'une autre? Je m'intéresse aux données mensuelles, donc des largeurs de bacs mensuelles ont tout leur sens. Pourquoi les réduire à 10? La vraie question est plutôt pourquoi ggplot2 fait ce qu'il fait, pas comment réduire la largeur de bac assez pour ne pas le voir. Quelque chose semble avoir dérouté ceux d'entre nous qui essaient de créer un graphique segmenté par mois, et je ne pense pas que cela aide à résoudre cela.

0 votes

De plus, avez-vous exécuté le code avec geom_histogram(binwidth = 10)?? Le résultat avec ce seul changement n'est certainement pas correct. Il serait préférable que vous téléchargiez un bloc de code pour que je puisse comprendre ce que vous voulez dire.

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