首页 > 解决方案 > 使用 ifelse 和 group by 改变 data.table 中的列

问题描述

我有一些 dplyr 代码要移至 data.table,这是我刚刚遇到的问题。如果大于或等于,我希望b存储在列中的从一行到下一行的差异。但是运行此代码后:ca3

df = data.frame(a = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3), 
                b = c(0, 1, 0, 1, 0, 1, 1, 0, 3, 4, 5))

setDT(df)
df[ , c := ifelse(a >= 3, c(0, diff(b)), b), by = .(a)]

里面的所有元素c都是0。这是为什么呢?

df 
    a b c
 1: 1 0 0
 2: 1 1 0
 3: 1 0 0
 4: 1 1 0
 5: 2 0 0
 6: 2 1 0
 7: 2 1 0
 8: 3 0 0
 9: 3 3 0
10: 3 4 0
11: 3 5 0

我认为是等效的 dplyr:

df = data.frame(a = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3), 
                b = c(0, 1, 0, 1, 0, 1, 1, 0, 3, 4, 5))

df %>% 
      group_by(a) %>% 
      mutate(c = ifelse( a >= 3, c(0, diff(b)), b))

标签: rdplyrdata.tablegrouping

解决方案


从 for 的帮助中ifelse(test, yes, no),它应该返回...

与来自 yes 或 no 值的测试和数据值具有相同长度和属性(包括维度和“类”)的向量。答案的模式将被强制从逻辑中获取,以首先容纳取自 yes 的任何值,然后容纳取自 no 的任何值。

然而:

> df %>% group_by(a) %>% do(print(.$a))
[1] 1 1 1 1
[1] 2 2 2
[1] 3 3 3 3
> data.table(df)[, print(a), by=a]
[1] 1
[1] 2
[1] 3

如帮助页面中所述,由于第一个参数的长度为 1,如果您为其他部分传递向量,则仅使用它们的第一个元素:

> ifelse(TRUE, 1:10, eleventy + million)
[1] 1

您可能应该if ... else ...在使用常量值时使用,例如...

> data.table(df)[, b := if (a >= 3) c(0, diff(b)) else b, by=a]

甚至更好,在这种情况下,您可以分配给一个子集:

> data.table(df)[a >= 3, b := c(0, diff(b)), by=a]

关于为什么adata.table 习语的长度为 1,请参阅其常见问题解答问题“在每个组内,为什么组变量长度为 1?”


推荐阅读