Используйте переменную в кавычках в вызове функции group_by ()%>% mutate () - PullRequest
4 голосов
/ 30 марта 2019

Воспроизводимый пример

cats <-
  data.frame(
    name = c(letters[1:10]),
    weight = c(rnorm(5, 10, 1), rnorm(5, 20, 3)),
    type = c(rep("not_fat", 5), rep("fat", 5))
  )

get_means <- function(df, metric, group) {
  df %>%
    group_by(.[[group]]) %>%
    mutate(mean_stat = mean(.[[metric]])) %>%
    pull(mean_stat) %>%
    unique()
}

get_means(cats, metric = "weight", group = "type")

Что я пробовал

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

Я попробовал все, включая использование quo (), eval () и substitute (), UQ (), !!, и целый ряд других вещей, чтобы попытаться заставить вещи внутри group_by () работать.

Это кажется очень простым, но я не могу понять.

Обоснование кода

Решение о том, что переменные должны быть в кавычках, заключается в том, что я использую их в вызовах ggplot aes_string (). Я исключил код ggplot внутри функции, чтобы упростить код, иначе было бы легко, потому что мы могли бы использовать стандартную оценку.

Ответы [ 5 ]

4 голосов
/ 30 марта 2019

Я думаю, что «предполагаемый» способ сделать это в среде tidyeval - ввести аргументы в виде имен (а не строк), а затем заключить аргументы в кавычки, используя enquo(). ggplot2 понимает операторы аккуратной оценки, так что это работает и для ggplot2.

Во-первых, давайте адаптируем функцию резюме dplyr в вашем примере:

library(tidyverse)
library(rlang)

get_means <- function(df, metric, group) {

  metric = enquo(metric)
  group = enquo(group)

  df %>%
    group_by(!!group) %>%
    summarise(!!paste0("mean_", as_label(metric)) := mean(!!metric))
}

get_means(cats, weight, type)
  type    mean_weight
1 fat            20.0
2 not_fat        10.2
get_means(iris, Petal.Width, Species)
  Species    mean_Petal.Width
1 setosa                0.246
2 versicolor            1.33 
3 virginica             2.03

Теперь добавьте в ggplot:

get_means <- function(df, metric, group) {

  metric = enquo(metric)
  group = enquo(group)

  df %>%
    group_by(!!group) %>%
    summarise(mean_stat = mean(!!metric)) %>% 
    ggplot(aes(!!group, mean_stat)) + 
      geom_point()
}

get_means(cats, weight, type)

enter image description here

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

plot_func = function(data, metric, group) {

  metric = enquo(metric)
  group = enquo(group)

  data %>% 
    ggplot(aes(!!group, !!metric)) + 
      geom_point() +
      geom_point(data=. %>% 
                   group_by(!!group) %>%
                   summarise(!!metric := mean(!!metric)),
                 shape="_", colour="red", size=8) + 
      expand_limits(y=0) +
      scale_y_continuous(expand=expand_scale(mult=c(0,0.02)))
}

plot_func(cats, weight, type)

enter image description here

К вашему сведению, вы можете разрешить функции принимать любое количество группирующих переменных (включая ни одной), используя аргумент ... и enquos вместо enquo (что также требует использования !!! (unquote-splice) ) вместо !! (без кавычек)).

get_means <- function(df, metric, ...) {

  metric = enquo(metric)
  groups = enquos(...)

  df %>%
    group_by(!!!groups) %>%
    summarise(!!paste0("mean_", quo_text(metric)) := mean(!!metric))
}
get_means(mtcars, mpg, cyl, vs)
    cyl    vs mean_mpg
1     4     0     26  
2     4     1     26.7
3     6     0     20.6
4     6     1     19.1
5     8     0     15.1
get_means(mtcars, mpg)
  mean_mpg
1     20.1
3 голосов
/ 30 марта 2019

Местоимение magrittr . представляет все данные, поэтому вы взяли среднее значение всех наблюдений.Вместо этого используйте чистое местоимение eval .data, которое представляет фрагмент кадра данных для текущей группы:

get_means <- function(df, metric, group) {
  df %>%
    group_by(.data[[group]]) %>%
    mutate(mean_stat = mean(.data[[metric]])) %>%
    pull(mean_stat) %>%
    unique()
}
2 голосов
/ 30 марта 2019

Если вы хотите использовать строки для имен, как в вашем примере, правильный способ сделать это - преобразовать строку в символ с sym и без кавычек с !!:

get_means <- function(df, metric, group) {
    df %>%
      group_by(!!sym(group)) %>%
      mutate(mean_stat = mean(!!sym(metric))) %>%
      pull(mean_stat) %>%
      unique()
}

get_means(cats, metric = "weight", group = "type")
[1] 10.06063 17.45906

Если вы хотите использовать голые имена в своей функции, тогда используйте enquo с !!:

get_means <- function(df, metric, group) {
    group <- enquo(group)
    metric <- enquo(metric)
    df %>%
      group_by(!!group) %>%
      mutate(mean_stat = mean(!!metric)) %>%
      pull(mean_stat) %>%
      unique()
}

get_means(cats, metric = weight, group = type)
[1] 10.06063 17.45906

Что происходит в вашем примере?

Интересно .[[group]], работает для группировки, но не так, как вы думаете.Это заменяет указанный столбец кадра данных как вектор, а затем создает новую переменную, по которой он группируется:

cats %>%
    group_by(.[['type']])

# A tibble: 10 x 4
# Groups:   .[["type"]] [2]
   name  weight type    `.[["type"]]`
   <fct>  <dbl> <fct>   <fct>        
 1 a       9.60 not_fat not_fat      
 2 b       8.71 not_fat not_fat      
 3 c      12.0  not_fat not_fat      
 4 d       8.48 not_fat not_fat      
 5 e      11.5  not_fat not_fat      
 6 f      17.0  fat     fat          
 7 g      20.3  fat     fat          
 8 h      17.3  fat     fat          
 9 i      15.3  fat     fat          
10 j      17.4  fat     fat  

Ваша проблема связана с оператором mutate.Вместо выбора mutate(mean_stat = mean(.[['weight']])) просто извлекает столбец weight как вектор, вычисляет среднее значение и затем присваивает это единственное значение новому столбцу

cats %>%
    group_by(.[['type']]) %>%
      mutate(mean_stat = mean(.[['weight']]))
# A tibble: 10 x 5
# Groups:   .[["type"]] [2]
   name  weight type    `.[["type"]]` mean_stat
   <fct>  <dbl> <fct>   <fct>             <dbl>
 1 a       9.60 not_fat not_fat            13.8
 2 b       8.71 not_fat not_fat            13.8
 3 c      12.0  not_fat not_fat            13.8
 4 d       8.48 not_fat not_fat            13.8
 5 e      11.5  not_fat not_fat            13.8
 6 f      17.0  fat     fat                13.8
 7 g      20.3  fat     fat                13.8
 8 h      17.3  fat     fat                13.8
 9 i      15.3  fat     fat                13.8
10 j      17.4  fat     fat                13.8
1 голос
/ 30 марта 2019

Я бы пошел с небольшой модификацией (если я правильно понимаю, чего бы вы хотели достичь):

 get_means <- function(df, metric, group) {
      df %>%
        group_by(!!sym(group)) %>%
        summarise(mean_stat = mean(!!sym(metric)))%>% pull(mean_stat)
    }
    get_means(cats, "weight", "type")

[1] 20.671772  9.305811

дает точно такой же вывод, как:

cats %>% group_by(type) %>% summarise(mean_stat=mean(weight)) %>%
  pull(mean_stat)

[1] 20.671772  9.305811
0 голосов
/ 04 апреля 2019

с использованием *_at функций:

library(dplyr)
get_means <- function(df, metric, group) {
  df %>%
    group_by_at(group) %>%
    mutate_at(metric,list(mean_stat = mean)) %>%
    pull(mean_stat) %>%
    unique()
}

get_means(cats, metric = "weight", group = "type")
# [1] 10.12927 20.40541

Данные

set.seed(1)
cats <-
  data.frame(
    name = c(letters[1:10]),
    weight = c(rnorm(5, 10, 1), rnorm(5, 20, 3)),
    type = c(rep("not_fat", 5), rep("fat", 5))
  )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...