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

Det er umulig å arbeide med dataanalyse uten å bruke betingelser for å lage eller endre variabler. De mest brukte funksjonene for 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)

  • I første posisjon (VILLKORET) spesifiseres betingelsen.
  • I andre posisjon (OM_SANT) spesifiseres hva funksjonen skal returnere hvis betingelsen er sann.
  • I tredje posisjon (OM_FALSKT) spesifiseres hva funksjonen skal returnere hvis betingelsen er usann.

Vi bruker nå denne funksjonen til å lage en ny variabel kalt ny_variabel. Denne variabelen får verdien 0 hvis Var er mindre enn 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 fungerte, bortsett fra at den første observasjonen fikk NA (missing) på ny_variabel. Forklaringen på dette er enkel: den observasjonen mangler informasjon om Var1, og da kan ikke betingelsen evalueres. Vi kan derimot spesifisere i betingelsen hvordan NA skal håndteres. I neste eksempel utvider vi betingelsen slik at også missing (NA) får verdien 0. Dette gjør vi ved å bruke funksjonen is.na() og operatoren |, som betyr "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 bli tungvint hvis flere betingelser må evalueres sekvensielt, noe som er svært vanlig. For å gjøre dette med ifelse() må det tredje argumentet 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

Merk at det siste argumentet (ovenfor spesifisert som "Övriga") er verdien som skal returneres hvis ingen av betingelsene stemmer. Merk også at alle parentesene må lukkes helt til slutt.

if_else()

I dplyr finnes funksjonen if_else(), som ligner på ifelse(). Forskjellene mellom disse funksjonene demonstreres nedenfor.

if_else() er strengere enn ifelse()

Legg merke til 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 ut til at koden fungerte. Nå gjentas identisk kode, bortsett 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 resulterte derimot i en feilmelding (error). Forklaringen på dette er at betingelsen returnerer en tekststreng ("Mindre än 4") hvis den er sann, og en numerisk verdi (0) hvis den er usann. Dette godtar ikke if_else(), fordi verdiene som kan returneres, skal være av samme type.

if_else() konverterer ikke datovariabler til numeriske verdier

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

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

## Evaluerer en betingelse og lagrer som nytt 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 koden gjentas der ifelse() erstattes med if_else():

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

## Evaluerer en betingelse og lagrer som nytt 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 gjentar det første eksemplet, men nå med if_else(), og spesifiserer en verdi 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 raskere enn ifelse()

Sist, men ikke minst, er if_else() raskere enn ifelse().

case_when()

I stedet for å lage nøstede ifelse()- eller if_else()-setninger kan case_when() brukes. Denne funksjonen gjør det mulig å spesifisere en rekke sekvensielle betingelser med effektiv syntaks. Betingelsene testes én om gangen. Merk at ~ brukes med case_when(). Til venstre for ~ angis betingelsen, og til høyre for ~ angis verdien som funksjonen skal returnere hvis betingelsen stemmer. Merk at vi kan spesifisere missing med funksjonen 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