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

Я прочитал эту тему, касающуюся похожей, но менее сложной проблемы: Используйте имена динамических переменных в `dplyr` а также vignette("programming", "dplyr"), но я все еще не могу правильно указать цитату. Я действительно застрял на этом этапе и хотел бы получить несколько советов от более опытных разработчиков.

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

library(dplyr)
library(stringi)

df <- setNames(as.data.frame(matrix(sample(1:10, 999, replace = T), 333, 3)), 
               stri_rand_strings(3, 10, pattern = "[A-Za-z]"))

group <- c("group1","group2","group3")

df <- cbind(df, group)

Следующая функция должна выполнить две вещи:

  1. рассчитать сумму каждого столбца в кадре данных по группе
  2. вычислить относительные пропорции каждого столбца во фрейме данных по группам
propsum <- function(df, expr){

  expr_quo <- enquo(expr)

  sum <- paste(quo_name(expr), "sum", sep = ".")
  prop <- paste(quo_name(expr), "prop", sep = ".")

  df %>%
    group_by(., group) %>%
    mutate(., !! sum :=  sum(!! expr_quo),
              !! prop := expr / !! sum * 100) -> df

  return(df)
}

for(i in length(df)-1){
  propsum(df, names(df)[i]) -> df_new
}

Ожидаемый результат - это фрейм данных с начальными столбцами, суммами по группам для каждого начального столбца и относительными пропорциями для каждого начального столбца по группам. Итак, в этом примере фрейм данных должен иметь 10 столбцов (1 столбец группирования, 3 столбца исходных данных, 3 столбца с суммами по группам, 3 столбца с относительными пропорциями по группам).

Однако я получаю следующую ошибку:

Error in sum(~names(df)[i]) : invalid 'type' (character) of argument

В виньетке приведен пример кода для аналогичной задачи:

my_mutate <- function(df, expr) {
  expr <- enquo(expr)
  mean_name <- paste0("mean_", quo_name(expr))
  sum_name <- paste0("sum_", quo_name(expr))

  mutate(df,
    !! mean_name := mean(!! expr),
    !! sum_name := sum(!! expr)
  )
}

my_mutate(df, a)
#> # A tibble: 5 x 6
#>      g1    g2     a     b mean_a sum_a
#>   <dbl> <dbl> <int> <int>  <dbl> <int>
#> 1     1     1     5     4      3    15
#> 2     1     2     3     2      3    15
#> 3     2     1     4     1      3    15
#> 4     2     2     1     3      3    15
#> # … with 1 more row

На данный момент я пробовал много разных вещей, но не могу заставить RHS использовать правильный столбец. Что я делаю неправильно?

1
CoCoL0r3s 12 Ноя 2019 в 14:06
Как вы определяете относительные пропорции каждого столбца? Относительно по отношению к?
 – 
NelsonGon
12 Ноя 2019 в 14:41
1
Предположим, у нас есть столбец с четырьмя значениями, каждое из которых равно 25. Сумма этого столбца будет равна 100, поэтому доля каждого значения составляет 0,25 или 25%. Если мы разделим этот столбец на две группы по 2 значения в каждой, относительная доля каждого значения будет 0,5 или 50%. Надеюсь, теперь все понятно, я не носитель английского языка. Прошу прощения, если я ошибся здесь.
 – 
CoCoL0r3s
12 Ноя 2019 в 15:57
Каких пропорций вы ожидаете от выборки данных? Проверьте мой "ответ" ниже и дайте мне знать, работает ли он так, как вы ожидаете.
 – 
NelsonGon
12 Ноя 2019 в 16:00

2 ответа

Я нашел решение, которым просто хочу поделиться, если кто-то столкнется с подобной задачей. Решение состоит в том, чтобы явно вызвать rlang::parse_expr() для сохранения имен переменных как выражений.

Вот рабочий пример:

library(dplyr)
library(stringi)

df <- setNames(as.data.frame(matrix(sample(1:10, 999, replace = T), 333, 3)), 
               stri_rand_strings(3, 10, pattern = "[A-Za-z]"))

group <- c("group1","group2","group3")

df <- cbind(df, group)

gpercentage <- function(df, a_var, p_var, sum_var){

  df %>%
    group_by(., group) %>%
    mutate(., !! sum_var := sum(!! a_var),
              !! p_var := !! a_var / sum(!! a_var)) -> df

  return(df)
}

i <- 1

for(i in seq_along(1:(length(df)-1))){

  a_var <- rlang::parse_expr(names(df)[i])
  p_var <- rlang::parse_expr(paste(names(df)[i], "P", sep = "."))
  sum_var <- rlang::parse_expr(paste(names(df)[i], "SUM", sep = "."))

df %>%
  gpercentage(., a_var, p_var, sum_var) -> df
}
1
CoCoL0r3s 13 Ноя 2019 в 17:54

Добиться этого можно было следующим образом. :

propsum <- function(df, grouping_column){

 df %>%
    group_by(!!sym(grouping_column)) %>%
    summarise_all(list(sum,function(x) 
      length(x)/nrow(.) * 100)) %>% 
    tidyr::pivot_longer(cols=-1,
                        names_to = "Variable",
                        values_to = "Value") %>% 
    mutate(Variable = gsub("fn1","sum",Variable),
           Variable = gsub("fn2","prop",Variable))

}
propsum(iris,"Species") 

Использование df в вопросе:

propsum(df,"group")  
# A tibble: 18 x 3
   group  Variable        Value
   <fct>  <chr>           <dbl>
 1 group1 dVFQteFGjs_sum  628  
 2 group1 wiQCPUeIvC_sum  599  
 3 group1 yBvktNXcfd_sum  644  
 4 group1 dVFQteFGjs_prop  33.3
 5 group1 wiQCPUeIvC_prop  33.3
 6 group1 yBvktNXcfd_prop  33.3
 7 group2 dVFQteFGjs_sum  630  
 8 group2 wiQCPUeIvC_sum  606  
 9 group2 yBvktNXcfd_sum  656  
10 group2 dVFQteFGjs_prop  33.3
11 group2 wiQCPUeIvC_prop  33.3
12 group2 yBvktNXcfd_prop  33.3
13 group3 dVFQteFGjs_sum  636  
14 group3 wiQCPUeIvC_sum  581  
15 group3 yBvktNXcfd_sum  635  
16 group3 dVFQteFGjs_prop  33.3
17 group3 wiQCPUeIvC_prop  33.3
18 group3 yBvktNXcfd_prop  33.3

Чтобы вернуться к широкому кругу (можно использовать pivot_wider, я считаю, что spread "быстрее"),

    propsum(df,"group") %>% 
  tidyr::spread(Variable,Value)
# A tibble: 3 x 7
  group dVFQteFGjs_prop dVFQteFGjs_sum wiQCPUeIvC_prop wiQCPUeIvC_sum
  <fct>           <dbl>          <dbl>           <dbl>          <dbl>
1 grou~            33.3            628            33.3            599
2 grou~            33.3            630            33.3            606
3 grou~            33.3            636            33.3            581
# ... with 2 more variables: yBvktNXcfd_prop <dbl>,
#   yBvktNXcfd_sum <dbl>
0
NelsonGon 12 Ноя 2019 в 15:56
1
Интересный подход, но не совсем то, что я искал, поскольку я хочу сохранить все строки - даже если все строки одной группы имеют одинаковое значение в столбцах суммы - поскольку они мне понадобятся позже. Тем временем я нашел подходящее решение, поделюсь им в отдельном awnser. Спасибо за ваш вклад, это дало мне возможность подумать над некоторыми новыми идеями!
 – 
CoCoL0r3s
13 Ноя 2019 в 17:48