2
votes

Comment trouver la date la plus proche d'une date donnée?

J'essaye de trouver comment trouver la date la plus proche dans 1 objet de zoo à une date donnée dans un autre objet de zoo (peut également utiliser data.frame). Supposons que j'ai:

...
2018-12-02   2  NA
...
2018-12-14  14  2018-12-14
2018-12-15  15  2018-12-14
2018-12-16  16  2018-12-14
...
2019-01-01  32  2018-12-14
2019-01-02  33  2019-01-02
2019-01-03  34  2019-01-02
...

Pour chaque date dans dates.zoo je voudrais l'aligner avec la date précédente la plus proche dans Monthly.zoo code >. ( NA si aucune date mensuelle n'est trouvée). L'objet data.frame / zoo que j'attends est donc:

dates.zoo <- zoo(data.frame(val=seq(1:121)), order.by = seq.Date(as.Date('2018-12-01'), as.Date('2019-03-31'), "days"))
monthly.zoo <- zoo(data.frame(val=c(1,2,4)), order.by = c(as.Date('2018-12-14'), as.Date('2019-1-2'), as.Date('2019-2-3')))

NOTE: je préférerais une solution Base-R mais d'autres seraient intéressantes à voir aussi


4 commentaires

peut-être utile: stackoverflow.com/questions/23342647/...


serait intéressant de voir un formulaire générique où il pourrait facilement être configuré pour être au lieu de juste T-1 pour pouvoir supporter T-2, T-3, ...


Je suis sûr qu'il y aura un moyen intelligent avec les données, la réponse au tableau au lien ^^. et avec la réponse de base R, je suppose que vous pouvez échanger la partie which.min pour sélectionner la deuxième, troisième plus grande


Vérifiez findInterval


4 Réponses :


3
votes

Une jointure progressive utilisant data.table peut être utilisée. Voir aussi: https://www.r-bloggers.com/ Understanding-data-table-rolling-joins /

Aussi une solution utilisant la solution base-R

data.table solution

> dates.df[64:69,]
           val      dates closest_month
2019-02-02  64 2019-02-02    2019-01-02
2019-02-03  65 2019-02-03    2019-01-02
2019-02-04  66 2019-02-04    2019-02-03
2019-02-05  67 2019-02-05    2019-02-03
2019-02-06  68 2019-02-06    2019-02-03
2019-02-07  69 2019-02-07    2019-02-03

solution base R

> monthly.df[,nearest:=(dates)][dates.df,roll = Inf]
     val      dates    nearest i.val
  1:  NA 2018-12-01       <NA>     1
  2:  NA 2018-12-02       <NA>     2
  3:  NA 2018-12-03       <NA>     3
  4:  NA 2018-12-04       <NA>     4
  5:  NA 2018-12-05       <NA>     5
 ---                                
118:   4 2019-03-27 2019-02-03   117
119:   4 2019-03-28 2019-02-03   118
120:   4 2019-03-29 2019-02-03   119
121:   4 2019-03-30 2019-02-03   120
122:   4 2019-03-31 2019-02-03   121

Résultats: data.table

dates.df <- zoo(data.frame(val=seq(1:121)), order.by = seq.Date(as.Date('2018-12-01'), as.Date('2019-03-31'), "days"))
monthly.df <- zoo(data.frame(val=c(1,2,4)), order.by = c(as.Date('2018-12-14'), as.Date('2019-1-2'), as.Date('2019-2-3')))

dates.df <- data.frame(val=dates.df$val,dates=attributes(dates.df)$index)
monthly.df <- data.frame(val=monthly.df$val,dates=attributes(monthly.df)$index)

min_distances <- as.numeric(dates.df$dates)- matrix(rep(as.numeric(monthly.df$dates),nrow(dates.df)),ncol=length(monthly.df$dates),byrow=T)
min_distances <- as.data.frame(t(min_distances))

closest <- sapply(min_distances,function(x) 
  { 
    w <- which(x==min(x[x>0])); 
    ifelse(length(w)==0,NA,w) 
  })

dates.df$closest_month <- monthly.df$dates[closest]

Résultats base R

library(data.table)
dates.df <- data.table(val=seq(1:121), dates = seq.Date(as.Date('2018-12-01'), as.Date('2019-03-31'), "days"))
monthly.df <- data.table(val=c(1,2,4,5), dates = c(as.Date('2018-12-14'), as.Date('2019-1-2'), as.Date('2019-2-3')))

setkeyv(dates.df,"dates")
setkeyv(monthly.df,"dates")

#monthly.df[,nearest:=(dates)][dates.df,roll = 'nearest'] #closest date
monthly.df[,nearest:=(dates)][dates.df,roll = Inf] #Closest _previous_ date


1 commentaires

Il semble que oui et que j'aurais dû lire de plus près - donc un roll = Inf serait applicable ici. Je vais m'ajuster. Merci!



1
votes

Si, pour chaque date dans dates.df , vous souhaitez obtenir la date la plus proche dans Monthly.df qui est inférieure à la date donnée, et Monthly.df est trié par date ascendante, vous pouvez utiliser la méthode ci-dessous. Il compte le nombre de lignes dans Monthly.df avec un index inférieur à la date donnée, ce qui équivaut à l'index si mothly.df est trié par date ascendante. S'il n'y a aucune ligne de ce type, l'index est changé en NA.

inds <- rowSums(outer(index(dates.df), index(monthly.df), `>`))
inds[inds == 0] <- NA
dates.df_monthmatch <- index(monthly.df)[inds]


dates.df_monthmatch
#   [1] NA           NA           NA           NA           NA           NA          
#   [7] NA           NA           NA           NA           NA           NA          
#  [13] NA           NA           "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14"
#  [19] "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14"
#  [25] "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14" "2018-12-14"
#  [31] "2018-12-14" "2018-12-14" "2018-12-14" "2019-01-02" "2019-01-02" "2019-01-02"
#  [37] "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02"
#  [43] "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02"
#  [49] "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02"
#  [55] "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02"
#  [61] "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-01-02" "2019-02-03"
#  [67] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
#  [73] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
#  [79] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
#  [85] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
#  [91] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
#  [97] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
# [103] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
# [109] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
# [115] "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03" "2019-02-03"
# [121] "2019-02-03"


0 commentaires

0
votes

Voici une possibilité, même si j'ai dû changer l'objet en un bloc de données afin d'attribuer les dates d'index du zoo. Ce code compare le mois, puis l'année et enfin le jour avec des critères inférieurs ou égaux à la date à comparer. Si aucune date ne correspond à ce critère, un NA est attribué. Ces comparaisons ont été effectuées avec le package 'lubridate' vérifiant les éléments de date individuels, puis avec lequel indexer logiquement la meilleure correspondance.

monthly.df <- zoo(data.frame(val=c(1,2,4)), order.by = c(as.Date('2018-12- 
14'), as.Date('2019-1-2'), as.Date('2017-2-3')))

....

#Result
> monthly.df_Fin
           val      match value
2017-02-03   4       <NA>    NA
2018-12-14   1 2018-12-14    14
2019-01-02   2 2019-01-02    33

Supposons que nous ayons changé une valeur en dehors de la plage de critères:

library(zoo)
library(lubridate)

dates.df <- zoo(data.frame(val=seq(1:121)), order.by = seq.Date(as.Date('2018-12-01'), as.Date('2019-03-31'), "days"))
monthly.df <- zoo(data.frame(val=c(1,2,4)), order.by = c(as.Date('2018-12-14'), as.Date('2019-1-2'), as.Date('2019-2-3')))

month_m<-month(monthly.df)
month_d<-month(dates.df)

year_m<-year(monthly.df)
year_d<-year(dates.df)

day_m<-day(monthly.df)
day_d<-day(dates.df)

index<-list()
Index<-list()

for( i in 1:length(monthly.df)){

index[[i]]<-which(month_m[i] == month_d & year_m[i] == year_d
                  & day_d <= day_m[i])

test<-unlist(index[[i]])

   #Assigns NA if no suitable match is found
   if(length(test)==0){
    print("NA")
    Index[[i]]=NA
    }else {
    Index[[i]]<-tail(test, n=1)
    }                      
}

Test<-unlist(Index)
monthly.df_Fin<-as.data.frame(monthly.df)
dates.df_Fin<-as.data.frame(dates.df)
monthly.df_Fin$match<-as.character(row.names(dates.df_Fin)[Test])
monthly.df_Fin$value<-dates.df_Fin[Test,]

> monthly.df_Fin
           val      match value
2018-12-14   1 2018-12-14    14
2019-01-02   2 2019-01-02    33
2019-02-03   4 2019-02-03    65


0 commentaires

3
votes

Suite à la suggestion d'Henrik d'utiliser findInterval . Nous pouvons faire:

interval.idx <- findInterval(index(dates.zoo), index(monthly.zoo))
interval.idx <- ifelse(interval.idx == 0, NA, interval.idx)
dates.zoo$month <- index(monthly.zoo)[interval.idx]


1 commentaires

Cette réponse doit vraiment être marquée comme la réponse acceptée. Raison: Je pense que toutes les autres réponses ont des performances de O (N) où N est le nombre d'indices de zoo qui doivent être recherchés. En revanche, la documentation findInterval dit qu'il utilise un algorithme de log (N) rapide (recherche binaire?).