Разница между двумя строками geom_smooth () - PullRequest
1 голос
/ 27 мая 2019

Я сделал график для своих данных, и теперь я хотел бы иметь разницу в y для каждого x, который был оценен geom_smooth (). Существует похожий вопрос , на который, к сожалению, нет ответа. Например, как получить различия для следующего графика (данные приведены ниже):

plot

EDIT

Были сделаны два предложения, но я до сих пор не знаю, как рассчитать разницу.

Первое предложение было получить доступ к данным из объекта ggplot. Я сделал это с

pb <- ggplot_build(p)
pb[["data"]][[1]]

Такой подход работает, но данные не используют одинаковые значения x для групп. Например, первое значение x первой группы равно -3.21318853, но для второй группы x не равно -3.21318853, поэтому я не могу рассчитать разницу y для -3.21318853 между обеими группами

Второе предложение было посмотреть, какая формула используется в geom_smooth (). В описании пакета говорится, что " loess () используется для менее чем 1000 наблюдений; в противном случае mgcv :: gam () используется с формулой = y ~ s (x, bs =" cs ") ". Мой N больше 60000, следовательно, по умолчанию используется gam. Я не знаком с игрой; Кто-нибудь может дать краткий ответ, как рассчитать разницу между двумя строками с учетом только что описанных вещей?

R код

library("ggplot2") # library ggplot
set.seed(1) # make example reproducible
n <- 5000 # set sample size
df <- data.frame(x= rnorm(n), g= factor(rep(c(0,1), n/2))) # generate data
df$y <- NA # include y in df
df$y[df$g== 0] <- df$x[df$g== 0]**2 + rnorm(sum(df$g== 0))*5 # y for group g= 0
df$y[df$g== 1] <-2 + df$x[df$g== 1]**2 + rnorm(sum(df$g== 1))*5 # y for g= 1 (with intercept 2)
ggplot(df, aes(x, y, col= g)) + geom_smooth() + geom_point(alpha= .1) # make a plot

Ответы [ 2 ]

3 голосов
/ 27 мая 2019

Привет и добро пожаловать на переполнение стека,

Первое предложение хорошее. Чтобы сопоставить x-последовательности, вы можете интерполировать значения между ними, используя функцию approxstats).

library("ggplot2") # library ggplot
set.seed(1) # make example reproducible
n <- 5000 # set sample size
df <- data.frame(x= rnorm(n), g= factor(rep(c(0,1), n/2))) # generate data
df$y <- NA # include y in df
df$y[df$g== 0] <- df$x[df$g== 0]**2 + rnorm(sum(df$g== 0))*5 # y for group g= 0
df$y[df$g== 1] <-2 + df$x[df$g== 1]**2 + rnorm(sum(df$g== 1))*5 # y for g= 1 (with intercept 2)

p <- ggplot(df, aes(x, y, col= g)) + geom_smooth() + geom_point(alpha= .1) # make a plot
pb <- ggplot_build(p) # Get computed data

data.of.g1 <- pb[['data']][[1]][pb[['data']][[1]]$group == 1, ] # Extract info for group 1
data.of.g2 <- pb[['data']][[1]][pb[['data']][[1]]$group == 2, ] # Extract info for group 2

xlimit.inf <- max(min(data.of.g1$x), min(data.of.g2$x)) # Get the minimum X the two smoothed data have in common
xlimit.sup <- min(max(data.of.g1$x), max(data.of.g2$x)) # Get the maximum X
xseq <- seq(xlimit.inf, xlimit.sup, 0.01) # Sequence of X value (you can use bigger/smaller step size)

# Based on data from group 1 and group 2, interpolates linearly for all the values in `xseq`
y.g1 <- approx(x = data.of.g1$x, y = data.of.g1$y, xout = xseq)
y.g2 <- approx(x = data.of.g2$x, y = data.of.g2$y, xout = xseq)

difference <- data.frame(x = xseq, dy = abs(y.g1$y - y.g2$y)) # Compute the difference
ggplot(difference, aes(x = x, y = dy)) + geom_line() # Make the plot

Выход: Expected output

1 голос
/ 14 июня 2019

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

Это, в основном, короткая версия сообщения в блоге , которое я написал год или около того.

Примерные данные ОП

set.seed(1) # make example reproducible
n <- 5000 # set sample size
df <- data.frame(x= rnorm(n), g= factor(rep(c(0,1), n/2))) # generate data
df$y <- NA # include y in df
df$y[df$g== 0] <- df$x[df$g== 0]**2 + rnorm(sum(df$g== 0))*5 # y for group g= 0
df$y[df$g== 1] <-2 + df$x[df$g== 1]**2 + rnorm(sum(df$g== 1))*5 # y for g= 1 (with intercept 2)

Начните с подгонки модели для данных примера:

library("mgcv")
m <- gam(y ~ g + s(x, by = g), data = df, method = "REML")

Здесь я подгоняю GAM с гладко-множительным взаимодействием (бит by), и для этой модели нам нужно также включить g как параметрический эффект, поскольку групповые сглаживания центрированы около 0, поэтому нам нужно включить групповые средние значения в параметрическую часть модели.

Далее нам нужна сетка данных вдоль xпеременная, по которой мы будем оценивать разницу между двумя оцененными сглаживаниями:

pdat <- with(df, expand.grid(x = seq(min(x), max(x), length = 200),
                            g = c(0,1)))
pdat <- transform(pdat, g = factor(g))

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

xp <- predict(m, newdata = pdat, type = "lpmatrix")

Следующий код, чтобы определить, какие строки и столбцы в xp принадлежат сглаживаниям для соответствующих уровней g;поскольку в модели есть только два уровня и только один сглаженный член, это совершенно тривиально, но для более сложных моделей это необходимо, и важно правильно настроить имена компонентов, чтобы биты grep() работали.

## which cols of xp relate to splines of interest?
c1 <- grepl('g0', colnames(xp))
c2 <- grepl('g1', colnames(xp))
## which rows of xp relate to sites of interest?
r1 <- with(pdat, g == 0)
r2 <- with(pdat, g == 1)

Теперь мы можем различать строки xp для пары уровней, которые мы сравниваем

## difference rows of xp for data from comparison
X <- xp[r1, ] - xp[r2, ]

Поскольку мы фокусируемся на разнице, нам нужно обнулить все столбцы, а несвязан с выбранной парой сглаживаний, которая включает в себя любые параметрические члены.

## zero out cols of X related to splines for other lochs
X[, ! (c1 | c2)] <- 0
## zero out the parametric cols
X[, !grepl('^s\\(', colnames(xp))] <- 0

(В этом примере эти две строки делают одно и то же, но в более сложных примерах необходимы оба.)

Теперь у нас есть матрица X, которая содержит разницу между двумя базисными разложениями для пары сглаживаний, которые нас интересуют, но чтобы получить это в терминах подгоночных значений ответа y, нам нужно умножитьэта матрица по вектору коэффициентов:

## difference between smooths
dif <- X %*% coef(m)

Теперь dif содержит разницу между двумя сглаживаниями.

We cснова использовать X и ковариационную матрицу модельных коэффициентов для вычисления стандартной ошибки этой разности, а затем 95% (в данном случае) доверительного интервала для разности оценок.

## se of difference
se <- sqrt(rowSums((X %*% vcov(m)) * X))

## confidence interval on difference
crit <- qt(.975, df.residual(m))
upr <- dif + (crit * se)
lwr <- dif - (crit * se)

Обратите внимание, что здесьпри вызове vcov() мы используем эмпирическую байесовскую ковариационную матрицу, но не ту, которая была исправлена ​​для выбора параметров гладкости.Функция, которую я покажу в ближайшее время, позволяет вам учитывать эту дополнительную неопределенность с помощью аргумента unconditional = TRUE.

Наконец, мы собираем результаты и график:

res <- data.frame(x = with(df, seq(min(x), max(x), length = 200)),
                  dif = dif, upr = upr, lwr = lwr)

ggplot(res, aes(x = x, y = dif)) +
  geom_ribbon(aes(ymin = lwr, ymax = upr, x = x), alpha = 0.2) +
  geom_line()

Это дает

enter image description here

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

r$> m0 <- gam(y ~ g + s(x), data = df, method = "REML")

r$> AIC(m0, m)
         df      AIC
m0  9.68355 30277.93
m  14.70675 30285.02

r$> anova(m0, m, test = 'F')
Analysis of Deviance Table

Model 1: y ~ g + s(x)
Model 2: y ~ g + s(x, by = g)
  Resid. Df Resid. Dev     Df Deviance      F Pr(>F)
1    4990.1     124372                              
2    4983.9     124298 6.1762   73.591 0.4781 0.8301

Заключение

В сообщении, которое я упомянул, есть функция, которая превращает описанные выше шаги в простую функцию, smooth_diff():

smooth_diff <- function(model, newdata, f1, f2, var, alpha = 0.05,
                        unconditional = FALSE) {
    xp <- predict(model, newdata = newdata, type = 'lpmatrix')
    c1 <- grepl(f1, colnames(xp))
    c2 <- grepl(f2, colnames(xp))
    r1 <- newdata[[var]] == f1
    r2 <- newdata[[var]] == f2
    ## difference rows of xp for data from comparison
    X <- xp[r1, ] - xp[r2, ]
    ## zero out cols of X related to splines for other lochs
    X[, ! (c1 | c2)] <- 0
    ## zero out the parametric cols
    X[, !grepl('^s\\(', colnames(xp))] <- 0
    dif <- X %*% coef(model)
    se <- sqrt(rowSums((X %*% vcov(model, unconditional = unconditional)) * X))
    crit <- qt(alpha/2, df.residual(model), lower.tail = FALSE)
    upr <- dif + (crit * se)
    lwr <- dif - (crit * se)
    data.frame(pair = paste(f1, f2, sep = '-'),
               diff = dif,
               se = se,
               upper = upr,
               lower = lwr)
}

Используя эту функцию, мы можем повторить весь анализ и отобразить разницу следующим образом:

out <- smooth_diff(m, pdat, '0', '1', 'g')
out <- cbind(x = with(df, seq(min(x), max(x), length = 200)),
             out)

ggplot(out, aes(x = x, y = diff)) +
  geom_ribbon(aes(ymin = lower, ymax = upper, x = x), alpha = 0.2) +
  geom_line()

Я не буду показывать график здесь, поскольку он идентичен показанному выше, за исключением меток оси.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...