Двухфакторный дисперсионный анализ с полосой погрешностей в R

Мы проводим занятия по статистике для студентов-биологов и пытаемся использовать R в качестве платформы вычислений и визуализации данных. Насколько это возможно, мы хотели бы избегать использования лишних пакетов и делать что-нибудь ужасно «модное» в R; Основное внимание в курсе уделяется статистике, а не программированию. Тем не менее, мы не нашли очень хорошего способа создания графика полосы ошибок в R для двухфакторного дисперсионного анализа. Мы используем пакет ggplot2 для построения графика, и хотя у него есть встроенный метод stat_summary для генерации полос ошибок 95% CI, способ их вычисления не всегда может быть правильным. Ниже я вручную просматриваю код для дисперсионного анализа и вручную вычисляю 95% доверительных интервалов (со стандартной ошибкой, рассчитанной на основе общей остаточной дисперсии, а не только для сводного метода внутригрупповой дисперсии ggplot). В конце концов, собственно сюжет.

Итак, вопрос в том ... есть ли более простой / быстрый / простой способ сделать все это?

#   LIZARD LENGTH DATA
island.1 <- c(0.2, 5.9, 6.1, 6.5)
island.2 <- c(5.6, 14.8, 15.5, 16.4)
island.3 <- c(0.8, 3.9, 4.3, 4.9)
sex.codes <- c("Male", "Female", "Male", "Female")

#   PUTTING DATA TOGETHER IN A DATA FRAME
df.1 <- data.frame(island.1, island.2, island.3, sex.codes)

#   MELTING THE DATA FRAME INTO LONG FORM
library(reshape)
df.2 <- melt(df.1)

#   MEAN BY CELL
mean.island1.male <- with(df.2, mean(value[variable == "island.1" & sex.codes == "Male"]))
mean.island1.female <- with(df.2, mean(value[variable == "island.1" & sex.codes == "Female"]))
mean.island2.male <- with(df.2, mean(value[variable == "island.2" & sex.codes == "Male"]))
mean.island2.female <- with(df.2, mean(value[variable == "island.2" & sex.codes == "Female"]))
mean.island3.male <- with(df.2, mean(value[variable == "island.3" & sex.codes == "Male"]))
mean.island3.female <- with(df.2, mean(value[variable == "island.3" & sex.codes == "Female"]))

#   ADDING CELL MEANS TO DATA FRAME
df.2$means[df.2$variable == "island.1" & df.2$sex.codes == "Male"] <- mean.island1.male
df.2$means[df.2$variable == "island.1" & df.2$sex.codes == "Female"] <- mean.island1.female
df.2$means[df.2$variable == "island.2" & df.2$sex.codes == "Male"] <- mean.island2.male
df.2$means[df.2$variable == "island.2" & df.2$sex.codes == "Female"] <- mean.island2.female
df.2$means[df.2$variable == "island.3" & df.2$sex.codes == "Male"] <- mean.island3.male
df.2$means[df.2$variable == "island.3" & df.2$sex.codes == "Female"] <- mean.island3.female

#   LINEAR MODEL
lizard.model <- lm(value ~ variable*sex.codes, data=df.2)

#   CALCULATING RESIDUALS BY HAND:
df.2$residuals.1 <- df.2$value - df.2$means

#   CONFIRMING RESIDUALS FROM LINEAR MODEL:
df.2$residuals.2 <- residuals(lizard.model)

#   TWO FACTOR MAIN EFFECT ANOVA
lizard.anova <- anova(lizard.model)        

#   INTERACTION PLOT
interaction.plot(df.2$variable, df.2$sex.codes, df.2$value)

#   SAMPLE SIZE IN EACH CELL
n <- length(df.2$value[df.2$variable == "island.1" & df.2$sex.codes == "Male"])
# > n
# [1] 2

#   NOTE: JUST FOR CLARITY, PRETEND n=10
n <- 10

#   CALCULATING STANDARD ERROR
island.se <- sqrt(lizard.anova$M[4]/n)

#   HALF CONFIDENCE INTERVAL
island.ci.half <- qt(0.95, lizard.anova$D[4]) * island.se

#   MAKING SUMMARY DATA FRAME
summary.df <- data.frame(
        Means = c(mean.island1.male,
                mean.island1.female,
                mean.island2.male,
                mean.island2.female,
                mean.island3.male,
                mean.island3.female),
        Location = c("island1",
                "island1",
                "island2",
                "island2",
                "island3",
                "island3"),
        Sex = c("male",
                "female",
                "male",
                "female",
                "male",
                "female"),      
        CI.half = rep(island.ci.half, 6)        
        )

# > summary.df
# Means Location    Sex  CI.half
# 1  3.15  island1   male 2.165215
# 2  6.20  island1 female 2.165215
# 3 10.55  island2   male 2.165215
# 4 15.60  island2 female 2.165215
# 5  2.55  island3   male 2.165215
# 6  4.40  island3 female 2.165215

#   GENERATING THE ERRORBAR PLOT
library(ggplot2)

qplot(data=summary.df,
        y=Means,
        x=Location,
        group=Sex,
        ymin=Means-CI.half,
        ymax=Means+CI.half,
        geom=c("point", "errorbar", "line"),
        color=Sex,
        shape=Sex,
        width=0.25) + theme_bw()

ggplot2 errobar график двусторонней основной эффекты Anova


person James Waters    schedule 02.11.2011    source источник
comment
В приведенном выше коде я вручную вычислил различные средства обработки, но, очевидно, в этом нет необходимости; это просто то, что мы хотим, чтобы ученики прочувствовали расчеты. Для построения графика полосы ошибок все, что нам действительно нужно от дисперсионного анализа, - это среднеквадратическая ошибка, которую R вычисляет довольно быстро и которая используется при вычислении 95% доверительного интервала. Тем не менее, чтобы составить эту цифру, необходимо создать дополнительный фрейм данных summary.df.   -  person James Waters    schedule 02.11.2011
comment
Я получил сообщение: вы не хотите использовать необычные пакеты. Тем не менее, я должен сказать: вы используете пакет Hadley Wickham для построения графика, почему бы не упорядочить свои данные (в частности, по ячейкам и т. Д.) Способом Hadley: хотя бы взгляните на reshape и plyr.   -  person Matt Bannert    schedule 02.11.2011


Ответы (3)


Вот еще одна попытка использования пакета sciplot. Альтернативные способы вычисления доверительных интервалов можно передать в параметре ci.fun.

lineplot.CI(variable,value, group =sex.codes , data = df.2, cex = 1.5,
            xlab = "Location", ylab = "means", cex.lab = 1.2, x.leg = 1,
            col = c("blue","red"), pch = c(16,16))

введите описание изображения здесь

person George Dontas    schedule 02.11.2011

Должен признаться, я совершенно сбит с толку вашим кодом. Не воспринимайте это как личную критику, но я настоятельно рекомендую вам научить своих учеников максимально использовать возможности R. Они могут получить от этого только пользу, и мой опыт показывает, что они быстрее понимают, что происходит, если я не бросаю им в голову строки и строки кода.

Во-первых, вам не нужно рассчитывать средние вручную. Просто сделать:

df.2$mean <- with(df.2,ave(value,sex.codes,variable,FUN=mean))

См. Также ?ave. Это более понятно, чем беспорядок кода в вашем примере. Если у вас есть lizard.model, вы можете просто использовать

fitted(lizard.model)

и сравните эти значения со средними.

Тогда я с вами категорически не согласен. То, что вы рассчитываете, не является стандартной ошибкой вашего прогноза. Чтобы сделать это правильно, используйте функцию predict()

outcome <- predict(lizard.model,se.fit=TRUE)
df.2$CI.half <- outcome$se / 2

Чтобы получить доверительный интервал на основе прогнозируемых средних значений, вы должны использовать правильные формулы, если вы когда-либо хотите, чтобы ваши ученики поняли это правильно. Взгляните на раздел 3.5 этого невероятно замечательного Практическая регрессия и Anova с использованием R из далека. Он содержит массу примеров кода, где все удобно и лаконично рассчитано вручную. Он будет полезен как вам, так и вашим ученикам. Я многому из них научился и часто использую как руководство при объяснении этих вещей студентам.

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

summary.df <- unique(df.2[,-c(3,5,6)])
names(summary.df) <- c('Sex','Location','Means','CI.half')

И теперь вы можете просто запустить свой сюжетный код в том виде, в каком он есть.

В качестве альтернативы, если вам нужна ошибка прогноза для ваших значений, вы можете использовать следующее:

lizard.predict <- predict(lizard.model,interval='prediction')
df.2$lower <- lizard.predict[,2]
df.2$upper <- lizard.predict[,3]

summary.df <- unique(df.2[,-3])
names(summary.df)[1:3] <- c('Sex','Location','Means')


qplot(data=summary.df,
        y=Means,
        x=Location,
        group=Sex,
        ymin=lower,
        ymax=upper,
        geom=c("point", "errorbar", "line"),
        color=Sex,
        shape=Sex,
        width=0.25) + theme_bw()

PS: Если я здесь и там говорю резко, это не предназначено. Английский не мой родной язык, и я до сих пор не знаком с тонкостями языка.

person Joris Meys    schedule 02.11.2011

[Возможное бессовестное продвижение] Вам следует рассмотреть функции compareCats и rxnNorm в пакете HandyStuff, доступном по адресу www.github.com/bryanhanson / HandyStuff Предупреждение: я не уверен, что он работает без проблем с R 2.14. В частности, rxnNorm выглядит как сюжет, который вы пытаетесь создать, плюс он дает вам множество вариантов с точки зрения обобщающей статистики и украшений сюжета. Но для этого необходимо, чтобы ваши ученики установили отдельный пакет, поэтому, возможно, вы исключите его (но он позволяет студентам сосредоточиться на представлении и анализе данных). График из примера? RxnNorm включен здесь. введите описание изображения здесь

С помощью rxnNorm у вас есть выбор из нескольких способов вычисления CI, контролируемых аргументом "method". Вот актуальные функции (из пакета ChemoSpec).

> seX <- function (x)  sd(x, na.rm = TRUE)/sqrt(length(na.omit(x)))
> <environment: namespace:ChemoSpec>
> 
> seXy <- function (x)  {
>     m <- mean(na.omit(x))
>     se <- seX(x)
>     u <- m + se
>     l <- m - se
>     c(y = m, ymin = l, ymax = u) } <environment: namespace:ChemoSpec>
> 
> 
> seXy95 <- function (x)  {
>     m <- mean(na.omit(x))
>     se <- seX(x)
>     u <- m + 1.96 * se
>     l <- m - 1.96 * se
>     c(y = m, ymin = l, ymax = u) } <environment: namespace:ChemoSpec>
> 
> 
> seXyIqr <- function (x)  {
>     i <- fivenum(x)
>     c(y = i[3], ymin = i[2], ymax = i[4]) } <environment: namespace:ChemoSpec>
> 
> seXyMad <- function (x)  {
>     m <- median(na.omit(x))
>     d <- mad(na.omit(x))
>     u <- m + d
>     l <- m - d
>     c(y = m, ymin = l, ymax = u) } <environment: namespace:ChemoSpec>
person Bryan Hanson    schedule 02.11.2011
comment
Как рассчитываются CI в этом пакете? - person James Waters; 03.11.2011
comment
У вас есть выбор из нескольких методов CI, которые обрабатываются набором функций в пакете ChemoSpec. Они не помещаются в поле для комментариев, я добавлю к своему предыдущему ответу. - person Bryan Hanson; 03.11.2011