Is it possible to use `ifelse` in a dplyr chain to specify whether or not to filter a data set?

dplyr

#1

I have a dataframe of students with a school ID. I want to run a set of reports for each school, as well as the board. Filters in the report are based on the school name, but I also want the same report for the board.

What I want, is to put an ifelse statement into the filter line, where if group == school, filter the data, of group == board, then do not filter the data,

School <- c ("School A", "School B", "School C", "School D")
N <- c (123, 245, 333, 298)

df <- data.frame(School, N)

group <- "School A"

Something like

df %>%
  ifelse (group == "School A", (dplyr::filter(School == "School A")),
          df)

#2

It can be done in the following way:

library(tidyverse)
School <- c ("School A", "School B", "School C", "School D")
N <- c (123, 245, 333, 298)

df <- data.frame(School, N)

group <- "School A"

to_be <- function(df, group){
  if(group == "School A")
    dplyr::filter(df, School == group)
  else df
}

df %>%
  to_be(group = "board")
#>     School   N
#> 1 School A 123
#> 2 School B 245
#> 3 School C 333
#> 4 School D 298

df %>%
  to_be(group = "School A")
#>     School   N
#> 1 School A 123

Created on 2018-06-28 by the reprex package (v0.2.0).


#3

Close, but I need it to run for all schools. I did a slight modifcation:

to_be <- function(df, group){
  if(group == group)
    dplyr::filter(df, School == group)
  else df
}

And it runs for all four schools:

group <- "School A"
df %>%
   to_be(group = group)
#>    School   N
#> 1 School A 123

group <- "School B"
 df %>%
   to_be(group = group)
#>   School   N
#> 1 School B 245

group <- "School C"
df %>%
  to_be(group = group)
#>   School   N
#> 1 School C 333

group <- "School D"
df %>%
  to_be(group = group)
#>    School   N
#>1 School D 298

However, when I use the board, it returns an empty data frame

group <- "Board"
df %>%
  to_be(group = group)
# [1] School N     
# <0 rows> (or 0-length row.names)

#4

Then it's easier to achieve with grepl:

library(tidyverse)
School <- c ("School A", "School B", "School C", "School D")
N <- c (123, 245, 333, 298)

df <- data.frame(School, N)

group <- "School A"

to_be <- function(df, group){
  if(grepl(pattern = "School ", x = group))
    dplyr::filter(df, School == group)
  else df
}

df %>%
  to_be(group = "board")
#>     School   N
#> 1 School A 123
#> 2 School B 245
#> 3 School C 333
#> 4 School D 298

df %>%
  to_be(group = "School A")
#>     School   N
#> 1 School A 123

group <- "School B"
df %>%
  to_be(group = group)
#>     School   N
#> 1 School B 245

group <- "School C"
df %>%
  to_be(group = group)
#>     School   N
#> 1 School C 333

group <- "School D"
df %>%
  to_be(group = group)
#>     School   N
#> 1 School D 298

Created on 2018-06-28 by the reprex package (v0.2.0).


#5

Awesome, thank you. I switched it around as my schools don't actually have "School" in their name

to_be <- function(df, group){
  if(grepl(pattern = "Board", x = group))
    df
  else (dplyr::filter(df, School == group))
  
}

#6

You could use %in%:

## School-specific report
group <- "School A"
df %>%
  filter(School %in% group)
#     School   N
# 1 School A 123

## Board report
group <- c("School A", "School B", "School C", "School D")
df %>%
  filter(School %in% group)
#     School   N
# 1 School A 123
# 2 School B 245
# 3 School C 333
# 4 School D 298

#7

That works only for the schools. If group == "Board" then it doesn't fit my needs.


#8

You might want to modify your grepl() based solution a bit, because right now it will apply the "Board" filter if that string is found anywhere in a school name — it's not exclusive to the specific string "Board". This may or may not matter given your list of school names, but in principle it would be better to modify that regular expression so that it actually represents your intention. Contrived example:

library(tidyverse)
df <- data.frame(
  School = c("School A", "School B", "School C", "School D", "Boarding School"), 
  N = c(123, 245, 333, 298, 321)
)

# This could have unintended consequences
to_be <- function(df, group){
  if(grepl(pattern = "Board", x = group))
    df
  else (dplyr::filter(df, School == group))
  
}

df %>% to_be("School A")
#>     School   N
#> 1 School A 123
df %>% to_be("Boarding School")
#>            School   N
#> 1        School A 123
#> 2        School B 245
#> 3        School C 333
#> 4        School D 298
#> 5 Boarding School 321

# Use a more precise regular expression
to_be2 <- function(df, group){
  if(grepl(pattern = "^Board$", x = group))
    df
  else (dplyr::filter(df, School == group))
  
}

df %>% to_be2("School A")
#>     School   N
#> 1 School A 123
df %>% to_be2("Boarding School")
#>            School   N
#> 1 Boarding School 321
df %>% to_be2("Board")
#>            School   N
#> 1        School A 123
#> 2        School B 245
#> 3        School C 333
#> 4        School D 298
#> 5 Boarding School 321

While we're tossing out solutions, I might do something like this:

filter_for <- function(data, group) {
  schools <- unique(data$School)
  group <- switch(EXPR = group,
                  "Board" = schools,
                  group)
  
  data %>%
    dplyr::filter(School %in% group)
}

df %>% filter_for("School A")
#>     School   N
#> 1 School A 123
df %>% filter_for("Board")
#>            School   N
#> 1        School A 123
#> 2        School B 245
#> 3        School C 333
#> 4        School D 298
#> 5 Boarding School 321