Reputation: 1833
How do I change 3 values as a set in a vector, eg:
v=c(1,1,1,1,1,6,2,3,4,4,4,4,4,2)
r=v[which(v %in% c(6,2,3))] = c(6,0,8)
r
[1] 1 1 1 1 1 6 0 8 4 4 4 4 4 6
The result comes with a warning: number of items to replace is not a multiple of replacement length.
The idea result I need is :
[1] 1 1 1 1 1 6 0 8 4 4 4 4 4 2
I only want the 3 values to change when they are as a set/group, not individually, any suggestion would be great appreciated!
Edit: I'm Sorry guys, actually there are more than one set of 6,2,3 in my data, the example should be look like:
v=c(1,1,2,1,1,6,2,3,4,4,4,4,4,2,6,2,3,4)
And the result be:
[1] 1 1 2 1 1 6 0 8 4 4 4 4 4 2 6 0 8 4
Upvotes: 0
Views: 411
Reputation: 81743
A quite different way using regular expressions:
as.numeric(strsplit(gsub("6 2 3", "6 2 8", paste(v, collapse = " ")), " ")[[1]])
# [1] 1 1 1 1 1 6 2 8 4 4 4 4 4 2
This will replace all instances of c(6, 2, 3)
.
Upvotes: 1
Reputation: 93938
Base R function using rle
seqrepl <- function(v,find,repl) {
l <- length(find)
rv <- rle(v %in% find)
start <- cumsum(rv$lengths)[which(rv$lengths==l & rv$values==TRUE)-1]+1
v[start:(start+(l-1))] <- repl
v
}
Result:
seqrepl(v=v,find=c(6,2,3),repl=c(6,0,8))
#[1] 1 1 1 1 1 6 0 8 4 4 4 4 4 2
An alternative using embed
:
seqrepl2 <- function(v,find,repl) {
l <- length(find)
start <- which(colSums(t(embed(v,l)) == rev(find)) == l)
v[start:(start+(l-1))] <- repl
v
}
seqrepl2(v=v,find=c(6,2,3),repl=c(6,0,8))
#[1] 1 1 1 1 1 6 0 8 4 4 4 4 4 2
Upvotes: 1
Reputation: 43265
You can use rollapply
from the zoo
package:
rollapply(v, width=3, function(x) all(x == c(6, 2, 3)))
# [1] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
And then you can use that boolean to replace the values you want:
s <- which(rollapply(v, width=3, function(x) all(x == c(6, 2, 3))))
v[s:(s+2)] <- c(6, 0, 8)
In one happy function:
rolling_replace <- function(v, p, r) {
l <- length(r)
s <- which(rollapply(v, width=l, function(x) all(x == p)))
v[s:(s+l-1)] <- r
return (v)
}
Upvotes: 4