Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Developing R package when functions are written in S4 and using roxygen2

I took the following code from An R Companion to Applied Regression - Chapter 8. Everything works fine except the R code written in S4. When I'm building the documentation, I'm getting lreg5-class.Rd rather than lreg5.Rd and could not get lreg5 function. This is my first attempt to build an R package.

lreg5 Function

#' An S4 class to Logistic Regression.
#'
#' @export
#'  
#' @slot coefficients Coefficients
#' @slot var Variance Covariance Matrix
#' @slot deviance Deviance
#' @slot predictors Predictors of the model
#' @slot iterations No of iterations for convergence

setClass(
   Class = "lreg5"
 , slots =  list(
        coefficients="numeric"
      , var="matrix"
      , deviance="numeric"
      , predictors="character"
      , iterations="numeric"
     )
  )


lreg5 <-
  function(X, y, predictors=colnames(X), max.iter=10,
        tol=1E-6, constant=TRUE, ...) {
    if (!is.numeric(X) || !is.matrix(X))
        stop("X must be a numeric matrix")
    if (!is.numeric(y) || !all(y == 0 | y == 1))
        stop("y must contain only 0s and 1s")
    if (nrow(X) != length(y))
        stop("X and y contain different numbers of observations")
    if (constant) {
        X <- cbind(1, X)
        colnames(X)[1] <- "Constant"
    }
    b <- b.last <- rep(0, ncol(X))
    it <- 1
    while (it <= max.iter){
        p <- as.vector(1/(1 + exp(-X %*% b)))
        var.b <- solve(crossprod(X, p * (1 - p) * X))
        b <- b + var.b %*% crossprod(X, y - p)
        if (max(abs(b - b.last)/(abs(b.last) + 0.01*tol)) < tol) break
        b.last <- b
        it <- it + 1
    }
    if (it > max.iter) warning("maximum iterations exceeded")
    dev <- -2*sum(y*log(p) + (1 - y)*log(1 - p))
    result <- new("lreg5", coefficients=as.vector(b), var=var.b,
        deviance=dev, predictors=predictors, iterations=it)
    result
}

setMethod("show", signature(object="lreg5"),
    definition=function(object) {
            coef <- object@coefficients
            names(coef) <- object@predictors
            print(coef)
        }
    )


setMethod("summary", signature(object="lreg5"),
    definition=function(object, ...) {
            b <- object@coefficients
            se <- sqrt(diag(object@var))
            z <- b/se
            table <- cbind(b, se, z, 2*(1-pnorm(abs(z))))
            colnames(table) <- c("Estimate", "Std.Err", "Z value", "Pr(>z)")
            rownames(table) <- object@predictors
            printCoefmat(table)
            cat("\nDeviance =", object@deviance,"\n")
        }
    )

Packaging

# Step 0: Packages you will need
library(devtools)
library(roxygen2)

# Step 1: Create your package directory
setwd("WD")

create("PackageName")


# Step 2: Add functions

# Step 3: Add documentation

# Step 4: Process your documentation
setwd("./PackageName")
devtools::document()

# Step 5: Install!
setwd("..")
#load_all("PackageName")
devtools::install("PackageName")

# Stp 6: Load the Package!
library(PackageName)
help(PackageName)
like image 349
MYaseen208 Avatar asked Aug 29 '14 13:08

MYaseen208


2 Answers

A few tips:

  • first of all, put your class definitions and your functions in different files. This will help roxygen get everything in the right .Rd document
  • second, use an editor that helps you in the process. I'm very happy using RStudio, as it has all building tools (including roxygen2) incorporated. Take a look at the documentation of RStudio for more info.
  • third, there are no roxygen2 commands in the code you show here. You need to do at least a bit of effort. The roxygen system allows you to comment your functions in the .R file itself and generate the help files based on these comments. But without comments, you're not going to generate much.

A bit more specific:

You can define which information is going to which .Rd file with the roxygen tag @rdname. I use this if I want to group some things in one file, but spread the information over different .Rd files. Another option is @describeIn, which is introduced more recently. This latter tag allows you to specify in which .Rd file a certain function/class/... has to be described.

For more information, see this vignette on creating .Rd files using roxygen2

I strongly suggest you take a look at the different vignettes to get some ideas. Documenting S4 classes and methods using roxygen2 isn't as trivial as one would desire, but it's definitely doable. For an example, take a look at eg the Bioconductor package unifiedWMWqPCR. I've worked on that one and use roxygen2 for the S4-based package. The source can be downloaded from following link:

http://www.bioconductor.org/packages/release/bioc/html/unifiedWMWqPCR.html

like image 67
Joris Meys Avatar answered Sep 30 '22 15:09

Joris Meys


@Joris has said most of this already, but perhaps I can help clarify. You have both a class and a function called lreg5 in this example. You must export the class and the function if you want both of them to be seen. In your sample code only the class is documented and exported. It is a class, so roxygen2 is clever enough to give it the name lreg5-class, which is why you get a file called man/lreg5-class.Rd.

To fix the problem you report, add something like this to your code (I have put a full sample repository here: https://github.com/jefferis/lreg which shows the significant steps along the way.)

#' carry out regression
#' @param X,y inputs
#' @param predictors defaults to \code{colnames(x)}
#' @param max.iter maximum number of iterations (default: 10)
#' @param tol tolerance
#' @param constant add a constant
#' @param ... Additional arguements (currently ignored)
#' @export
#' @return an object of class lreg5
#' @seealso \code{\link{lreg5-class}}
#' @examples
#' lreg5(X=matrix(rnorm(3*100),ncol=3, dimnames = list(NULL, letters[1:3])), 
#'       y=sample(0:1,100, replace=TRUE))

lreg5 <-function(X, y, predictors=colnames(X), max.iter=10,
        tol=1E-6, constant=TRUE, ...) {
## rest of function definition ...
}

The @export tag in this block is the crucial bit, although you will need to document the function arguments and give a title; examples are also a good way to ensure that the function is actually exported and works. Now you will see that you have a file called man/lreg5.Rd and the lreg5 function will be available.

Note that as far as I know there, is no need to put the lreg5 function and the lreg5 class definition+methods in different source code files – though this may help to organise things better.

like image 40
Greg Avatar answered Sep 30 '22 15:09

Greg