Reputation: 4079
I've encountered an issue wrapping nested sapply pasting code into ifelse() that checks to see that all the components are non-NA. The sapplys work great when they aren't in the ifelse()... Why is this?
Given some parameters:
a = c(1, 2, 3)
b = c("a", "b")
c = c("X", "Y")
Here's how I've managed to paste together all the combinations
as.vector(sapply(sapply(a, function(x){paste(x, b, sep = "")}),
function(x){paste(x, c, sep = "")}))
The output is this. It's exactly what I want:
[1] "1aX" "1aY" "1bX" "1bY" "2aX" "2aY" "2bX" "2bY" "3aX" "3aY" "3bX" "3bY"
However, if I put the exact same code in an ifelse() that checks to make sure the parameters aren't NA, the output is different.
ifelse(!is.na(a) & !is.na(b) & !is.na(c),
as.vector(sapply(sapply(a, function(x){paste(x, b, sep = "")}),
function(x){paste(x, c, sep = "")})), "Error")
[1] "1aX" "1aY" "1bX"
Warning messages:
1: In !is.na(a) & !is.na(b) :
longer object length is not a multiple of shorter object length
2: In !is.na(a) & !is.na(b) & !is.na(c) :
longer object length is not a multiple of shorter object length
Why? It's obvious that a, b, and c are different lengths; I don't see why that matters in an ifelse(). To clarify, the !is.na() is checking to see if the whole vector is NA, NOT for things like c(1, NA, 3) because I'll be using the code in a context where that won't ever happen. I'm doing this because the ifelse's are part of larger function where the parameters default to NA; certain combinations of non-NA parameters require a certain actions. For example, if b = NA, then the code above should produce an ERROR. How can I accomplish both the nested pasting AND the conditional checking?
Upvotes: 0
Views: 3102
Reputation: 20329
ifelse
does an elementwise check of a vector and uses the corresponding value at the specific position where the 'condition' was 'TRUE' or 'FALSE' the 'then' and the 'else' case respectively. You could use ifelse
for example to replace each negative element in a vector by the position value :
d <- c(1, -1, 2, -2, 3, -3)
order <- seq_along(d)
ifelse(d < 0, order, d)
# [1] 1 2 2 4 3 6
So the first elemt of d
is not meeting the criterion so it is replaced by the first element of d
. The second element does however meet the criterion so it is replaced by the second element of order
and so on. That's why all the vectors should be of equal length and if not, R
uses its recycling techniques.
So what you want to do is to use a simple if
statement
a <- c(1, 2, 3)
b <- c("a", "b")
d <- c("X", "Y")
if (all(!is.na(c(a, b, d))))
as.vector(sapply(sapply(a, function(x){paste(x, b, sep = "")}),
function(x) {paste(x, d, sep = "")})) else
"Error"
# [1] "1aX" "1aY" "1bX" "1bY" "2aX" "2aY" "2bX" "2bY" "3aX" "3aY" "3bX" "3bY"
d <- NA
if (all(!is.na(c(a, b, d))))
as.vector(sapply(sapply(a, function(x){paste(x, b, sep = "")}),
function(x) {paste(x, d, sep = "")})) else
"Error"
# [1] "Error"
However, your code is difficult to read and you can increase readibility by the following code:
a <- c(1, 2, 3)
b <- c("a", "b")
d <- c("X", "Y")
if (all(!is.na(c(a, b, d)))) apply(expand.grid(a, b, d), 1, paste, collapse = "") else "Error"
# [1] "1aX" "2aX" "3aX" "1bX" "2bX" "3bX" "1aY" "2aY" "3aY" "1bY" "2bY" "3bY"
d <- NA
if (all(!is.na(c(a, b, d)))) apply(expand.grid(a, b, d), 1, paste, collapse = "") else "Error"
# [1] "Error"
expand.grid
creates all combinations of the three vectors. apply
runs through all rows (which is the first dimension, that's why the 1
as a second argument to apply and then applies paste
to each row.
Hope that helps.
Upvotes: 1
Reputation: 19950
In all honesty, with a situation like this I would not use ifelse
and instead use the if
and else
components separately. ifelse
only returns a value of the same shape as your test (hence your initial output only providing 3 outputs) as you can see explained in this question. I cannot think of concise way to get all the combinations tested without some regex which seems nothing more than an unneeded complication. The following should work just fine.
if(!any(is.na(c(a,b,c)))){
as.vector(sapply(sapply(a, function(x){paste(x, b, sep = "")}),
function(x){paste(x, c, sep = "")}))
}else{
"Error"
}
Upvotes: 1