First, you should really not rbind
the labels c('A', 'B', 'C')
to your dataframe, because this causes all the numbers in foo
to become strings, not numbers! Keep them separate (you never use that first row of foo in your code, anyway).
I can think of a few ways to do this, and I'm sure there are others I haven't thought of too.
First I'll make a matrix that's like yours but just without the c('A', 'B', 'C')
so that my numbers are actually numbers, not characters.
foo <- data.frame(matrix(rexp(150, rate=.1), ncol=15))
labels <- c('A', 'B', 'C')
colnames(foo) <- make.unique(rep(paste("ID", c(1:5), sep=""), rep(3, 5)))
First method I can think of (pretty direct) - flatten your dataframe to a vector and find the max of every 3 values, then reshape back into the shape you wanted res
to be.
foo.flat <- as.vector(t(foo)) # transpose as R is column-wise and I want row-wise
# split(foo.flat, ceiling(1:length(foo.flat)/3)) # splits into chunks of 3, so:
ms <- vapply(split(foo.flat, ceiling(1:length(foo.flat)/3)),
which.max, # function to apply to each chunk of 3
-1, # template value for vapply
USE.NAMES=F)
Now just convert 1 to A, 2 to B, 3 to C and reshape back into matrix (res
):
res <- matrix(labels[ms], byrow=T, ncol=ncol(foo)/3)
Second method I can think of - reshape your matrix to long form (reshape2
) and use plyr
to do the calculation for each (row, ID). (maybe more elegant but more confusing?, up to you)
foo$observation <- 1:nrow(foo)
library(reshape2)
foo.long <- melt(foo, id='observation', variable.name='ID')
# fix IDs, i.e. ID1.2 --> ID1 etc
foo.long$ID <- gsub('\\.[1-9]+$', '', foo.long$ID)
# > head(foo.long[order(foo.long$observation, foo.long$ID),])
# observation ID value
# 1 1 ID1 15.751959
# 11 1 ID1 20.386724
# 21 1 ID1 9.423799
# 31 1 ID2 4.560623
# 41 1 ID2 1.140642
# 51 1 ID2 37.009728
observation
is just the row each number came from, with ID
being the ID.
Now for each (observation, variable) find the index of the maximum.
library(plyr)
intermediate <- ddply(foo.long, .(observation, ID), function (x) which.max(x$value))
> head(intermediate)
# observation ID V1
# 1 1 ID1 2
# 2 1 ID2 3
# 3 1 ID3 3
# 4 1 ID4 2
# 5 1 ID5 3
# 6 2 ID1 1
Now just reshape the V1 column back to a matrix (converting indices to your labels)
res <- matrix(labels[intermediate$V1], byrow=T, ncol=floor(ncol(foo)/3)))
You could do something similar with data.table
as well which could be faster depending on the size of your matrix.