Reputation: 21
I'm fairly new to R and want to write a piecewise function , my code is not working , if you can help please comment :
a<-rnorm(1000)
ifelse(0<a & a<=.3,C=sqrt(30*a),NA)
ifelse(.3<a & a<=.7,C=5*a+15/10),NA)
ifelse(.7<a & a<=1,c=8-sqrt(-30*a+30),NA)
thank you
Upvotes: 1
Views: 281
Reputation: 269556
Regarding the code in the question:
The assignment should be done outside the ifelse, not inside
ifelse will first evaluate both legs for all values so if you pass a negative a
it will try to take the square root of a negative number even if that leg is not chosen. As a result it is better to avoid using ifelse
entirely here.
nested ifelse's can be hard to follow.
The solutions below avoid ifelse
. The first one uses if ... else ... . Unlike ifelse
it only evaluates legs that are actually used. It only works for scalars but we can use Vectorize
to transform the scalar function that we write into a function which accepts vectors.
The second solution separates out the values of a
which are between 0 and 1 from the others so that we never attempt to take a square root of a negative number and so NA's are not involved in the main computation. It uses the idiom cond1 * y1 + cond2 * y2 + ... where y1, y2, ... are numeric non-NA expressions and cond1, cond2, ... are mutually exclusive conditions. For each vector component only one of the conditions is TRUE so R multiplies the corresponding expression by 1 keeping it and the others by 0 eliminating them.
1) Use if/else and Vectorize it. This works even for negative values of a without issuing warnings which might otherwise be issued due to negative square roots and no packages are used.
f <- Vectorize(function(a)
if (0 < a & a <= 0.3) sqrt(30 * a)
else if (0.3 < a & a <= 0.7) 5 * a + 15 / 10
else if (0.7 < a & a <= 1) 8 - sqrt(-30 * a + 30)
else NA)
a <- seq(0, 1, 0.01)
c <- f(a)
head(c)
## [1] NA 0.5477226 0.7745967 0.9486833 1.0954451 1.2247449
tail(c)
## [1] 6.775255 6.904555 7.051317 7.225403 7.452277 8.000000
plot(c ~ a, type = "l")
2) Another approach is to do it in a vectorized manner but handle the values leading to an NA separately so that it never attempts to take a square root of a negative number. Again this uses no packages.
f2 <- function(x) {
ix <- x > 0 & x <= 1
a <- x[ix]
replace(NA * x, ix, (0 < a & a <= 0.3) * sqrt(30 * a) +
(0.3 < a & a <= 0.7) * (5 * a + 15 / 10) +
(0.7 < a & a <= 1) * (8 - sqrt(-30 * a + 30)))
}
# test
c2 <- f(a)
identical(c, c2)
## [1] TRUE
Upvotes: 2
Reputation: 545588
Three issues:
=
isn’ the assignment operator, it’s the syntax for named arguments.ifelse()
function call argument list, assign the resultc = ifelse(
0 < a & a <= 0.3, sqrt(30 * a),
ifelse(
0.3 < a & a <= 0.7, 5 * a + 15 / 10,
ifelse(0.7 < a & a <= 1, 8 - sqrt(-30 * a + 30), NA)
)
)
However, this isn’t very readable. You can use case_when()
from the ‘dplyr’ package instead to make the code more readable:
c = case_when(
0 < a & a <= 0.3 ~ sqrt(30 * a),
0.3 < a & a <= 0.7 ~ 5 * a + 15 / 10
0.7 < a & a <= 1 ~ 8 - sqrt(-30 * a + 30)
TRUE ~ NA
)
Both of these solutions have the unfortunate side-effect of raising warnings due to NaN values (which are caused by the code calculating square roots of negative numbers). See G. Grothendieck’s answer for ways around this issue.
Upvotes: 5