dplyr передать NULL в group_by - PullRequest
       4

dplyr передать NULL в group_by

0 голосов
/ 16 октября 2018

Возможно, на этот вопрос уже где-то был дан ответ, но я не могу найти ответ ... Отметьте как дубликат и понизьте голос, как вам нравится, но кто-то, пожалуйста, помогите мне:)

Короткий вопрос

Как я могу передать NULL в dplyr::group_by внутри функции?

library(dplyr)

dt <- data.frame(a = sample(LETTERS[1:2], 100, replace = TRUE), b = sample(LETTERS[3:4], 100, replace = TRUE), value = rnorm(100,5,1))

f1 <- function(dt, a, b, c) {
  dt %>% group_by(a, b, c) %>% summarise(mean = mean(value))
}

f1(dt, a = "a", b = "b", c = NULL)

# Error in grouped_df_impl(data, unname(vars), drop) : 
#  Column `c` is unknown 

Длинное объяснение

Я пишу функцию гдеСтолбец «b» может быть задан как NULL, что означает, что функция должна игнорировать этот столбец.Если столбец «b» задан как символ, функция должна использовать столбец для суммирования данных.Например:

f2 <- function(dt, a, b) {
  if(is.null(b)) {
    dt %>% group_by(a) %>% summarize(mean = mean(value))
  } else {
    dt %>% group_by(a, b) %>% summarize(mean = mean(value))
  }
}

Реальная функция довольно длинная и сложная, и использует каналы dplyr, чтобы сделать весь код суммирования короче.У меня есть несколько условий, приводящих к разным выходам и суммированию альтернатив, и поэтому я сократил операторы if else, сгруппировав сначала и суммировав в отдельном шаге:

f3 <- function(dt, a, b, type = "mean") {
  if(is.null(b)) {
    tmp <- dt %>% group_by(a) 
  } else {
    tmp <- dt %>% group_by(a, b)
  }

  if(type == "mean") {
    tmp %>% summarize(mean = mean(value))
  } else {
    tmp %>% summarise(sum = sum(value))
  }
}

Если можно было передать NULL вgroup_by функция, я мог бы значительно сократить свой код (NULL должен быть в любом случае пустым, и такая передача работает со многими функциями, такими как reshape2::melt от того же автора).

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

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

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

library(tidyverse)
library(rlang)

set.seed(2)
dt <- data.frame(a = sample(LETTERS[1:2], 100, replace = TRUE), 
                 b = sample(LETTERS[3:4], 100, replace = TRUE), 
                 value = rnorm(100,5,1))

f1 = function(data, value.var, ...,  type="mean") {

  groups = enquos(...)
  value.var = enquo(value.var)

  names(type) = paste0(type, "_", quo_text(value.var))
  type = syms(type)

  data %>% group_by(!!!groups) %>% 
    summarise_at(vars(!!value.var), funs(!!!type))
}

f1(dt, value, a, b)
  a     b     mean_value
  <fct> <fct>      <dbl>
1 A     C           5.01
2 A     D           5.05
3 B     C           4.95
4 B     D           5.13
f1(dt, value)
  mean_value
       <dbl>
1       5.03
weird_func = function(x) {
  paste(round(cos(x),1)[1:3], collapse="/")
}

f1(dt, value, a, b, type=c("mean", "min", "median", "max", "weird_func"))
  a     b     mean_value min_value median_value max_value weird_func_value
  <fct> <fct>      <dbl>     <dbl>        <dbl>     <dbl> <chr>           
1 A     C           5.01      3.26         5.07      7.08 1/-0.1/1        
2 A     D           5.05      2.90         5.33      6.36 -0.4/0.9/0      
3 B     C           4.95      3.66         4.73      7.11 0.5/-0.5/0.7    
4 B     D           5.13      2.98         5.46      7.05 0/0.7/0.7
f1(mtcars, mpg, cyl, type=c("mean", "median"))
    cyl mean_mpg median_mpg
  <dbl>    <dbl>      <dbl>
1     4     26.7       26  
2     6     19.7       19.7
3     8     15.1       15.2
0 голосов
/ 17 октября 2018

Я думаю, вам нужно сначала преобразовать его из NULL в NA, как это (так как из ваших ответов вам просто нужно передать значение без участия в расчетах)

library(dplyr)

dt <- data.frame(a = sample(LETTERS[1:2], 100, replace = TRUE), b = sample(LETTERS[3:4], 100, replace = TRUE), value = rnorm(100,5,1))

f1 <- function(dt, a, b, c) {
  dt %>% 
    mutate(c = ifelse(is_empty(c)==TRUE,NA,c)) %>% 
    group_by(a, b,c) %>% 
    summarise(mean = mean(value))
}

f1(dt, a = "a", b = "b",c=NULL)

Результаты:

# A tibble: 4 x 4
# Groups:   a, b [?]
  a     b     c      mean
  <fct> <fct> <lgl> <dbl>
1 A     C     NA     5.27
2 A     D     NA     5.18
3 B     C     NA     5.27
4 B     D     NA     5.49
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...