For looking up methdos for a particular class, there is methods in R, e.g.
> methods(class="lm")
[1] add1 alias anova case.names coerce
[6] confint cooks.distance deviance dfbeta dfbetas
[11] drop1 dummy.coef effects extractAIC family
[16] formula hatvalues influence initialize kappa
[21] labels logLik model.frame model.matrix nobs
[26] plot predict print proj qr
[31] residuals rstandard rstudent show simulate
[36] slotsFromS3 summary variable.names vcov
Unfortunately this does not list all applicable methods: for instance AIC is missing in the above list, and I guess there are yet many more. From the AIC documentation, it can be concluded that it is applicable because it asks for a logLik method, but this can not be concluded from the output of methods.
Is there some way to find out which methods an object accepts?
Meanwhile I have figured out why AIC cannot be found as a method of lm, even though it is applicable. Instead of deleting my question, I here provide an answer in case others might find it helpful, too.
AIC is not an S4 "generic" method, but uses the S3 object mechanism which is based on naming conventions (resolved by UseMethod):
> # not an S4 method:
> isGeneric("AIC")
[1] FALSE
> # S3 methods that looks up with UseMethod():
> AIC
function (object, ..., k = 2)
UseMethod("AIC")
> methods(AIC)
[1] AIC.default* AIC.logLik*
This means that AIC calls AIC.logLik if called on an object of class logLik, but on all other cases it calls AIC.default, which tries to call logLik on the given object:
# internal method (*) => ':::' operator required to list code
> stats:::AIC.default
function (object, ..., k = 2)
{
ll <- if (isNamespaceLoaded("stats4"))
stats4::logLik
else logLik
if (!missing(...)) {
# [...]
}
else {
lls <- ll(object)
-2 * as.numeric(lls) + k * attr(lls, "df")
}
}
As this is hidden in the implementation of AIC.default and is not apparent from the naming convention, it is impossible for methods(class="lm") to figure out that AIC is a method of lm.
Related
I want to deploy a machine learning model that was created using mlr. Therefor I tried wrapping the learned model's predictor applying carrier::crate() function:
predictor <- carrier::crate(
function(data){
predict(object = model,
newdata = data)},
model = model)
In general this seems to work, however when I execute the code on the target environmet, which does not have installed mlr, I get the following error:
Error in UseMethod("predict"): no applicable method for 'predict' applied to an object of class "WrappedModel"
After some investigations I found out that in the learning environment with mlr the class of predict() is
class(predict)
# [1] "nonstandardGenericFunction"
# attr(,"package")
# [1] "methods"
whereas in the predicting environment without mlr it is
class(predictor)
# [1] "function"
So mlr appears to change the predict fucntion in a way that I do not understand yet.
Is there a way to carry the "mlr adjusted" predict function with from the current environment to another one? In the end something like this (which doesn't work) would help me
predictor <- carrier::crate(
function(data){
my_predict(data) -> p
return(p)},
model = model,
my_predict(data) = predict(object = model, newdata = data))
I'd like to understand the steps R goes through to find the appropriate function when mixing S3 and S4. Here's an example:
set.seed(1)
d <- data.frame(a=rep(c('a', 'b'), each=15),
b=rep(c('x', 'y', 'z'), times=5),
y=rnorm(30))
m <- lme4::lmer(y ~ b + (1|a), data=d)
l <- lsmeans::lsmeans(m, 'b')
multcomp::cld(l)
I don't fully understand what happens when the final line gets executed.
multcomp::cld prints UseMethod("cld"), so S3 method dispatch.
isS4(l) shows that l is an S4 class object.
It seems that, despite calling an S3 generic, the S3 dispatch system is completely ignored. Creating a function print.lsmobj <- function(obj) print('S3') (since class(l) is lsmobj) and running cld(l) does not print "S3".
showMethods(lsmobj) or showMethods(ref.grid) (the super class), do not list anything that resembles a cld function.
Using debugonce(multcomp::cld) shows that the function that is called eventually is cld.ref.grid from lsmeans.
I was wondering, however, how to realise that cld.ref.grid will eventually be called without any "tricks" like debugonce. That is, what are the steps R performs to get to cld.ref.grid.
In order for S3 methods to be registered, the generic has to be available. Here, I write a simple foo method for merMod objects:
> library(lme4)
> foo.merMod = function(object, ...) { "foo" }
> showMethods(class = "merMod")
Function ".DollarNames":
<not an S4 generic function>
Function "complete":
<not an S4 generic function>
Function "formals<-":
<not an S4 generic function>
Function "functions":
<not an S4 generic function>
Function: getL (package lme4)
x="merMod"
Function "prompt":
<not an S4 generic function>
Function: show (package methods)
object="merMod"
> methods(class = "merMod")
[1] anova as.function coef confint cooks.distance
[6] deviance df.residual drop1 extractAIC family
[11] fitted fixef formula getL getME
[16] hatvalues influence isGLMM isLMM isNLMM
[21] isREML logLik model.frame model.matrix ngrps
[26] nobs plot predict print profile
[31] ranef refit refitML rePCA residuals
[36] rstudent show sigma simulate summary
[41] terms update VarCorr vcov weights
Neither list includes foo. But if we define the generic, then it shows up in methods() results:
> foo = function(object, ...) UseMethod("foo")
> methods(class = "merMod")
[1] anova as.function coef confint cooks.distance
[6] deviance df.residual drop1 extractAIC family
[11] fitted fixef foo formula getL
[16] getME hatvalues influence isGLMM isLMM
[21] isNLMM isREML logLik model.frame model.matrix
[26] ngrps nobs plot predict print
[31] profile ranef refit refitML rePCA
[36] residuals rstudent show sigma simulate
[41] summary terms update VarCorr vcov
[46] weights
Now it includes foo
Similarly, in your example, methods() will reveal the existence of cld if you do library(multcomp), because that is where the generic for cld sits.
The older R documentation (pre-2016) used to contain more details than the current documentation but roughly speaking, the process is as follows in descending order of priority:
1) if the function is a standard S4 generic and any of the arguments in the signature are S4 (according to isS4), then the best S4 method is chosen according to the usual rules.
2) if the function is a nonstandard S4 generic then its body is executed, which at some point then calls S4 dispatch itself.
3) if the function is a S3 generic function then S3 dispatch takes place on the first argument (except for internal generic binary operators).
4) if the function isn't a generic at all, then it is evaluated in the usual way with lazy evaluation for all its arguments.
Note that from the help page from setGeneric:
"Functions that dispatch S3 methods by calling UseMethod are ordinary functions, not objects from the "genericFunction" class. They are made generic like any other function, but some special considerations apply to ensure that S4 and S3 method dispatch is consistent (see Methods_for_S3)."
I have a function that tries to apply a piece-wise regression model to my data.
In some cases, the data has a generous amount of missing values and I don't have a good estimator of where the knots will be. I decided to bypass the piece-wise and go for a simple linear regression:
try(piecewise) if error go to lm with just one slope
Here's the code that does it. Note that lin.reg is a helper function that outputs predict() for the lm object in the x range. It does not create any problem.
piece <- function(x,y){
# just in case this comes with column names or something
y <- as.numeric(y)
# create the lm object
lm.obj <- lm(y~x)
# Try to fit piecewise
seg <- try(segmented(lm.obj,seg.Z=~x))
# print(seg)
if("try-error" %in% class(seg)) {
# Print that you are using a linear regression and not the piece-wise
print("Using linear Regression")
# Call helper function
result <- lin.reg(x,y)
# Get out of the error/function
return(result)
}
# Use the piece-wise
result <- predict(segmented::segmented(lm.obj,seg.Z=~x),
newdata = data.frame(x,y))
print("Using piece-wise regression")
return(result)
}
Problem(s)
I get this error when piece-wise goes wrong
Error: at least one coef is NA: breakpoint(s) at the boundary? (possibly with many x-values replicated)
But it is unreliable/unpredictable, sometimes it gets ignored and sometimes it breaks the function. I am looping over the rows of a data frame with the y values and the same call gets to different rows before braking.
I believe it has to do with the if("try-error" %in% class(seg)) that might not be the best way to catch the error.
I added some printing to make sure. Here's when it works properly, note iteration 284 gave error and went to simple linear.
[1] "Using piece-wise regression"
[1] 283
[1] "segmented" "lm"
[1] "Using piece-wise regression"
[1] 284
Error : at least one coef is NA: breakpoint(s) at the boundary? (possibly with many x-values replicated)
[1] "try-error"
[1] "Using linear Regression"
And here's when it doesn't, seems like the try() call is not returning error as it should
[1] "Using piece-wise regression"
[1] 312
[1] "segmented" "lm"
[1] "Using piece-wise regression"
[1] 313
[1] "segmented" "lm"
Error: at least one coef is NA: breakpoint(s) at the boundary? (possibly with many x-values replicated)
Adding the argument silent=T in the try block worked out for me.
I have one problem with packages intersection.
I use arfima and forecast packages which both have identical name methods (like AIC, BIC, etc).
So, when I try to run code, I have a problem with methods mismatch - R try to use method from the last loaded package. Yes, I can call exported methods by"::", I know. The main problem connected with internal code of these methods - they use own package methods in the methods body without "::". For example, when I try to use run this code:
require(arfima);
require(forecast);
x <- rnorm(100);
fit <- arfima::arfima(x);
summary(fit);
It gives those errors:
> fit <- arfima::arfima(x);
Note: autoweed is ON. It is possible, but not likely,
that unique modes may be lost.
Beginning the fits with 2 starting values.
> summary(fit);
Error in AIC.logLik(logl) :unrealized type (29) in 'eval'
So, this code w/o loaded forecast package works well (I ran it in "clear" R session):
require(arfima);
#require(forecast);
x <- rnorm(100);
fit <- arfima::arfima(x);
summary(fit);
# Note: autoweed is ON. It is possible, but not likely,
# that unique modes may be lost.
# Beginning the fits with 2 starting values.
#
# summary(fit);
#
# Call:
#
# arfima::arfima(z = x)
#
#
# Mode 1 Coefficients:
# Estimate Std. Error Th. Std. Err. z-value Pr(>|z|)
# d.f -0.0208667 0.0770519 0.0779679 -0.27081 0.78653
# Fitted mean -0.0432115 0.0845518 NA -0.51107 0.60930
# sigma^2 estimated as 0.851957; Log-likelihood = 8.51214; AIC = -11.0243; BIC = 282.976
#
# Numerical Correlations of Coefficients:
# d.f Fitted mean
# d.f 1.00 -0.09
# Fitted mean -0.09 1.00
#
# Theoretical Correlations of Coefficients:
# d.f
# d.f 1.00
#
# Expected Fisher Information Matrix of Coefficients:
# d.f
# d.f 1.65
So, is it possible to hide package for the part or executed code? Something like this:
require(arfima);
#require(forecast);
hide(forecast);
x <- rnorm(100);
fit <- arfima::arfima(x);
summary(fit);
unhide(forecast);
or like this:
require(arfima);
#require(forecast);
used(arfima);
x <- rnorm(100);
fit <- arfima::arfima(x);
summary(fit);
unused(arfima);
Perhaps you should update all your package installations and retry. I don't get any error with your code using a newly installed binary copy of arfima. AIC is generic and when you look at the loaded methods you do see one for objects of class "arfima", so no use of the "::" function should be needed:
> methods(AIC)
[1] AIC.arfima* AIC.default* AIC.logLik*
see '?methods' for accessing help and source code
There is a detach function but it's often not enough to completely set aside a package unless its "unload" parameter is set to TRUE, and in this case may be overkill since your diagnosis appears to be incorrect. This shows that strategy to be feasible but I still suspect unnecessary:
require(arfima);
require(forecast);
detach(package:forecast,unload=TRUE)
x <- rnorm(100);
fit <- arfima::arfima(x);
summary(fit); library(forecast)
This shows that there are class specific methods for summary so there should be no errors caused by package confusion there:
> methods(summary)
[1] summary,ANY-method summary,DBIObject-method
[3] summary,quantmod-method summary.aov
[5] summary.aovlist* summary.arfima*
[7] summary.Arima* summary.arma*
[9] summary.aspell* summary.check_packages_in_dir*
[11] summary.connection summary.data.frame
[13] summary.Date summary.default
[15] summary.ecdf* summary.ets*
[17] summary.factor summary.forecast*
snipped res
Furthermore you are incorrect in thinking there is a forecast::BIC. With forecast and arfima loaded we see only :
methods(BIC)
[1] BIC.arfima*
(And there is no suggestion on the Index page of forecast that either AIC or BIC are defined within that package. So any object created by the forecast functions would be handled by AIC.default and would be expected to fail with BIC.
I need to extract the p attribute of a specific row in the output of cox.zph function in R.
To put you in question, I describe my problem as follows, step by step with an example:
require('survival')
# I create the simplest test data set
test1 <- list(time=c(4,3,1,1,2,2,3),
status=c(1,1,1,0,1,1,0),
x=c(0,2,1,1,1,0,0),
sex=c(0,0,0,0,1,1,1))
# Fit a stratified model
coxmodel <- coxph(Surv(time, status) ~ x + strata(sex), test1)
And then, I use cox.zph function:
zph <- cox.zph(coxmodel)
With the following output:
rho chisq p
x 0.354 0.322 0.57
I tried to get p attribute value (in this case, 0.57) by using attributes(zph) but p doesn't appear in order to do something like zph$p. I also used plyr package with no results.
Does anyone could help me? Thanks.
Here it is:
zph$table[ , "p" ]
The you can access the members of the zph object using $, since it is a list:
names( zph )
# returns:
# [1] "table" "x" "y" "var" "call" "transform"
Then look up zph$table and presto.
In R, usually the different "slots" of an object are accessible through $ (S3 OO framework) or # (S4 OO framework).