10

I have a vector

x <- c(-1, 0, 1, 2, 3)

I want all values less than 1 to be replaced by 1.

How?

Is there a no-loop solution?

  • 3
    looking around I came across with [this](http://stackoverflow.com/a/11275230/1315767) and [this](http://stackoverflow.com/a/19571891/1315767), which can be useful. – Jilber Urbina Dec 04 '13 at 20:46

4 Answers4

29

Use logical indexing with replacement:

x[ x<1 ] <- 1
Matthew Lundberg
  • 42,009
  • 6
  • 90
  • 112
  • That's one of the winners in the benchmarks @Jilber linked to. I used to use `pmax` or `pmin`, but think I'll switch. This is just as readable and has the advantage the I won't incorrectly use `pmin` to set a minimum. – IRTFM Dec 04 '13 at 21:09
  • How to do this for all columns of a datatable? And avoid any errors if there are text values – sjd Dec 06 '18 at 12:17
10

pmax is a good candidate for this

  > pmax(x, 1)
    [1] 1 1 1 2 3
Jilber Urbina
  • 58,147
  • 10
  • 114
  • 138
7

The other solutions are more appropriate. This is just for fun:

(x > 1) * (x - 1) + 1
#[1] 1 1 1 2 3

Simple replacement (@Matthew Lundberg) is the most efficient solution:

library(microbenchmark)
microbenchmark(pmax(1, x),
               "[<-"(x, x < 1, 1),
               (x > 1) * (x - 1) + 1)

# Unit: microseconds
#                   expr    min      lq  median      uq    max neval
#             pmax(1, x) 15.494 16.2545 16.5165 16.9365 52.165   100
#     `[<-`(x, x < 1, 1)  1.466  1.6920  2.3325  2.7485 23.683   100
#  (x > 1) * (x - 1) + 1  2.084  2.2870  2.7880  3.2080  8.958   100
Sven Hohenstein
  • 80,497
  • 17
  • 145
  • 168
  • @lebatsnok This is more readable, but you need two comparisons. – Sven Hohenstein Dec 04 '13 at 22:52
  • Ok but here's something even funnier (and perhaps quite readable if you think about it): `(foo <- x <1)*1 + (!foo)*x` – lebatsnok Dec 05 '13 at 12:21
  • @lebatsnok Good idea. You can omit the `*1`. – Sven Hohenstein Dec 05 '13 at 15:01
  • 1
    or `(function(C) C + (!C) * x)(x<1)` – lebatsnok Dec 05 '13 at 20:40
  • Revisiting this a few years later, I get a lot less difference between the replacement and your expression for the `x` in the question, but the max is lower for the replacement. However, if you use a much longer vector the results are a wash for our two approaches, with `pmax` the clear winner. (`x <- sample(c(-1,0,1,2,3), 1e7, replace=TRUE)`) – Matthew Lundberg Mar 25 '17 at 21:33
2

Another option would be replace:

x <- c(-1, 0, 1, 2, 3)

replace(x, x < 1,1)
# [1] 1 1 1 2 3
loki
  • 9,816
  • 7
  • 56
  • 82