在ggplot中,如何限制LOESS图中y轴的值大于0?

17

以下是我的代码:

#data
sites <- 
  structure(list(site = c(928L, 928L, 928L, 928L, 928L, 928L, 928L,
                          928L, 928L, 928L, 928L, 928L, 928L, 928L,
                          928L, 928L, 928L, 928L, 928L, 928L, 928L,
                          928L, 928L, 928L, 928L, 928L), 
                 date = c(13493L, 13534L, 13566L, 13611L, 13723L,
                          13752L, 13804L, 13837L, 13927L, 14028L,
                          14082L, 14122L, 14150L, 14182L, 14199L,
                          16198L, 16279L, 16607L, 16945L, 17545L,
                          17650L, 17743L, 17868L, 17941L, 18017L, 18092L),
                 y = c(7L, 7L, 17L, 18L, 17L, 17L, 10L, 3L, 17L, 24L, 
                       11L, 5L, 5L, 3L, 5L, 14L, 2L, 9L, 9L, 4L, 7L,
                       6L, 1L, 0L, 5L, 0L)), 
            .Names = c("site", "date", "y"),
            class = "data.frame", row.names = c(NA, -26L))

#convert to date
x<-as.Date(sites$date, origin="1960-01-01") 

#plot smooth, line goes below zero!
qplot(data=sites, x, y, main="Site 349") 
(p <- qplot(data = sites, x, y, xlab = "", ylab = ""))
(p1 <- p + geom_smooth(method = "loess",span=0.5, size = 1.5))

一些LOESS线和置信区间低于零,我想将图形限制在0和正数之间(因为负数没有意义)。 enter image description here

我该怎么做?

2个回答

17

我赞同Matt Parker的建议,您需要改变拟合过程。对于仅为正数的数据,通常的一种选择是在对数尺度上进行拟合,然后取指数以得到原始尺度上的结果。这将确保仅有正值。

生成具有以下某些问题的随机数据:

 d <- data.frame(x=0:100)
 d$y <- exp(rnorm(nrow(d), mean=-d$x/40, sd=0.8))
 qplot(x,y,data=d) + stat_smooth() 

现在我们可以使用ggplot的转换功能对y值进行对数转换,但在指数比例尺上显示结果(这与原始比例尺相对应):

qplot(x,y,data=d) + stat_smooth() + scale_y_log10()+coord_trans(ytrans="pow10")

您可以在 coord_trans 帮助页面上看到类似的示例。如果您不喜欢y轴,可以操作间断点和标签。 根据问题更新进行编辑 自问题最初提出以来,ggplot2 发生了一些变化,原始答案没有处理0的情况。
选项1
解决方案的主要思路相同:找到一个将可能值的范围映射到-Inf到Inf的转换,对其进行loess平滑,然后将结果反向转换。 对数转换如果没有零就很好。 如果包括0,则我认为所需的函数不存在,但通常有效的可能性是 log(1 + x) 转换。 它是内置的,但我们还需要逆转换 exp(x)-1
library(scales)
#create exp(x)-1 transformation, the inverse of log(1+p)
expm1_trans <-  function() trans_new("expm1", "expm1", "log1p")

qplot(x, y, data=sites) + stat_smooth(method="loess") +
  scale_y_continuous(trans=log1p_trans()) +
  coord_trans(ytrans=expm1_trans())

对log(1+x)变换后的数据进行Loess拟合

选项2

第二个选项是在Matt Parker答案的评论中提出的建议的扩展:使用一个考虑结果整数性质的回归方法。这意味着使用针对计数的过度离散化(以防万一)泊松回归。虽然你不能做loess,但你可以进行样条拟合。你可以调整自由度来控制平滑程度。

library(splines)
qplot(x, y, data=sites) + stat_smooth(method="glm", family="quasipoisson", 
                                      formula = y ~ ns(x, 3))

使用过度离散泊松回归进行样条拟合

这两个选项给出了相似的结果,这是一件好事。


Aniko,我在尝试时遇到了以下错误:错误:外部函数调用中的NA/NaN/Inf(arg 1) - Nate
1
@Nate:如果你的 y 值中包含 0,那么就会出现 "NA / NaN / Inf..." 错误。由于 log(0) 是未定义的,所以这并不奇怪。此方法仅适用于正值。根据上下文情况(例如,如果 y 表示计数),你可以使用 y+1 而不是 y,但这会变得混乱。 - Aniko
@Aniko,我编辑了问题,添加了数据和图表。你能否编辑你的答案,+coord_trans(ytrans="pow10") 部分对我不起作用... - zx8754
@zx8754,coord_trans帮助页面上的示例展示了如何实现这一点。尝试使用ytrans = exp_trans(10) - aosmith
1
对于选项2,我收到了“错误:未知参数:family”的消息。 - mhwombat
显示剩余3条评论

4

我没有样例数据,无法进行测试,但是

qplot(data=sites, x, y, main="Site 349")  
(p <- qplot(data = sites, x, y, xlab = "", ylab = "")) 
(p1 <- p + geom_smooth(method = "loess",span=0.5, size = 1.5)) 
p1 + theme_bw() + opts(title = "Site 349") + ylim(0, foo)

(其中foo是您绘图的合适上限)可能会起到作用。 与基本图形不同,ggplot中的xlim()和ylim()命令实际上限制了用于制作图形的数据,而不仅仅是绘图窗口。 它可能还会限制geom_smooth()(但我不确定)。

编辑:阅读更多后,您还可以考虑切换geom_smooth正在使用的模型。 再次,无法查看您的数据是一个问题。 但是,例如,如果它是二进制的-您可以添加stat_smooth(method="glm", family="binomial")以获得逻辑平滑线。 请参见?stat_smooth获取更多信息。


结构(列表(site = c(928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L, 928L), date = c(13493L, 13534L, 13566L, 13611L, 13723L, 13752L, 13804L, 13837L, 13927L, 14028L, 14082L, 14122L, 14150L, 14182L, 14199L, 16198L, 16279L, 16607L, 16945L, 17545L, 17650L, 17743L, 17868L, 17941L, 18017L, 18092L), y = c(7L, 7L, 17L, 18L, 17L, 17L, 10L, 3L, 17L, 24L, 11L, 5L, 5L, 3L, 5L, 14L, 2L, 9L, 9L, 4L, 7L, 6L, 1L, 0L, 5L, 0L)), .Names = c("site", "date", "y")) - Nate
, class = "data.frame", row.names = c(NA, -26L)) - Nate
我使用以下代码将日期转换为R语言中的日期格式: x<-as.Date(sites$date, origin="1960-01-01") - Nate
家族在广义线性模型中应该是泊松分布,但置信区间似乎比LOESS小得多,而且更加直线。这很随意,但我希望有更多像LOESS那样的波浪线。你有什么想法吗? - Nate
很遗憾,stat_smooth 的默认模型是 GAM,而我对此并不了解。 - Matt Parker
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接