#r #dplyr
#r #dplyr
Вопрос:
Я пытаюсь разобраться в синтаксисе работы с dplyr, и у меня возникают проблемы с тем, как передать более одного столбца другой функции (например, str_detect). Я хочу выполнить поиск по tibble и выбрать все строки, в которых обнаружена определенная строка. Я могу запустить это для определенного столбца (например, col3 в примере ниже), но хотел бы просмотреть некоторые и / или все столбцы.
library(dplyr)
library(stringr)
col1 <- c("plate_ABC", "text", "text", "text")
col2 <- c("text", "this is plate B", "text", "text")
col3 <- c("text", "text", "C-plate", "text")
df <- as_tibble(data_frame(col1, col2, col3))
df %>% filter(str_detect(col3, "plate"))
Вывод:
df %>% filter(str_detect(col3, "plate"))
## A tibble: 1 x 3
# col1 col2 col3
# <chr> <chr> <chr>
#1 text text C-plate
Желаемый результат:
df %>% filter(str_detect(?SOME/ALL Cols?, "plate"))
## A tibble: 3 x 3
# col1 col2 col3
# <chr> <chr> <chr>
#1 plate_ABC text text
#2 text this is plate B text
#3 text text C-plate
Комментарии:
1. Ваше ожидаемое решение не должно использовать
filter
илиreduce
? Я в замешательстве2. Нет, извините, если я неясен. Любые и все решения великолепны. Я очень новичок в программировании (всего пару недель), поэтому мне требуется некоторое время, чтобы понять синтаксис и определить наиболее удобный способ использования различных команд. Я думаю, что в этом случае rowwise %>% filter наиболее интуитивно понятен для моего понимания.
3. Он
rowwise
должен быть медленным по сравнению с векторизованным вариантом4. Спасибо, что обратили на это внимание. Я думаю, что сейчас скорость не является проблемой (таблицы данных очень малы), но я буду иметь это в виду по мере того, как буду лучше знакомиться с кодированием.
Ответ №1:
Вы можете использовать across
:
library(dplyr)
library(stringr)
df %>% filter(Reduce(`|`, across(.fns = ~str_detect(., "plate"))))
# col1 col2 col3
# <chr> <chr> <chr>
#1 plate_ABC text text
#2 text this is plate B text
#3 text text C-plate
Или по строкам :
df %>%
rowwise() %>%
filter(any(str_detect(c_across(), 'plate')))
Если у вас более старая версия dplyr
(<1.0.0), вы можете использовать filter_all
/ filter_at
:
df %>% filter_all(any_vars(str_detect(., 'plate')))
Комментарии:
1. Спасибо @Ronak. Итак, я думаю, что понимаю первое: str_detect будет оценивать по всем столбцам, пытаясь найти табличку. Если какая-либо из этих оценок равна T, строка будет отфильтрована. И rowwise означает, что каждая строка оценивается индивидуально. Это также должно быть полезно при генерации сводной статистики по столбцам. Я борюсь со вторым решением, так как мне трудно понять синтаксис и что именно указывают точка, тильда, …. Я постараюсь разобраться в этом по ходу дела.
2. тильда(
~
) используется для применения синтаксиса в стиле формулы. Это похоже на анонимную функцию.
Ответ №2:
Мы можем использовать base R
для этого
df[Reduce(`|`, lapply(df, grepl, pattern = 'plate')),]
-вывод
# A tibble: 3 x 3
# col1 col2 col3
# <chr> <chr> <chr>
#1 plate_ABC text text
#2 text this is plate B text
#3 text text C-plate
Или с помощью rowSums
df[rowSums(`dim<-`(grepl('plate', as.matrix(df)), dim(df))) > 0,]
Или с помощью tidyverse
library(dplyr)
library(purrr)
library(stringr)
df %>%
filter(across(everything(), ~ str_detect(., 'plate')) %>%
reduce(`|`))
# A tibble: 3 x 3
# col1 col2 col3
# <chr> <chr> <chr>
#1 plate_ABC text text
#2 text this is plate B text
#3 text text C-plate
Тесты
Тайминги для немного большего набора данных
df1 <- df[rep(seq_len(nrow(df)), 1e6), ]
system.time(df1 %>% filter(Reduce(`|`, across(.fns = ~str_detect(., "plate")))))
# user system elapsed
# 1.597 0.139 1.736
system.time(df1 %>%
rowwise() %>%
filter(any(str_detect(c_across(), 'plate'))))
# user system elapsed
#178.694 1.477 180.864
system.time(df1 %>% filter_all(any_vars(str_detect(., 'plate'))) )
# user system elapsed
# 1.461 0.061 1.499
system.time(df1[Reduce(`|`, lapply(df1, grepl, pattern = 'plate')),])
# user system elapsed
# 2.792 0.025 2.778
system.time(df1 %>%
filter(across(everything(), ~ str_detect(., 'plate')) %>%
reduce(`|`)))
# user system elapsed
# 1.471 0.054 1.505
Комментарии:
1. Спасибо @akrun. Для решения «tidyverse» применяется тот же комментарий, что и выше (хотя его уже легче понять, используя
reduce
как отдельную команду, а не вкладывая ее в filter. Есть чему поучиться!