Есть ли способ использовать два оператора ' ... ' в функции в R?
я хочу написать функцию, которая называет как plot()
и legend()
и было бы идеально, если бы пользователь мог указать ряд дополнительных аргументов, которые затем передаются либо plot()
или legend()
. Я знаю, что могу достичь этого для одной из двух функций, используя ...
:
foo.plot <- function(x,y,...) {
plot(x,y,...)
legend("bottomleft", "bar", pch=1)
}
foo.plot(1,1, xaxt = "n")
мимо xaxt = "n"
на участок. Но есть ли способ, например, пройти, например title = "legend"
до legend()
звонок без prespecifying аргументов в функцию заголовок?
обновление из принятого ответа:
сначала я проверил, какой из параметров я хочу передать я использую затем мне пришлось иметь дело с перекрывающимися аргументами (получить их через наконец, в этом примере обновление 2 на основе комментария Гэвина Симпсона (см. также комментарии в ответе Витошки): (III) Вы можете использовать Итак, вы правы во всех своих пунктах для моего примера. Но для моей реальной функции (с гораздо большим количеством переменных) я столкнулся с этими проблемами и хотел документировать их здесь. Извините за то, что неточный.legend
и для plot
. Первым шагом к этой цели было увидеть, какие аргументы legend
уникальные для legend
и не часть сюжета и / или par:legend.args <- names(formals(legend))
plot.args <- c(names(formals(plot.default)), names(par()))
dput(legend.args[!(legend.args %in% plot.args)])
dput()
здесь, потому что строку plot.args <- c(names(formals(plot.default)), names(par()))
всегда называет новый пустой участок, который я не хочу. Итак, я использовал вывод dput
в следующей функции.dput(largs.all[(largs.all %in% pargs.all)])
). Для некоторых это было тривиально (например, x
, y
), другие прошли в обе функции (например, pch
). Но, в моем реальном приложении я использую другие стратегии (например, разные имена переменных для adj
, но не реализовано в этом примере).do.call
функция должна была быть изменена двумя способами. Во-первых, какая часть (т. е. называемые функции) должна быть символом (т. е. 'plot'
вместо plot
). И список аргументов должен быть построен несколько иначе.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))
pch
передается как plot
и legend
, title
передается только в legend
и ylim
только plot
.
i) это верно.
(ii) это всегда может быть персонаж. Но если у вас есть переменная с тем же именем, что и функция, вам нужно процитировать имя функции в 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
c(x = 1, y = 1, list())
и он отлично работает. Однако то, что я действительно сделал (не в Примере, который я дал, а в моей реальной функции), - это следующий:c(x = 1, y = 1, xlim = c(0, 2), list(bla='foo'))
Пожалуйста, сравните это с: c(list(x = 1, y = 1, xlim = c(0, 2)), list(bla='foo'))
В первом случае, список содержит два элемента xlim
, xlim1
и xlim2
(каждый скаляр), в последнем случае список содержит только xlim
(который является вектором длины 2, что я и хотел).
4 ответов
автоматический способ:
foo.plot <- function(x,y,...) {
lnames <- names(formals(legend))
pnames <- c(names(formals(plot.default)), names(par()))
dots <- list(...)
do.call('plot', c(list(x = x, y = x), dots[names(dots) %in% pnames]))
do.call('legend', c("bottomleft", "bar", pch = 1, dots[names(dots) %in% lnames]))
}
pch должен быть отфильтрован из имен, чтобы избежать дублирования в legend
вызов в случае, если пользователь поставляет "pch", но у вас есть идея.
Отредактировано Jan 2012 Carl W: "do.вызов " работает только с функциями в кавычках, как в обновлениях Хенрика. Я отредактировал его здесь, чтобы избежать путаницы в будущем.
эти вещи получить сложно, и нет простых решений без указания дополнительных аргументов в функции. Если бы ты ... --6--> и в plot
и legend
звонки вы бы в конечном итоге получать предупреждения при прохождении в legend
-конкретные аргументы. Например, с помощью:
foo.plot <- function(x,y,...) {
plot(x,y,...)
legend("bottomleft", "bar", pch = 1, ...)
}
вы получаете следующие предупреждения:
> foo.plot(1, 1, xjust = 0.5)
Warning messages:
1: In plot.window(...) : "xjust" is not a graphical parameter
2: In plot.xy(xy, type, ...) : "xjust" is not a graphical parameter
3: In axis(side = side, at = at, labels = labels, ...) :
"xjust" is not a graphical parameter
4: In axis(side = side, at = at, labels = labels, ...) :
"xjust" is not a graphical parameter
5: In box(...) : "xjust" is not a graphical parameter
6: In title(...) : "xjust" is not a graphical parameter
есть способы обойти эту проблему, см. plot.default
и его локальные функции, определенные как обертки вокруг функций, таких как axis
, box
etc. где у вас было бы что-то вроде localPlot()
обертка, встроенная функция и вызовите это, а не plot()
напрямую.
bar.plot <- function(x, y, pch = 1, ...) {
localPlot <- function(..., legend, fill, border, angle, density,
xjust, yjust, x.intersp, y.intersp,
text.width, text.col, merge, trace, plot = TRUE, ncol,
horiz, title, inset, title.col, box.lwd,
box.lty, box.col, pt.bg, pt.cex, pt.lwd) plot(...)
localPlot(x, y, pch = pch, ...)
legend(x = "bottomleft", legend = "bar", pch = pch, ...)
}
(совсем почему то 'plot'
аргумент нуждается в дефолте вне меня, но он не будет работать, не давая ему по умолчанию TRUE
.)
теперь это работает без предупреждений:
bar.plot(1, 1, xjust = 0.5, title = "foobar", pch = 3)
как вы обрабатываете графические параметры, такие как будет - bty
повлияет на тип поля графика и легенда типа коробки. Обратите внимание также, что я обработал 'pch'
по-другому, потому что если кто-то использовать этот аргумент в bar.plot()
вызов, вы бы i) используя различные символы в легенде/сюжете, и вы получите предупреждение или ошибку о 'pch'
в два раза соответствующие.
как вы можете видеть, это становится довольно сложно...
ответ Джориса дает интересное решение, которое я прокомментировал, напомнило мне о аргументах контрольных списков в таких функциях, как lme()
. Вот мой версия ответа Джориса, реализующая идею этого контрольного списка:
la.args <- function(x = "bottomleft", legend = "bar", pch = 1, ...)
c(list(x = x, legend = legend, pch = pch), list(...))
foo.plot <- function(x,y, legend.args = la.args(), ...) {
plot(x, y, ...)
do.call(legend, legend.args)
}
который работает так, используя второй пример вызова Джори, соответствующим образом модифицированный:
foo.plot(1,1, xaxt = "n", legend.args=la.args(bg = "yellow", title = "legend"))
вы можете быть как вам нравится при настройке la.args()
function-здесь я устанавливаю только значения по умолчанию для Аргументов Joris и объединяю любые другие. Было бы легче, если бы la.args()
содержит все аргументы Легенды со значениями по умолчанию.
один из способов-использовать списки аргументов в сочетании с do.call
. Это не самое красивое решение, но оно работает.
foo.plot <- function(x,y,legend.args,...) {
la <- list(
x="bottomleft",
legend="bar",
pch=1
)
if (!missing(legend.args)) la <- c(la,legend.args)
plot(x,y,...)
do.call(legend,la)
}
foo.plot(1,1, xaxt = "n")
foo.plot(1,1, xaxt = "n",legend.args=list(bg="yellow",title="legend"))
один недостаток заключается в том, что вы не можете указать, например, pch=2, например, в легенде.список аргументов. Вы можете обойти это с некоторыми предложениями if, я оставлю это вам, чтобы продолжить возиться с ним.
Edit: см. ответ Гэвина Симпсона для лучшей версии этой идеи.
мы могли бы построить formalize
функция, которая сделает любую функцию совместимой с точками:
formalize <- function(f){
# add ... to formals
formals(f) <- c(formals(f), alist(...=))
# release the ... in the local environment
body(f) <- substitute({x;y},list(x = quote(list2env(list(...))),y = body(f)))
f
}
foo.plot <- function(x,y,...) {
legend <- formalize(legend) # definition is changed locally
plot(x,y,...)
legend("bottomleft", "bar", pch = 1, ...)
}
foo.plot(1,1, xaxt = "n", title = "legend")
он показывает предупреждения, хотя, потому что сюжет получает аргументы, которые он не ожидает.
мы могли бы использовать suppressWarnings
на вызов сюжета (но он будет подавлять все предупреждения, очевидно), или мы могли бы разработать другую функцию, чтобы сделать plot
функция (или любая функция) локально более толерантна к точкам:
casualize <- function(f){
f_name <- as.character(substitute(f))
f_ns <- getNamespaceName(environment(f))
body(f) <- substitute({
# extract all args
args <- as.list(match.call()[-1]);
# relevant args only
args <- args[intersect(names(formals()),names(args))]
# call initial fun with relevant args
do.call(getExportedValue(f_ns, f_name),args)})
f
}
foo.plot <- function(x,y,...) {
legend <- formalize(legend)
plot <- casualize(plot)
plot(x,y,...)
legend("bottomleft", "bar", pch = 1, ...)
}
foo.plot(1,1, xaxt = "n", title = "legend")
теперь он работает нормально!