How to define a piecewise function in R
Asked Answered
B

6

6

I want to define a piecewise function using R, however, my R code goes wrong. Any suggestion is welcome.

x<-seq(-5, 5, by=0.01)
  for (x in -5:5){
  if (-0.326 < x < 0.652) fx<- 0.632
  else if (-1.793<x<-1.304) fx<- 0.454  
  else if (1.630<x<2.119) fx<-0.227  
  else fx<- 0 }
Broadbrim answered 9/1, 2012 at 11:56 Comment(1)
It might be worth clarifying that this example is a step function, a special class of piecewise functions. For step functions, stepfun (mentioned by @KenWIlliams) and approxfun both work well. approxfun also accommodates piecewise-linear functions (but not general piecewise functions).Tagmeme
W
12

Or you could use ifelse.

fx <- ifelse(x > -0.326 & x <0.625, 0.632,
   ifelse(x > -1.793 & x < -1.304,  0.454,
   ifelse(x > 1.630 & x < 2.119, 0.227, 0)))
Waterer answered 9/1, 2012 at 12:17 Comment(0)
X
17

Try this:

x <- seq(-5, 5, 0.01)
fx <- (x > -0.326 & x <0.625) * 0.632 +
      (x > -1.793 & x < -1.304) * 0.454 +
      (x > 1.630 & x < 2.119) * 0.227
plot(x, fx)
Xyster answered 9/1, 2012 at 12:41 Comment(1)
Nice. Given Gabor's experience, I bet this runs faster (for large number of 'segments') than my "switch" approach.Curule
G
13

I'm a little late to the party, but I couldn't resist posting a couple more ways to do this. Both take advantage of R capabilities for working with intervals on the real line.

If you define your cut points and function values in the vectors cuts and vals like so:

cuts <- c( -Inf, -1.793, -1.304, -0.326, 0.625, 1.630, 2.119 )
vals <- c(    0,  0.454,      0,  0.632,     0, 0.227,     0 )

Then you can use findInterval to efficiently look up the values of x in your cutpoints:

fx <- vals[findInterval(x, c(-Inf, cuts))]

If this function needed to do fancier stuff than just look up a constant value, you can put expressions or functions or whatever you want in vals, possibly using a list if you want.

Alternatively, since this function is a step function, you can use stepfun:

f <- stepfun(cuts[-1], vals)
fx <- f(x)

Then you also get to use the nice plotting methods of stepfun too.

Gramps answered 3/10, 2012 at 22:18 Comment(0)
W
12

Or you could use ifelse.

fx <- ifelse(x > -0.326 & x <0.625, 0.632,
   ifelse(x > -1.793 & x < -1.304,  0.454,
   ifelse(x > 1.630 & x < 2.119, 0.227, 0)))
Waterer answered 9/1, 2012 at 12:17 Comment(0)
M
2

Maybe if you split the conditions

if((-1.793<x) & (x < 0.652)) ...

EDIT: This seems not to be all, here is a different approach:

x<-seq(-5, 5, by=0.01)
fx <- function(x) {
    res <- rep(0, length(x))
    res[(-0.326 < x) & (x < 0.652)] <- 0.632
    res[(-1.793<x) & (x < (-1.304))] <- 0.454  
    res[(1.630<x) & (x <2.119)] <- 0.227  
    return(res)
}
fx(x)
Messier answered 9/1, 2012 at 12:2 Comment(4)
Thank you for your answer. I will have a try.Broadbrim
Be careful with those bitwise &. It's no problem in this case but better stick with &&, and likewise || instead of |.Leis
@fotNelton: This is R, not c++ :-) . There is a difference between & and &&, but neither is a bitwise operator.Curule
Thanks, good to know! I guess I'll have to look that up again. EDIT: Ah, I see. One is vectorized whereas the other one is not.Leis
H
2

Yet another option, this time using cut.

regions <- c(-Inf, -1.793, -1.304, -0.326, 0.652, 1.63, 2.119, Inf)
group <- cut(x, regions)
f_values <- c(0, 0.454, 0, 0.632, 0, 0.227, 0)
(fx <- f_values[group])
Hinze answered 9/1, 2012 at 12:53 Comment(0)
C
1

Unless you have varying cutoff points, I'd use switch . Here is an example w/ simplified cut values.

xcuts<-1:10 #the values at which you change fx assignment
xx<- seq(1.5,10,5, by =10) #vector of fx values to be selected
switch(max(which(x>xcuts)), 
1= fx<-xx[1], 
2= fx<-xx[2], 
..."et cetera"... 
) 

Loop over x.

Curule answered 9/1, 2012 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.