Reputation: 4686
I have a data frame with a few columns containing values, and a column containing the name of the relevant column. e.g.
df <- data.frame(p1=c("A", "B", "A"),
p2=c("C", "C", "D"),
name=c("p2", "p1", "p1"), stringsAsFactors=FALSE)
What I want to do is to retrieve a value from the column specified by the name
field, i.e. the output as below.
> df
p1 p2 name value
1 A C p2 C
2 B C p1 B
3 A D p1 A
I currently get by with df$value <- ifelse(df$name=="p1", df$p1, ifelse(df$name=="p2", df$p2, NA))
, which is inelegant and unscalable if there are more than just p1
and p2
.
Any suggestion on a better approach for this?
Upvotes: 1
Views: 1611
Reputation: 886938
You could try
df$value <- df[cbind(seq_len(nrow(df)), match(df$name, names(df)))]
The above is a vectorized solution. Or if you need only a compact solution (based on the number of characters)
diag(as.matrix(df[,df$name]))
#[1] "C" "B" "A"
df1 <- df[rep(1:nrow(df),1e5),]
akrun <- function() {df1[cbind(seq_len(nrow(df1)),
match(df1$name, names(df1)))]}
colonel <- function() {apply(df1, 1 ,function(u) u[u['name']])}
library(microbenchmark)
microbenchmark(akrun(), colonel(), times=20L, unit='relative')
#Unit: relative
# expr min lq mean median uq max neval cld
# akrun() 1.0000 1.0000 1.00000 1.00000 1.00000 1.00000 20 a
#colonel() 118.2858 102.3968 46.25946 77.92023 59.15559 23.56562 20 b
Upvotes: 2
Reputation: 31161
Or very simply (but using a loop):
df$value = apply(df, 1 ,function(u) u[u['name']])
#> df
# p1 p2 name value
#1 A C p2 C
#2 B C p1 B
#3 A D p1 A
Upvotes: 1