Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: temporarily overriding functions and scope/namespace

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"?

like image 332
Stefan Avatar asked Feb 22 '12 08:02

Stefan


2 Answers

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

like image 131
G. Grothendieck Avatar answered Oct 23 '22 05:10

G. Grothendieck


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.

like image 31
mdsumner Avatar answered Oct 23 '22 04:10

mdsumner