library(distr6) set.seed(42) knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
Implementing your own decorator is the easiest extension you can make to distr6, the only difficulty arises in determining what should go in your decorator. If you plan on making a pull-request with a new decorator to distr6 then we would expect the following:
The DistributionDecorator
class is an abstract class that doesn't add any methods or variables, its sole purpose is to define decorators in an abstract sense as a way to collate them. Decorators could function as named lists, for example the CoreStatistics
decorator could instead be a list called 'CoreStatistics' with each named element being one of the added methods. However lists provide slightly less flexibility and would need to be created as the package is loaded, as opposed to a relatively small class that sits in the package namespace.
A decorator is just a class, inheriting from DistributionDecorator
, with public methods that are added to a distribution when decorated. For example, the CoreStatistics
decorator has a definition:
CoreStatistics <- R6::R6Class("CoreStatistics", inherit = DistributionDecorator)
and methods (below are just two, again shortened)
CoreStatistics$set("public", "mgf", function(t) { return(self$genExp(trafo = function(x) {return(exp(x*t))})) }) CoreStatistics$set("public","genExp",function(trafo = NULL){ if(is.null(trafo)){ trafo = function() return(x) formals(trafo) = alist(x = ) } message(.distr6$message_numeric) return(suppressMessages(integrate(function(x) { pdfs = self$pdf(x) xs = trafo(x) xs[pdfs==0] = 0 return(xs * pdfs) }, lower = self$inf(), upper = self$sup())$value)) })
We don't have too many requirements in defining decorators apart from insisting on using the global '.distr6$message_numeric' string as a message to tell users that numeric results may not be exact. We use messages instead of warnings as warnings stack, which quickly becomes annoying.
Finally, we just want to point you in the direction of useful Distribution
private variables that are rarely used outside of decorators, as an example take the survival
method from the ExoticStatistics
class
ExoticStatistics$set("public", "survival", function(x1, log.p = FALSE) { if(private$.isCdf) self$cdf(x1 = x1, lower.tail = FALSE, log.p = log.p) else { message(.distr6$message_numeric) surv = integrate(self$pdf, x1, self$sup())$value if(log.p) return(log(surv)) else return(surv) } })
Note the use of private$.isCdf
, this checks to see if the cdf
method is defined in the Distribution
. Remember that every Distribution
object always have public d/p/q/r methods defined but do not necessarily have a private d/p/q/r method which actually does all the work. Therefore every Distribution
includes flags for whether or not the d/p/q/r method is defined. To test this yourself, create a custom distribution object with a pdf only, and see what happens when you call the cdf
method (it will return NULL).
Therefore the use of private$.isCdf
tells the decorator whether it can use analytical results or if numeric integration is required.
Note: if the user has already decorated their distribution with FunctionImputation
then even if your decorator thinks the results are analytical, they are not. However this is taken into account by the printed message in the imputed d/p/q/r methods.
DistributionDecorator
.isCdf
, .isPdf
, .isQuantile
, .isRand
methods to exploit analytical results.Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.