【问题标题】:R function to plot binned means and model fit, ggplotR函数绘制分箱均值和模型拟合,ggplot
【发布时间】:2013-01-31 14:50:20
【问题描述】:

样本数据:

    pp.inc <- structure(list(has.di.rec.pp = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0), m.dist.km2 = c(-34.4150009155273, 6.80600023269653, -6.55499982833862, 
-61.7700004577637, 15.6840000152588, -11.2869997024536, -26.9729995727539, 
0, 81.9940032958984, -35.1459999084473, -12.5179996490479, 0, 
21.5919990539551, 81.9940032958984, -20.7770004272461, 85.9469985961914, 
-15.2959995269775, -75.5879974365234, 81.9940032958984, 3.04999995231628, 
-17.1490001678467, -25.806999206543, -16.0060005187988, -14.91100025177, 
-12.9020004272461, -16.0060005187988, 5.44000005722046, -34.4150009155273, 
81.9940032958984, 3.61400008201599, 13.7379999160767, 2.71300005912781, 
4.31300020217896), treated = c(0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 
0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 
1, 1)), .Names = c("has.di.rec.pp", "m.dist.km2", "treated"), row.names = c(NA, 
-33L), class = c("data.table", "data.frame"))

代码:

library(data.table)
library(ggplot2)

rddplot <- function(data, outcome, runvar, treatment = treated, span, bw, ...){
    data <- data.table(data)
    data.span  <- data[abs(runvar) <= span, ]
    data.span <- data.span[ , bins := cut(runvar, 
                                          seq(-span, span, by = bw), 
                                          include.lowest = TRUE, right = FALSE)]
    data.span.plot <- data.span[ , list(avg.outcome = mean(outcome), 
                                      avg.runvar = mean(runvar), 
                                      treated = max(treatment),
                                      n.iid = length(outcome)), keyby = bins]
    data.span.plot <- data.span.plot[ , runvar := head(seq(-span, span, by = bw), -1)]
    bp <- ggplot(data = data.span.plot, aes(x = runvar, y = avg.outcome))
    bp <- bp + geom_point(aes(colour = n.iid))
    bp <- bp + stat_smooth(data = data.span, aes(x = runvar, y = outcome,
                                                group = factor(treatment)), ...)
    bp
    return(bp)
}

rddplot(pp.inc, has.di.rec.pp, m.dist.km2, treated, 50, 5)

如果我不将其包装在函数中,此代码运行完美。我是 R 的新手,只是很少使用它。我究竟做错了什么?我是否遗漏了一些明显的东西,还是与data.tableggplot2 有关?我认为这可能与ggplot有关,因为其他问题提到存在问题并且应该使用aes_string。我可以重写data.table 部分以使用基本函数。但我认为错误已经发生在此之前,在第二行。我该如何完成这项工作?

编辑:

[原标题: R函数在eval(expr,envir,enclos)中返回错误:找不到对象'name']

我有时间再次查看并制定了解决方案,因此我也稍微修改了标题。使用eval() 对我来说并没有真正奏效,所以我选择了[['columname']] 选择路线。我已经放弃了data.table(和plyr),所以它只使用base 函数,除了ggplot2。我很高兴看到任何关于如何改进它的 cmets。如果有一些基本缺陷,请告诉我。如果不是,我稍后会在我的解决方案中添加答案。

我已更改 bin 计算,以便在零处始终有一个断点,这是必要的。默认 binwidth 由 Silverman 规则确定。我正在考虑单独计算模型拟合并返回它,因为 ggplot 中的模型选择是有限的,但是我想不出一个很好的方法来将它用于各种不同的模型,例如 lm 或 loess,而且它并不严格必要的。我实际上想覆盖一个细条形图,显示每个 bin 中的观察次数,但发现这在 ggplot 中是不可能的(我知道这 一般 是一个坏主意,但有几个很好发表的使用类似图表的论文)。我觉得 size 在这里没有吸引力,但这些确实是次要的抱怨。

感谢您让我走上正确的道路。

我的解决方案:

rddplot <- function(data, outcome, runvar, treatment = treated, 
                    span, bw = bw.nrd0(data[[runvar]]), ...){
    breaks <- c(sort(-seq(0, span, by = bw)[-1]), seq(0, span, by = bw))
    data.span  <- data[abs(data[[runvar]]) <= max(breaks), ]
    data.span$bins <- cut(data.span[[runvar]], breaks, 
                          include.lowest = TRUE, right = FALSE)
    data.span.plot <- as.data.frame(cbind(tapply(data.span[[outcome]], data.span$bins, mean),
                            tapply(data.span[[runvar]], data.span$bins, mean),
                            tapply(data.span[[treatment]], data.span$bins, max),
                            tapply(data.span[[outcome]], data.span$bins, length),
                            tapply(data.span[[outcome]], data.span$bins, sum)))
    colnames(data.span.plot) <- c("avg.outcome", "avg.runvar", "treated", "n.iid", "n.rec")
    data.span.plot$runvar <- head(breaks, -1)
    print(data.span.plot)
    bp <- ggplot(data = data.span.plot, aes(x = runvar, y = avg.outcome))
    bp <- bp + geom_point(aes(size = n.iid))
    bp <- bp + stat_smooth(data = data.span, aes_string(x = runvar, y = outcome,
                                                group = treatment), ...)
    print(bp)
}

呼叫:

rddplot(pp.inc, "has.di.rec.pp", "m.dist.km2", "treated", 50, 
        method = lm, formula = y ~ poly(x, 4, raw = TRUE))

【问题讨论】:

  • 您没有向我们提供您在最后一行代码中调用的任何内容的值。它们是否应该被引用列的名称?如果是这样,您需要探索为data.table 构建expressionseval。要使ggplot 在函数中工作,您需要使用print(ggplot(...))。看起来您正在绘制 x 变量,这些变量在您的初始 ggplot 调用中不存在并且没有给我们 pp.inc50 来使用它们。
  • ...而您可能需要使用aes_string(),您的意图是将变量作为字符参数传递给您的函数,然后它们将它们传递给ggplot。
  • 最后一行中调用的所有内容都在我提供的示例数据中给出,我现在用换行符重新格式化它,以便更容易复制。 pp.inc50 是一个错字,应该是 data.span,现在更正。
  • 这里的部分问题是您只是对符号感到困惑。 has.di.rec.pp 作为符号仅表示 data.table pp.inc 上下文中的任何内容。只需在控制台中输入has.di.rec.pp。它会告诉你“找不到对象”。因此,就 R 而言,将该符号作为参数传递给您的函数基本上是没有意义的。你可能需要把它变成一个字符,然后像贾斯汀提到的那样走 eval() 和表达式路线。
  • Joran 和我正在讨论 data.table 的这个怪癖。通常它比加速更令人头疼。但这也是理解 R 评估和解析方式的绝佳练习。您问题的aes_string 部分仍然正确,除非您构建函数以输出已知变量,否则您将需要它。 (即取runvar 而不是runvar.name

标签: r ggplot2 data.table


【解决方案1】:

我有一个使用data.table 和一些deparse(substitute())setnames 诡计的方法....

rddplot <- function(data, outcome, runvar, treatment = treated, span, bw, ...){
 # convert to data.table 
 data <- data.table(data)
 # get the column names as defined in the call to rddplot 
  outname <- deparse(substitute(outcome))
  runname <- deparse(substitute(runvar))
  treatname <- deparse(substitute(treatment))
 # rename these columns with the argument namses
  setnames(data, old = c(outname,runname,treatname), new = c('outcome','runvar', 'treatment'))

  # breaks as defined in the second example
  breaks <- c(sort(-seq(0, span, by = bw)[-1]), seq(0, span, by = bw))
   # the stuff you were doing before
   data.span  <- data[abs(runvar) <= span, ]
  data.span <- data.span[ , bins := cut(runvar, 
                                        breaks, 
                                        include.lowest = TRUE, right = FALSE)]
  data.span.plot <- data.span[ , list(avg.outcome = mean(outcome), 
                                      avg.runvar = mean(runvar), 
                                      treated = max(treatment),
                                      n.iid = length(outcome)), keyby = bins]
  # note I've removed trying to add `runvar` column to data.span.plot....)
  bp <- ggplot(data = data.span.plot, aes(x = avg.runvar, y = avg.outcome))
  bp <- bp + geom_point(aes(colour = n.iid))
  bp <- bp + stat_smooth(data = data.span, aes(x = runvar, y = outcome,
                                               group = treatment), ...)
  bp

}



rddplot(pp.inc, has.di.rec.pp, m.dist.km2, treated, 50, 5)

请注意,如果您没有在函数内转换为 data.table,并且假设 data 参数是 data.table,那么您可以使用 on.exit() 来恢复通过引用更改的名称。

【讨论】:

  • 谢谢,这是deparse/setnames 组合的一个很好的解决方案。是的,数据参数通常是data.table,该行只是为了确保这一点。我意识到我提供的小样本数据并不能真正说明我在做什么,但我已经用我的原始数据对其进行了测试,工作完美无瑕,看起来也一样好。而且我想绘制avg.runvar 无论如何都是更优雅的解决方案。非常感谢,接受这个。
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
  • 2017-02-01
  • 1970-01-01
相关资源
最近更新 更多