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

#r #tidyverse #summarize

Вопрос:

У меня есть фрейм данных, содержащий данные, которые выглядят примерно так:

 df <- data.frame(
    group1 = c("High","High","High","Low","Low","Low"),
    group2 = c("male","female","male","female","male","female"),
    one = c("yes","yes","yes","yes","no","no"), 
    two = c("no","yes","no","yes","yes","yes"), 
    three = c("yes","no","no","no","yes","yes")
)
 

Я хочу суммировать количество ответов » да » / » нет » в переменных one two , и three которые обычно я бы использовал df %>% group_by(group1,group2,one) %>% summarise(n()) . Есть ли какой-либо способ суммировать все три столбца, а затем связать их все в один выходной файл df без необходимости вручную выполнять код над каждым столбцом? Я пробовал использовать цикл for, но не могу group_by() распознать имя colname, которое я даю в качестве входных данных

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

1. Принял ответ

Ответ №1:

Получите данные в длинном формате и count :

 library(dplyr)
library(tidyr)

df %>% pivot_longer(cols = one:three) %>% count(group1, group2, value)

#  group1 group2 value     n
#  <chr>  <chr>  <chr> <int>
#1 High   female no        1
#2 High   female yes       2
#3 High   male   no        3
#4 High   male   yes       3
#5 Low    female no        2
#6 Low    female yes       4
#7 Low    male   no        1
#8 Low    male   yes       2
 

Ответ №2:

Это может быть сделано dplyr только в (нет необходимости использовать tidyr::pivot_* ), хотя и в несколько ином формате вывода. (Этот работает даже без rowwise , хотя я не знаю точной причины этого)

 df <- data.frame(
  group1 = c("High","High","High","Low","Low","Low"),
  group2 = c("male","female","male","female","male","female"),
  one = c("yes","yes","yes","yes","no","no"), 
  two = c("no","yes","no","yes","yes","yes"), 
  three = c("yes","no","no","no","yes","yes")
)
library(dplyr)

df %>%
  group_by(group1, group2) %>%
  summarise(yes_count = sum(c_across(everything()) == 'yes'),
            no_count = sum(c_across(one:three) == 'no'), .groups = 'drop')
#> # A tibble: 4 x 4
#>   group1 group2 yes_count no_count
#>   <chr>  <chr>      <int>    <int>
#> 1 High   female         2        1
#> 2 High   male           3        3
#> 3 Low    female         4        2
#> 4 Low    male           2        1
 

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

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

1. Причина в том, что это == преобразование его в логический вектор. Проверять df %>% group_by(group1, group2) %>% summarise(new = list(c_across(everything()) == "yes")) %>% pull(new)

2.т. е. когда вы делаете c_across , он возвращает vector df %>% group_by(group1, group2) %>% summarise(new = list(c_across(everything()))) -> out проверку «Сейчас out » и df «и out$new «. С rowwise , существует ограничение, что он сгруппирован по строкам. Но здесь этого ограничения нет. Таким образом, он раскрывается в обычном столбце для каждой группы

3.Также вы могли бы использовать table с unnest_wider df %>% group_by(group1, group2) %>% summarise(count = list(table(c_across(everything()))), .groups = 'drop') %>% unnest_wider(count)

4. Спасибо @akrun за объяснение. Понял.

Ответ №3:

С помощью data.table

 library(data.table)
melt(setDT(df), id.var = c('group1', 'group2'))[, .(n = .N),
    .(group1, group2, value)]
 

-выход

     group1 group2 value n
1:   High   male   yes 3
2:   High female   yes 2
3:    Low female   yes 4
4:    Low   male    no 1
5:    Low female    no 2
6:   High   male    no 3
7:    Low   male   yes 2
8:   High female    no 1
 

С base R помощью , мы можем использовать by и table

 by(df[3:5], df[1:2], function(x) table(unlist(x)))