将函数概括为向量格式 data.table

Generalise a function to vector format data.table

我有以下数据结构,我想在其中逐行插入数据直到某一年:

require('data.table')
test_dt <- data.table(iso1 = c('BTN', 'IND', 'BGD'),
 iso2 = c('AFG', 'AFG', 'AFG'),
 year = c(2006, 2003, 2006))

我想出了以下函数,它适用于单行情况,但不适用于一般情况:

interpolate_rows <- function(dt, stop_year = 2008)  {
  
    year <- as.integer(dt[, .SD, .SDcols = 'year'])
    
    # If year is less than stop year, fill in observations:
    if (year < stop_year) {
      time_delta <- seq(year, stop_year)
            
      # Explode bilateral country observation:
      dt <- dt[rep(dt[, .I], length(time_delta))]
      
      # Replace year column w/ time_delta sequence:
      dt <- dt[, year := time_delta]
      }
    
  return(dt)
}
## Output
bar <- interpolate_rows(test_dt[1])

bar
  iso1  iso2   year
 1:  BTN    AFG    2006
 2:  BTN    AFG    2007
 3:  BTN    AFG    2008

我想要的是:

bar <- interpolate_rows(test_dt)

bar
  iso1  iso2   year
 1:  BTN    AFG    2006
 2:  BTN    AFG    2007
 3:  BTN    AFG    2008
 6:  IND    AFG    2003
 7:  IND    AFG    2004
 8:  IND    AFG    2005
 9:  IND    AFG    2006
 10:  IND    AFG    2007
 11:  IND    AFG    2008
 14:  BGD    AFG    2006
 14:  BGD    AFG    2007
 14:  BGD    AFG    2008

我知道罪魁祸首很可能是这条线 year <- as.integer(dt[, .SD, .SDcols = 'year']),但我不知道如何用它代替工作向量解决方案。我试图在 interpolate_rows() 中嵌套一个 lapply() 函数来提取每个独特组的年份并尝试使用 Map(),但是其中 none 产生了可行的解决方案。

任何帮助我找到可行的矢量解决方案的人,将不胜感激。

使用 dplyrtidyr 库的一种方式。

library(dplyr)
library(tidyr)

interpolate_rows <- function(dt, stop_year = 2008)  {
  dt %>%
    group_by(iso1, iso2) %>%
    complete(year = year : stop_year) %>%
    ungroup
}

interpolate_rows(test_dt)

#  iso1  iso2   year
#   <chr> <chr> <dbl>
# 1 BGD   AFG    2006
# 2 BGD   AFG    2007
# 3 BGD   AFG    2008
# 4 BTN   AFG    2006
# 5 BTN   AFG    2007
# 6 BTN   AFG    2008
# 7 IND   AFG    2003
# 8 IND   AFG    2004
# 9 IND   AFG    2005
#10 IND   AFG    2006
#11 IND   AFG    2007
#12 IND   AFG    2008

另一种方式-

library(data.table)

interpolate_rows <- function(dt, stop_year = 2008)  {
  vals <- seq(dt$year, stop_year)
  dt[rep(1, length(vals))][, year := vals]
}

rbindlist(by(test_dt, seq(nrow(test_dt)), interpolate_rows))

如果只使用 by:

test_dt[, .(year = min(year):stop_year), by = .(iso1, iso2)]

#     iso1 iso2 year
#  1:  BTN  AFG 2006
#  2:  BTN  AFG 2007
#  3:  BTN  AFG 2008
#  4:  IND  AFG 2003
#  5:  IND  AFG 2004
#  6:  IND  AFG 2005
#  7:  IND  AFG 2006
#  8:  IND  AFG 2007
#  9:  IND  AFG 2008
# 10:  BGD  AFG 2006
# 11:  BGD  AFG 2007
# 12:  BGD  AFG 2008