Javier
Javier

Reputation: 730

Rounding in nested IF ELSE For loop

Is there a cleaner way to compare my 3rd column of my dataframe and divide it accordingly by either 1000, 100 or 10? My code works but is there a better way to write this?

After dividing, I would like to assign it to either "Round to 1000" and so on.

df <- data.frame(y = c(1,2,3), y1 = c(2,3,4), y2 = c(1000, 100, 10))

    df$Type <- 0
    for (i in 1:nrow(df)){
      if (df[i,3] %% 1000 == 0 ){
        df[i,4] <- "Round to 1000"
      } else if (df[i,3] %% 100 == 0){
        df[i,4] <- "Round to 100"
      } else if (df[i,3] %% 10 == 0){
        df[i,4] <- "Round to 10"
      } else {
        df[i,4] <- "None"
      }
    }

Upvotes: 0

Views: 77

Answers (3)

r.user.05apr
r.user.05apr

Reputation: 5456

Or:

df <- data.frame(y = c(1,2,3), y1 = c(2,3,4), y2 = c(1000, 100, 10))

txt <- c("Round to 1000", "Round to 100", "Round to 10", "None")
div <- c(1000, 100, 10, 1)
df$Type <- lapply(df$y2, function(x) {
  txt[which.max(unlist(lapply(div, function(y) (x %% y) == 0)))]
})
#> df
#y y1   y2          Type
#1 1  2 1000 Round to 1000
#2 2  3  100  Round to 100
#3 3  4   10   Round to 10

Upvotes: 0

Daniel
Daniel

Reputation: 7832

From my experience, most nested if-else statements can be replaced by switch() or dplyr::case_when():

library(dplyr)

df <- data.frame(
  y = c(1, 2, 3, 5),
  y1 = c(2, 3, 4, 5),
  y2 = c(1000, 100, 10, 5)
)

df %>% 
  mutate(Type = case_when(
    y2 %% 1000 == 0 ~ "Round to 1000",
    y2 %% 100 == 0 ~ "Round to 100",
    y2 %% 10 == 0 ~ "Round to 10",
    TRUE ~ "NONE"
  ))
#>   y y1   y2          Type
#> 1 1  2 1000 Round to 1000
#> 2 2  3  100  Round to 100
#> 3 3  4   10   Round to 10
#> 4 5  5    5          NONE

In my opinion, case_when() can be written in a clean, readable way...

Upvotes: 3

LyzandeR
LyzandeR

Reputation: 37879

In these cases I usually go for sapply. It helps because it outputs an atomic vector which can be inserted in your data.frame.

df$type <- sapply(df$y2, function(x) {
  if (x %% 1000 == 0 ){
    out <- "Round to 1000"
  } else if (x %% 100 == 0){
    out <- "Round to 100"
  } else if (x %% 10 == 0){
    out <- "Round to 10"
  } else {
    out <- "None"
  }
  out
})

Output

df
#  y y1   y2          type
#1 1  2 1000 Round to 1000
#2 2  3  100  Round to 100
#3 3  4   10   Round to 10

Talking about best ways, the following using standard subsetting is an option.

df$type <- 'None'
for (i in c(10, 100, 1000)) {
  df$type[df$y2 %% i == 0] <- paste('Round to', i)
}

Upvotes: 2

Related Questions