How to apply unique functions to multiple rows of the dataset using if statement in R?

Viewed 55

In this data set (df), I want to apply a different equation based on species to calculate a new output stored in "Std.Val1"

Year Site Quadrant species Val1 Val2 Std.Val1
2019   1     1        A    20    30     NA
2019   1     1        B    NA    25     NA
2019   1     2        C    20    10     NA
2019   1     2        D    11    22     NA

c_A <- 1
m_A <- 2

c_B <- 2
m_B <- 5

c_C <- 10
m_C <- 12

c_D <- 11
m_D <- 2

I tried following this example to write my code

df %>%
    mutate(
      Std.Val1 = ifelse(species == "A" ~ ((log(Val1) - c_A )/(m_A)),
                 ifelse(species == "B" ~ ((log(Val1) - c_B )/(m_B)),
                 ifelse(species == "C" ~ ((log(Val1) - c_C )/(m_C)),
                 ifelse(species == "D" ~ ((log(Val1) - c_D )/(m_D)), NA))))
              )

but I keep running into an error that reads: "Error: Problem with mutate() input Std.Val1. x 'language' object cannot be coerced to type 'logical' ℹ Input nem1 is ifelse(...)"

3 Answers

Here is a data.table approach that avoids a lot of typing

library(data.table)
# Sample data
DT <- fread("Year Site Quadrant species Val1 Val2
2019   1     1        A    20    30     
2019   1     1        B    NA    25     
2019   1     2        C    20    10     
2019   1     2        D    11    22     ")
# Create lookup table with values to calculate with
DT.lookup <- data.table( species = LETTERS[1:4], 
                         vc = c(1,2,10,11), 
                         vm = c(2,3,12,2))
#    species vc vm
# 1:       A  1  2
# 2:       B  2  3
# 3:       C 10 12
# 4:       D 11  2

# Join and calculate in 1 step
DT[DT.lookup, Std.Val1 := (log(Val1) - i.vc )/(i.vm), on = .(species)][]

#    Year Site Quadrant species Val1 Val2   Std.Val1
# 1: 2019    1        1       A   20   30  0.9978661
# 2: 2019    1        1       B   NA   25         NA
# 3: 2019    1        2       C   20   10 -0.5836890
# 4: 2019    1        2       D   11   22 -4.3010524

The syntax that you are using is for case_when and not ifelse. case_when is used to avoid writing such multiple nested ifelse statements.

library(dplyr)

df %>%
  mutate(Std.Val1 = case_when(species == "A" ~ ((log(Val1) - c_A )/(m_A)),
                              species == "B" ~ ((log(Val1) - c_B )/(m_B)),
                              species == "C" ~ ((log(Val1) - c_C )/(m_C)),
                              species == "D" ~ ((log(Val1) - c_D )/(m_D))))

If none of the condition match it will return NA by default.

store the parameters as vectors will also make it much simpler:

c_value <- c(A = 1, B = 2, C = 10, D = 11)
m_value <- c(A = 2, B = 5, C = 12, D = 2)

dtt %>%
    mutate(Std.Val1 = (log(Val1) - c_value[species])/m_value[species])

#    Year Site Quadrant species Val1 Val2   Std.Val1
# 1: 2019    1        1       A   20   30  0.9978661
# 2: 2019    1        1       B   NA   25         NA
# 3: 2019    1        2       C   20   10 -0.5836890
# 4: 2019    1        2       D   11   22 -4.3010524
Related