【问题标题】:merge data.frames based on year and fill in missing values根据年份合并data.frames并填写缺失值
【发布时间】:2017-04-20 14:00:18
【问题描述】:

我有两个要合并在一起的 data.frames。第一个是:

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

第二个:

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')

我希望合并后的值与年份匹配,然后填写该年每一天的其余值。

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

【问题讨论】:

标签: r dataframe


【解决方案1】:

到目前为止,已经发布了三种不同的方法:

Frank 在聊天中提出了第四种方法,称为 update join

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

结果证明这是四个中最快和最简洁的。

基准测试结果:

为了确定哪种方法在速度方面最有效,我使用microbenchmarkpackage 设置了一个基准。

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

由于sample 已修改,因此必须在每次运行基准测试之前重新创建它。这是由基准中包含的一个函数完成的(create data)。 创建数据的时间需要从其他时间中减去。

所以,即使对于大约 1800 行的小数据集,update join 也是最快的,几乎是第二次 merge 的两倍,其次是 matchdplyr 排在最后,比 update join 慢 4 倍以上(减去 create data 的时间)。

基准代码

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 = i.y, Z = i.Z)]
  }
)

【讨论】:

  • @Frank 非常感谢您抽出宝贵时间帮助改进基准代码并验证结果。
【解决方案2】:

您可以使用match

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

【讨论】:

    【解决方案3】:

    如果yZsample 中始终为零,则您不需要它们,因此您只需像这样加入一年:

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

    【讨论】:

      【解决方案4】:

      有什么反对在您的新 df 中使用带有年份的专栏吗?如果没有,您可以在“样本”中生成一个并使用合并功能

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

      这将导致如下结果:

      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
      

      如果您感到困扰,您可以再次删除年份列。

      【讨论】:

        猜你喜欢
        • 2020-10-28
        • 1970-01-01
        • 1970-01-01
        • 2021-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多