将数据 table 中的行熔化或复制一定次数并在 R 中包含计数器

Melt or Replicate rows in a data table a certain number of times and include counter in R

我想 "expand" 一个数据框,将某些列上的信息复制到第五列指示的次数。

用 R 完成这个任务的效率最高的是什么? (对数据开放 Table 或 Dplyer,重塑解决方案)。

原文Dataframe/DataTable:

   f_1 f_2        d_1        d_2 i_1
1:   1   A 2016-01-01       <NA>  NA
2:   2   A 2016-01-02       <NA>  NA
3:   2   B 2016-01-03 2016-01-01   2
4:   3   C 2016-01-04       <NA>  NA
5:   4   D 2016-01-05 2016-01-02   5

想要Dataframe/DataTable

    f_1 f_2        d_1        d_2 i_1
 1:   1   A 2016-01-01       <NA>  NA
 2:   2   A 2016-01-02       <NA>  NA
 3:   2   B 2016-01-03 2016-01-01   1
 4:   2   B 2016-01-03 2016-01-01   2
 5:   3   C 2016-01-04       <NA>  NA
 6:   4   D 2016-01-05 2016-01-02   1
 7:   4   D 2016-01-05 2016-01-02   2
 8:   4   D 2016-01-05 2016-01-02   3
 9:   4   D 2016-01-05 2016-01-02   4
10:   4   D 2016-01-05 2016-01-02   5

可重现的数据:

DT <- data.table(
  f_1 = factor(c(1,2,2,3,4)),
  f_2 = factor(c("A", "A", "B", "C", "D")),
  d_1 = as.Date(c("2016-01-01","2016-01-02","2016-01-03","2016-01-04","2016-01-05")),
  d_2 = as.Date(c(NA,NA,"2016-01-01",NA,"2016-01-02")),
  i_1 = as.integer(c(NA,NA,2,NA,5)))

谢谢,如有重复请见谅。我正在为这种重塑练习而苦恼。

这是一个data.table解决方案。基本上,按要复制的那些列分组,并使用 i_1

中的数字生成整数序列
DT[, .(i_1=if(!is.na(i_1)) seq_len(i_1) else i_1), 
    by=c(names(DT)[-ncol(DT)])]

输出:

    f_1 f_2        d_1        d_2 i_1
 1:   1   A 2016-01-01       <NA>  NA
 2:   2   A 2016-01-02       <NA>  NA
 3:   2   B 2016-01-03 2016-01-01   1
 4:   2   B 2016-01-03 2016-01-01   2
 5:   3   C 2016-01-04       <NA>  NA
 6:   4   D 2016-01-05 2016-01-02   1
 7:   4   D 2016-01-05 2016-01-02   2
 8:   4   D 2016-01-05 2016-01-02   3
 9:   4   D 2016-01-05 2016-01-02   4
10:   4   D 2016-01-05 2016-01-02   5

或使用 data.table 的另一种方式。对于每一行,使用 i_1 创建一个数字序列,并使用 c(.SD[, -"i_1], ..... 将原始数据添加到该序列,最后删除 by

DT[, c(.SD[, -"i_1"], .(i_1=if (!is.na(i_1)) seq_len(i_1) else i_1)), 
    by=seq_len(DT[,.N])][,-1L]

NA 时,您可以将 i_1 替换为 1 吗?如果是这样,下面的内容会稍微更具可读性:

首先,将行重复指定的次数(临时 考虑 i_1 的缺失值,使用 replace @Frank 提供):

DT_out = DT[rep(1:.N, replace(i_1, is.na(i_1), 1L))]

如果我们已经替换了 DT[is.na(i_1), i_1 := 1L],这可能只是 DT[rep(1:.N, i_1)]

剩下的就是更新 i_1 的值。根据您的数据的详细信息,有更简单的版本。这里我认为是更通用的版本:

DT_out[!is.na(i_1), i_1 := rowidv(.SD), .SDcols = !'i_1'][]
#     f_1 f_2        d_1        d_2 i_1
#  1:   1   A 2016-01-01       <NA>  NA
#  2:   2   A 2016-01-02       <NA>  NA
#  3:   2   B 2016-01-03 2016-01-01   1
#  4:   2   B 2016-01-03 2016-01-01   2
#  5:   3   C 2016-01-04       <NA>  NA
#  6:   4   D 2016-01-05 2016-01-02   1
#  7:   4   D 2016-01-05 2016-01-02   2
#  8:   4   D 2016-01-05 2016-01-02   3
#  9:   4   D 2016-01-05 2016-01-02   4
# 10:   4   D 2016-01-05 2016-01-02   5

rowidrowidv 给出由传递的变量定义的组内的行号。您可以与 rowid(f_2)rowid(f_1)rowid(f_1, f_2) 进行比较,以了解我的意思。 rowidv(.SD)rowid(f_1, f_2, d_1, d_2) 的 shorthand,因为我们从 .SD.

的列中排除了 i_1