fusionar data.frames en función del año y completar los valores perdidos

Tengo dos data.frames que quiero fusionar. El primero es:

datess <- seq(as.Date('2005-01-01'), as.Date('2009-12-31'), 'days') sample<- data.frame(matrix(ncol = 3, nrow = length(datess))) colnames(sample) <- c('Date', 'y', 'Z') sample$Date <- datess 

El segundo:

 a <- data.frame(matrix(ncol = 3, nrow = 5)) colnames(a) <- c('a', 'y', 'Z') a$Z <- c(1, 3, 4, 5, 2) a$a <- c(2005, 2006, 2007, 2008, 2009) a$y <- c('abc', 'def', 'ijk', 'xyz', 'thanks') 

Y me gustaría que la fusionada coincida con el año y luego complete el rest de los valores de cada día de ese año.

 Date y Z 2005-01-01 abc 1 2005-01-02 abc 1 2005-01-03 abc 1 {cont} 2009-12-31 thanks 2 

Puedes usar el match

 matched< -match(format(sample$Date,"%Y"),a$a) sample$y<-a$y[matched] sample$Z<-a$Z[matched] 

Hasta el momento, se han publicado tres enfoques diferentes:

  • usando match()
  • usando dplyr
  • usando merge()

Hay un cuarto enfoque llamado actualización de join sugerido por Frank en el chat:

 library(data.table) setDT(sample)[, yr := year(Date)][setDT(a), on = .(yr = a), `:=`(y = iy, Z = iZ)] 

que resultó ser el más rápido y conciso de los cuatro.

Resultados de referencia:

Para decidir cuál de los enfoques es el más eficiente en términos de velocidad, he establecido un punto de referencia usando el paquete microbenchmark .

 Unit: microseconds expr min lq mean median uq max neval create_data 248.827 291.116 316.240 302.0655 323.588 665.298 100 match 4488.685 4545.701 4752.226 4649.5355 4810.763 6881.418 100 dplyr 6086.609 6275.588 6513.997 6385.2760 6625.229 8535.979 100 merge 2871.883 2942.490 3183.712 3004.6025 3168.096 5616.898 100 update_join 1484.272 1545.063 1710.651 1659.8480 1733.476 3434.102 100 

A medida sample se modifica la sample , debe crearse de nuevo antes de cada ejecución de referencia. Esto se hace mediante una función que también se incluye en el índice de referencia ( crear datos ). Los tiempos para crear datos deben restarse de los otros tiempos.

Entonces, incluso para el pequeño conjunto de datos de aproximadamente 1800 filas, la unión de actualización es la más rápida, casi el doble de rápida que la segunda, seguida de la coincidencia , y dplyr es la última, más de 4 veces más lenta que la actualización (con el tiempo para crear datos restados).

Código de referencia

 datess < - seq(as.Date('2005-01-01'), as.Date('2009-12-31'), 'days') a <- data.frame(Z = c(1, 3, 4, 5, 2), a = 2005:2009, y = c('abc', 'def', 'ijk', 'xyz', 'thanks'), stringsAsFactors = FALSE) setDT(a) make_sample <- function() data.frame(Date = datess, y = NA_character_, Z = NA_real_) library(data.table) library(magrittr) microbenchmark::microbenchmark( create_data = make_sample(), match = { sample <- make_sample() matched<-match(format(sample$Date,"%Y"),a$a) sample$y<-a$y[matched] sample$Z<-a$Z[matched] }, dplyr = { sample <- make_sample() sample <- sample %>% dplyr::mutate(a = format(Date, "%Y") %>% as.numeric) %>% dplyr::inner_join(a %>% dplyr::select(a), by = "a") }, merge = { sample < - make_sample() sample2 <- data.frame(Date = datess) sample2$a <- lubridate::year(sample2$Date) sample <- base::merge(sample2, a, by="a") }, update_join = { sample <- make_sample() setDT(sample)[, yr := year(Date)][a, on = .(yr = a), `:=`(y = iy, Z = iZ)] } ) 

Si y y Z son siempre cero en la sample , no los necesita allí, así que todo lo que tiene que hacer es unirse en un año como este:

  library(dplyr) sample %>% mutate(a = format(Date, "%Y") %>% as.numeric) %>% inner_join(a %>% select(a)) 

¿Hay algo en contra de tener una columna con año en tu nueva df? Si no, podrías generar uno en ‘muestra’ y usar la función de fusión

 require(lubridate) #to make generating the year easy sample2< -data.frame(Date=datess) sample2$a<-year(sample2$Date) df<-merge(sample2,a,by="a") 

esto dará como resultado algo como esto:

 head(df) a Date y Z 1 2005 2005-01-01 abc 1 2 2005 2005-01-02 abc 1 3 2005 2005-01-03 abc 1 4 2005 2005-01-04 abc 1 5 2005 2005-01-05 abc 1 6 2005 2005-01-06 abc 1 

Luego, puede eliminar la columna del año nuevamente si le molesta.