Последовательная агрегация с одним вызовом pivot_wider

#r #tidyverse #tidyr

#r #tidyverse #тидыр

Вопрос:

Рассмотрим фрейм данных:

 df <- data.frame(x = c(1,2,1,1), y = c("a", "a", "b", "a"))
 

Применив приведенный ниже код

 library(tidyverse)

df %>% 
  pivot_wider(x, names_from = y, values_from = y, values_fn = length, names_prefix = "tot_", values_fill = 0) %>% 
  mutate(per_a = 100*tot_a / rowSums(select(.,starts_with("tot_")))) %>% 
  mutate(per_b = 100*tot_b / rowSums(select(.,starts_with("tot_"))))
 

человек получает результат

    <dbl> <int> <int> <dbl> <dbl>
 1     1     2     1  66.7  33.3
 2     2     1     0 100     0
 

Мой вопрос таков: возможно ли получить тот же результат, используя один вызов pivot_wider , то есть без каких-либо mutate ?

Комментарии:

1. о чем proportions(table(df), 1)*100 ?

2. Спасибо, @Onyambu. Однако мотивация моего вопроса заключается в том, чтобы выяснить, может ли такой результат быть достигнут с помощью одного pivot_wider .

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

4. В документации говорится, что value_fn это может быть список функций, @Onyambu.

5. да, все объединяется. т.е. в том, что они работают для каждой группы и дают единый / единый результат. в вашем случае у вас есть группы a и b, которые являются зависимыми, поскольку 66.7 получается путем подсчета всех vlues в a И B перед разделением.

Ответ №1:

Я думаю, вам нужно будет group_by дважды, если вы хотите выполнить pivot_wider один раз, не mutate используя каждый per столбец в процентах отдельно.

 df %>%
  group_by(x,y) %>%
  count(name="tot") %>%
  group_by(x) %>%
  mutate(per = tot / sum(tot)) %>%
  pivot_wider(id_cols = x, names_from=y, values_from=c(tot,per))

## A tibble: 2 x 5
## Groups:   x [2]
#      x tot_a tot_b per_a  per_b
#  <dbl> <int> <int> <dbl>  <dbl>
#1     1     2     1 0.667  0.333
#2     2     1    NA 1     NA   
 

Для меня это было бы «аккуратно», поскольку вы выполняете все вычисления в 2 сгруппированных развертках в длинных, аккуратных данных, а не пытаетесь вручную выбрать несколько столбцов в широком формате. формат.

Ответ №2:

дополнительный вариант решения

данные.таблица

 df <- data.frame(x = c(1,2,1,1), y = c("a", "a", "b", "a"))

library(data.table)
library(magrittr)
setDT(df)[, list(tot = .N), by = list(x, y)] %>% 
  .[, perc := proportions(tot), by = list(y)] %>% 
  dcast(x ~ y, value.var = c("tot", "perc"), fill = 0)
#>    x tot_a tot_b    perc_a perc_b
#> 1: 1     2     1 0.6666667      1
#> 2: 2     1     0 0.3333333      0
 

Создано 2021-12-10 пакетом reprex (v2.0.1)

tidyverse

 library(tidyverse)
df <- data.frame(x = c(1,2,1,1), y = c("a", "a", "b", "a"))

df %>%
  pivot_wider(
    x,
    names_from = y,
    values_from = y,
    values_fn = length,
    names_prefix = "tot_",
    values_fill = 0
  ) %>% 
  mutate(across(starts_with("tot_"), proportions, .names = "{.col}_prop"))
#> # A tibble: 2 x 5
#>       x tot_a tot_b tot_a_prop tot_b_prop
#>   <dbl> <int> <int>      <dbl>      <dbl>
#> 1     1     2     1      0.667          1
#> 2     2     1     0      0.333          0
 

Создано 2021-12-10 пакетом reprex (v2.0.1)