Mäkinen Jussi
2005-Jun-01 06:47 UTC
[R] A suggestion to improve ifelse behaviour with vector yes/noarguments
> Thomas Lumley wrote: > > On Tue, 31 May 2005, Duncan Murdoch wrote: > > > > > >>M??kinen Jussi wrote: > >> > >>>Dear All, > >>> > >>>I luckily found the following feature (or problem) when tried to > >>>apply > >>>ifelse-function to an ordered data. > >>> > >>> > >>> > >>>>test <- c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) ifelse(test, > >>>>0, 1:4) > >>> > >>>[1] 0 0 0 4 1 2 3 > >>> > > > > <snippage> > > > >>As Dimitris said, this is just recycling. I think getting rid of > >>recycling > >>on vectors with length greater than 1 would have been a good decision in S > >>about 15 years ago, but it's too late now. > > > > > > It wouldn't help the original poster, though. I agree that > > 0,0,0,4,1,2,3 > > is a slightly weird result, but I can't think of any reasonable model for > > the behaviour of ifelse() that would give any other result except an error > > message. [or 0,NA,NA,4,NA,NA,NA, I suppose]. > > I would vote for the error message. I can't think of a single example > where a vector of length 7 is needed, and a vector of length 4 is > recycled to give it, that *doesn't* give a slightly weird result. > > Maybe this is something that should have been changed in R 2.0.0; we > squandered that change from 1.x.x to 2.x.x. > > Duncan MurdochHello, I'm happy with the modified ifelse: ifelse.o <- function (test, yes, no) { storage.mode(test) <- "logical" ans <- test nas <- is.na(test) if (any(test[!nas])) ans[test & !nas] <- rep(yes, length.out = length(ans))[test & !nas] if (any(!test[!nas])) ### Changed ans[!test & !nas] <- rep(no, length.out = length(ans[!test & !nas])) ans[nas] <- NA ans } giving me:> test <- c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) > ifelse.o(test, 0, 1:4)[1] 0 0 0 1 2 3 4 and> test <- c(FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) > ifelse.o(test, 0, 1:4)[1] 1 2 0 0 0 3 4 1 2 3 4 comparing to:> ifelse(test, 0, 1:4)[1] 1 2 0 0 0 2 3 4 1 2 3 So in 'ifelse.o' the recycling starts from the first element of the 'no' -argument and continues the cycling in a logical way (IMHO). That is something I expected 'ifelse' would do. Thank you for your comments, Jussi M??kinen
Duncan Murdoch
2005-Jun-01 11:12 UTC
[R] A suggestion to improve ifelse behaviour with vector yes/noarguments
M??kinen Jussi wrote:> Hello, > > I'm happy with the modified ifelse: > > ifelse.o <- function (test, yes, no) > { > storage.mode(test) <- "logical" > ans <- test > nas <- is.na(test) > if (any(test[!nas])) > ans[test & !nas] <- rep(yes, length.out = length(ans))[test & > !nas] > if (any(!test[!nas])) > ### Changed > ans[!test & !nas] <- rep(no, length.out = length(ans[!test & !nas])) > ans[nas] <- NA > ans > }I wouldn't be: > x [1] -0.4539550 -1.3023478 0.9034912 1.7485065 0.6910265 -0.7712547 [7] -0.6345585 1.8296632 2.1207810 0.7643834 > ifelse.o(x > 0, 1:10, 0) [1] 0 0 3 4 5 0 0 8 9 10 > ifelse.o(x > 0, 0, 1:10) [1] 1 2 0 0 0 3 4 0 0 0 I'd call these results fairly perverse. Duncan Murdoch
Mäkinen Jussi
2005-Jun-01 14:13 UTC
[R] A suggestion to improve ifelse behaviour with vector yes/noarguments
Thanks Duncan, For pointing this out. You maybe saved my day. I agree that these results are neither something I would like to see. I changed the code to be: ifelse.o <- function (test, yes, no) { storage.mode(test) <- "logical" ans <- test nas <- is.na(test) if (any(test[!nas])) ### Own change ans[test & !nas] <- rep(yes, length.out = length(ans[test & !nas])) if (any(!test[!nas])) ### Own change ans[!test & !nas] <- rep(no, length.out = length(ans[!test & !nas])) ans[nas] <- NA ans } which yields now> x <- rnorm(10) > ifelse.o(x > 0, 1:10, 0)[1] 1 2 0 3 0 4 0 5 6 0> ifelse.o(x > 0, 0, 1:10)[1] 0 0 1 0 2 0 3 0 0 4 I'm even more happy now :-) I will anyway need to use this modificate function for just one specific problem. Hopefully there is no other hooks, Jussi M??kinen Jussi wrote:> Hello, > > I'm happy with the modified ifelse: > > ifelse.o <- function (test, yes, no) > { > storage.mode(test) <- "logical" > ans <- test > nas <- is.na(test) > if (any(test[!nas])) > ans[test & !nas] <- rep(yes, length.out = length(ans))[test & > !nas] > if (any(!test[!nas])) > ### Changed > ans[!test & !nas] <- rep(no, length.out = length(ans[!test & !nas])) > ans[nas] <- NA > ans > }I wouldn't be: > x [1] -0.4539550 -1.3023478 0.9034912 1.7485065 0.6910265 -0.7712547 [7] -0.6345585 1.8296632 2.1207810 0.7643834 > ifelse.o(x > 0, 1:10, 0) [1] 0 0 3 4 5 0 0 8 9 10 > ifelse.o(x > 0, 0, 1:10) [1] 1 2 0 0 0 3 4 0 0 0 I'd call these results fairly perverse. Duncan Murdoch