R语言 geom_smooth()使用中位数而不是均值

2skhul33  于 2023-06-03  发布在  其他
关注(0)|答案(3)|浏览(183)

我正在用ggplot构建一个图。我有一些数据,其中y基本上与X无关,但我随机地在X的低值处有一些Y的极值。像这样:

set.seed(1)
X <- rnorm(500, mean=5)
y <- rnorm(500)
y[X < 3] <- sample(c(0, 1000), size=length(y[X < 3]),prob=c(0.9, 0.1),  
    replace=TRUE)

我想指出的是,MEDIAN y值在X值上仍然是常数。我可以看到这基本上是正确的:

mean(y[X < 3])
median(y[X < 3])

如果我做一个geom_smooth()图,它确实意味着,并且非常受离群值的影响:

ggplot(data=NULL, aes(x=X, y=y)) + geom_smooth()

我有几个可能的解决办法。例如,我可以首先使用group_by/summarize来创建一个分组中位数的数据集,然后绘制它。我宁愿不这样做,因为在我的真实的数据中,我有很多分面和分组变量,这将是一个很大的跟踪(非理想)。很多图肯定看起来更好,但日志没有很好的解释在我的应用程序(中位数确实有很好的解释)

ggplot(data=NULL, aes(x=X, y=y)) + geom_smooth() + 
  scale_y_log10()

最后,我知道geom_quantile,但我认为我用错了。有没有办法添加一个错误栏?还有-这个geom_quantile图看起来太平滑了,我不明白为什么它是向下倾斜的。我用错了吗?

ggplot(data=NULL, aes(x=X, y=y)) + 
  geom_quantile(quantiles=c(0.5))

我意识到这个问题可能有很多解决方法,但如果可能的话,我想使用geom_smooth,并提供一个参数,告诉它使用中位数。我希望geom_smooth与一致性进行并排比较。我想把平均值和中位数geom_smooths并排显示“嘿,看,Y和X之间的超强模式是由一些大的离群值驱动的,如果我们只看中位数,模式就会消失”。
谢谢!

djp7away

djp7away1#

您可以创建自己的方法在geom_smooth中使用。只要你有一个函数,它产生一个对象,predict泛型在该对象上工作,以获取一个具有一个名为x的列的 Dataframe ,并将其转换为y的适当值。
作为示例,让我们创建一个简单的模型,沿着运行的中位数进行插值。我们将它 Package 在自己的类中,并给予它自己的predict方法:

rolling_median <- function(formula, data, n_roll = 11, ...) {
  x <- data$x[order(data$x)]
  y <- data$y[order(data$x)]
  y <- zoo::rollmedian(y, n_roll, na.pad = TRUE)
  structure(list(x = x, y = y, f = approxfun(x, y)), class = "rollmed")
}

predict.rollmed <- function(mod, newdata, ...) {
  setNames(mod$f(newdata$x), newdata$x)
}

现在我们可以在geom_smooth中使用我们的方法:

ggplot(data = NULL, aes(x = X, y = y)) + 
  geom_smooth(formula = y ~ x, method = "rolling_median", se = FALSE)

当然,这看起来不是很“平坦”,但它比标准geom_smooth()的黄土方法计算的线要平坦得多:

ggplot(data = NULL, aes(x = X, y = y)) + 
  geom_smooth(formula = y ~ x, color = "red", se = FALSE) +
  geom_smooth(formula = y ~ x, method = "rolling_median", se = FALSE)

现在,我明白这与“中位数回归”是不一样的,所以你可能希望探索不同的方法,但是如果你想让geom_smooth来绘制它们,这就是你可以做的。注意,如果需要标准错误,则需要让predict函数返回一个成员名为fitse.fit的列表

qlzsbp2j

qlzsbp2j2#

这里是@Allan的答案的修改,它使用固定的x窗口而不是固定数量的点。这对于不规则时间序列和同时具有多个观测值(x值)的序列非常有用。它使用了一个循环,所以效率不是很高,而且对于较大的数据集会很慢。

# running median with time window

library(dplyr)
library(ggplot2)
library(zoo)

# some irregular and skewed data
set.seed(1)
x <- seq(2000, 2020, length.out = 400) # normal time series, gives same result for both methods
x <- sort(rep(runif(40, min = 2000, max = 2020), 10)) # irregular and repeated time series
y <- exp(runif(length(x), min = -1, max = 3))
data <- data.frame(x = x, y = y)
# ggplot(data) + geom_point(aes(x = x, y = y))

# 2 year window
xwindow <- 2
nwindow <- xwindow * length(x) / 20 - 1

# rolling median
rolling_median <- function(formula, data, n_roll = 11, ...) {
  x <- data$x[order(data$x)]
  y <- data$y[order(data$x)]
  y <- zoo::rollmedian(y, n_roll, na.pad = TRUE)
  structure(list(x = x, y = y, f = approxfun(x, y)), class = "rollmed")
}

predict.rollmed <- function(mod, newdata, ...) {
  setNames(mod$f(newdata$x), newdata$x)
}

# rolling time window median
rolling_median2 <- function(formula, data, xwindow = 2, ...) {
  x <- data$x[order(data$x)]
  y <- data$y[order(data$x)]
  ys <- rep(NA, length(x)) # for the smoothed y values
  xs <- setdiff(unique(x), NA) # the unique x values
  i <- 1 # for testing
  for (i in seq_along(xs)){
    j <- xs[i] - xwindow/2 < x & x < xs[i] + xwindow/2 # x points in this window
    ys[x == xs[i]] <- median(y[j], na.rm = TRUE) # y median over this window
  }
  y <- ys
  structure(list(x = x, y = y, f = approxfun(x, y)), class = "rollmed2")
}

predict.rollmed2 <- function(mod, newdata, ...) {
  setNames(mod$f(newdata$x), newdata$x)
}

# plot smooth
ggplot(data) +
  geom_point(aes(x = x, y = y)) +
  geom_smooth(aes(x = x, y = y, colour = "nwindow"), formula = y ~ x, method = "rolling_median", se = FALSE, method.args = list(n_roll = nwindow)) +
  geom_smooth(aes(x = x, y = y, colour = "xwindow"), formula = y ~ x, method = "rolling_median2", se = FALSE, method.args = list(xwindow = xwindow))

x1c 0d1x由reprex package(v2.0.1)于2022-01-05创建

v7pvogib

v7pvogib3#

低代码解决方案是使用加性分位数回归平滑:

ggplot(data = NULL, aes(x = X, y = y)) + 
  geom_quantile(method = "rqss", quantiles = 0.5, lambda = 0.2)

正如艾伦·卡梅隆所指出的,这真的很“平滑”--它只是在y轴上放大了。lambda越高,越平滑。

相关问题