在 data.table `by` 中使用 `fifelse`,其中测试列是 `by` 的一部分

ami*_*itr 3 r data.table

我想向 a 添加一列,data.table该列是使用 定义的组内的序列by,但对子句中使用的列之一使用条件by。我尝试使用fifelse以下示例:

dt <- data.table::data.table(
  id = c(1, 1, 2, 2, 2, 3),
  clk = c(1, 1, 0, 2, 2, 5),
  val = LETTERS[1:6]
)

dt[, seq_clk := fifelse(clk != 0, seq_len(.N), NA_integer_), by = .(id, clk)]
Run Code Online (Sandbox Code Playgroud)

这会导致以下错误

fifelse(clk != 0, seq_len(.N), NA_integer_) 中的错误:“yes”的长度为 2,但必须为 1 或“test”的长度 (1)。

我期望得到的结果可以通过以下代码实现

dt[, seq_2 := seq_len(.N), by = .(id, clk)][
, seq_clk := fifelse(clk != 0, seq_2, NA_integer_)][
  , seq_2 := NULL]
Run Code Online (Sandbox Code Playgroud)

这使

   id clk val seq_clk
1:  1   1   A       1
2:  1   1   B       2
3:  2   0   C      NA
4:  2   2   D       1
5:  2   2   E       2
6:  3   5   F       1
Run Code Online (Sandbox Code Playgroud)

尽管上面的代码有效,但我不明白为什么第一个示例中的单行代码不起作用。问题似乎在于应用于fifelse子句中列出的列by。它适用于不在by.

我还注意到在这种情况下其他功能无法按我的预期工作。例如:

dt[, sum_id_by_clk := fifelse(clk != 0, sum(id), NA_integer_), by = .(id, clk)]
Run Code Online (Sandbox Code Playgroud)

不会给出错误,但会产生错误的结果:

   id clk val sum_id_by_clk
1:  1   1   A             1
2:  1   1   B             1
3:  2   0   C            NA
4:  2   2   D             2
5:  2   2   E             2
6:  3   5   F             3
Run Code Online (Sandbox Code Playgroud)

我预计最后一列中第 1-2 行的值为 2,第 4-5 行的值为 4。

我在这里缺少什么?

Uwe*_*Uwe 7

如果我理解正确的话,OP 想要对每个id, clk组中的列进行编号,同时跳过其中的行clk == 0

为此,rowid()可以方便地使用 data.table 的函数:

dt[clk != 0, seq_clk := rowid(id, clk)][]
Run Code Online (Sandbox Code Playgroud)
      id   clk    val seq_clk
1:     1     1      A       1
2:     1     1      B       2
3:     2     0      C      NA
4:     2     2      D       1
5:     2     2      E       2
6:     3     5      F       1
Run Code Online (Sandbox Code Playgroud)

第 3 行通过引用从更新中排除,并NA默认获取。


OP 想知道为什么分组变量的计算没有给出预期的结果。这是一个解释的尝试。

根据?data.table,data.table 语法的一般形式为:

DT[ i,  j,  by ] # + extra arguments
    |   |   |
    |   |    -------> grouped by what?
    |    -------> what to do?
     ---> on which rows?
Run Code Online (Sandbox Code Playgroud)

大声读出这一点的方法是:“将DT, 子集行按i,然后计算j按 分组by

因此,通常出现在 中的变量by 并不意味着在表达式中使用j特殊符号的解释进一步强调了这一点 .SD(参见?.SD):

.SD是一个包含data.tableD的子x,不包括by( 或keyby任何列。

这可以通过以下方式验证

dt[clk != 0, print(.SD), by = .(id, clk)][]
Run Code Online (Sandbox Code Playgroud)
      val seq_clk
1:      A       1
2:      B       2
      val seq_clk
1:      D       1
2:      E       2
      val seq_clk
1:      F       1
Empty data.table (0 rows and 2 cols): id,clk
Run Code Online (Sandbox Code Playgroud)

分组变量可以通过特殊符号 访问.BY,例如,

dt[clk != 0, str(.BY), by = .(id, clk)][]
Run Code Online (Sandbox Code Playgroud)
List of 2
 $ id : num 1
 $ clk: num 1
List of 2
 $ id : num 2
 $ clk: num 2
List of 2
 $ id : num 3
 $ clk: num 5
Empty data.table (0 rows and 2 cols): id,clk
Run Code Online (Sandbox Code Playgroud)

因此,每个分组变量对于每个组仅存在一次,这解释了为什么sum(id)仅返回id