Filter baris yang berisi string tertentu

188

Saya harus memfilter bingkai data menggunakan kriteria baris yang berisi string RTB.

Saya menggunakan dplyr.

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

Saya tahu saya dapat menggunakan fungsi filterdi dplyrtapi saya tidak persis bagaimana untuk menceritakannya untuk memeriksa isi dari string.

Khususnya saya ingin memeriksa konten di kolom TrackingPixel. Jika string berisi label RTBsaya ingin menghapus baris dari hasilnya.

Gianluca
sumber
28
Saya belum pernah menggunakan dplyr, tetapi melihat bantuan di ?dplyr::filtersaya akan menyarankan sesuatu seperti filter(df, !grepl("RTB",TrackingPixel))mungkin?
thelatemail
2
Ini sebenarnya dekat dengan apa yang ingin saya capai. Satu-satunya masalah adalah mempertahankan string yang menyertakan label RTBdan tidak menunjukkan yang lain.
Gianluca
Saya baru saja memasukkan sunting tersembunyi, yang dibalik sekarang dengan menambahkan !di depan grepl- coba lagi.
thelatemail
4
Atau gunakan argumen invertdan . Ekspresi reguler membuat bekerja dengan teks seribu kali lebih mudah. valuegrep
Rich Scriven
4
@thelatemail grepltidak berfungsi pada postgres untuk saya, apakah ini untuk MySQL?
Statwonk

Jawaban:

256

Jawaban atas pertanyaan sudah diposting oleh @latemail di komentar di atas. Anda dapat menggunakan ekspresi reguler untuk argumen kedua dan selanjutnya filterseperti ini:

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

Karena Anda belum memberikan data asli, saya akan menambahkan contoh mainan menggunakan mtcarsset data. Bayangkan Anda hanya tertarik pada mobil yang diproduksi oleh Mazda atau 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

Jika Anda ingin melakukan hal sebaliknya, yaitu mengecualikan mobil Toyota dan Mazda, filterperintahnya terlihat seperti ini:

dplyr::filter(mtcars, !grepl('Toyota|Mazda', type))
alex23lemm
sumber
bagaimana jika nama kolom berisi spasi. seperti Melacak Piksel.
MySchizoBuddy
3
pastikan Anda menggunakan fungsi filter dari paket dplyr, bukan paket statistik
JHowIX
2
@MySchizoBuddy: Jika nama kolom berisi ruang putih, Anda dapat memilih variabel menggunakan backticks. Mengubah contoh di atas: mtcars$`my type` <- rownames(mtcars)dan kemudianmtcars %>% filter(grepl('Toyota|Mazda', `my type`))
alex23lemm
13
perhatikan bahwa ini tidak berfungsi ketika objek adalah tbl_sqlas grepltidak menerjemahkan ke sql.
David LeBauer
opsi 1 adalah untuk mengetahui dengan pasti bahwa dplyr dimuat terakhir. opsi 2 adalah Anda awalan dplyr :: filter.
userJT
157

Larutan

Hal ini dimungkinkan untuk menggunakan str_detectsatu stringrpaket termasuk dalam tidyversepaket. str_detectmengembalikan Trueatau Falseapakah vektor yang ditentukan berisi beberapa string tertentu. Dimungkinkan untuk memfilter menggunakan nilai boolean ini. Lihat Pengantar stringr untuk detail tentang stringrpaket.

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

Hal-hal baik tentang Stringr

Kita harus menggunakan stringr::str_detect()daripada base::grepl(). Ini karena ada alasan berikut.

  • Fungsi-fungsi yang disediakan oleh stringrpaket dimulai dengan awalan str_, yang membuat kode lebih mudah dibaca.
  • Argumen pertama dari fungsi-fungsi stringrpaket adalah selalu data.frame (atau nilai), kemudian muncul parameternya. (Terima kasih, 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"

Tolok ukur

Hasil uji benchmark adalah sebagai berikut. Untuk kerangka data yang besar, str_detectlebih cepat.

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
sumber
1
Mengapa stringr pilihan yang lebih baik daripada grep?
CameronNemo
2
@CameronNemo Fungsi-fungsi yang disediakan oleh stringrpaket dimulai dengan awalan str_, yang membuat kode lebih mudah dibaca. Dalam kode R modern baru-baru ini, disarankan untuk menggunakan stringr.
Keiku
3
Saya pikir ini adalah preferensi yang sangat pribadi dan saya setuju dengan @CameronNemo yang base Rsama baiknya stringr. Jika Anda memberi kami beberapa 'fakta sulit' seperti benchmarking, dan tidak hanya menyatakan "direkomendasikan" (Siapa yang merekomendasikannya?), Ini akan sangat dihargai. Terima kasih
Tjebo
2
Alasan lain adalah konsistensi dalam kerangka kerja rapi: argumen pertama dari suatu fungsi selalu adalah data.frame (atau nilai), kemudian muncul parameter.
Paolo
22

Jawaban ini mirip dengan yang lain, tetapi menggunakan yang disukai stringr::str_detectdan dplyr 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

Dibuat pada 2018-06-26 oleh paket reprex (v0.2.0).

Jelatang
sumber
1
str_detectada dalam stringrpaket
jsta
3

sunting termasuk across()sintaks yang lebih baru

Inilah tidyversesolusi lain , menggunakan filter(across())atau sebelumnya filter_at. Keuntungannya adalah Anda dapat dengan mudah memperluas ke lebih dari satu kolom .

Di bawah ini juga solusi dengan filter_alluntuk menemukan string di kolom apa pun , menggunakan diamondssebagai contoh, mencari string "V"

library(tidyverse)

String hanya dalam satu kolom

# 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

Sintaks yang sekarang digantikan untuk hal yang sama adalah:

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

String di semua kolom:

# 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

Sintaks yang sekarang digantikan untuk hal yang sama adalah:

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

Saya mencoba mencari alternatif lain untuk hal-hal berikut, tetapi saya tidak segera menemukan solusi yang baik:

    #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

Pembaruan: Terima kasih kepada pengguna Petr Kajzar dalam jawaban ini , berikut ini juga pendekatan untuk yang di atas:

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