Избегайте написания длинного оператора if-else в R - PullRequest
0 голосов
/ 25 января 2019

Я столкнулся с ситуацией, когда у меня есть такие данные:

df <- data.frame(id = 1:1000, 
                   x = sample(0:30, 1000, replace = T), 
                   y = sample(50:10000, 1000, replace = T))

Я хочу назначить другой столбец с именем z на основе нескольких условий, например

if x <= 5 & y <= 100, z = 1
if x > 5 & x <= 10 & y <= 100, z = 2
if x > 10 & x <= 12 & y <= 100, z = 3
if x > 12 & x <= 20 &  y <= 100, z = 4
if x > 20 & x <= 30 &  y <= 100, z = 5
if x <= 5 & y > 100 & y <= 1000, z = 6
if x > 5 & x <= 10 & y > 100 & y <= 1000 z = 7
if x > 10 & x <= 12 & y > 100 & y <= 1000, z = 8
if x > 12 & x <= 20 & y > 100 & y <= 1000, z = 9
if x > 20 & x <= 30 & y > 100 & y <= 1000, z = 10
.
.
.

and so. I hope you get the drift.

Очевидное решение для меня - это написать длинную инструкцию ifelse примерно так:

df %>% mutate(z = ifelse(x <= 5 & y <= 100, 1, 
                  ifelse(x > 5 & x <= 10 & y <= 100, 2,
                  ifelse(x > 10 & x <= 12 & y <= 100, 3))),
          ........... and son on)

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

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Если в операторах if else есть шаблон, мы можем заранее создать набор выражений и использовать !!!, чтобы снять их и разделить на аргументы в case_when:

x_gt_cond <- rep(c(-Inf, 5, 10, 12, 20), 2)
x_le_cond <- rep(c(5, 10, 12, 20 ,30), 2)
y_gt_cond <- rep(c(-Inf, 100), each = 5)
y_le_cond <- rep(c(100, 1000), each = 5)
z <- 1:10
cases <- paste("x > ", x_gt_cond, "& x <= ", x_le_cond, 
               "& y > ", y_gt_cond, "& y <= ", y_le_cond, "~ ", z)

library(dplyr)
library(rlang)
df %>%
  mutate(z = case_when(!!!parse_exprs(cases)))

Хитрость заключается в том, чтобы использовать -Inf и Inf для нижней и верхней границ, чтобы у вас были сбалансированные условия для x и y. Что элегантно в этом решении, так это то, что вы можете добавлять больше условий, просто изменяя векторы _cond.

Выход:

> cases
 [1] "x >  -Inf & x <=  5 & y >  -Inf & y <=  100 ~  1"
 [2] "x >  5 & x <=  10 & y >  -Inf & y <=  100 ~  2"  
 [3] "x >  10 & x <=  12 & y >  -Inf & y <=  100 ~  3" 
 [4] "x >  12 & x <=  20 & y >  -Inf & y <=  100 ~  4" 
 [5] "x >  20 & x <=  30 & y >  -Inf & y <=  100 ~  5" 
 [6] "x >  -Inf & x <=  5 & y >  100 & y <=  1000 ~  6"
 [7] "x >  5 & x <=  10 & y >  100 & y <=  1000 ~  7"  
 [8] "x >  10 & x <=  12 & y >  100 & y <=  1000 ~  8" 
 [9] "x >  12 & x <=  20 & y >  100 & y <=  1000 ~  9" 
[10] "x >  20 & x <=  30 & y >  100 & y <=  1000 ~  10"

       id  x    y  z
1       1 13 8440 NA
2       2  3 1467 NA
3       3  5 2699 NA
4       4 24 5286 NA
5       5  5 2378 NA
6       6 16  268  9
7       7 19 2910 NA
8       8 19  706  9
9       9 24 6212 NA
10     10  7 6026 NA
...
0 голосов
/ 25 января 2019

Похоже, что функция case_when в dplyr - это то, что вы ищете.В вашем случае это может выглядеть примерно так:

df %>% mutate(z = case_when(
   x <= 5 & y <= 100 ~ 1,
   x > 5 & x <= 10 & y <= 100 ~ 2,
   x > 10 & x <=12 & y <= 100 ~ 3
  )
 )

edit: Изменен ответ, чтобы отразить, что case_when находится в пакете dplyr.Спасибо за комментарии ниже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...