如果一组行不包含特定单词,则过滤dplyr链中的行

时间:2019-04-12 14:35:11

标签: r dplyr

假设我有以下数据集:

df <- read.table(header=TRUE, text="name value
stranger_things_mc Stranger_Land
stranger_things_confidence 100
stranger_things_importance 1
stranger_things_answer Stranger_Things
immigrant_crime_number 140
immigrant_crime_confidence 100
immigrant_crime_importance 3
immigrant_crime_answer 50
dog_things_mc Stranger_Land
dog_things_confidence 100
dog_things_importance 1
dog_things_answer Stranger_Things
fighting_stats_number 140
fighting_stats_confidence 100
fighting_stats_answer 50")

每第四行应该都包含三个后缀(_confidence,_importance,_answer),尽管有时不包含(例如上述“ fighting_stats”)。行有时带有(_mc)后缀,而其他时候它会说(_number或_slider)。

我想过滤出包含(_number或_slider)以及与该_number或_slider列关联的三行的任何行。因此,在上面的示例中,结果输出为:

df <- read.table(header=TRUE, text="name value
stranger_things_mc Stranger_Land
stranger_things_confidence 100
stranger_things_importance 1
stranger_things_answer Stranger_Things
dog_things_mc Stranger_Land
dog_things_confidence 100
dog_things_importance 1
dog_things_answer Stranger_Things")

我可以像这样过滤掉特定的列:

final_results <- df %>% 
  filter(!str_detect(name, "_number") & !str_detect(name, "_slider"))

,但无法弄清楚如何删除所有关联的列。通用算法应为:

首先,在名称列中找到带有“ _number”或“ _slider”的行,并获取其前面的文本。在上面的示例中,将是“ fighting_stats”和“ immigrant_crime”。然后,删除任何包含该文本的行。

2 个答案:

答案 0 :(得分:2)

我们基于每四行是一个新块(gl)的条件创建一个分组列,然后在{name的filter元素所在的组中first而不是_number_slider,然后ungroup并删除创建的临时“ grp”列

library(dplyr)
df %>% 
    group_by(grp = as.integer(gl(n(), 4, n()))) %>% 
    filter(!str_detect(first(name), "_(number|slider)")) %>%
    ungroup %>%
    select(-grp)

更新

基于来自OP的注释,即块由它们的公共前缀确定,然后提取第一个word,将其用作分组变量并像以前一样进行filter

library(stringr)
df %>%
  group_by(grp = word(name, 1, sep="_")) %>% 
  filter(!str_detect(first(name), "_(number|slider)"))

ungroup部分与以前的相同

如果存在重复的前缀(即不相邻的前缀),并且需要将其视为单独的块,请使用rleid中的data.table创建分组变量

df %>%
  group_by(grp = rleid(word(name, 1, sep="_"))) %>%
  filter(!str_detect(first(name), "_(number|slider)"))

答案 1 :(得分:1)

这是我要解决的方法:

groups <- df %>% 
  mutate(grp = str_extract(name, '.*(?=_confidence|_importance|_answer|_mc|_number|_slider)'),
         sfx = str_extract(name, '(_confidence|_importance|_answer|_mc|_number|_slider)')) %>% 
  group_by(grp) %>% 
  summarize(confidence = '_confidence' %in% sfx,
            importance = '_importance' %in% sfx,
            answer = '_answer' %in% sfx,
            mc = '_mc' %in% sfx,
            number = '_number' %in% sfx,
            slider = '_slider' %in% sfx) %>% 
  ungroup() %>% 
  gather(sfx, contains, -grp) %>% 
  filter(contains == TRUE) %>% 
  select(-contains)


df %>% 
  mutate(grp = str_extract(name, '.*(?=_confidence|_importance|_answer|_mc|_number|_slider)')) %>% 
  anti_join(groups %>% 
               filter(sfx == 'number') %>%
               select(grp))

从高层次上讲,我正在创建一个中间数据框,其中包含字符串name的词干和后缀,并使用词干创建一个组,并确定其中包含哪些后缀集列表每组茎。然后,我们不是在原始数据帧上使用filter,而是在中间数据帧上使用filter,然后在原始数据帧上使用anti_join

让我们详细介绍一下:

 mutate(grp = str_extract(name, '.*(?=_confidence|_importance|_answer|_mc|_number|_slider)'),
         sfx = str_extract(name, '(_confidence|_importance|_answer|_mc|_number|_slider)'))

这部分是我们如何使用正则表达式将name列拆分为其组成部分。

group_by(grp) %>% 
  summarize(confidence = '_confidence' %in% sfx,
            importance = '_importance' %in% sfx,
            answer = '_answer' %in% sfx,
            mc = '_mc' %in% sfx,
            number = '_number' %in% sfx,
            slider = '_slider' %in% sfx) %>% 
  ungroup()

在这里,我们按照我称为grp的“词根”进行分组,然后查找每个后缀。这部分有点笨拙,如果您的数据中有更多组,则需要扩展。

gather(sfx, contains, -grp) %>% 
  filter(contains == TRUE) %>% 
  select(-contains)

在这里,我们将数据转换为“长”样式的数据框,并且仅保留每个组中实际包含的后缀。

这完成了中间数据帧。

df %>% 
  mutate(grp = str_extract(name, '.*(?=_confidence|_importance|_answer|_mc|_number|_slider)'))

我们首先必须在原始数据帧上创建grp列,以使anti_join正常工作。

anti_join(groups %>% 
               filter(sfx == 'number') %>%
               select(grp))

最后,我们将中间数据帧的过滤版本anti_join转换为原始数据帧。我相信这将达到您想要的效果。

希望有帮助!