กรองแถวที่มีสตริงที่แน่นอน


188

RTBฉันต้องกรองกรอบข้อมูลโดยใช้เป็นเกณฑ์แถวผู้ที่อยู่ในที่มีอยู่สตริง

dplyrฉันใช้

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

ฉันรู้ว่าฉันสามารถใช้ฟังก์ชั่นfilterในdplyrแต่ฉันทำไม่ได้ว่าวิธีการบอกให้ตรวจสอบเนื้อหาของสตริง

TrackingPixelโดยเฉพาะอย่างยิ่งผมต้องการที่จะตรวจสอบเนื้อหาในคอลัมน์ หากสตริงมีฉลากRTBฉันต้องการลบแถวจากผล


28
ฉันไม่เคยใช้dplyrแต่ดูที่ความช่วยเหลือใน?dplyr::filterฉันขอแนะนำบางอย่างเช่นfilter(df, !grepl("RTB",TrackingPixel))อาจจะ?
thelatemail

2
นี่เป็นสิ่งที่ใกล้เคียงกับสิ่งที่ฉันต้องการบรรลุ ปัญหาเดียวคือการรักษาสตริงเหล่านั้นซึ่งรวมถึงฉลากRTBและไม่แสดงอื่น ๆ
Gianluca

ฉันเพิ่งแก้ไขการซ่อนตัวที่ย้อนกลับตอนนี้โดยเพิ่ม!ด้านหน้าgrepl- ลองอีกครั้ง
thelatemail

4
หรือใช้invertและการขัดแย้งของvalue grepนิพจน์ทั่วไปทำให้การทำงานกับข้อความง่ายขึ้นเป็นพันเท่า
Rich Scriven

4
@thelatemail greplใช้งานไม่ได้กับ postgres สำหรับฉันนี่คือ MySQL
Statwonk

คำตอบ:


257

คำตอบสำหรับคำถามถูกโพสต์โดย @latemail ในความคิดเห็นด้านบน คุณสามารถใช้นิพจน์ปกติสำหรับอาร์กิวเมนต์ที่สองและต่อมาของfilterสิ่งนี้:

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

เนื่องจากคุณไม่ได้ให้ข้อมูลต้นฉบับฉันจะเพิ่มตัวอย่างของเล่นโดยใช้mtcarsชุดข้อมูล ลองนึกภาพคุณสนใจรถยนต์ที่ผลิตโดยมาสด้าหรือโตโยต้าเท่านั้น

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

หากคุณต้องการที่จะทำในลักษณะตรงกันข้ามคือไม่รวมรถยนต์โตโยต้าและมาสด้าfilterคำสั่งจะเป็นดังนี้:

dplyr::filter(mtcars, !grepl('Toyota|Mazda', type))

จะทำอย่างไรถ้าชื่อคอลัมน์มีช่องว่าง เช่นเดียวกับพิกเซลการติดตาม
MySchizoBuddy

3
ตรวจสอบให้แน่ใจว่าคุณใช้ฟังก์ชันตัวกรองจากแพ็คเกจ dplyr ไม่ใช่แพ็คเกจสถิติ
JHowIX

2
@MySchizoBuddy: หากชื่อคอลัมน์มี white space คุณสามารถเลือกตัวแปรโดยใช้ backticks แก้ไขตัวอย่างข้างต้น: mtcars$`my type` <- rownames(mtcars)จากนั้นmtcars %>% filter(grepl('Toyota|Mazda', `my type`))
alex23lemm

13
โปรดทราบว่าสิ่งนี้ไม่ทำงานเมื่อวัตถุเป็นtbl_sqlเช่นgreplไม่แปลเป็น SQL
David LeBauer

ตัวเลือกที่ 1 คือการรู้ว่าโหลด dplyr ล่าสุด ตัวเลือกที่ 2 คือคำนำหน้าคุณ dplyr :: ตัวกรอง
userJT

157

สารละลาย

มันเป็นไปได้ที่จะใช้str_detectของstringrแพคเกจที่รวมอยู่ในtidyverseแพคเกจ str_detectผลตอบแทนTrueหรือFalseเป็นไปว่าเวกเตอร์ที่ระบุมีสตริงที่เฉพาะเจาะจง เป็นไปได้ที่จะกรองโดยใช้ค่าบูลีนนี้ ดูIntroduction to stringrสำหรับรายละเอียดเกี่ยวกับstringrแพ็คเกจ

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

สิ่งที่ดีเกี่ยวกับ Stringr

เราควรใช้ค่อนข้างกว่าstringr::str_detect() base::grepl()นี่เป็นเพราะมีเหตุผลดังต่อไปนี้

  • ฟังก์ชั่นที่จัดทำโดยstringrแพคเกจเริ่มต้นด้วยคำนำหน้าstr_ซึ่งทำให้รหัสอ่านง่ายขึ้น
  • อาร์กิวเมนต์แรกของฟังก์ชั่นของstringrแพ็กเกจคือ data.frame (หรือค่า) เสมอจากนั้นจะมีพารามิเตอร์มา (ขอบคุณเปาโล)
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"

เกณฑ์มาตรฐาน

ผลการทดสอบเกณฑ์มาตรฐานมีดังนี้ สำหรับดาต้าเฟรมขนาดใหญ่str_detectนั้นเร็วกว่า

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

1
ทำไมตัวเลือก stringr ถึงดีกว่า grep
CameronNemo

2
@CameronNemo ฟังก์ชั่นที่จัดทำโดยstringrแพคเกจเริ่มต้นด้วยคำนำหน้า str_ ซึ่งทำให้รหัสอ่านง่ายขึ้น ในรหัส R ที่ทันสมัยล่าสุดขอแนะนำให้ใช้ stringr
Keiku

3
ฉันคิดว่านี่เป็นความชอบส่วนตัวและฉันเห็นด้วยกับ @CameronNemo ที่base Rดีพอstringrๆกับ หากคุณจะให้ 'ข้อเท็จจริงที่ยากลำบาก' แก่เราเช่นการเปรียบเทียบและไม่เพียง แต่ระบุว่า "ขอแนะนำ" (ใครแนะนำให้ใช้?) นี่จะได้รับการชื่นชมอย่างมาก ขอบคุณ
Tjebo

2
เหตุผลอื่นคือความสอดคล้องในกรอบ tidyverse: อาร์กิวเมนต์แรกของฟังก์ชั่นมักจะ data.frame (หรือค่า) จากนั้นพารามิเตอร์มา
เปาโล

22

คำตอบนี้คล้ายกับคนอื่น ๆ แต่การใช้ที่แนะนำstringr::str_detectและ rownames_to_columndplyr

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

สร้างเมื่อ 2018-06-26 โดยแพ็คเกจ reprex (v0.2.0)


1
str_detectอยู่ในstringrแพ็คเกจ
jsta

4

แก้ไขรวมถึงacross()ไวยากรณ์ที่ใหม่กว่า

นี่เป็นอีกหนึ่งtidyverseวิธีการแก้ปัญหาโดยใช้หรือก่อนหน้านี้filter(across()) filter_atข้อดีคือคุณสามารถขยายได้มากกว่าหนึ่งคอลัมน์

ด้านล่างยังเป็นโซลูชันที่มีfilter_allเพื่อค้นหาสตริงในคอลัมน์ใด ๆโดยใช้diamondsเป็นตัวอย่างค้นหาสตริง "V"

library(tidyverse)

สตริงในคอลัมน์เดียวเท่านั้น

# 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

ไวยากรณ์ที่ถูกแทนที่ตอนนี้สำหรับอันเดียวกันจะเป็น:

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

สตริงในคอลัมน์ทั้งหมด:

# 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

ไวยากรณ์ที่ถูกแทนที่ตอนนี้สำหรับอันเดียวกันจะเป็น:

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

ฉันพยายามหาทางเลือกต่าง ๆ สำหรับสิ่งต่อไปนี้ แต่ฉันไม่ได้คิดวิธีแก้ปัญหาที่ดีในทันที:

    #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

อัปเดต: ขอบคุณผู้ใช้ Petr Kajzar ในคำตอบนี้ต่อไปนี้เป็นวิธีการข้างต้น:

diamonds %>%
   filter(rowSums(across(everything(), ~grepl("V", .x))) > 0)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.