Reputation: 103
This is a problem about efficiency in R. I have two numerical vectors with names
attributes, and I want to efficiently assign the values of one vector to the other based on the common names
.
For example, the first vector is defined as:
set.seed(1);
a<-rep(NA,10);
names(a)<-1:10;
d<-a; # we will need this later
a
1 2 3 4 5 6 7 8 9 10
NA NA NA NA NA NA NA NA NA NA
and the second vector is defined as:
b<-sample(letters, 5);
names(b)<-sample(1:10, 5);
b
9 10 6 5 1
"g" "j" "n" "u" "e"
now the following code does exactly what I want, it looks for all names(b)
that are common with names(a)
and assigns to those places in a
the values of b
:
for(p in 1:length(b)){
a[which(names(a) == names(b)[p])]<-b[p]
};
a
1 2 3 4 5 6 7 8 9 10
"e" NA NA NA "u" "n" NA NA "g" "j"
My question is: is there a better more efficient way of doing this? I am dealing with much larger vectors and I keep thinking that there must be a better way of doing this.
A more sophisticated method like:
d[which(names(d) %in% names(b))]<- b
d
1 2 3 4 5 6 7 8 9 10
"g" NA NA NA "j" "n" NA NA "u" "e"
all.equal(a,d)
[1] "4 string mismatches"
produces wrong results because it requires that names(b)
and names(a)
are ordered first, which also does not seem to be an optimal strategy.
Any ideas would be greatly appreciated!
Upvotes: 4
Views: 3680
Reputation: 173547
I would probably just do this:
a[names(b)] <- b
> a
# 1 2 3 4 5 6 7 8 9 10
# "e" NA NA NA "u" "n" NA NA "g" "j"
If b
is not a subset of a
, for example:
set.seed(45)
a <- rep(NA, 10)
names(a) <- sample(10)
# 7 3 2 9 10 8 1 5 4 6
# NA NA NA NA NA NA NA NA NA NA
b <- sample(letters, 5)
names(b) <- sample(1:15, 5)
# 7 14 2 5 3
# "j" "w" "h" "k" "z"
len <- length(a)
a[names(b)] <- b
a[1:len]
# 7 3 2 9 10 8 1 5 4 6
# "j" "z" "h" NA NA NA NA "k" NA NA
Upvotes: 1
Reputation: 7784
CORRECT ANSWER:
Based on a comment from @flodel
a[match(names(b), names(a))] <- b
OLD ANSWER:
This gets close. It does not preserve the names of a
. I am not sure why. You could reassign the names of a
after the fact.
a <- b[match(names(a),names(b))]
Upvotes: 4
Reputation: 4444
a[intersect(names(b), names(a))] <- b[intersect(names(b), names(a))]
> a
1 2 3 4 5 6 7 8 9 10
"e" NA NA NA "u" "n" NA NA "g" "j"
Upvotes: 4
Reputation: 44525
Try this:
a[names(a) %in% names(b)] <- b[names(a[names(a) %in% names(b)])]
Upvotes: 1