I\'m facing nested ifelse()
structures:
df1$var <- ifelse(x < a, u, ifelse(x < b, v, ifelse(x < c, w, ...)))
whereby t
You could use case_when from the dplyr
library:
df1$y <- case_when(
x == 1 ~ "s",
x == 2 ~ "t",
x == 3 ~ "u",
x == 4 ~ "v",
TRUE ~ "w"
)
Note that the final case above (TRUE
) is the blanket else condition which will catch all cases not matching any earlier conditions.
I would create an empty vector then fill each condition one by one.
df1$y = rep(NA,nrow(df1))
df1$y[x < a] = u(x[x < a])
df1$y[x > a & x < b] = v(x[x > a & x < b])
df1$y[x > b & x < c] = w(x[x > b & x < c])
...
I have found this to be the clearest way of setting the values, I find it much easier to see at a glance what is happening when you have more conditions than nested ifelse statements.
df1$y = ifelse(x < a, u(x), ifelse(x < b, v(x), ifelse(x < c, w(x), ...)) )
An improvement on this would be to predefine the condition elements, that way each group is only calculated once:
aEls = which(x < a)
bEls = which(x > a & x < b)
cEls = which(x > b & x < c)
...
y = rep(NA,nrow(df1))
y[aEls] = u(x[aEls])
y[bEls] = v(x[bEls])
y[cEls] = w(x[cEls])
...
df$y = y
In base R
, if there are multiple elements to be replaced, create a key/value dataset and do a merge
keyval <- data.frame(x = c(1, 2, 3, 4), y = c("s", "t", "u", "v"), stringsAsFactors = FALSE)
new <- merge(df1, keyval, by = 'x', all.x = TRUE)[['y']]
new[is.na(new)] <- "w"
df1$x <- new
set.seed(24)
df1 <- data.frame(x = rbinom(100, 5, .5))
Since you insist on base R, here are two possibilities:
Define a mapping data.frame
:
# Define mapping
map <- cbind.data.frame(
x = c(1, 2, 3, 4, NA),
y = c("s", "t", "u", "v", "w"));
Method 1: match
entries from map
to df1
.
# match entries
df1$y <- map[match(df1$x, map$x), 2];
df1$y[is.na(df1$y2)] <- "w";
Method 2: Loop through all mappings, and replace using direct indexing:
# for loop
df1$y <- factor("w", levels = map$y);
for (i in 1:nrow(map)) df1$y[df1$x == map$x[i]] <- map$y[i];
Output:
tail(df1);
# x y
#95 4 v
#96 1 s
#97 4 v
#98 2 t
#99 4 v
#100 1 s
Note, the second method will also work for inequalities.
set.seed(2017);
df1 <- data.frame(x = rbinom(100, 5, .5))