Delete column containing two strings and a factor occurring once or twice in R

Viewed 44

I have following matrix with numbers of 0 and 1 containing always the same number of strings per column. The minimum number of strings in one column is 2. I would like to delete columns when they fulfill both conditions together.

  1. containing only two strings (10 and 01),
  2. and if 01 occurs only one or two times. But I would like to leave all the other columns as they are:
    r1 <- c("10","001","0001","01","100","10")
    r2 <- c("01","001","0001","10","100","10")
    r3 <- c("10","100","1000","10","010","01")
    r4 <- c("10","010","0100","10","001","10")
    r5<- c("01","010","0010","10","001","10")
    r6<- c("01","010","0010","10","001","01")
    
    n.mat <- rbind(r1,r2,r3,r4,r5,r6)

The output:

    r1 <- c("10","001","0001","100")
    r2 <- c("01","001","0001","100")
    r3 <- c("10","100","1000","010")
    r4 <- c("10","010","0100","001")
    r5<- c("01","010","0010","001")
    r6<- c("01","010","0010","001")
    
    n.mat <- rbind(r1,r2,r3,r4,r5,r6)

Removing column 4 and 6.

My code so far is:

del_two<- function(x){
  length(unique(x)) != 2
}
msa_protein.mat_1<-msa_protein.mat[, apply(msa_protein.mat, 2, del_two)] 

But I am not so sure how to add the if function.

1 Answers

You can add & to combine logical selections with "AND" logic. Though in this case I think you want to remove those values rather than keep them, so you need to negate ! the final selection:

n.mat[, apply(n.mat, 2, FUN=function(x) !(length(unique(x)) == 2 & sum(x == '01') <= 2))]

or even:

n.mat[, !apply(n.mat, 2, FUN=function(x) length(unique(x)) == 2 & sum(x == '01') <= 2)]

You could also express it as either logical condition failing, combined with | "OR" logic:

n.mat[, apply(n.mat, 2, FUN=function(x) length(unique(x)) != 2 | sum(x == '01') > 2)]

All giving:

#   [,1] [,2]  [,3]   [,4] 
#r1 "10" "001" "0001" "100"
#r2 "01" "001" "0001" "100"
#r3 "10" "100" "1000" "010"
#r4 "10" "010" "0100" "001"
#r5 "01" "010" "0010" "001"
#r6 "01" "010" "0010" "001"

There might also be tricky ways to do this using column sums, which might be substantially faster if you have massive data, e.g.:

n.mat[, !(
 (colSums(n.mat == "01") <= 2) &
 colSums(matrix(n.mat %in% c("10","01"), nrow=nrow(n.mat), ncol=ncol(n.mat))) == nrow(n.mat)
)]
Related