【问题标题】:Filter rows which contain a certain string过滤包含特定字符串的行
【发布时间】:2014-05-16 00:19:00
【问题描述】:

我必须使用包含字符串RTB 的行作为标准来过滤数据框。

我正在使用dplyr

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

我知道我可以在dplyr 中使用函数filter,但我不知道如何告诉它检查字符串的内容。

我特别想检查TrackingPixel 列中的内容。如果字符串包含标签RTB 我想从结果中删除该行。

【问题讨论】:

  • 我从来没有使用过dplyr,但是在查看?dplyr::filter 的帮助时,我可能会建议filter(df, !grepl("RTB",TrackingPixel)) 之类的东西?
  • 这实际上接近我想要实现的目标。唯一的问题是维护那些包含标签 RTB 的字符串而不显示其他字符串。
  • 我刚刚进行了隐身编辑,现在通过在 grepl 前面添加 ! 来反转它 - 再试一次。
  • 或使用grepinvertvalue 参数。正则表达式使处理文本变得简单一千倍。
  • @thelatemail grepl 不适用于我的 postgres,这是 MySQL 的吗?

标签: r filter dplyr


【解决方案1】:

@latemail 已经在上面的 cmets 中发布了问题的答案。您可以对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))

【讨论】:

  • 如果列名包含空格怎么办。比如跟踪像素。
  • 确保您使用的是 dplyr 包中的过滤功能,而不是 stats 包
  • @MySchizoBuddy:如果列名包含空格,您可以使用反引号选择变量。修改上面的例子:mtcars$`my type` &lt;- rownames(mtcars) 然后mtcars %&gt;% filter(grepl('Toyota|Mazda', `my type`))
  • 请注意,当对象是 tbl_sql 时,这不起作用,因为 grepl 不会转换为 sql。
  • 选项 1 是确定 dplyr 最后加载。选项 2 是你的前缀 dplyr::filter.
【解决方案2】:

解决方案

可以使用tidyverse 包中包含的stringr 包的str_detectstr_detect 返回TrueFalse 指定向量是否包含特定字符串。可以使用此布尔值进行过滤。有关stringr 包的详细信息,请参阅Introduction to 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(或值),然后是参数。(谢谢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"

基准测试

基准测试的结果如下。对于大数据框,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

【讨论】:

  • 为什么 stringr 比 grep 更好?
  • @CameronNemo stringr 包提供的函数以前缀 str_ 开头,这使得代码更易于阅读。在最近的现代 R 代码中,推荐使用 stringr。
  • 我认为这是一个非常个人的偏好,我同意@CameronNemo 的观点,即base Rstringr 一样好。如果您向我们提供一些“确凿的事实”,例如基准测试,而不仅仅是说明“推荐”(谁推荐它?),我们将不胜感激。谢谢
  • 另一个原因是tidyverse框架的一致性:函数的第一个参数总是data.frame(或值),然后是参数。
  • 刚刚偶然发现这篇文章,我知道这是一个古老的讨论,但以防万一其他人来到这里:人们可能更喜欢 str_detect 而不是 grepl 的一个原因是,如果有一个缺失值 @987654343 @ 将返回 NA,而 grepl 将返回 FALSE,这可能会产生误导。
【解决方案3】:

此答案与其他答案相似,但使用首选 stringr::str_detect 和 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

reprex package (v0.2.0) 于 2018 年 6 月 26 日创建。

【讨论】:

    【解决方案4】:

    edit 包含较新的 across() 语法

    这是另一个tidyverse 解决方案,使用filter(across()) 或以前的filter_at。优点是您可以轻松地将扩展到多列

    下面还有一个使用filter_all 的解决方案,以便在 any 列中找到字符串, 以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 in this answer,这里还有一种方法:

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

    【讨论】:

    • 你知道为什么diamonds %&gt;% filter(across(everything(), ~grepl('V', .))) 返回一个空标题吗?我认为简单地删除 ! 会返回任何列中带有 V 的所有行?
    • @DylanRussell 抱歉回答迟了。我认为这很明显 - 使用一切意味着您正在寻找在所有列中都有“V”的行。
    • ~ !grepl ~ 和 ! 有什么用你能告诉我吗?
    猜你喜欢
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    • 2019-08-30
    • 2018-04-17
    • 1970-01-01
    相关资源
    最近更新 更多