1

I'm trying to get this function to work and to run faster. It takes as input a matrix, and checks to see if x and y are both greater than 0 but less than 10. Having it run faster is also key because it currently takes long with all of these conditional statements

m 
   x  y
B -1 -1
C 50 11
D 50  5
A 51 10

val_10 = 10
myfun(m, val_10)

and the output should looks like this

     [,1] [,2]
[1,]    0   0
[2,]   10   10
[3,]   10    5
[4,]   10   10

but instead it looks like this

     [,1] [,2]
[1,]    0   -1
[2,]   10   11
[3,]   10    5
[4,]   10   10

m[1,2] should be 0 and m[2,2] should be 10 in the output

myfun looks like this

myfun = function(m, val_10){
v = matrix(NA, nrow = nrow(m), ncol = 2)
for (i in 1:nrow(m) ) {
    old = m[i, ]

    #check if smaller than 0
    if (m[i,1] < 0) {
        m[i, ] = c(0, old[2])
    }

    #check if bigger than val_10
    else if (m[i,1] > val_10 ){ 
        m[i, ] = c(val_10, old[2] )
    }

    #check if smaller than 0
    else if (m[i, 2] < 0){
        m[i, ] = c(old[1], 0)
    }

    #check if bigger than val_10
    else if (m[i, 2] > val_10){
        m[i, ] = c(old[1], val_10 )
    }

    v[i,] = m[i, ]
}
return(v)
}

Edit

this works for the example matrix, but not for another example. This matrix gave me the value "5" for the entire y column

   x y
B 49 6
C 50 7
D 50 5
A 51 6

`dim<-`(c(0,5,val_10)[(findInterval(m, c(5, val_10))+1L)], dim(m))

     [,1] [,2]
[1,]   10    5
[2,]   10    5
[3,]   10    5
[4,]   10    5
user3067923
  • 437
  • 2
  • 14
  • This is sometimes call a '[clamp](http://stackoverflow.com/search?q=%5Br%5D+clamp)' (the first two hits at least are useful), and @josilber suggests [this approach](http://stackoverflow.com/questions/32599695/clamp-variable-within-range/32599818#32599818): `pmin(pmax(m, 0), 10)` – Martin Morgan Jan 30 '16 at 10:37

3 Answers3

4

A simple option may consist in logical subsetting and replacement, like this:

m[m < 0] <- 0
m[m > 10] <- 10
#> m
#   x  y
#B  0  0
#C 10 10
#D 10  5
#A 10 10

In this vectorized approach, any explicit if or ifelse statement is avoided.

If you want to wrap this into a function you might use

my_fun <- function(m,val) {m[m > val] <- val; m[m < 0] <- 0; return(m)}     

data:

m <- structure(c(-1L, 50L, 50L, 51L, -1L, 11L, 5L, 10L), .Dim = c(4L,2L), 
                    .Dimnames = list(c("B", "C", "D", "A"), c("x", "y")))
RHertel
  • 23,412
  • 5
  • 38
  • 64
1

We can try findInterval

myfun <- function(mat, val){
 i1 <- findInterval(mat, c(0, val))
 `dim<-`(ifelse(i1==1, mat, c(0,val)[i1]), dim(mat))
}

myfun(m, 10)
#     [,1] [,2]
#[1,]   10    0
#[2,]   10   10
#[3,]   10    5
#[4,]   10   10

Using the new example

myfun(m2, 10)
#     [,1] [,2]
#[1,]   10    6
#[2,]   10    7
#[3,]   10    5
#[4,]   10    6

data

m <- structure(c(-1L, 50L, 50L, 51L, -1L, 11L, 5L, 10L),
.Dim = c(4L, 
2L), .Dimnames = list(c("B", "C", "D", "A"), c("x", "y")))

m2 <- cbind(c(49, 50, 50, 51), c(6, 7, 5, 6))
akrun
  • 874,273
  • 37
  • 540
  • 662
  • this works for the example matrix, but not for another example. This matrix gave me the value "5" for the entire y column (49, 6) (50, 7) (50, 5) (51, 6) – user3067923 Jan 30 '16 at 07:37
1

this one gets me the result you're looking with only the ifelse function:

in_vec <- c(-1,50,50,51,-1,11,5,10)
mtx <- matrix(in_vec, nrow = 4, byrow = F)
my_val <- 10
ifelse(mtx < 0, 0, ifelse(mtx >= my_val, my_val, mtx))

In general, this looks like a pretty good case for using a nested ifelse function. I certainly use that function on a regular basis at work and it is much simpler for vectorized tasks like this than a cumbersome for-loop / if-else combination.

Alex Thompson
  • 506
  • 5
  • 13