I have a generic function foo
, a respective default method, and e.g. a formula method. If the default method is dispatched the call
to foo.default
should be returned as an attr
ibute, whereas if foo.formula
was dispatched, X
and y
arguments should be replaced by the fo
rmula argument. Moreover, the defaults in foo.default
should also be returned (in both methods), even if not explicitly specified by the user.
It already looks not too bad, but so far I fail to pass the call through from foo.formula
to foo.default
.
foo <- function(x, ...) UseMethod('foo')
foo.formula <- function(fo, data, ...) {
.cl <- match.call()
y <- model.response(model.frame(fo, data))
X <- model.matrix(fo, data)
foo.default(X, y, .cl=.cl)
}
foo.default <- function(X, y, bar=FALSE, method='1A', beta=2, ...) {
if (!exists('.cl')) .cl <- match.call()
fa <- formalArgs(foo.default)
m <- match(names(.cl), fa, nomatch=0)
.cl <- c(as.list(.cl), as.list(args(foo.default))[-m])
.cl[[1]] <- as.name('foo')
`attr<-`(lm.fit(X, y)$coefficients, 'call', as.call(.cl))
}
foo(X1, y1)
# (Intercept) hp
# 30.09886054 -0.06822828
# attr(,"call")
# foo(X = X1, y = y1, bar = FALSE, method = "1A", beta = 2,
# ... = , NULL)
foo(mpg ~ hp, mtcars)
# (Intercept) hp
# 30.09886054 -0.06822828
# attr(,"call")
# foo(X = X, y = y, .cl = .cl, bar = FALSE, method = "1A",
# beta = 2, ... = , NULL)
Desired output in about:
foo(X1, y1)
# (Intercept) hp
# 30.09886054 -0.06822828
# attr(,"call")
# foo(X = X1, y = y1, bar = FALSE, method = "1A", beta = 2, ...)
foo(mpg ~ hp, mtcars)
# (Intercept) hp
# 30.09886054 -0.06822828
# attr(,"call")
# foo(fo = mpg ~ hp, data = mtcars, bar = FALSE, method = '1A', beta = 2, ...)
How can I do that?
Data:
y1 <- c(`Mazda RX4` = 21, `Mazda RX4 Wag` = 21, `Datsun 710` = 22.8,
`Hornet 4 Drive` = 21.4, `Hornet Sportabout` = 18.7, Valiant = 18.1,
`Duster 360` = 14.3, `Merc 240D` = 24.4, `Merc 230` = 22.8, `Merc 280` = 19.2,
`Merc 280C` = 17.8, `Merc 450SE` = 16.4, `Merc 450SL` = 17.3,
`Merc 450SLC` = 15.2, `Cadillac Fleetwood` = 10.4, `Lincoln Continental` = 10.4,
`Chrysler Imperial` = 14.7, `Fiat 128` = 32.4, `Honda Civic` = 30.4,
`Toyota Corolla` = 33.9, `Toyota Corona` = 21.5, `Dodge Challenger` = 15.5,
`AMC Javelin` = 15.2, `Camaro Z28` = 13.3, `Pontiac Firebird` = 19.2,
`Fiat X1-9` = 27.3, `Porsche 914-2` = 26, `Lotus Europa` = 30.4,
`Ford Pantera L` = 15.8, `Ferrari Dino` = 19.7, `Maserati Bora` = 15,
`Volvo 142E` = 21.4)
X1 <- structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 110, 110, 93, 110,
175, 105, 245, 62, 95, 123, 123, 180, 180, 180, 205, 215, 230,
66, 52, 65, 97, 150, 150, 245, 175, 66, 91, 113, 264, 175, 335,
109), dim = c(32L, 2L), dimnames = list(c("Mazda RX4", "Mazda RX4 Wag",
"Datsun 710", "Hornet 4 Drive", "Hornet Sportabout", "Valiant",
"Duster 360", "Merc 240D", "Merc 230", "Merc 280", "Merc 280C",
"Merc 450SE", "Merc 450SL", "Merc 450SLC", "Cadillac Fleetwood",
"Lincoln Continental", "Chrysler Imperial", "Fiat 128", "Honda Civic",
"Toyota Corolla", "Toyota Corona", "Dodge Challenger", "AMC Javelin",
"Camaro Z28", "Pontiac Firebird", "Fiat X1-9", "Porsche 914-2",
"Lotus Europa", "Ford Pantera L", "Ferrari Dino", "Maserati Bora",
"Volvo 142E"), c("(Intercept)", "hp")), assign = 0:1)
CodePudding user response:
How about this?
foo.default <- function(X, y, bar=FALSE, method='1A', beta=2, ...) {
ell <- list(...)
fa <- Filter(Negate(is.null), as.list(args(foo.default)))
if (!exists('.cl', where = ell)) {
.cl <- as.list(match.call())
m <- match(names(.cl), names(fa), nomatch = 0)
.cl <- c(.cl, fa[-m])
} else {
.cl <- c(as.list(ell$.cl), fa[-c(1, 2)])
}
`attr<-`(lm.fit(X, y)$coefficients, 'call', as.call(.cl))
}
foo(X1, y1)
#(Intercept) hp
#30.09886054 -0.06822828
#attr(,"call")
#foo.default(X = X1, y = y1, bar = FALSE, method = "1A", beta = 2,
# ... = )
foo(mpg ~ hp, mtcars)
#(Intercept) hp
#30.09886054 -0.06822828
#attr(,"call")
#foo.formula(fo = mpg ~ hp, data = mtcars, bar = FALSE, method = "1A",
# beta = 2, ... = )
A couple of comments:
There is a bit of a (minor?) awkwardness here: Inside
else
, excluding the first two argumentsX
andy
offoo.default
whenfoo.formula
had been dispatched first, is hard-coded here. So I'm not sure how this will generalise when you introduce another method, e.g.foo.bar(df, ...)
. As long asdf
replaces the first two arguments offoo.default
that'll be fine; if not, more work is required.I don't fully understand where the
NULL
fromas.list(args(foo.default))
comes from. I think it's becauseargs
returnsNULL
which gets turned into a finalNULL
element when coercing toas.list
. Either way, it can be removed withFilter(Negate(is.null), ...)
.I haven't tidied up the ellipsis argument. Instead of
... =
I take it you want...
inside thecall
.