Фильтр данных полностью определяется пользователем - несколько столбцов и фильтров - PullRequest
0 голосов
/ 27 февраля 2020

Я пытаюсь создать функцию, которая позволит пользователю определять бесконечное количество столбцов и применять соответствующие фильтры к этим столбцам.

df <- data.frame(a=1:10, b=round(runif(10)), c=round(runif(10)))
|a| b|c|
|1| 1|1|
|2| 0|0|
|3| 0|1|
|4| 1|0|
|5| 1|0|
|6| 1|0|
|7| 1|1|
|8| 1|1|
|9| 1|0|
|10|1|1|

Я бы хотел, чтобы пользователь мог фильтровать данные на основе любого столбца и применять различные фильтры к каждому столбцу. Я знаю, что следующее не работает. Но это была бы общая идея.

test <- function(df, fCol, fParam){
    df %>% filter(fCol[1] %in% fParam[1] | fCol[2] %in% fParam[2])
}
test(df, c("b","c"),c(1,0)
# Which I would want it to return
|a|b|c|
|4|1|0|
|5|1|0|
|6|1|0|
|9|1|0|

Проблема, с которой я сталкиваюсь, заключается в том, что я не буду знать, сколько столбцов пользователь захочет отфильтровать, и не буду знать имена столбцов.

Любая помощь будет принята с благодарностью. Пожалуйста, задавайте вопросы, если они у вас есть. Я старался изо всех сил, чтобы дать представление.

Ответы [ 2 ]

2 голосов
/ 27 февраля 2020

Я считаю, что это должно удовлетворить то, что вы хотите

library(tidyr)
library(dplyr)
test <- function(df,
                 fCol,
                 fParam,
                 match_type = "any")
   {
  if(!is.element(match_type, c("any","all"))|length(match_type)!=1){
    stop()
  }
  df <- df %>% ungroup() %>%
    mutate(..id..=1:n())
  meta <- data.frame(fCol=fCol,fParam=fParam)
  logi <- df %>%
    select("..id..",fCol) %>%
    gather(key = "key", value = "value", -..id..) %>%
    left_join(., y = meta, by = c("key"="fCol")) %>%
    mutate(match = value==fParam) %>%
    select(-key,-value, -fParam) %>%
    group_by_at(setdiff(names(.),"match")) %>%
    summarise(match = ifelse(match_type%in%"any",any(match), all(match)))
  df2 <- left_join(df, logi, by = intersect(colnames(df),colnames(logi))) %>%
    filter(match)%>%
    select(-match, -..id..)
  return(df2)
}

df <- data.frame(a=1:10, b=round(runif(10)), c=round(runif(10)))
df
#    a b c
#1   1 0 1
#2   2 1 0
#3   3 0 0
#4   4 0 1
#5   5 0 1
#6   6 0 1
#7   7 1 0
#8   8 1 1
#9   9 1 0
#10 10 1 0

#use "any" to do an | match
test(df, c("b","c"),c(1,0), match_type = "any")
#   a b c
#1  2 1 0
#2  3 0 0
#3  7 1 0
#4  8 1 1
#5  9 1 0
#6 10 1 0

#use "all" to do an & match
test(df, c("b","c"),c(1,0), match_type = "all")
#   a b c
#1  2 1 0
#2  7 1 0
#3  9 1 0
#4 10 1 0

Вы также можете указать одно и то же имя для fCol несколько раз, если хотите найти несколько значений

test(df, c("b","b"),c(1,0)) #matches everything but you get the point
0 голосов
/ 27 февраля 2020

(мой первоначальный ответ):

Я не уверен, что это вполне дает вам процесс, который вы хотите, но вот моя лучшая попытка, прежде чем хватит терпения !!! : -)

Я уверен, что есть хороший способ сделать этот фильтр AND не OR, но я сам не могу туда добраться. (Может быть, комбинация map_dfc и inner_join?)

Правка: попал в конец! Усовершенствованный код ниже (исходный код удален).

suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(tibble))
suppressPackageStartupMessages(library(purrr))

my_df <- tibble(
  a=1:10,
  b=round(runif(10)),
  c=round(runif(10))
  )
my_df
#> # A tibble: 10 x 3
#>        a     b     c
#>    <int> <dbl> <dbl>
#>  1     1     1     0
#>  2     2     1     0
#>  3     3     0     1
#>  4     4     0     0
#>  5     5     1     1
#>  6     6     0     1
#>  7     7     0     0
#>  8     8     0     1
#>  9     9     1     0
#> 10    10     1     0

col_names <- c("b", "c")
tests <- c(1, 0)

#  option 1: with a named function:

make_test_frame <- function(col_name, test) {
  tibble({{col_name}} := test)
}

my_df1 <- map2_dfc(col_names, tests, make_test_frame) %>% 
  inner_join(x = my_df)
#> Joining, by = c("b", "c")
my_df1
#> # A tibble: 4 x 3
#>       a     b     c
#>   <int> <dbl> <dbl>
#> 1     1     1     0
#> 2     2     1     0
#> 3     9     1     0
#> 4    10     1     0

# 2. or with an anonymous function:

my_df1 <- map2_dfc(
  col_names, tests,
  function(col_name, test) {
    tibble({{col_name}} := test)
  }
) %>% 
  inner_join(x = my_df)
#> Joining, by = c("b", "c")
my_df1
#> # A tibble: 4 x 3
#>       a     b     c
#>   <int> <dbl> <dbl>
#> 1     1     1     0
#> 2     2     1     0
#> 3     9     1     0
#> 4    10     1     0

# 3. or as one big, hairy function:

filter_df <- function(df, col_names, tests) {
  map2_dfc(
    col_names, tests,
    function(col_name, test) {
      tibble({{col_name}} := test)
    }
  ) %>% 
    inner_join(x = df)
}

my_df1 <- filter_df(my_df, col_names = c("b", "c"), tests = c(1, 0))
#> Joining, by = c("b", "c")
my_df1
#> # A tibble: 4 x 3
#>       a     b     c
#>   <int> <dbl> <dbl>
#> 1     1     1     0
#> 2     2     1     0
#> 3     9     1     0
#> 4    10     1     0

Создано в 2020-02-28 пакетом Представить (v0.3.0)

...