Quiero escribir una función que llame tanto a plot()
como a legend()
y sería ideal si el usuario pudiera especificar una cantidad de argumentos adicionales que luego pasan a plot()
o legend()
. Sé que puedo lograr esto para una de las dos funciones utilizando ...
:¿Hay alguna manera de usar dos sentencias '...' en una función en R?
foo.plot <- function(x,y,...) {
plot(x,y,...)
legend("bottomleft", "bar", pch=1)
}
foo.plot(1,1, xaxt = "n")
Esto pasa xaxt = "n"
para trazar. Pero, ¿hay alguna manera, por ejemplo, de aprobar, p. title = "legend"
a la llamada legend()
sin especificar previamente los argumentos en el encabezado de la función?
actualización de la respuesta aceptada: pensé que el camino de Vitoshka era el más elegante para lograr lo que quería. Sin embargo, hubo algunos problemas menores con los que tuve que lidiar hasta que funcionó como yo quería.
Al principio, verifiqué cuál de los parámetros quiero pasar a legend
y cuál a plot
. El primer paso en este sentido fue a ver qué argumentos de legend
son únicos para legend
y no forma parte de la trama y/o par:
legend.args <- names(formals(legend))
plot.args <- c(names(formals(plot.default)), names(par()))
dput(legend.args[!(legend.args %in% plot.args)])
utilizo dput()
aquí, porque la línea plot.args <- c(names(formals(plot.default)), names(par()))
siempre llama a una nueva parcela de vacío, que me No quería. Entonces, utilicé la salida dput
en la siguiente función.
A continuación, tuve que lidiar con los argumentos superpuestos (obténgalos a través de dput(largs.all[(largs.all %in% pargs.all)])
). Para algunos, esto era trivial (por ejemplo, x
, y
), otros pasan a ambas funciones (por ejemplo, pch
). Pero, en mi aplicación real, incluso uso otras estrategias (por ejemplo, nombres de variables diferentes para adj
, pero no implementadas en este ejemplo).
Finalmente, la función do.call
tuvo que cambiarse de dos maneras. En primer lugar, la parte what (es decir, llamada funciones) debe ser un carácter (es decir, 'plot'
en lugar de plot
). Y la lista de argumentos debe construirse ligeramente diferente.
foo.plot <- function(x,y,...) {
leg.args.unique <- c("legend", "fill", "border", "angle", "density", "box.lwd", "box.lty", "box.col", "pt.bg", "pt.cex", "pt.lwd", "xjust", "yjust", "x.intersp", "y.intersp", "text.width", "text.col", "merge", "trace", "plot", "ncol", "horiz", "title", "inset", "title.col", "title.adj")
leg.args.all <- c(leg.args.unique, "col", "lty", "lwd", "pch", "bty", "bg", "cex", "adj", "xpd")
dots <- list(...)
do.call('plot', c(list(x = x, y = x), dots[!(names(dots) %in% leg.args.unique)]))
do.call('legend', c(list("bottomleft", "bar"), dots[names(dots) %in% leg.args.all]))
}
foo.plot(1,1,pch = 4, title = "legendary", ylim = c(0, 5))
En este ejemplo, pch
se pasa a tanto plot
y legend
, title
solamente se pasa a legend
, y ylim
sólo para plot
.
Actualización 2 basado en un comentario por Gavin Simpson (véase también los comentarios en la respuesta de Vitoshka):
(i) Eso es correcto.
(ii) Siempre puede ser un personaje. Pero si tiene una variable con el mismo nombre que la función, entonces es necesario citar el nombre de función en do.call
:
min.plot <- function(x,y,plot=TRUE) if(plot == TRUE) do.call(plot, list(x = x, y = y))
min.plot(1,1)
Error in do.call(plot, list(x = x, y = y)) :
'what' must be a character string or a function
(iii) Puede utilizar c(x = 1, y = 1, list())
y funciona bien.Sin embargo, lo que realmente hice (no en el ejemplo que he dado, pero en mi función real) es la siguiente: c(x = 1, y = 1, xlim = c(0, 2), list(bla='foo'))
favor compare esto con: c(list(x = 1, y = 1, xlim = c(0, 2)), list(bla='foo'))
En el primer caso, la lista contiene dos elementos xlim
, xlim1
y xlim2
(cada uno es un escalar), en el último caso la lista tiene solo xlim
(que es vector de longitud 2, que es lo que yo quería).
Por lo tanto, tiene razón en todos sus puntos para mi ejemplo. Pero, para mi función real (con muchas más variables), encontré estos problemas y quería documentarlos aquí. Perdón por ser impreciso.
Un par de puntos en su Q editada; i) 'par()' * solo * producirá un gráfico en blanco si no hay un dispositivo actualmente abierto. Esto es inofensivo ya que estás a punto de tramarlo, ii) Estás equivocado sobre el argumento '' what'' - puede ser un personaje o una función, iii) No necesitas '' list() 'wrapping in' list ("bottomleft", "bar") ', porque todo está forzado a un tipo común --- testigo' c ("foo", 1:10, list (a = 1:10, b = letters [1: 3 ])) '. Buen control de Q y seguimiento, pero es bueno limpiar tus ediciones para mayor precisión. –
@Gavin Consulte mi actualización para obtener una respuesta a sus puntos. – Henrik
buena actualización y es bueno señalar los casos de esquina como este. ¡Bravo! En el nombre de función/nombre de la variable clash, siempre puedes estar más a la defensiva y usar 'graphics :: plot' y' graphics :: legend' para estar doblemente seguro de que se encuentra la función correcta. –