Я хочу написать функцию, которая вызывает как 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()
без предварительного указания аргументов в заголовке функции?
Обновление от принятого ответа: Я думал, что путь ВитошКа был самым элегантным, чтобы выполнить то, что я хотел. Однако были некоторые незначительные проблемы, с которыми мне приходилось сталкиваться, пока все не заработало.
Сначала я проверил, какой из параметров я хочу передать legend
, а какой plot
. Первым шагом для этого было выяснить, какие аргументы legend
являются уникальными для legend
и не являются частью сюжета и / или пар:
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
пришлось изменить двумя способами. Во-первых, часть what (то есть вызываемые функции) должна быть символом (то есть '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
.
Обновление 2 на основе комментария Гэвина Симпсона (см. Также комментарии к ответу Витошки):
(Я) Это правильно.
(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
(iii) Вы можете использовать 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, что является в розыске).
Итак, вы правы во всех ваших пунктах для моего примера. Но для моей реальной функции (с гораздо большим количеством переменных) я столкнулся с этими проблемами и хотел документировать их здесь. Извините за неточность.