Построение «неявного» фильтра с помощью dplyr

#r #dplyr #data.table

#r #дплыр #данные.таблица

Вопрос:

Я ищу способ создания «неявного» фильтра, который в dplyr выполняет то же самое, что и приведенный ниже код, используя data.table.

 library(data.table)

df_test = data.frame(Idx = c(1, 2, 3, 4, 5, 6, 7, 9),
                     Cond = c(T, T, F, T, T,F, F, T),
                     Val = c(T, T, F, T, T, F, T, T))

setDT(df_test)

df_test[Cond == TRUE, Res := cumsum(Val)]
  

Спасибо за вашу помощь
Лучший Александр

Комментарии:

1. Вы имеете в виду, что скользящая функция будет применена к определенному столбцу после применения фильтра в другом столбце

2. library(tidyverse) df_test %>% filter(Cond == T) %>% mutate(res = cumsum(Val))

Ответ №1:

Вопрос используется data.table для условного вычисления совокупной суммы на основе значений Cond столбца.

Приведенный минимальный пример генерирует следующий результат:

 > df_test[Cond == TRUE, Res := cumsum(Val)]
> df_test
   Idx  Cond   Val Res
1:   1  TRUE  TRUE   1
2:   2  TRUE  TRUE   2
3:   3 FALSE FALSE  NA
4:   4  TRUE  TRUE   3
5:   5  TRUE  TRUE   4
6:   6 FALSE FALSE  NA
7:   7 FALSE  TRUE  NA
8:   9  TRUE  TRUE   5
  

Обратите внимание, что значение Res в строке 8 равно 5.

К сожалению, это нелегко воспроизвести, dplyr потому cumsum() что функция не работает условно внутри if_else() функции.

 library(dplyr)
df_test = data.frame(Idx = c(1, 2, 3, 4, 5, 6, 7, 9),
                     Cond = c(T, T, F, T, T,F, F, T),
                     Val = c(T, T, F, T, T, F, T, T))
                   
df_test %>% mutate(Res = if_else(Cond, cumsum(Val), NA_integer_))
  

…выдает значение 6 в строке 8 фрейма выходных данных.

 > df_test %>% mutate(Res = if_else(Cond, cumsum(Val), NA_integer_))
  Idx  Cond   Val Res
1   1  TRUE  TRUE   1
2   2  TRUE  TRUE   2
3   3 FALSE FALSE  NA
4   4  TRUE  TRUE   3
5   5  TRUE  TRUE   4
6   6 FALSE FALSE  NA
7   7 FALSE  TRUE  NA
8   9  TRUE  TRUE   6
  

Чтобы исправить это, необходимо отфильтровать ИСТИННЫЕ значения от FALSE, вычислить совокупную сумму только для истинных строк и объединить ложные значения обратно в результат.

 df_test %>% 
     filter(Cond == TRUE) %>% 
     mutate(Res = cumsum(Val)) -> df_true 
df_test %>% filter(Cond == FALSE) %>%
     bind_rows(.,df_true) %>% arrange(Idx)
  

… и вывод:

   Idx  Cond   Val Res
1   1  TRUE  TRUE   1
2   2  TRUE  TRUE   2
3   3 FALSE FALSE  NA
4   4  TRUE  TRUE   3
5   5  TRUE  TRUE   4
6   6 FALSE FALSE  NA
7   7 FALSE  TRUE  NA
8   9  TRUE  TRUE   5
  

Ответ №2:

В dplyr семантике:

 df_test %>% mutate(Res = if_else(!Cond, NA_integer_, cumsum(if_else(Cond, as.integer(Val), 0L))))
  

Комментарии:

1. Этот синтаксис не дает совпадающих результатов data.table . Последняя строка в data.table выходных данных имеет значение Res = 5, тогда как в dplyr нем равно 6. Следовательно, cumsum() функция в dplyr не применяется условно на основе входных данных.

2. привет, к сожалению, результаты не совпадают. ваше последнее значение равно 6, тогда как оно должно быть 5

3. @LenGreski Спасибо, что указали на это. Я исправил свой ответ

Ответ №3:

Отвечает ли это на ваш запрос:

 > df_test = data.frame(Idx = c(1, 2, 3, 4, 5, 6, 7, 9),
                       Cond = c(T, T, F, T, T,F, F, T),
                       Val = c(T, T, F, T, T, F, T, T))
> df_test
  Idx  Cond   Val
1   1  TRUE  TRUE
2   2  TRUE  TRUE
3   3 FALSE FALSE
4   4  TRUE  TRUE
5   5  TRUE  TRUE
6   6 FALSE FALSE
7   7 FALSE  TRUE
8   9  TRUE  TRUE
> df_test %>% mutate(Res = if_else(Cond, cumsum(Cond), NA_integer_))
  Idx  Cond   Val Res
1   1  TRUE  TRUE   1
2   2  TRUE  TRUE   2
3   3 FALSE FALSE  NA
4   4  TRUE  TRUE   3
5   5  TRUE  TRUE   4
6   6 FALSE FALSE  NA
7   7 FALSE  TRUE  NA
8   9  TRUE  TRUE   5
> 
  

или

 > df_test %>% filter(Cond == TRUE) %>% mutate(Res = cumsum(Val)) %>% right_join(df_test) %>% arrange(Idx)
Joining, by = c("Idx", "Cond", "Val")
  Idx  Cond   Val Res
1   1  TRUE  TRUE   1
2   2  TRUE  TRUE   2
3   3 FALSE FALSE  NA
4   4  TRUE  TRUE   3
5   5  TRUE  TRUE   4
6   6 FALSE FALSE  NA
7   7 FALSE  TRUE  NA
8   9  TRUE  TRUE   5
> 
  

Комментарии:

1. К сожалению, нет. Вы подводите итог Cond. По совпадению это дает правильный результат. Но на основе Cond столбцы Val должны суммироваться при значении TRUE

2. Как насчет моего второго кода? Я суммирую, используя столбец Val.

Ответ №4:

Кажется, я сам нашел решение. Комментарии приветствуются, если вы обнаружите какие-либо недостатки…

 library(dplyr)

df_test %>%
  group_by(Cond) %>%
  mutate(Res = ifelse(Cond == 1, cumsum(Val), NA)) %>%
  ungroup()