Selecting columns based on its element, not its name

Viewed 53

Suppose I want to select all the columns that contain 1.

test.dat <- tibble(
  name = c("A","B","C"),
  alert_A = c(1,1,1),
  alert_B = c(1,1,0),
  alert_C = c(1,0,1),
  alert_D = c(1,0,0),
  alert_E = c(0,0,0)
)

> test.dat
# A tibble: 3 x 6
  name  alert_A alert_B alert_C alert_D alert_E
  <chr>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
1 A           1       1       1       1       0
2 B           1       1       0       0       0
3 C           1       0       1       0       0
> test.want
# A tibble: 3 x 5
  name  alert_A alert_B alert_C alert_D
  <chr>   <dbl>   <dbl>   <dbl>   <dbl>
1 A           1       1       1       1
2 B           1       1       0       0
3 C           1       0       1       0

In tidyselect, contains() is matching based on string pattern on the column's name. What I want is to match based on whether a column contains a numeric value.

My guess is to use is.element within select, but how can I achieve this?

test.answer <- test.dat %>%
  select(name,~is.element(1,.))
#error
2 Answers

We can use where with a check on the type to return character columns (is.character) or (|) if it is a numeric type (is.numeric), then also check if any value is 1

library(dplyr)
test.dat %>% 
   select(where(~ is.character(.)||(is.numeric(.) && any(. == 1))))

-output

# A tibble: 3 x 5
#  name  alert_A alert_B alert_C alert_D
#  <chr>   <dbl>   <dbl>   <dbl>   <dbl>
#1 A           1       1       1       1
#2 B           1       1       0       0
#3 C           1       0       1       0

In base R, you can use colSums assuming you always want to include the first column.

test.dat[c(1, which(colSums(test.dat[-1] == 1) > 0) + 1)]

#   name  alert_A alert_B alert_C alert_D
#  <chr>   <dbl>   <dbl>   <dbl>   <dbl>
#1 A           1       1       1       1
#2 B           1       1       0       0
#3 C           1       0       1       0

Or with Filter :

Filter(function(x) is.character(x) || any(x == 1), test.dat)
Related