Consider the following R code:
local({
lm <- function(x) x^2
lm(10)
})
This temporarily overrides the lm
function, but once local
has been executed it will "be back to normal". I am wondering why the same approach does not seem to work in this next simple example:
require(car)
model <- lm(len ~ dose, data=ToothGrowth)
local({
vcov <- function(x) hccm(x) #robust var-cov matrix
confint(model) # confint will call vcov, but not the above one.
})
The confint
function uses the vcov
function to obtain standard errors for the coefficients, and the idea is to use a robust var-cov matrix by temporarily overriding vcov
, without doing things "manually" or altering functions.
Both vcov and confint are generic functions, I don't know if this is the reason it does not work as intended. It is not the specific example I am interested in as such; rather the conceptual lesson. Is this a namespace or a scope "issue"?
We show how to do this using proxy objects (see Proxies section of this document), first using the proto package and then without:
1) proto. Since confint.lm
is calling vcov
we need to ensure that (a) our new replacement for vcov
is in the revised confint.lm
's environment and (b) the revised confint.lm
can still access the objects from its original. (For example, confint.lm
calls the hidden function format.perc
in stats so if we did not arrange for the second point to be true that hidden function could not be accessed.)
To perform the above we make a new confint.lm
which is the same except it has a new environment (the proxy environment) which contains our replacment vcov
and whose parent in turn is the original confint.lm
environment. Below, the proxy environment is implemented as a proto object where the key items to know here are: (a) proto objects are environments and (b) placing a function in a proto object in the way shown changes its environment to be that proto object. Also to avoid any problems from S3 dispatch of confint
to confint.lm
we call the confint.lm
method directly.
Although the hccm
does not seem to have any different result here we can verify that it was run by noticing the output of the trace
:
library(car)
library(proto)
trace(hccm)
model <- lm(len ~ dose, data=ToothGrowth)
proto(environment(stats:::confint.lm), # set parent
vcov = function(x) hccm(x), #robust var-cov matrix
confint.lm = stats:::confint.lm)[["confint.lm"]](model)
For another example, see example 2 here.
2) environments. The code is a bit more onerous without proto (in fact it roughly doubles the code size) but here it is:
library(car)
trace(hccm)
model <- lm(len ~ dose, data=ToothGrowth)
local({
vcov <- function(x) hccm(x) #robust var-cov matrix
confint.lm <- stats:::confint.lm
environment(confint.lm) <- environment()
confint.lm(model) # confint will call vcov, but not the above one.
}, envir = new.env(parent = environment(stats:::confint.lm)))
EDIT: various improvements in clarity
This is because the functions confint
and vcov
are both in the namespace "stats". The confint() you call here effectively gets stats::vcov, pretty much because that is what namespaces are for - you are allowed to write your own versions of things but not to the detriment of otherwise expected behaviour.
In your first example, you can still safely call other functions that rely on stats::lm and that will not get upset by your local modification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With