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 Réponses :
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
> 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
> 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
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]
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
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!
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"
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
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]
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?).
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