Skip to content

Methods

Koen Derks edited this page Sep 2, 2021 · 24 revisions

How to add a new method to jfa

So you want to add a new statistical method to jfa? Great, your help on this open-source project is much appreciated. First, if you do not have a fork and a clone of the jfa repository, please read the Wiki page on setting up the jfa repository. In this guide, we will go through the process of adding a new method to jfa step by step. You will learn where to change the underlying R code and how to make a pull request to merge your changes.

Step 1: Create a new method in methods.R

Navigate to your local clone of the jfa repository. You should see a folder R in there. In this folder is a file methods.R that contains most of the existing methods in jfa.

As possible input options for your new method, you have (among others) access to:

  • conf.level: A numeric value between 0 and 1 specifying the confidence level.
  • n: An integer larger than 0 specifying sample size used.
  • bookvalues: A numeric vector of book values of length n.
  • bookvalues: A numeric vector of audit values of length n.
  • taints: A numeric vector of taints of length n.
  • N.units: An integer larger than 0 specifying the population size.

For example, check out the function .rohrbach() that calculates Rohrbach's augmented variance estimator.

.rohrbach <- function(taints, conf.level, n, N.units = NULL, r.delta) {
  if (is.null(N.units))
    stop("'N.units' missing for evaluation")
  w                     <- 1 - taints
  mu                    <- mean(taints)
  vars                  <- sum(w^2) / n - (2 - (r.delta / n)) * ((1 / 2) * ((sum(w^2) / n) - stats::var(w)))
  result                <- list()
  result[["ub"]]        <- mu + stats::qnorm(p = conf.level) * sqrt((1 - (n / N.units)) * (vars / n))
  result[["mle"]]       <- sum(taints) / n
  result[["precision"]] <- result[["ub"]] - result[["mle"]]
  return(result)
}

As you can see, the function takes taints, conf.level, n, N.units, and r.delta as input arguments, performs some calculations, and then returns a returns a result object. It is essential that this result object contains at least the three following entries: ub (the upper bound), mle (the most likely error), and precision (the precision; usually the upper bound minus the most likely error).

Step 2: Add the new method to the evaluation() function

Now you are ready to add your method to jfa. Navigate to the file evaluation.R and locate the following piece of code:

    out <- switch(method,
                  'stringer' = .stringer(t, conf.level, n.obs), # Classical evaluation using the Stringer bound
                  'stringer.meikle' = .stringer(t, conf.level, n.obs, correction = 'meikle'), # Classical evaluation using the Stringer bound with Meikle's adjustment
                  'stringer.lta' = .stringer(t, conf.level, n.obs, correction = 'lta'), # Classical evaluation using the Stringer bound with the LTA adjustment
                  'stringer.pvz' = .stringer(t, conf.level, n.obs, correction = 'pvz'), # Classical evaluation using the Stringer bound with PvZ adjustment
                  'rohrbach' = .rohrbach(t, conf.level, n.obs, N.units, r.delta), # Classical evaluation using Rohrbachs augmented variance bound
                  'moment' = .moment(t, conf.level, n.obs, m.type), # Classical evaluation using the Modified Moment bound
                  'coxsnell' = .coxsnell(t, conf.level, n.obs, cs.a, cs.b, cs.mu, 1 + prior.x, 1 + prior.n - prior.x), # Bayesian evaluation using the Cox and Snell bound
                  'mpu' = .mpu(t, conf.level, n.obs), # Classical evaluation using the Mean-per-unit estimator
                  'direct' = .direct(bookvalues, auditvalues, conf.level, N.items, n.obs, N.units), # Classical evaluation using the Direct estimator
                  'difference' = .difference(bookvalues, auditvalues, conf.level, N.items, n.obs), # Classical evaluation using the Difference estimator
                  'quotient' = .quotient(bookvalues, auditvalues, conf.level, N.items, n.obs), # Classical evaluation using the Quotient estimator
                  'regression' = .regression(bookvalues, auditvalues, conf.level, N.items, n.obs, N.units), # Classical evaluation using the Regression estimator
                  'newmethod' = NULL) # Add new method here
    mle <- out[["mle"]]
    ub <- out[["ub"]]
    precision <- out[["precision"]]

Comment out the code and replace NULL with the function that calls your new method (e.g., .functionFromMethodsFile()). The evaluation() function should handle the rest by saving the appropriate results in the following code. Test your method carefully by calling the evaluation(), summary.jfaEvaluation(), and plot.jfaEvaluation() functions.

Step 3: Creating a unit test for your method

Each new method requires a benchmark so that it can regularly be tested for inconsistencies and errors. Check out how to contribute a new benchmark to jfa for a guide on how to add a unit test that checks your method.

Step 4: Making a pull request

When the benchmark of the new method has passed the tests, you may commit and push your changes to your version of the jfa repository. Next, you can open a new pull request to merge your changes into the development branch of koenderks/jfa. First, navigate to the koenderks/jfa repository on GitHub. There, go to the Pull request tab. Click on New pull request.

image

Select Compare across forks, and select the branch that contains your added benchmark.

image

Click on Create pull request to create the pull request. You are now finished adding the method. Keep an eye on the status and conversation in the pull request to see if any changes are requested by the maintainer.