Изменять столбец в data.table с помощью ifelse и группировать по - PullRequest
2 голосов
/ 12 июня 2019

У меня есть некоторый код dplyr, который я перехожу в data.table, с этой проблемой я только что столкнулся. Я хочу, чтобы разница от одной строки к следующей в b сохранялась в столбце c, если a больше или равно 3. Однако после запуска этого кода:

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))

Ответы [ 2 ]

3 голосов
/ 12 июня 2019

Из справки для ifelse(test, 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

Как объяснено на страницах справки, поскольку первый аргумент имеет длину один, если вы передаете векторы для других частей, используется только их первый элемент:

> 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]

Относительно того, почему a имеет длину 1 для идиомы data.table, см. Вопрос с часто задаваемыми вопросами «Внутри каждой группы, почему групповые переменные длина-1?»

1 голос
/ 12 июня 2019

Я создаю набор данных, который имеет ненулевые значения для b в качестве первого элемента каждой группы на a, чтобы проиллюстрировать лучше.В вашем предыдущем наборе данных были все нули, а также c(0,diff(b)) начинался с нуля, поэтому его было трудно дифференцировать.

В данном случае получается, что ifelse представляет собой вектор длины 1.

library(data.table)

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

Смотрите ниже:

setDT(df)[ , c := ifelse(a >= 3, c(0, diff(b)), b), by = .(a)][]
#>     a  b  c
#>  1: 1 10 10
#>  2: 1  1 10
#>  3: 1  0 10
#>  4: 1  1 10
#>  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

Теперь давайте посмотрим на некоторые другие примеры;здесь я использую простой вектор длины 4 (вместо c(0,diff(b))):

setDT(df)[ , c := ifelse(a >= 3L, c(20,2,3,4), -999), by=a][]
#>     a  b    c
#>  1: 1 10 -999
#>  2: 1  1 -999
#>  3: 1  0 -999
#>  4: 1  1 -999
#>  5: 2  0 -999
#>  6: 2  1 -999
#>  7: 2  1 -999
#>  8: 3  0   20
#>  9: 3  3   20
#> 10: 3  4   20 
#> 11: 3  5   20

Вы видите, что первый элемент все еще присваивается всем строкам c для этой группы a.

Обходное решение использует diff на a, чтобы увидеть, когда оно не меняется ( т.е. diff(a)==0), и использовать это в качестве псевдогруппы вместе сдругое состояние;как показано ниже:

setDT(df)[, c := ifelse(a >= 3 & c(F,diff(a)==0), c(0,diff(b)), b)][]
#>     a  b  c
#>  1: 1 10 10
#>  2: 1  1  1
#>  3: 1  0  0
#>  4: 1  1  1
#>  5: 2  0  0
#>  6: 2  1  1
#>  7: 2  1  1
#>  8: 3  0  0
#>  9: 3  3  3
#> 10: 3  4  1
#> 11: 3  5  1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...