Упростить R-код для вычисления групповой статистики по нескольким факторам

#r #dplyr #grouping #factors

#r #dplyr #группировка #факторы

Вопрос:

Я пытаюсь вычислить дисперсию для каждой группы в наборе данных с несколькими факторами. Например, приведенный ниже набор данных представляет собой первые 6 строк фрейма данных с 5 столбцами: 4 фактора по два уровня каждый (нет и да) и 1 непрерывная переменная:

Фактор A Фактор B Фактор C Фактор D varX
ДА ДА ДА НЕТ 66.8
НЕТ ДА ДА НЕТ 66.0
ДА НЕТ НЕТ НЕТ 58.4
НЕТ ДА ДА ДА 68.3
ДА ДА ДА НЕТ 61.8
ДА НЕТ НЕТ НЕТ 67.3

Что я хочу сделать, так это создать сводную таблицу, подобную приведенной ниже:

Фактор SD (НЕТ) SD (ДА) Коэффициент SD
Фактор A 3.79 3.51 1.08
Фактор B 3.44 3.83 1.11
Фактор C 3.77 3.53 1.07
Фактор D 3.92 3.32 1.18

Для каждого фактора я рассчитал стандартное отклонение на каждом уровне («Нет» и «Да»), а также соотношение двух стандартных отклонений.

Вот код, который я использую для этого:

 #
# Define modify function for SD ratio column
#
sd_ratio<-function(x,y){
  return(max(x,y)/min(x,y))
}
#
# Set up storage
#
nc<-4 # number of factors in data
testDataSum<-tibble(SD_No=rep(NA,nc),
                   SD_Yes=rep(NA,nc),
                   SD_Ratio=rep(NA,nc))
#
Factor<-vector("list",4)
SDList<-vector("list",4)
#
# For Loop. Group data by factors 1,2,3,4
#
for (i in 1:4){
  Factor[[i]]<-names(testData[,i])
  SDList[[i]]<-testData %>% 
  group_by(testData[,i])%>%
  summarize(SD=sd(VarX))
}
# Load summary DF with data by unlisting SDList
#
testDataSum$SD_No<-as.vector(matrix(unlist(SDList),ncol=4,byrow=T)[,3])
testDataSum$SD_Yes<-as.vector(matrix(unlist(SDList),ncol=4,byrow=T)[,4])
testDataSum$SD_Ratio=modify2(testDataSum$SD_No,testDataSum$SD_Yes,sd_ratio)
#
# Load formatted factor names and put it at the front
#
testDataSum<-testDataSum %>% 
  mutate(Factor=unlist(Factor)) %>%
  relocate(Factor)
# Show results
testDataSum
 

Я прошу помощи в упрощении этого кода. Это работает, но кажется ужасно уродливым и сложным, не говоря уже о том, что к нему трудно вернуться позже и изменить. Я считаю, что есть гораздо более простой способ сделать это без цикла for и без неуклюжего процесса снятия списка SDList с использованием строк «as.vector (matrix (…». Я просмотрел документацию для DPLYR и PLYR, особенно раздел группировки, но я сбит с толку. Любые предложения приветствуются.

Вот ссылка на репозиторий github с кодом и csv-файл со 192 строками, который вы можете использовать для создания результирующей таблицы.

Ссылка на Git Hub для кода и данных

Ответ №1:

Вы можете попробовать использовать reshape2 , dplyr , и tidyr

Когда я читаю ваши данные, имена столбцов нарушаются, поэтому я заранее переименовываю их.

 library(dplyr)
library(tidyr)
library(reshape2)

names(df) <- c("A","B","C","D","VarX")


df %>%
  melt(id.vars = "VarX", variable.name = "Factor") %>%
  group_by(Factor, value) %>%
  summarize(sd = sd(VarX)) %>%
  pivot_wider(id_cols = Factor, values_from = sd, names_from = value, names_glue = "sd_{value}") %>%
  mutate(SD_ratio = pmax(sd_No,sd_Yes)/pmin(sd_No,sd_Yes))

  Factor sd_No sd_Yes SD_ratio
  <fct>  <dbl>  <dbl>    <dbl>
1 A       3.51   3.79     1.08
2 B       3.83   3.44     1.11
3 C       3.53   3.77     1.07
4 D       3.92   3.32     1.18
 

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

1. Это, безусловно, чище и лаконичнее. Мне нужно будет изучить его, чтобы понять все, что вы сделали Спасибо за быстрый ответ.

2. Вот еще один способ сделать это, используя pivot_longer вместо melt:

3. names(df) <- c("A","B","C","D","VarX") df<-df%>% pivot_longer(c(1:4),names_to="Factor",values_to="value")%>% group_by(Factor,value)%>% summarize(sd=sd(VarX))%>% pivot_wider(id_cols = Factor, values_from = sd, names_from = value, names_glue = "sd_{value}")%>% mutate(SD_ratio = pmax(sd_No,sd_Yes)/pmin(sd_No,sd_Yes)) df

4. @PeterG Это тоже хороший способ: D.