2010-11-16 28 views
1

Alors que la classe difftime est assez simple à utiliser lorsque l'on veut obtenir des différences annuelles ou quotidiennes entre les dates. Les différences mensuelles n'étaient pas aussi simples pour moi. Étant donné que l'arithmétique norme R permet des objets soustractions date Boucler dans un vecteur serait possible, mais here's une solution qui évite mise en boucle:Comment créer des périodes ordonnées mensuellement à partir de datevector sans boucler?

# generate reproducible example 
x<-seq(as.Date("2010-11-15"),as.Date("2011-05-20"),"months") 
y<-seq(as.Date("2010-04-15"),as.Date("2012-05-20"),"months") 
z<-seq(as.Date("2012-08-15"),as.Date("2013-05-20"),"months") 

d <- c(z,x,y) 

# my function – suggestions are welcome! 
getPeriods <- function(datevector){ 
x <- floor((as.numeric(datevector)-as.numeric(min(datevector)))/30) +1 
return(x) 
} 

# This returns a vector of monthly ordered periods. 

Y at-il une meilleure façon plus native de le faire? Par exemple, puis-je utiliser seq() en combinaison avec longueur() en quelque sorte? Je n'ai pas pu le faire parce que l'argument "to" de seq ne permet pas les vecteurs. Tout commentaire apprécié, d'ailleurs j'espère que cela peut aider quelqu'un d'autre ...

+0

J'ai lu votre question trois fois maintenant, et je ne la comprends toujours pas. – hadley

+0

@hadley: pas exactement le badge que j'aimerais gagner. La réponse de Joris Meys est exactement ce qui m'intéressait. Peut-être que simplement lire le code et poser la question de la révision standard: "Y at-il une meilleure façon de le faire?" est ce que j'aurais dû faire. Si vous avez fait référence à la partie seq(): je voulais simplement dire que length (seq (startdate, enddate)) ne fonctionne pas si enddate est un vecteur et startdate ne l'est pas (sans looping). –

Répondre

1

La solution standard consiste à convertir en POSIXlt et à partir de là.

getPeriods2 <- function(datevector){ 
    tmp <- as.POSIXlt(datevector) 
    tmp <- tmp$year*12 + tmp$mon 
    tmp - min(tmp) + 1 
} 

Ce ne tient pas compte des différences de jours, ce qui signifie que la différence entre le 31 mars et avril 1 est 1 dans ma fonction, mais est 0 dans le vôtre.

Maintenant que les mois ne sont pas de longueurs égales, la définition d'une "période mensuelle" est de toute façon délicate. Surtout quand vous courez sur quelques années, où vous pourriez avoir le problème de l'année bissextile. Une différence mensuelle n'est pas objective: dans votre définition, une différence mensuelle est de 30 jours, ce qui peut être discuté. Certaines personnes préfèrent 365/12 comme différence, de sorte qu'il reste correct lorsque les périodes s'étendent sur plusieurs années.

Voir par exemple votre fonction:

> tt <- as.Date(c("1998-01-01","2012-01-01")) 
> tt 
[1] "1998-01-01" "2012-01-01" 

> getPeriods(tt) 
[1] 1 171 

> diff(getPeriods(tt))/12 
[1] 14.16667 

> diff(getPeriods2(tt))/12 
[1] 14 

Ainsi, selon votre fonction, il y a 2 mois plus de 14 ans que il est en réalité. Mon idée personnelle: utiliser des mois comme des mois, pas comme des périodes de 30 jours. Je ne dis pas que votre fonction est mauvaise, c'est une approche qui est plus souvent utilisée. Mais en ce qui me concerne, vous allez sûrement avoir des ennuis à un moment donné.

+0

À l'heure actuelle, je viens de réorganiser certaines données mal enregistrées, qui n'utilisaient pas de champ de date mais des colonnes séparées pour l'année et le mois. C'est pourquoi j'ai mis en place les jours moi-même afin d'obtenir un champ de date unique standard pour l'utilisation avec les modèles ts et panneaux. Ainsi, le premier problème ne s'applique pas à moi, mais je suis d'accord - il vaut mieux le faire comme vous l'avez suggéré (ne peut en voir aucun inconvénient). En fait, j'étais sur cette piste, mais j'ai lu une suggestion de Brian D. Ripley sur la liste qui a utilisé tmp $ mois, ce qui ne fonctionne pas. Plus str() n'est pas revenu beaucoup ici. tmp $ mon est difficile à trouver pour les débutants ... –

+0

@ ran2: Je suis définitivement d'accord sur celui-là. POSIXlt et POSIXct ne sont pas les plus faciles à utiliser, les fonctions du paquet 'zoo' sont souvent plus intuitives. J'utilise les anciennes classes plus par habitude que toute autre chose, en dehors du fait que j'aime charger le moins de paquets possible. J'ai eu une fois trop de conflits entre les paquets pour me sentir toujours à l'aise en utilisant des tonnes d'entre eux. –

2

Le paquet zoo possède de nombreuses fonctions utiles pour les séries temporelles, telles que les fonctions yearmon et as.yearmon. Les dates internes sont converties à l'année avec des mois décimaux (month.index/12), donc si vous voulez une sortie entière, vous devez probablement multiplier par 12 et arrondir. Avec votre vecteur de date, d:

require(zoo) 
d.ym <- as.yearmon(d) 
#Edit: 
#to get the number of months from the first record: 
round(as.numeric(d.ym -d.ym[1])*12) 

# calculate successive differences as integer months 
diffdates <- round(diff(as.numeric(d.ym))*12) 
diffdates 

L'effort impliqué dans l'apprentissage du zoo en vaut la peine. C'est un paquet mature. Il y a un grand nombre de questions sur R-help que Grothendieck répond par rapport aux fonctions du zoo.

+0

'' [1] 1 1 1 1 1 1 1 1 -30 1 1 1 1 1 1 -13 1 1 [20] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [39] 1 1 1 est ce que j'obtiens de ceci ... –

+0

Oui, ça a l'air juste vu le vecteur d'entrée. Attendiez-vous quelque chose d'autre? Peut-être que vous vouliez une fonction qui vient de renvoyer une différence à partir d'un point de départ constant: round (as.numeric (d.ym -d.ym [1]) * 12) –

+1

devinez que ce serait le cas. Vous voulez mettre à jour votre réponse? –

0

Vous pouvez également le faire avec l'ensemble lubrifiant. Nous devrions d'abord les transformer en objets POSIXt. L'heure d'été créera des surprises pour nous (c'est-à-dire que les dates ne seront pas exactement séparées d'un mois), définissons donc le fuseau horaire sur Temps universel coordonné, qui ne participe pas à l'heure d'été.

d <- as.POSIXlt(d, tz = "UTC") 

Maintenant, nous soustrayons pour obtenir les intervalles de temps impliqués, et divisons par un mois.

(d - min(d))/months(1) 

Ceci nous donne le nombre de mois après la première date à laquelle chaque date se produit. (Je pense que c'est ce que vous recherchez, mais je m'excuse si j'ai mal interprété vos intentions). Ce type de division est dans la dernière version de lubridate, que j'ai soumis à cran ce matin. Cela peut donc prendre quelques jours pour être mis à jour. Je m'excuse.

+1

Erreur dans UseMethod ("months"): Aucune méthode applicable pour les 'mois' appliqués à un objet de classe "c ('double', 'numeric')" ... causé par mois (1) –