Effective alternative to ifelse loop
Would it be so kind and suggest a quick solution (vectorized and data.table)?
Here's some sample data and current loop:
set.seed(123)
df <- data.frame(x=abs(rnorm(10)*100),y=abs(rnorm(10)*100))
df$ratio <- df$x/df$y
for(i in 1:nrow(df)) {
df[,c("x")][i] <- ifelse(df[,c("ratio")][i]>1.5,1.5*df[,c("y")][i],df[,c("x")][i])
}
So, I replace the column values x
if the walkie-talkie is larger1.5
+3
source to share
2 answers
try
library(data.table)
setDT(df)[ratio > 1.5, x := 1.5*y]
Or solution base R
transform(df, x=ifelse(ratio > 1.5, 1.5*y, x))
Benchmarks
set.seed(123)
df1 <- data.frame(x=abs(rnorm(1e6)*100),y=abs(rnorm(1e6)*100))
df1$ratio <-df1$x/df1$y
f1 <- function(){ as.data.table(df1)[ratio > 1.5, x:= 1.5*y]}
f2 <- function(){ indx <- df1$ratio > 1.5
df1$x[indx] <- df1$y[indx]*1.5}
f3 <- function(){transform(df1, x=ifelse(ratio > 1.5, 1.5*y, x))}
#Another option suggested by @Steven Beaupré
f4 <- function(){mutate(df1, x=ifelse(ratio > 1.5, 1.5*y, x))}
microbenchmark(f1(),f2(),f3(),f4(), unit='relative', times=40L)
#Unit: relative
#expr min lq mean median uq max neval cld
#f1() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 40 a
#f2() 2.829316 2.749836 2.047366 2.645489 1.301990 2.070973 40 b
#f3() 12.693416 12.954443 9.060991 12.689862 6.170528 7.935411 40 c
#f4() 13.231567 13.300574 9.147105 12.636984 6.217343 6.286193 40 c
+4
source to share