A S
A S

Reputation: 1235

How to select only positive rows from a matrix?

It must be something very obvious but somehow I can't figure it out...

Let's say I have a matrix M like this:

       A          B             name.C     name.D         name.E
row 1  0.05160408 0.11090351    1.0206272 -0.318932333    0.2983051
row 2  0.04906006 0.10500725    0.9654350 -0.253736273    0.2883013
row 3  0.04651603 0.09940203    0.9102428 -0.188540213    0.2782974
row 4  0.04397201 0.09413984    0.8550506 -0.123344153    0.2682935
row 5  0.04142799 0.08928137    0.7998585 -0.058148093    0.2582896
row 6  0.03888397 0.08489596    0.7446663  0.007047967    0.2482858
row 7  0.03633994 0.08106041    0.6894741  0.072244028    0.2382819
row 8  0.03379592 0.07785605    0.6342819  0.137440088    0.2282780

And I want to subset a matrix with only rows where all the values in names.* columns are positive.

I can produce a logical matrix like that: M[grep("^name",colnames(M))] > 0. But how to get the real numbers then? As a matrix, again, not as a vector?

Upvotes: 0

Views: 3763

Answers (3)

agstudy
agstudy

Reputation: 121568

Personaly I would use rowSums for such task , but you can also use another method (for fun) based on %*% operator :

indx <- grep('^name', colnames(mat)) 
xx = (mat[,indx] >0)%*%t(mat[,indx] >0)
mat[colnames(xx)[diag(xx)==length(indx)],]

#               A          B    name.C      name.D    name.E
# row 6 0.03888397 0.08489596 0.7446663 0.007047967 0.2482858
# row 7 0.03633994 0.08106041 0.6894741 0.072244028 0.2382819
# row 8 0.03379592 0.07785605 0.6342819 0.137440088 0.2282780

Upvotes: 2

hvollmeier
hvollmeier

Reputation: 2986

Taking the data from @Akrun above a oneliner would be:

m1[-unique(which(m1[,3:5] < 0) %% nrow(m1)),]

explanation:

which(m1[,3:5] < 0)

gives you the indices of the negative numbers in the columns you are interested in and with %% nrow(m1) you get the row indices where you find negative numbers. You use unique as we are only interested in the unique rows.

Upvotes: 1

akrun
akrun

Reputation: 887098

You could create a column index using grep for the column names that start with name. Then subset those columns and check whether the elements are greater than (>) 0. Then do the sum of each row of the logical index with rowSums and check if that equals to the length of indx. Use that to subset the rows of m1.

 indx <- grep('^name', colnames(m1))
 m1[rowSums(m1[,indx]>0)==length(indx),]
 #              A          B    name.C      name.D    name.E
 #row 6 0.03888397 0.08489596 0.7446663 0.007047967 0.2482858
 #row 7 0.03633994 0.08106041 0.6894741 0.072244028 0.2382819
 #row 8 0.03379592 0.07785605 0.6342819 0.137440088 0.2282780

Another option would to convert the logical index columns (m1[,indx]>0) to data.frame and check whether each row have all TRUE using & and Reduce

 m1[Reduce(`&`, as.data.frame(m1[,indx] >0)),]

Or you could use apply with MARGIN=1 to check the condition for each row. But, this method may be a bit slower for large datasets. (from @David Arenburg's comments)

  m1[apply(m1[, indx], 1, function(x) all(x > 0)), ]
 

data

 m1 <- structure(c(0.05160408, 0.04906006, 0.04651603, 0.04397201, 0.04142799, 
 0.03888397, 0.03633994, 0.03379592, 0.11090351, 0.10500725, 0.09940203, 
 0.09413984, 0.08928137, 0.08489596, 0.08106041, 0.07785605, 1.0206272, 
 0.965435, 0.9102428, 0.8550506, 0.7998585, 0.7446663, 0.6894741, 
 0.6342819, -0.318932333, -0.253736273, -0.188540213, -0.123344153, 
 -0.058148093, 0.007047967, 0.072244028, 0.137440088, 0.2983051, 
 0.2883013, 0.2782974, 0.2682935, 0.2582896, 0.2482858, 0.2382819, 
 0.228278), .Dim = c(8L, 5L), .Dimnames = list(c("row 1", "row 2", 
 "row 3", "row 4", "row 5", "row 6", "row 7", "row 8"), c("A", 
 "B", "name.C", "name.D", "name.E")))

Upvotes: 3

Related Questions