Skip to content

tidyselect + where VS if_any/if_all, and error messages #5732

Description

@jthomasmock

Number one, extremely happy to have the new features for filter and/or mutate() with if_any/if_all!!! This feature was not something I expected but am so excited to have!!!

🥳

There is one problem I'd like to raise and one question:

Problem: The error message for using if_any() without a column selector results in some potentially bad recommendations (related to my question as well).

Question: What is the "correct" way of select-ing columns based on values within that column?


While I understand the use of if_any with filter(), I'm also interested in the similar option for select(). when cleaning data or working with "wide" data that I'm putting into a table I fairly often want to select() specific columns that contain a specific value/string/range/etc. IE just knowing it's numeric or text is not sufficient, but rather I'm interested in specifics of the column's contents.

I have a reprex below that shows some of my testing steps in trying to answer this question.

library(dplyr, warn.conflicts = FALSE)
library(palmerpenguins)
#> Warning: package 'palmerpenguins' was built under R version 4.0.2


# Filter specific columns 

ex1 <- penguins %>% 
  filter(if_any(contains("bill"), ~.x == 39.1| .x == 18.7))

ex1
#> # A tibble: 6 x 8
#>   species island bill_length_mm bill_depth_mm flipper_length_… body_mass_g sex  
#>   <fct>   <fct>           <dbl>         <dbl>            <int>       <int> <fct>
#> 1 Adelie  Torge…           39.1          18.7              181        3750 male 
#> 2 Adelie  Biscoe           37.7          18.7              180        3600 male 
#> 3 Adelie  Dream            39            18.7              185        3650 male 
#> 4 Chinst… Dream            45.4          18.7              188        3525 fema…
#> 5 Chinst… Dream            51.5          18.7              187        3250 male 
#> 6 Chinst… Dream            50.2          18.7              198        3775 fema…
#> # … with 1 more variable: year <int>

# I realize this is a toy example 
# that could be solved by normal filter
ex2 <- penguins %>% 
  filter(bill_length_mm == 39.1| bill_depth_mm == 18.7)

all.equal(ex1, ex2)
#> [1] TRUE

# Skip tidyselect ---------------------------------------------------------

# What happens when you don't indicate tidyselect or colname?
# Throws a useful error but in this case leads down
# a not very useful path
penguins %>% 
  filter(if_any(~.x == 39.1))
#> Error: Problem with `filter()` input `..1`.
#> x Formula shorthand must be wrapped in `where()`.
#> 
#>   # Bad
#>   data %>% select(~.x == 39.1)
#> 
#>   # Good
#>   data %>% select(where(~.x == 39.1))
#> ℹ Input `..1` is `if_any(~.x == 39.1)`.

# So then let's follow the recommendation of:
# data %>% select(where(~.x == 39.1))
# this fails because it's not vectorized I believe?

penguins %>% 
  select(where(~.x == 39.1))
#> Error: `where()` must be used with functions that return `TRUE` or `FALSE`.

# Still fails with everything() indicated
penguins %>% 
  select(where(if_any(everything(), ~.x == 39.1)))
#> Error: `across()` must only be used inside dplyr verbs.

# Use base::any ---------------------------------------------------------

# So for this behavior, I want to select a specific column
# by values present in that column
# this WILL work if you use base::any() without any extra work!!!
# But I wanted to make sure this is a good idea?

penguins %>% 
  select(where(~any(.x == 36.7, na.rm = TRUE)))
#> # A tibble: 344 x 1
#>    bill_length_mm
#>             <dbl>
#>  1           39.1
#>  2           39.5
#>  3           40.3
#>  4           NA  
#>  5           36.7
#>  6           39.3
#>  7           38.9
#>  8           39.2
#>  9           34.1
#> 10           42  
#> # … with 334 more rows

Created on 2021-02-03 by the reprex package (v0.3.0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    featurea feature request or enhancementselection 🧺tidyselect, scoped verbs, etc.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions