Check if any n columns contains the same value using R?

Viewed 158

I have a dataframe that looks like this:

 a    b    c    d      
 1    0    1    1      
.5    1    1    0      
.5    1   .5   .5     
 0    0    1    NA     
 0    1    0    1     

I want to check if any of the 3 columns contain the same value so the output would look like this:

 val.   count
  1      1
 .5      1 

I am trying to do this for any n number of columns. So far, I am just hard coding each scenario by using ifelse statements as shown below:

  t <- data %>%
        select(ivs) %>% 
        mutate(all = ifelse(a == 1 &
                          b == 1 &        
                          c == 1 &       
                          d == 1, 1 , 0),
               any_3 = ifelse((a == 1 &
                     b == 1 &
                     c == 1 &       
                     d == 0) | 
                    (a == 1 &
                     b == 1 &
                     c == 0 &       
                     d == 1) |
                    (a == 0 &
                     b == 1 &
                     c == 1 &       
                     d == 1)|
                    (a == 0 &
                     b == 1 &
                     c == 1 &       
                     d == 1), 1, 0 ) )
        

I'm hoping there is a better way to do this. Thank you!

2 Answers

Here is a simpler way, but the output is slightly different.
It works by

  1. Using combn to get the combinations of the (column) vectors of df1, 3 by 3;
  2. for each combination, apply an anonymous function that checks if all values are equal to the first, therefore all equal to one another.

Then, table the results and coerce to data.frame.

val <- combn(df1, 3, function(x) {
  apply(x, 1, function(y) if(!anyNA(y) & all(y == y[1])) y[1] else NA )
})
as.data.frame(table(val))
#  val Freq
#1 0.5    1
#2   1    1

Data

df1 <- read.table(text = "
a    b    c    d
 1    0    1    1
.5    1    1    0
.5    1   .5   .5
 0    0    1    NA
 0    1    0    1
", header = TRUE)

Here's an approach with purrr:pmap and dplyr::enframe. It's a bit esoteric, but doesn't require the overhead of combn.

pmap works on data.frames row wise. We can access the row with c(...). Once we have generated the counts with table, we can use map_dfr to subset the table to those with counts greater than our target. This would work if there were say 6 columns and say 3 were 1 and 3 were 2. enframe allows us to convert a named vector into a data.frame. the _dfr version of map returns a data.frame bound by row. Finally, we can tally, to count up the number of times a value has occurred anywhere in the table more than 3 times.

library(dplyr)
library(purrr)
data %>% 
   pmap(~table(c(...))) %>%
   map_dfr(~enframe(.x[.x >= 3])) %>%
   group_by(name) %>%
   tally
# A tibble: 2 x 2
  name      n
  <chr> <int>
1 0.5       1
2 1         1

I'll leave it to you to change the rownames to your liking. A weakness of this approach is that the values become character when table is called.

Related