在 data.table `by` 中使用 `fifelse`,其中测试列是 `by` 的一部分
Using `fifelse` within data.table `by`, where test column is part of `by`
我想向 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)]
这会导致以下错误
Error in fifelse(clk != 0, seq_len(.N), NA_integer_) :
Length of 'yes' is 2 but must be 1 or length of 'test' (1).
我期望得到的结果可以通过下面的代码实现
dt[, seq_2 := seq_len(.N), by = .(id, clk)][
, seq_clk := fifelse(clk != 0, seq_2, NA_integer_)][
, seq_2 := NULL]
这给出了
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
虽然上面的代码有效,但我不明白为什么第一个例子中的单行代码不起作用。问题似乎在于将 fifelse
应用于 by
子句中列出的列。它适用于不在 by
.
中的列
我还注意到在这种情况下其他功能无法正常工作。例如:
dt[, sum_id_by_clk := fifelse(clk != 0, sum(id), NA_integer_), by = .(id, clk)]
没有给出错误,但产生了不正确的结果:
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
我希望最后一列中的值对于第 1-2 行为 2,对于第 4-5 行为 4。
我在这里错过了什么?
我们可能会更改分组列的名称
dt[, seq_clk := fifelse(clk != 0, seq_len(.N), NA_integer_),
by = .(id, clk2 = clk)]
-输出
> dt
id clk val seq_clk
<num> <num> <char> <int>
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
或rep
test
部分
dt[, seq_clk := fifelse(rep(clk, .N) != 0, seq_len(.N),
NA_integer_), by = .(id, clk)]
>
> dt
id clk val seq_clk
<num> <num> <char> <int>
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
有可能分组列在条件中使用时,只使用第一个元素
> dt[, length(clk) , clk]
clk V1
<num> <int>
1: 1 1
2: 0 1
3: 2 1
4: 5 1
而通过更改分组列
> dt[, length(clk) , .(clk2 = clk)]
clk2 V1
<num> <int>
1: 1 2
2: 0 1
3: 2 2
4: 5 1
和 fifelse/ifelse
都要求所有参数的长度相同(尽管 NA_integer_
的长度为 1 - 回收,最好也复制它)
如果我理解正确,OP 想要对每个 id, clk
组中的列进行编号,同时跳过 clk == 0
的行
为此,可以方便地使用data.table的rowid()
函数:
dt[clk != 0, seq_clk := rowid(id, clk)][]
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?
大声朗读的方法是:“取 DT
,子集行 i
, 然后 计算 j
分组by
.
因此,通常出现在 by
中的变量并不意味着 可以在 j
表达式中使用。 特殊符号 .SD
的解释进一步强调了这一点(参见?.SD
):
.SD
is a data.table
containing the Subset of x
's Data for each group,
excluding any columns used in by
(or keyby
).
这可以通过
验证
dt[clk != 0, print(.SD), by = .(id, clk)][]
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)][]
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)
returns 仅 id
.
我想向 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)]
这会导致以下错误
Error in fifelse(clk != 0, seq_len(.N), NA_integer_) : Length of 'yes' is 2 but must be 1 or length of 'test' (1).
我期望得到的结果可以通过下面的代码实现
dt[, seq_2 := seq_len(.N), by = .(id, clk)][
, seq_clk := fifelse(clk != 0, seq_2, NA_integer_)][
, seq_2 := NULL]
这给出了
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
虽然上面的代码有效,但我不明白为什么第一个例子中的单行代码不起作用。问题似乎在于将 fifelse
应用于 by
子句中列出的列。它适用于不在 by
.
我还注意到在这种情况下其他功能无法正常工作。例如:
dt[, sum_id_by_clk := fifelse(clk != 0, sum(id), NA_integer_), by = .(id, clk)]
没有给出错误,但产生了不正确的结果:
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
我希望最后一列中的值对于第 1-2 行为 2,对于第 4-5 行为 4。
我在这里错过了什么?
我们可能会更改分组列的名称
dt[, seq_clk := fifelse(clk != 0, seq_len(.N), NA_integer_),
by = .(id, clk2 = clk)]
-输出
> dt
id clk val seq_clk
<num> <num> <char> <int>
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
或rep
test
部分
dt[, seq_clk := fifelse(rep(clk, .N) != 0, seq_len(.N),
NA_integer_), by = .(id, clk)]
>
> dt
id clk val seq_clk
<num> <num> <char> <int>
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
有可能分组列在条件中使用时,只使用第一个元素
> dt[, length(clk) , clk]
clk V1
<num> <int>
1: 1 1
2: 0 1
3: 2 1
4: 5 1
而通过更改分组列
> dt[, length(clk) , .(clk2 = clk)]
clk2 V1
<num> <int>
1: 1 2
2: 0 1
3: 2 2
4: 5 1
和 fifelse/ifelse
都要求所有参数的长度相同(尽管 NA_integer_
的长度为 1 - 回收,最好也复制它)
如果我理解正确,OP 想要对每个 id, clk
组中的列进行编号,同时跳过 clk == 0
为此,可以方便地使用data.table的rowid()
函数:
dt[clk != 0, seq_clk := rowid(id, clk)][]
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?
大声朗读的方法是:“取 DT
,子集行 i
, 然后 计算 j
分组by
.
因此,通常出现在 by
中的变量并不意味着 可以在 j
表达式中使用。 特殊符号 .SD
的解释进一步强调了这一点(参见?.SD
):
.SD
is adata.table
containing the Subset ofx
's Data for each group, excluding any columns used inby
(orkeyby
).
这可以通过
验证dt[clk != 0, print(.SD), by = .(id, clk)][]
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)][]
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)
returns 仅 id
.