Compare column values against first row and retain original values in R

Viewed 73

I have a large (of course) matrix of spectroscopic data, with each column representing a different mass value, and the rows representing samples from the analysis. A small example...

mydata <- matrix(c(c(1.95,6,1,0),c(1.76,3,2,14),c(3.67,2,1.55,7),c(0.57,3,8,12),c(2.33,3,16,2)),nrow = 4, ncol = 5)
rnames <- c("threshold", "S1", "S2", "S3")
row.names(mydata)<- rnames

#           [,1]  [,2] [,3]  [,4]  [,5]
# threshold 1.95  1.76 3.67  0.57  2.33
# S1        6.00  3.00 2.00  3.00  3.00
# S2        1.00  2.00 1.55  8.00 16.00
# S3        0.00 14.00 7.00 12.00  2.00

The first row represents a threshold value, and to be considered, the sample value must be 3x the threshold. I want to compare the first row value against all values in the subsequent rows within the column, and return the cell value if it is =>3x the first row value, and replace the cell with a "0" otherwise.

So, for that small sample data, the output matrix I would hope to achieve would look like:

mydata2 <- matrix(c(c(1.95,6,0,0),c(1.76,0,0,14),c(3.67,0,0,0),c(0.57,3,8,12),c(2.33,0,16,0)),nrow = 4, ncol = 5)
row.names(mydata2) <- rnames

#           [,1]  [,2] [,3]  [,4]  [,5]
# threshold 1.95  1.76 3.67  0.57  2.33
# S1        6.00  0.00 0.00  3.00  0.00
# S2        0.00  0.00 0.00  8.00 16.00
# S3        0.00 14.00 0.00 12.00  0.00

I'm thinking there is a way to use apply to run this, but my knowledge of R does not extend that far (yet).

I should note that the threshold (first) row was initially a separate 1xn matrix, which was inserted into the first row using InsertRow. If it would be easier to compare the data matrix against a 'threshold' matrix, rather than comparing rows intra-matrix, all the better.

Thank you for your help in solving this!

4 Answers

You can repeat the first row of your matrix to the same size as the remainding rows. Then do the comparison, which gives a boolean matrix. Multiply this with the original values.

mydata[-1, ] <- mydata[-1, ] * (mydata[-1, ] >= 3 * mydata[rep(1, nrow(mydata) - 1), ])

mydata
#           [,1]  [,2] [,3]  [,4]  [,5]
# threshold 1.95  1.76 3.67  0.57  2.33
# S1        6.00  0.00 0.00  3.00  0.00
# S2        0.00  0.00 0.00  8.00 16.00
# S3        0.00 14.00 0.00 12.00  0.00

The same principle could be used if your thresholds are stored in a separate matrix.

sweep is made for this, and will be quick:

mydata[-1,][sweep(mydata[-1,], 2, mydata[1,], FUN=`/`) < 3] <- 0
mydata

#          [,1]  [,2] [,3]  [,4]  [,5]
#threshold 1.95  1.76 3.67  0.57  2.33
#S1        6.00  0.00 0.00  3.00  0.00
#S2        0.00  0.00 0.00  8.00 16.00
#S3        0.00 14.00 0.00 12.00  0.00

You definitely can use apply, and you just need to write a function with the logic you need to apply over each column.

apply(mydata, 2, function(x) c(x[1], x[-1]*(x[-1] >= 3*x[1])))

>           [,1]  [,2] [,3]  [,4]  [,5]
> threshold 1.95  1.76 3.67  0.57  2.33
> S1        6.00  0.00 0.00  3.00  0.00
> S2        0.00  0.00 0.00  8.00 16.00
> S3        0.00 14.00 0.00 12.00  0.00

The question's pretty much answered above but here's another alternative in case of having the threshold row as a separate matrix (or equivalent vector).

threshold <- c(1.95, 1.76, 3.67, 0.57, 2.33)   

Assuming mydata is the original matrix without the threshold row:

t(apply(mydata, 1, function(x) ifelse(x < 3*threshold, 0, x)))

#    [,1] [,2] [,3] [,4] [,5]
# S1    6    0    0    3    0
# S2    0    0    0    8   16
# S3    0   14    0   12    0
Related