我想向 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。
我在这里缺少什么?
如果我理解正确的话,OP 想要对每个id, clk组中的列进行编号,同时跳过其中的行clk == 0
为此,rowid()可以方便地使用 data.table 的函数:
dt[clk != 0, seq_clk := rowid(id, clk)][]
Run Code Online (Sandbox Code Playgroud)
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
第 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)
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
分组变量可以通过特殊符号 访问.BY,例如,
dt[clk != 0, str(.BY), by = .(id, clk)][]
Run Code Online (Sandbox Code Playgroud)
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
因此,每个分组变量对于每个组仅存在一次,这解释了为什么sum(id)仅返回id。