Фильтр Dataframe с аккуратной оценкой - PullRequest
0 голосов
/ 27 апреля 2018

Я работаю с большим набором данных. Во-первых, для некоторых столбцов (X1, X2, ...) я пытаюсь определить диапазон значений (a, b), состоящий из повторяющихся значений (a> n, b> n). Далее я хочу отфильтровать строку на основе условия, которое соответствует соответствующим столбцам для результата, указанного на предыдущем шаге.

Вот воспроизводимый пример, моделирующий сценарий, с которым я столкнулся,

library(tidyverse)

set.seed(1122)

vecs <- lapply(X = 1:2, function(x) rep(c(1, 2, 3), times = 10) %>% sample() %>% head(10))
names(vecs) <- paste0("col_", 1:2)
dat <- vecs %>% as.data.frame()
dat

   col_1 col_2
1      3     2
2      1     1
3      1     1
4      1     2
5      1     2
6      3     3
7      3     3
8      2     1
9      1     3
10     2     2

Я могу определить диапазон следующим методом,

# Which col has repeated value more than 3 appearances?
more_than_3 <- function(df, var){
    var <- rlang::sym(var)

    df %>% 
        group_by(!!var) %>% 
        summarise(n = n()) %>% 
        filter(n > 3) %>% 
        pull(!!var) %>% 
        range()
}
cols_name <- c("col_1", "col_2")
some_range <- purrr::map(cols_name, more_than_3, df = dat)
names(some_range) <- cols_name
some_range

$col_1
[1] 1 1

$col_2
[1] 2 2

Однако, чтобы отфильтровать значения, которые выходят за пределы верхнего предела, это то, что я делаю.

dat %>% 
    filter(col_1 <= some_range[["col_1"]][2], 
           col_2 <= some_range[["col_2"]][2])

  col_1 col_2
1     1     1
2     1     1
3     1     2
4     1     2

Я считаю, что должен быть более эффективный и элегантный способ фильтрации результатов на основе аккуратной оценки. Может ли кто-нибудь указать мне правильное направление?

Большое спасибо заранее.

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Сначала давайте попробуем создать небольшую функцию, которая создает одно выражение фильтра для одного столбца. Эта функция возьмет символ и затем преобразуется в строку, но может быть и наоборот:

new_my_filter_call_upper <- function(sym, range) {
  col_name <- as.character(sym)

  col_range <- range[[col_name]]
  if (is.null(col_range)) {
    stop(sprintf("Can't find column `%s` to compute range", col_name), call. = FALSE)
  }

  expr(!!sym < !!col_range[[2]])
}

Давайте попробуем:

new_my_filter_call_upper(quote(foobar), some_range)
#> Error: Can't find column `foobar` to compute range

# It works!
new_my_filter_call_upper(quote(col_1), some_range)
#> col_1 < 3

Теперь мы готовы создать глаголы конвейера, которые будут принимать фрейм данных и имена столбцов.

# Probably cleaner to pass range as argument. Prefix with dot to allow
# columns named `range`.
my_filter <- function(.data, ..., .range) {
  # ensyms() guarantees there won't be complex expressions
  syms <- rlang::ensyms(...)

  # Now let's map the function to create many calls:
  calls <- purrr::map(syms, new_my_filter_call_upper, range = .range)

  # And we're ready to filter with those expressions:
  dplyr::filter(.data, !!!calls)
}

Давайте попробуем это:

dat %>% my_filter(col_1, col_2, .range = some_range)
#>   col_1 col_2 NA.
#> 1     2     1   1
#> 2     2     2   1
0 голосов
/ 27 апреля 2018

Мы могли бы использовать map2

library(purrr)
map2(dat, some_range,  ~ .x < .y[2]) %>%
      reduce(`&`) %>%
      dat[.,]
#     col_1 col_2
#1     2     2
#2     1     1
#3     1     2
#6     1     1

Или с pmap

pmap(list(dat, some_range %>% 
                     map(2)), `<`) %>% 
      reduce(`&`) %>%
      dat[.,]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...