#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 строками, который вы можете использовать для создания результирующей таблицы.
Ответ №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.