tumultous_rooster
tumultous_rooster

Reputation: 12550

Function using ifelse not returning a vector in R

I wrote a somewhat grotesque function, which should simply return a vector with two values.

For example, if you put in 33, you should get back c(30, 40). It couldn't get much simpler than this.

return_a_range <- function(number){
    ans <-  ifelse(  (30  <= number & number <= 40), c(30, 40),
                     (ifelse( (40  < number  & number  <= 50), c(40, 50),
                              (ifelse( (50  < number  & number  <= 60), c(50, 60),
                                       (ifelse( (60  < number  & number  <= 70), c(60, 70),
                                                (ifelse( (70  < number  & number  <= 80), c(70, 80),
                                                         (ifelse( (80  < number  & number  <= 100), c(80, 100),                      
                                                                           ans <- c("NA"))))))))))))
    return(ans)}

return_a_range(33)

Why is this returning only 30? How am I not getting back c(30, 40)? Why did R decide to only return the value in the first position of the vector?

EDIT
Although most of the responses are concerned with (justifiably!) spanking me for writing a lousy ifelse statement, I think the real question was recognized and answered best by @MrFick in the comments directly below.

Upvotes: 4

Views: 775

Answers (3)

talat
talat

Reputation: 70266

You could just use:

> c(floor(33 / 10), ceiling(33 / 10))*10
[1] 30 40

Or as a function - thanks to @Khashaa for a nice modification (in the comments):

f <- function(x) if(abs(x) >= 100) NA else c(floor(x / 10), floor(x/10) + 1)*10
f(44)
#[1] 40 50

f(40)
#[1] 40 50

This kind of functions will be a lot more efficient than multiple nested ifelses.


I overlooked initially that you want to return 30 - 40 for a input value of 40 (I thought you wanted 40 - 50 which is what the above function does).

So this is a slightly more elaborate function which should implement that behavior:

ff <- function(x) {
  if (abs(x) >= 100L) {
    NA 
  } else {
    y <- floor(x / 10L) * 10L
    if (x %% 10L == 0L) {
      c(y - 10L, y) 
    } else {
      c(y, y + 10L)
    }
  }
}

And in action:

ff(40)
#[1] 30 40
ff(45)
#[1] 40 50

Or if you had a vector of numbers you could lapply/sapply over it:

( x <- sample(-100:100, 3, F) )
#[1]  73  89 -97

lapply(x, ff)
#[[1]]
#[1] 70 80
#
#[[2]]
#[1] 80 90
#
#[[3]]
#[1] -100  -90

Or

sapply(x, ff)
#     [,1] [,2] [,3]
#[1,]   70   80 -100
#[2,]   80   90  -90

Upvotes: 4

David Arenburg
David Arenburg

Reputation: 92292

Here's another variation using %/% which will work for f2(40) case too (but my fail somewhere else?)

f2 <- function(x) if(abs(x) >= 100) NA else c(x %/% 10, (x + 10) %/% 10) * 10
f2(40)
## [1] 40 50

Upvotes: 2

LyzandeR
LyzandeR

Reputation: 37879

If you really want to use your function the way you use it and not go with docendo's answer (where for this problem I don't see why) you can do the following (in case you need to do something similar in the future):

return_a_range <- function(number){
  ans <-  ifelse(  (30  <= number & number <= 40), a<-c(30, 40),
                   (ifelse( (40  < number  & number  <= 50), a<-c(40, 50),
                            (ifelse( (50  < number  & number  <= 60), a<-c(50, 60),
                                     (ifelse( (60  < number  & number  <= 70), a<-c(60, 70),
                                              (ifelse( (70  < number  & number  <= 80), a<-c(70, 80),
                                                       (ifelse( (80  < number  & number  <= 100), a<-c(80, 100),                      
                                                                a <- c("NA"))))))))))))
  return(a)}

> return_a_range(33)
[1] 30 40

> return_a_range(62)
[1] 60 70

The only thing I did was to save the vector in a variable a on each ifelse.

Upvotes: 1

Related Questions