51

I'm looking for a function which will replace all occurrences of one value with another value. For example I'd like to replace all zeros with ones. I don't want to have to store the result in a variable, but want to be able to use the vector anonymously as part of a larger expression.

I know how to write a suitable function myself:

> vrepl <- function(haystack, needle, replacement) {
+   haystack[haystack == needle] <- replacement
+   return(haystack)
+ }
> 
> vrepl(c(3, 2, 1, 0, 4, 0), 0, 1)
[1] 3 2 1 1 4 1

But I'm wondering whether there is some standard function to do this job, preferrably from the base package, as an alternative from some other commonly used package. I believe that using such a standard will likely make my code more readable, and I won't have to redefine that function wherever I need it.

MvG
  • 57,380
  • 22
  • 148
  • 276

8 Answers8

87

Perhaps replace is what you are looking for:

> x = c(3, 2, 1, 0, 4, 0)
> replace(x, x==0, 1)
[1] 3 2 1 1 4 1

Or, if you don't have x (any specific reason why not?):

replace(c(3, 2, 1, 0, 4, 0), c(3, 2, 1, 0, 4, 0)==0, 1)

Many people are familiar with gsub, so you can also try either of the following:

as.numeric(gsub(0, 1, x))
as.numeric(gsub(0, 1, c(3, 2, 1, 0, 4, 0)))

Update

After reading the comments, perhaps with is an option:

with(data.frame(x = c(3, 2, 1, 0, 4, 0)), replace(x, x == 0, 1))
A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
  • 5
    Remember that this doesn't change `x` unless reassigned. – A5C1D2H2I1M1N2O1R2T1 Jul 31 '12 at 10:06
  • The specific reason why not to name `x` is that the expression computing `x` might itself be rather long. And I want to avoid clobbering my namespace with too many variables. So I had hoped for a way to avoid having to name the vector, or having to duplicate its expression. `gsub` and its character intermediate doesn't feel right either, in terms of performance as well as precision, particularly when dealing with floating point numbers. – MvG Jul 31 '12 at 10:21
  • I definitely could and should use `replace` in my own `vrepl` implementation, unless someone will come up with an answer which obsoletes my own function altogether. So thanks for pointing that out! – MvG Jul 31 '12 at 10:24
  • @MvG, what about: `with(data.frame(x = c(3, 2, 1, 0, 4, 0)), replace(x, x == 0, 1))`? – A5C1D2H2I1M1N2O1R2T1 Jul 31 '12 at 17:32
20

Another simpler option is to do:

 > x = c(1, 1, 2, 4, 5, 2, 1, 3, 2)
 > x[x==1] <- 0
 > x
 [1] 0 0 2 4 5 2 0 3 2
nico
  • 50,859
  • 17
  • 87
  • 112
  • 1
    This requires saving the intermediate result to a named variable, which I want to avoid, as I stated in my question. – MvG Jul 31 '12 at 10:25
  • @MvG: sorry, missed that part. Anyway it is much more maintainable to save it in a variable – nico Jul 31 '12 at 12:54
5

A simple way to do this is using base ifelse, which is vectorized (there's also if_else from dplyr that handles missing values). If the condition is satisfied, we use a replacement value, otherwise we use the original value.

v <- c(3, 2, 1, 0, 4, 0)
ifelse(v == 0, 1, v)

We can avoid a named variable by using a pipe.

c(3, 2, 1, 0, 4, 0) %>% ifelse(. == 0, 1, .)

A common task is to do multiple replacements. Instead of nested ifelse statements, we can use case_when from dplyr:

case_when(v == 0 ~ 1,
          v == 1 ~ 2,
          TRUE ~ v)

Old answer:

For factor or character vectors, we can use revalue from plyr:

> revalue(c("a", "b", "c"), c("b" = "B"))
[1] "a" "B" "c"

This has the advantage of only specifying the input vector once, so we can use a pipe like

x %>% revalue(c("b" = "B"))
qwr
  • 9,525
  • 5
  • 58
  • 102
3

To replace more than one number:

vec <- 1:10
replace(vec, vec== c(2,6), c(0,9)) #2 and 6 will be replaced by 0 and 9.

Edit:

for a continous series, you can do this vec <- c(1:10); replace(vec, vec %in% c(2,6), c(0,9)) but for vec <- c(1:10,2,2,2); replace(vec, vec %in% c(2,6), 0) we can replace multiple values with one value.

Angel
  • 184
  • 1
  • 14
TheMI
  • 1,715
  • 1
  • 14
  • 13
  • 10
    >Warning message: In replace(vec, vec == c(2, 6), c(0, 9)) : number of items to replace is not a multiple of replacement length – Rorschach Jul 25 '15 at 14:28
3

Why the fuss?

replace(haystack, haystack %in% needles, replacements)

Demo:

haystack <- c("q", "w", "e", "r", "t", "y")
needles <- c("q", "w")
replacements <- c("a", "z")

replace(haystack, haystack %in% needles, replacements)
#> [1] "a" "z" "e" "r" "t" "y"
MS Berends
  • 4,489
  • 1
  • 40
  • 53
  • Note this answer assumes needles and replacements are 1-to-1 (recycling vectors if necessary). – qwr Aug 01 '19 at 05:10
  • 3
    This depends on the sorting of `haystack`, `needles` and `replacements`, too. Switch the latter to `c("w", "q")` and `c("z", "a")` and `replace()` will produce nonsense. – Jan Marvin Jul 23 '21 at 15:31
3

If someone is looking for a dplyr alternative to replace, case_match is a simple variant to case_when that works on vectors. It is available since 1.1.0.

x = c(3, 2, 1, 0, 4, 0)
case_match(x, 0 ~ 1, .default = x)
#[1] 3 2 1 1 4 1

It can also work with multiple replacements:

x = c(3, 2, 1, 0, 4, 0)
case_match(x, 
           c(0, 2, 4) ~ "even",
           c(1, 3) ~ "odd")
#[1] "odd"  "even" "odd"  "even" "even" "even"
Maël
  • 45,206
  • 3
  • 29
  • 67
2

The ifelse function would be a quick and easy way to do this.

Greg Snow
  • 48,497
  • 6
  • 83
  • 110
  • 1
    I would have to give the same vector twice, once for the `test` argument, and once as one of the result arguments, right? Doesn't seem any easier than the `replace` call mrdwab suggested. – MvG Jul 31 '12 at 20:53
  • Correct, but you could save it in a temporary variable and just reference that twice. `ifelse` and `replace` will both do the job. – Greg Snow Jul 31 '12 at 21:13
  • +1 this is actually a lot better than the correct solution, because it gives the extra option of applying a function to the values. Thanks @Greg Snow! – GerasimosPanagiotakopoulos Nov 27 '17 at 08:51
  • 2
    It would be helpful to provide an example of use. – qwr Nov 11 '18 at 22:34
1

If you want to replace lot of values in single go, you can use 'library(car)'.

Example

library(car)

x <- rep(1:5,3)

xr <- recode(x, '3=1; 4=2')

x
## [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
xr
## [1] 1 2 1 2 5 1 2 1 2 5 1 2 1 2 5
Soumya Boral
  • 1,191
  • 14
  • 28