Filtrer les lignes contenant une certaine chaîne

188

Je dois filtrer une trame de données en utilisant comme critère les lignes dans lesquelles est contenue la chaîne RTB.

J'utilise dplyr.

d.del <- df %>%
  group_by(TrackingPixel) %>%
  summarise(MonthDelivery = as.integer(sum(Revenue))) %>%
  arrange(desc(MonthDelivery))

Je sais que je peux utiliser la fonction filterdans dplyrmais je ne sais pas exactement comment lui dire de vérifier le contenu d'une chaîne.

En particulier, je souhaite vérifier le contenu de la colonne TrackingPixel. Si la chaîne contient l'étiquette, RTBje souhaite supprimer la ligne du résultat.

Gianluca
la source
28
Je n'ai jamais utilisé dplyr, mais en regardant l'aide, ?dplyr::filterje suggérerais quelque chose comme filter(df, !grepl("RTB",TrackingPixel))peut-être?
thelatemail
2
C'est en fait proche de ce que je veux réaliser. Le seul problème est de conserver les chaînes qui incluent l'étiquette RTBet de ne pas afficher les autres.
Gianluca
Je viens de mettre une modification furtive, qui est maintenant inversée en ajoutant le !devant grepl- réessayez.
thelatemail
4
Ou utilisez les arguments invertet valuede grep. Les expressions régulières facilitent mille fois le travail avec du texte.
Rich Scriven
4
@thelatemail greplne fonctionne pas sur postgres pour moi, est-ce pour MySQL?
Statwonk

Réponses:

257

La réponse à la question a déjà été publiée par le @latemail dans les commentaires ci-dessus. Vous pouvez utiliser des expressions régulières pour le deuxième argument et les suivants filtercomme ceci:

dplyr::filter(df, !grepl("RTB",TrackingPixel))

Puisque vous n'avez pas fourni les données d'origine, j'ajouterai un exemple de jouet utilisant l' mtcarsensemble de données. Imaginez que vous ne soyez intéressé que par les voitures produites par Mazda ou Toyota.

mtcars$type <- rownames(mtcars)
dplyr::filter(mtcars, grepl('Toyota|Mazda', type))

   mpg cyl  disp  hp drat    wt  qsec vs am gear carb           type
1 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4      Mazda RX4
2 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4  Mazda RX4 Wag
3 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1 Toyota Corolla
4 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1  Toyota Corona

Si vous souhaitez faire l'inverse, à savoir exclure les voitures Toyota et Mazda, la filtercommande ressemble à ceci:

dplyr::filter(mtcars, !grepl('Toyota|Mazda', type))
alex23lemm
la source
et si le nom de la colonne contient un espace. comme Tracking Pixels.
MySchizoBuddy
3
assurez-vous que vous utilisez la fonction de filtrage du package dplyr, pas le package stats
JHowIX
2
@MySchizoBuddy: Si le nom de la colonne contient un espace blanc, vous pouvez sélectionner la variable en utilisant des contre-indications. Modification de l'exemple ci-dessus: mtcars$`my type` <- rownames(mtcars)puismtcars %>% filter(grepl('Toyota|Mazda', `my type`))
alex23lemm
13
notez que cela ne fonctionne pas lorsque l'objet est un tbl_sqlas greplne se traduit pas en sql.
David LeBauer
l'option 1 est de savoir avec certitude que dplyr est chargé en dernier. l'option 2 est votre préfixe dplyr :: filter.
userJT
157

Solution

Il est possible d'utiliser str_detectle stringrpackage inclus dans le tidyversepackage. str_detectrenvoie Trueou Falseindique si le vecteur spécifié contient une chaîne spécifique. Il est possible de filtrer en utilisant cette valeur booléenne. Voir Introduction à stringr pour plus de détails sur le stringrpackage.

library(tidyverse)
# ─ Attaching packages ──────────────────── tidyverse 1.2.1 ─
# ✔ ggplot2 2.2.1     ✔ purrr   0.2.4
# ✔ tibble  1.4.2     ✔ dplyr   0.7.4
# ✔ tidyr   0.7.2     ✔ stringr 1.2.0
# ✔ readr   1.1.1     ✔ forcats 0.3.0
# ─ Conflicts ───────────────────── tidyverse_conflicts() ─
# ✖ dplyr::filter() masks stats::filter()
# ✖ dplyr::lag()    masks stats::lag()

mtcars$type <- rownames(mtcars)
mtcars %>%
  filter(str_detect(type, 'Toyota|Mazda'))
# mpg cyl  disp  hp drat    wt  qsec vs am gear carb           type
# 1 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4      Mazda RX4
# 2 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4  Mazda RX4 Wag
# 3 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1 Toyota Corolla
# 4 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1  Toyota Corona

Les bonnes choses à propos de Stringr

Nous devrions utiliser plutôt stringr::str_detect()que base::grepl(). C'est parce qu'il y a les raisons suivantes.

  • Les fonctions fournies par le stringrpackage commencent par le préfixe str_, ce qui facilite la lecture du code.
  • Le premier argument des fonctions de stringrpackage est toujours le data.frame (ou value), puis vient les paramètres. (Merci Paolo)
object <- "stringr"
# The functions with the same prefix `str_`.
# The first argument is an object.
stringr::str_count(object) # -> 7
stringr::str_sub(object, 1, 3) # -> "str"
stringr::str_detect(object, "str") # -> TRUE
stringr::str_replace(object, "str", "") # -> "ingr"
# The function names without common points.
# The position of the argument of the object also does not match.
base::nchar(object) # -> 7
base::substr(object, 1, 3) # -> "str"
base::grepl("str", object) # -> TRUE
base::sub("str", "", object) # -> "ingr"

Référence

Les résultats du test de référence sont les suivants. Pour les grandes trames de données, str_detectc'est plus rapide.

library(rbenchmark)
library(tidyverse)

# The data. Data expo 09. ASA Statistics Computing and Graphics 
# http://stat-computing.org/dataexpo/2009/the-data.html
df <- read_csv("Downloads/2008.csv")
print(dim(df))
# [1] 7009728      29

benchmark(
  "str_detect" = {df %>% filter(str_detect(Dest, 'MCO|BWI'))},
  "grepl" = {df %>% filter(grepl('MCO|BWI', Dest))},
  replications = 10,
  columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))
# test replications elapsed relative user.self sys.self
# 2      grepl           10  16.480    1.513    16.195    0.248
# 1 str_detect           10  10.891    1.000     9.594    1.281
Keiku
la source
1
Pourquoi stringr est-il une meilleure option que grep?
CameronNemo
2
@CameronNemo Les fonctions fournies par le stringrpackage commencent par le préfixe str_, ce qui facilite la lecture du code. Dans le code R moderne récent, il est recommandé d'utiliser stringr.
Keiku
3
Je pense que c'est une préférence très personnelle et je suis d'accord avec @CameronNemo qui base Rest aussi bon que stringr. Si vous pouviez nous fournir des «faits concrets» tels que l'analyse comparative, et non seulement dire «c'est recommandé» (qui le recommande?), Ce serait très apprécié. Merci
Tjebo
2
Une autre raison est la cohérence dans le framework tidyverse: le premier argument d'une fonction est toujours le data.frame (ou value), puis viennent les paramètres.
Paolo
22

Cette réponse est similaire à d'autres, mais en utilisant préféré stringr::str_detectet déplyr rownames_to_column.

library(tidyverse)

mtcars %>% 
  rownames_to_column("type") %>% 
  filter(stringr::str_detect(type, 'Toyota|Mazda') )

#>             type  mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#> 1      Mazda RX4 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#> 2  Mazda RX4 Wag 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
#> 3 Toyota Corolla 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
#> 4  Toyota Corona 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1

Créé le 2018-06-26 par le package reprex (v0.2.0).

Ortie
la source
1
str_detectest dans le stringrpaquet
jsta
4

edit a inclus la nouvelle across()syntaxe

Voici une autre tidyversesolution, en utilisant filter(across())ou précédemment filter_at. L'avantage est que vous pouvez facilement étendre à plus d'une colonne .

Ci-dessous également une solution avec filter_allpour trouver la chaîne dans n'importe quelle colonne, en utilisant diamondscomme exemple, la recherche de la chaîne "V"

library(tidyverse)

Chaîne dans une seule colonne

# for only one column... extendable to more than one creating a column list in `across` or `vars`!
mtcars %>% 
  rownames_to_column("type") %>% 
  filter(across(type, ~ !grepl('Toyota|Mazda', .))) %>%
  head()
#>                type  mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#> 1        Datsun 710 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
#> 2    Hornet 4 Drive 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
#> 3 Hornet Sportabout 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
#> 4           Valiant 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
#> 5        Duster 360 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
#> 6         Merc 240D 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2

La syntaxe maintenant remplacée pour le même serait:

mtcars %>% 
  rownames_to_column("type") %>% 
  filter_at(.vars= vars(type), all_vars(!grepl('Toyota|Mazda',.))) 

Chaîne dans toutes les colonnes:

# remove all rows where any column contains 'V'
diamonds %>%
  filter(across(everything(), ~ !grepl('V', .))) %>%
  head
#> # A tibble: 6 x 10
#>   carat cut     color clarity depth table price     x     y     z
#>   <dbl> <ord>   <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
#> 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.3  Good    J     SI1      64      55   339  4.25  4.28  2.73
#> 5  0.22 Premium F     SI1      60.4    61   342  3.88  3.84  2.33
#> 6  0.31 Ideal   J     SI2      62.2    54   344  4.35  4.37  2.71

La syntaxe maintenant remplacée pour le même serait:

diamonds %>% 
  filter_all(all_vars(!grepl('V', .))) %>%
  head

J'ai essayé de trouver une alternative transversale pour ce qui suit, mais je n'ai pas immédiatement trouvé une bonne solution:

    #get all rows where any column contains 'V'
    diamonds %>%
    filter_all(any_vars(grepl('V',.))) %>%
      head
    #> # A tibble: 6 x 10
    #>   carat cut       color clarity depth table price     x     y     z
    #>   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
    #> 1 0.23  Good      E     VS1      56.9    65   327  4.05  4.07  2.31
    #> 2 0.290 Premium   I     VS2      62.4    58   334  4.2   4.23  2.63
    #> 3 0.24  Very Good J     VVS2     62.8    57   336  3.94  3.96  2.48
    #> 4 0.24  Very Good I     VVS1     62.3    57   336  3.95  3.98  2.47
    #> 5 0.26  Very Good H     SI1      61.9    55   337  4.07  4.11  2.53
    #> 6 0.22  Fair      E     VS2      65.1    61   337  3.87  3.78  2.49

Mise à jour: Merci à l'utilisateur Petr Kajzar dans cette réponse , voici également une approche pour ce qui précède:

diamonds %>%
   filter(rowSums(across(everything(), ~grepl("V", .x))) > 0)
Tjebo
la source