3 votes

Combiner filtre, à travers, et starts_with pour rechercher des chaînes de caractères à travers les colonnes en R

Ceci est très similaire à la réponse donnée ici, mais je ne comprends pas pourquoi starts_with ne fonctionne pas :

diamonds %>% 
    filter(across(clarity, ~ grepl('^S', .))) %>% 
    head

# A tibble: 6 x 10
  carat cut       color clarity depth table price     x     y     z

1  0.23 Ideal     E     SI2      61.5    55  326  3.95  3.98  2.43
2  0.21 Premium   E     SI1      59.8    61  326  3.89  3.84  2.31
3  0.31 Good      J     SI2      63.3    58  335  4.34  4.35  2.75
4  0.26 Very Good H     SI1      61.9    55  337  4.07  4.11  2.53
5  0.3  Good      J     SI1      64.0    55  339  4.25  4.28  2.73
6  0.22 Premium   F     SI1      60.4    61  342  3.88  3.84  2.33

diamonds %>%
  filter(across(starts_with("c"),~grepl("^S" ,.))) %>% 
  head

# A tibble: 0 x 10
# ... with 10 variables: carat , cut , color , clarity , depth , table ,
#   price , x , y , z

3voto

r2evans Points 1187

Dplyr avant 1.0.4

diamonds %>%
  filter(rowSums(across(starts_with("c"),~grepl("^S" ,.))) > 0) 
# A tibble: 22,259 x 10
#    carat cut       color clarity depth table price     x     y     z
#                   
#  1  0.23 Ideal     E     SI2      61.5    55   326  3.95  3.98  2.43
#  2  0.21 Premium   E     SI1      59.8    61   326  3.89  3.84  2.31
#  3  0.31 Good      J     SI2      63.3    58   335  4.34  4.35  2.75
#  4  0.26 Very Good H     SI1      61.9    55   337  4.07  4.11  2.53
#  5  0.3  Good      J     SI1      64      55   339  4.25  4.28  2.73
#  6  0.22 Premium   F     SI1      60.4    61   342  3.88  3.84  2.33
#  7  0.31 Ideal     J     SI2      62.2    54   344  4.35  4.37  2.71
#  8  0.2  Premium   E     SI2      60.2    62   345  3.79  3.75  2.27
#  9  0.3  Ideal     I     SI2      62      54   348  4.31  4.34  2.68
# 10  0.3  Good      J     SI1      63.4    54   351  4.23  4.29  2.7 
# # ... with 22,249 more rows

Comment résoudre ou confirmer cela:

diamonds %>%
  filter({browser(); across(starts_with("c"),~grepl("^S" ,.)); })
# Appelé depuis: mask$eval_all_filter(dots, env_filter)
# debug à #1: across(starts_with("c"), ~grepl("^S", .))

across(starts_with("c"), ~ grepl("^S" , .))
# # A tibble: 53,940 x 4
#    carat cut   color clarity
#         
#  1 FALSE FALSE FALSE TRUE   
#  2 FALSE FALSE FALSE TRUE   
#  3 FALSE FALSE FALSE FALSE  
#  4 FALSE FALSE FALSE FALSE  
#  5 FALSE FALSE FALSE TRUE   
#  6 FALSE FALSE FALSE FALSE  
#  7 FALSE FALSE FALSE FALSE  
#  8 FALSE FALSE FALSE TRUE   
#  9 FALSE FALSE FALSE FALSE  
# 10 FALSE FALSE FALSE FALSE  
# # ... with 53,930 more rows

À mon avis, il semble évident que l'on voudrait toute une ligne avec au moins un TRUE (ou peut-être tous, mais je vais supposer "au moins un" pour l'instant). Comme il s'agit d'un cadre de logiques, nous pouvons utiliser rowSums, qui devrait sommer les fausses comme 0 et les vraies comme 1, donc

head(rowSums(across(starts_with("c"), ~ grepl("^S" , .))) > 0)
# [1]  TRUE  TRUE FALSE FALSE  TRUE FALSE

qui est un unique vecteur de logiques, une par ligne, ce qui est ce que dplyr::filter veut/besoin en fin de compte.

dplyr depuis 1.0.4

Voir https://www.tidyverse.org/blog/2021/02/dplyr-1-0-4-if-any/

diamonds %>%
  filter(if_any(across(starts_with("c"),~grepl("^S" ,.))))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X