如何使用产生R绘图的代码自动添加标题?

7

R的绘图功能非常适用于数据探索,因为它经常具有非常智能的默认值。例如,在使用公式进行绘图时,坐标轴的标签是从该公式派生的。换句话说,以下两个调用会产生相同的输出:

plot(x~y)
plot(x~y, xlab="x", ylab="y")

有没有办法获得类似的“智能自动命名”?例如,我想要调用:
plot(x~y, main=<something>)

同时产生与调用相同的输出
plot(x~y, main="plot(x~y)")

在某种内省机制下,<something>插入了所使用的调用。

在R中有没有这样的工具,可以通过某种标准机制或外部包来实现呢?

编辑:有人建议将公式指定为字符串,并将其作为参数提供给formula()调用以及main。这很有用,但它会忽略影响图形的参数,例如使用数据子集。更详细地说,我想要:

x<-c(1,2,3)
y<-c(1,2,3)
z<-c(0,0,1)
d<-data.frame(x,y,z)
plot(x~y, subset(d, z==0), main=<something>)

为了达到相同的效果,需要:
plot(x~y, subset(d, z==0), main="plot(x~y, subset(d, z==0))")
3个回答

7
我认为在不编写一个细节包装plot()的情况下是无法完成的。原因是R在调用函数时会评估“提供的参数”,而在调用函数的评估框架中,无法访问当前函数调用(有关详细信息,请参见这里)。
相比之下,“默认参数”在函数的评估框架中进行评估,从那里可以进行内省。以下是一些可能性(仅在您想要“myPlot”或“plot”出现在标题中的方式上有所不同:
## Function that reports actual call to itself (i.e. 'myPlot()') in plot title.
myPlot <- function(x,...) {
    cl <- deparse(sys.call())
    plot(x, main=cl, ...)
}

## Function that 'lies' and says that plot() (rather than myPlot2()) called it.
myPlot2 <- function(x,...) {
    cl <- sys.call()
    cl[[1]] <- as.symbol("plot")
    cl <- deparse(cl)
    plot(x, main=cl, ...)
}

## Try them out
x <- 1:10
y <- 1:10
par(mfcol=c(1,2))
myPlot(x,y)
myPlot2(y~x)

这里有一个更通用的解决方案:
plotCaller <- function(plotCall, ...) {
    main <- deparse(substitute(plotCall))
    main <- paste(main, collapse="\n")
    eval(as.call(c(as.list(substitute(plotCall)), main=main, ...)))
}

## Try _it_ out

plotCaller(hist(rnorm(9999), breaks=100, col="red"))

library(lattice)
plotCaller(xyplot(rnorm(10)~1:10, pch=16))

## plotCaller will also pass through additional arguments, so they take effect
## without being displayed
plotCaller(xyplot(rnorm(10)~1:10), pch=16)

deparse函数将尝试在行过长时进行折断(默认为60个字符)。在这种情况下,它会返回一个字符串向量。plot方法假设“main”是一个单独的字符串,所以main <- paste(main, collapse='\n')通过连接由deparse返回的所有字符串,使用\n进行连接来处理这个问题。

以下是需要使用示例:

plotCaller(hist(rnorm(9999), breaks=100, col="red", xlab="a rather long label",
    ylab="yet another long label"))

太棒了!我认为更通用的解决方案相当不错。它基本上与Greg Snow的myplot2相同,但我选择了这个,因为我认为更容易理解它是如何工作的。 - saffsd
@saffsd -- 你的编辑中有很棒的想法。谢谢!你能否提供一个示例,展示 paste(main, collapse="\n") 可以发挥作用的地方? - Josh O'Brien

3
当然有!这里是:
x = rnorm(100)
y = sin(x)

something = "y~x"

plot(formula(something),main=something)

enter image description here


1
“paste” 真的必要吗? - joran
你可以编辑它以获得你想要的内容,但你已经有了大致的想法。 - user2005253
你可以不用复制粘贴来完成它。 - user2005253
谢谢!这很不错,但并不完美,因为我想看到整个调用 - 特别是使用子集数据的格子图。我会详细说明例子。 - saffsd

3
你可能正在思考 match.call 的功能。然而,当作为参数传递时,它只能在函数内部正常工作,而不是在外部调用。你可以创建一个包装器函数来调用 match.call 然后将其他所有内容传递给 plot 或使用 substitute 来捕获调用,然后在评估之前修改它的调用:
x <- runif(25)
y <- rnorm(25, x, .1)

myplot <- function(...) {
    tmp <- match.call()
    plot(..., main=deparse(tmp))
}

myplot( y~x )
myplot( y~x, xlim=c(-.25,1.25) )

## or

myplot2 <- function(FUN) {
    tmp1 <- substitute(FUN)
    tmp2 <- deparse(tmp1)
    tmp3 <- as.list(tmp1)
    tmp4 <- as.call(c(tmp3, main=tmp2))
    eval(tmp4)
}

myplot2( plot(y~x) )
myplot2( plot(y~x, xlim=c(-.25,1.25) ) )

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