Dplyr ifelse(), if_else(), case_when(): Betingelser

Det er umuligt at arbejde med dataanalyse uden at bruge betingelser til at oprette eller ændre variabler. De mest anvendte funktioner til betingelser er ifelse() (fra base R), if_else() (fra dplyr) og case_when() (fra dplyr).

ifelse()

Med ifelse() fra base defineres en betingelse med følgende syntaks:

ifelse(VILLKORET, OM_SANT, OM_FALSKT)

  • På første position (VILLKORET) angives betingelsen.
  • På anden position (OM_SANT) angives, hvad funktionen skal returnere, hvis betingelsen er sand.
  • På tredje position (OM_FALSKT) angives, hvad funktionen skal returnere, hvis betingelsen er falsk.

Vi bruger nu denne funktion til at oprette en ny variabel kaldet ny_variabel. Denne variabel får værdien 0, hvis Var er mindre end 5, og ellers 1:

my_data |>
  mutate(ny_variabel = ifelse(Var1 < 5, 0, 1))
   Var1  Var2 Var3    Var4 Var5  ny_variabel
     NA     1 Ja     0.276 a              NA
      5    NA Nej    1.15  b               1
      9     3 NA     0.162 c               1
      5     6 Ja    -0.517 d               1
      3     1 Ja     1.36  e               0
      4    12 Ja    -0.454 f               0
     10     0 Ja    -0.445 g               1
     20    12 Nej    0.394 h               1
     30     3 NA    -0.642 i               1

Det fungerede, bortset fra at den første observation fik NA (missing) på ny_variabel. Forklaringen på dette er enkel: den observation mangler information om Var1, og så kan betingelsen ikke evalueres. Vi kan derimod angive i betingelsen, hvordan NA skal håndteres. I næste eksempel udvider vi betingelsen, så også missing (NA) får værdien 0. Det gør vi ved at bruge funktionen is.na() og operatoren |, som betyder "eller".

my_data |>
  mutate(ny_variabel = ifelse(Var1 < 5 | is.na(Var1), 0, 1))
   Var1  Var2 Var3    Var4 Var5  ny_variabel
1    NA     1 Ja     0.276 a               0
2     5    NA Nej    1.15  b               1
3     9     3 NA     0.162 c               1
4     5     6 Ja    -0.517 d               1
5     3     1 Ja     1.36  e               0
6     4    12 Ja    -0.454 f               0
7    10     0 Ja    -0.445 g               1
8    20    12 Nej    0.394 h               1
9    30     3 NA    -0.642 i               1

ifelse() kan blive besværlig, hvis flere betingelser skal evalueres sekventielt, hvilket er meget almindeligt. For at gøre dette med ifelse() skal det tredje argument erstattes med en ny ifelse(). Et eksempel på dette følger:

my_data |>
  mutate(ny_variabel = ifelse(Var1 < 4, "Mindre än 4",
                       ifelse(Var1 < 7, "Mindre än 7",
                       ifelse(Var1 < 10, "Mindre än 10", "Övriga"))))
   Var1  Var2 Var3    Var4 Var5  ny_variabel
1    NA     1 Ja     0.276 a     NA
2     5    NA Nej    1.15  b     Mindre än 7
3     9     3 NA     0.162 c     Mindre än 10
4     5     6 Ja    -0.517 d     Mindre än 7
5     3     1 Ja     1.36  e     Mindre än 4
6     4    12 Ja    -0.454 f     Mindre än 7
7    10     0 Ja    -0.445 g     Övriga
8    20    12 Nej    0.394 h     Övriga
9    30     3 NA    -0.642 i     Övriga

Bemærk, at det sidste argument (ovenfor angivet som "Övriga") er den værdi, der skal returneres, hvis ingen af betingelserne er opfyldt. Bemærk også, at alle parenteser skal lukkes helt til sidst.

if_else()

I dplyr findes funktionen if_else(), som ligner ifelse(). Forskellene mellem disse funktioner demonstreres nedenfor.

if_else() er mere striks end ifelse()

Bemærk følgende kommando og resultat med ifelse():

my_data |>
  mutate(ny_variabel = ifelse(Var1 < 4, "Mindre än 4", 0))
   Var1  Var2 Var3    Var4 Var5  ny_variabel
1    NA     1 Ja     0.276 a     NA
2     5    NA Nej    1.15  b     0
3     9     3 NA     0.162 c     0
4     5     6 Ja    -0.517 d     0
5     3     1 Ja     1.36  e     Mindre än 4
6     4    12 Ja    -0.454 f     0
7    10     0 Ja    -0.445 g     0
8    20    12 Nej    0.394 h     0
9    30     3 NA    -0.642 i     0

Det ser ud til, at koden fungerede. Nu gentages identisk kode, bortset fra at ifelse() erstattes med if_else():

my_data |>
  mutate(ny_variabel = if_else(Var1 < 4, "Mindre än 4", 0))
Error in `mutate()`:
! Problem while computing `ny_variabel = if_else(Var1 < 4, "Mindre än 4", 0)`.
Caused by error in `if_else()`:
! `false` must be a character vector, not a double vector.

Dette resulterede derimod i en fejl (error). Forklaringen på dette er, at betingelsen returnerer en tekststreng ("Mindre än 4"), hvis den er sand, og en numerisk værdi (0), hvis den er falsk. Det accepterer if_else() ikke, fordi de værdier, der kan returneres, skal være af samme type.

if_else() konverterer ikke datovariabler til numeriske værdier

Se følgende eksempel med resultat med ifelse():

## Opretter 3 datoer med funktionerne c() og ymd()
my_dates <- ymd(c('2021-10-01', '2022-10-02', '2021-10-03'))

## Evaluerer en betingelse og gemmer som nyt objekt
my_dates_2 <- ifelse(my_dates == '2022-10-02', my_dates + 1, my_dates)

## Vis hvilken klasse vektoren har
class(my_dates_2)
[1] "Numeric"

Den samme kode gentages, hvor ifelse() erstattes med if_else():

## Opretter 3 datoer med funktionerne c() og ymd()
my_dates <- ymd(c('2021-10-01', '2022-10-02', '2021-10-03'))

## Evaluerer en betingelse og gemmer som nyt objekt
my_dates_2 <- if_else(my_dates == '2022-10-02', my_dates + 1, my_dates)

## Vis hvilken klasse vektoren har
class(my_dates_2)
[1] "Date"

if_else() har et ekstra argument for missing

Vi gentager det første eksempel, men nu med if_else() og angiver en værdi for missing:

my_data |>
  mutate(ny_variabel = if_else(Var1 < 4, "Mindre än 4", "4 eller mer", missing="Saknas"))
   Var1  Var2 Var3    Var4 Var5  ny_variabel
1    NA     1 Ja     0.276 a     Saknas
2     5    NA Nej    1.15  b     4 eller mer
3     9     3 NA     0.162 c     4 eller mer
4     5     6 Ja    -0.517 d     4 eller mer
5     3     1 Ja     1.36  e     Mindre än 4
6     4    12 Ja    -0.454 f     4 eller mer
7    10     0 Ja    -0.445 g     4 eller mer
8    20    12 Nej    0.394 h     4 eller mer
9    30     3 NA    -0.642 i     4 eller mer

if_else() er hurtigere end ifelse()

Sidst, men ikke mindst, er if_else() hurtigere end ifelse().

case_when()

I stedet for at lave indlejrede ifelse()- eller if_else()-udtryk kan case_when() bruges. Denne funktion gør det muligt at angive en række sekventielle betingelser med effektiv syntaks. Betingelserne testes én ad gangen. Bemærk, at der med case_when() bruges ~. Til venstre for ~ angives betingelsen, og til højre for ~ angives den værdi, som funktionen skal returnere, hvis betingelsen er opfyldt. Bemærk, at vi kan angive missing med funktionen is.na().

my_data |> mutate(ny_variabel = case_when(Var1 == 3 ~ "Lika med 3",
                                          Var1 > 3 ~ "Större än 3",
                                          Var1 > 6 ~ "Större än 6",
                                          Var1 > 9 ~ "Större än 9",
                                          is.na(Var1) ~ "Saknas"))
   Var1  Var2 Var3    Var4 Var5  ny_variabel
1    NA     1 Ja     0.276 a     Saknas
2     5    NA Nej    1.15  b     Större än 3
3     9     3 NA     0.162 c     Större än 3
4     5     6 Ja    -0.517 d     Större än 3
5     3     1 Ja     1.36  e     Lika med 3
6     4    12 Ja    -0.454 f     Större än 3
7    10     0 Ja    -0.445 g     Större än 3
8    20    12 Nej    0.394 h     Större än 3
9    30     3 NA    -0.642 i     Större än 3