Dear Ravi, I wrote a small replacement for ifelse() which avoids such unnecessary evaluations (it bothered me a few times as well - so I decided to try a small replacement). ### Example: x = 1:10 FUN = list(); FUN[[1]] = function(x, y) x*y; FUN[[2]] = function(x, y) x^2; FUN[[3]] = function(x, y) x; # lets run multiple conditions # eval.by.formula(conditions, FUN.list, ... (arguments for FUN) ); eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, x, x-1) # Example 2 eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, 2, x) ### Disclaimer: - NOT properly tested; The code for the function is below. Maybe someone can experiment with the code and improve it further. There are a few issues / open questions, like: 1.) Best Name: eval.by.formula, ifelse.formula, ...? 2.) Named arguments: not yet; 3.) Fixed values inside FUN.list 4.) Format of expression for conditions: expression(cond1, cond2, cond3) vs cond1 ~ cond2 ~ cond3 ??? 5.) Code efficiency - some tests on large data sets & optimizations are warranted; Sincerely, Leonard ======= The latest code is on Github: https://github.com/discoleo/R/blob/master/Stat/Tools.Formulas.R eval.by.formula = function(e, FUN.list, ..., default=NA) { ?? ?tok = split.formula(e); ?? ?if(length(tok) == 0) return(); ?? ?FUN = FUN.list; ?? ?# Argument List ?? ?clst = substitute(as.list(...))[-1]; ?? ?len? = length(clst); ?? ?clst.all = lapply(clst, eval); ?? ?eval.f = function(idCond) { ?? ???? sapply(seq(length(isEval)), function(id) { ?? ???? ??? if(isEval[[id]] == FALSE) return(default); ?? ???? ??? args.l = lapply(clst.all, function(a) if(length(a) == 1) a else a[[id]]); ?? ???? ??? do.call(FUN[[idCond]], args.l); ?? ???? }); ?? ?} ?? ?# eval 1st condition: ?? ?isEval = eval(tok[[1]]); ?? ?rez = eval.f(1); ?? ?if(length(tok) == 1) return(rez); ?? ?# eval remaining conditions ?? ?isEvalAll = isEval; ?? ?for(id in seq(2, length(tok))) { ?? ???? if(tok[[id]] == ".") { ?? ???? ??? # Remaining conditions: tok == "."; ?? ???? ??? # makes sens only on the last position ?? ???? ??? if(id < length(tok)) warning("\".\" is not last!"); ?? ???? ??? isEval = ! isEvalAll; ?? ???? ??? rez[isEval] = eval.f(id)[isEval]; ?? ???? ??? next; ?? ???? } ?? ???? isEval = rep(FALSE, length(isEval)); ?? ???? isEval[ ! isEvalAll] = eval(tok[[id]])[ ! isEvalAll]; ?? ???? isEvalAll[isEval] = isEval[isEval]; ?? ???? rez[isEval] = eval.f(id)[isEval]; ?? ?} ?? ?return(rez); } # current code uses the formula format: # cond1 ~ cond 2 ~ cond3 # tokenizes a formula in its parts delimited by "~" # Note: # - tokenization is automatic for ","; # - but call MUST then use FUN(expression(_conditions_), other_args, ...); split.formula = function(e) { ?? ?tok = list(); ?? ?while(length(e) > 0) { ?? ???? if(e[[1]] == "~") { ?? ???? ??? if(length(e) == 2) { tok = c(NA, e[[2]], tok); break; } ?? ???? ??? tok = c(e[[3]], tok); ?? ???? ??? e = e[[2]]; ?? ???? } else { ?? ???? ??? tok = c(e, tok); break; ?? ???? } ?? ?} ?? ?return(tok); }
How to use ifelse without invoking warnings
2 messages · Leonard Mada
Dear Ravi, I have uploaded on GitHub a version which handles also constant values instead of functions. Regarding named arguments: this is actually handled automatically as well: eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, y=2, x) # [1]? 1? 4? 9 16 25? 6 14? 8 18 10 eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, x=2, x) # [1]? 4? 4? 4? 4? 4? 2 14? 2 18? 2 eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., list(FUN[[1]], 0, 1), y=2, x) ?# [1]? 0? 0? 0? 0? 0? 1 14? 1 18? 1 But it still needs proper testing and maybe optimization: it is possible to run sapply on the filtered sequence (but I did not want to break anything now). Sincerely, Leonard
On 10/9/2021 9:26 PM, Leonard Mada wrote:
Dear Ravi, I wrote a small replacement for ifelse() which avoids such unnecessary evaluations (it bothered me a few times as well - so I decided to try a small replacement). ### Example: x = 1:10 FUN = list(); FUN[[1]] = function(x, y) x*y; FUN[[2]] = function(x, y) x^2; FUN[[3]] = function(x, y) x; # lets run multiple conditions # eval.by.formula(conditions, FUN.list, ... (arguments for FUN) ); eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, x, x-1) # Example 2 eval.by.formula((x > 5 & x %% 2) ~ (x <= 5) ~ ., FUN, 2, x) ### Disclaimer: - NOT properly tested; The code for the function is below. Maybe someone can experiment with the code and improve it further. There are a few issues / open questions, like: 1.) Best Name: eval.by.formula, ifelse.formula, ...? 2.) Named arguments: not yet; 3.) Fixed values inside FUN.list 4.) Format of expression for conditions: expression(cond1, cond2, cond3) vs cond1 ~ cond2 ~ cond3 ??? 5.) Code efficiency - some tests on large data sets & optimizations are warranted; Sincerely, Leonard ======= The latest code is on Github: https://github.com/discoleo/R/blob/master/Stat/Tools.Formulas.R [...]