jay.sf
jay.sf

Reputation: 72838

How to get ifelse() throwing data frames?

Why won't these ifelse()s don't throw data frames (or matrices)?

x <- data.frame(a=1000, b=100)
> x
     a   b
1 1000 100

> ifelse(x[, 2] >= 8, x, NA)
[[1]]
[1] 1000

> ifelse(x[, 2] >= 8, x[1:2], NA)
[[1]]
[1] 1000

> ifelse(x[, 2] >= 8, cbind(x[[1]], x[[2]]), NA)
[1] 1000

The second column is always missing. How to get this right?

Upvotes: 1

Views: 56

Answers (2)

MKR
MKR

Reputation: 20095

Below quoted lines about return value from ifelse are very important (taken from help) in order to understand behavior of ifelse

Value

A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no.

Details

If yes or no are too short, their elements are recycled.

In summary it suggests that for each row and column in test section of the corresponding data values will be returned for yes and no.

Lets take few examples to understand it.

x <- data.frame(a=1000, b=100)


ifelse(x[, 2] >= 8, x, NA)
# Test : 1 row and 1 col. Hence return is x[1]
# [[1]]
#[1] 1000

ifelse(x[, 2] >= 8, x[,2], NA)
# Test : 1 row and 1 col. Hence return is x[,2][1]
#[1] 100

ifelse(x >= 8, x, NA)
# Test : 1 row and 2 cols. Hence return is x[1,1] and x[1,1]
# [[1]]
# [1] 1000
# 
# [[2]]
# [1] 100

ifelse(x >= 8, 4, NA)
# Test : 1 row and 2 cols. Values are recycled. 4 & 4 for corresponding row/col
#    a b
#[1,] 4 4 

#Change x to 2x2
x <- data.frame(a=1000:1001, b=100:101)
ifelse(x >= 8, 4, NA)
# Test : 2 row and 2 cols. Result will be 2 x 2 
#     a b
#[1,] 4 4
#[2,] 4 4

Now, what option does one have to handle such problem?

Certainly one can use if as suggested by @Jordi. But just be careful that if is not vectorised. Hence test condition is applied only on 1st element when vector is used in test condition. To handle those situations, functions like any or all comes very handy and make it possible to to use vector as part of test condition in if.

Upvotes: 3

Jordi
Jordi

Reputation: 1343

From its help file:

ifelse returns a value with the same shape as test which is filled with elements selected from either yes or no depending on whether the element of test is TRUE or FALSE.

So it does actually return a dataframe if the input is a dataframe:

ifelse(x[, 1, drop = FALSE] > 0, 10, NA)

But in your case it is easier to use

if (test) x else NA

Upvotes: 4

Related Questions