The previous tutorial introduced S3 and piping into distr6, allowing different approaches to calling R6 methods. In this tutorial we take a look at a big feature of distr6, decorators.

Note: Decorators in distr6 are still maturing, this means the API is still subject to minor changes and the internal computations may be improved, therefore we are aware performance may be sub-optimal.

Decorators

In object-oriented programming it is common to discuss ‘Design Patterns’ as specific methods used to solve coding problems, decorators are one of these ‘patterns’. For full definitions and details about design patterns, we refer the reader to the seminal Design Patterns textbook (Gamma et al. 1994). Briefly, the decorator design pattern is used to add functionality to an object. For example, say we have a class that only has a print method and a decorator for summary methods. Then the user can either use the object by itself with the print method only or optionally you can ‘decorate’ the object and thereby use the summary method. This is perhaps confusing in the abstract, so lets work through an example in distr6. We will go through a quick example and then return to which decorators are available and how they are used in distr6:

N <- Normal$new()
N$survival(1)
#> Error in eval(expr, envir, enclos): attempt to apply non-function
decorate(N, ExoticStatistics)
#> N is now decorated with ExoticStatistics
#> Norm(mean = 0, var = 1)
N$survival(1)
#> [1] 0.1586553

In the example above we:

  1. Constructed a Normal distribution
  2. Demonstrated that the survival method does not exist
  3. Decorated the distribution with the ExoticStatistics decorator
  4. Successfully evaluated the survival function

CoreStatistics

The CoreStatistics decorator includes methods for numeric calculations, a generalised moments kthmoment function and a generalised expectation method genExp. All methods can be viewed using ?CoreStatistics. In distr6 we have a strict design principle that only analytic results should be provided as methods in distributions, i.e. we only return results for which we know the output is 100% accurate. The CoreStatistics decorator allows you to return numeric results when analytic ones aren’t available. For example,

w <- Weibull$new()

# Error as the characteristic function doesn't exist
w$cf(1)
#> Error in eval(expr, envir, enclos): attempt to apply non-function
decorate(w, CoreStatistics)
#> w is now decorated with CoreStatistics
#> Weibull(shape = 1, scale = 1)

# Warning as numerical integration used
w$cf(5)
#> Results from numeric calculations are approximate only. Better results may be available.
#> Results from numeric calculations are approximate only. Better results may be available.
#> [1] 0.0384616+0.1923076i

The decorator also introduces generalised moment and expectation methods. The help documentation for these ?kthmoment and ?genExp give a full overview to these methods, but both are very useful for modelling, inference and deriving other numeric results.

ExoticStatistics

The ExoticStatistics decorator includes more complex numeric methods as well as survival, hazard and cumulative hazard that may or may not be analytic expressions (depending what is available in the distribution object). Once again all the methods added by this decorator can be viewed using ?ExoticStatistics. For example,

N <- Normal$new()

# Error as the characteristic function doesn't exist
N$hazard(1)
#> Error in eval(expr, envir, enclos): attempt to apply non-function
decorate(N, ExoticStatistics)
#> N is now decorated with ExoticStatistics
#> Norm(mean = 0, var = 1)

# No warning as an analytic expression using pdf and cdf are used
N$hazard(1)
#> [1] 1.525135

# Warning as numeric calculations are used
N$pdfPNorm(2,0,4)
#> Results from numeric calculations are approximate only. Better results may be available.
#> [1] 0.3755628

FunctionImputation

The final implemented decorator is used to impute missing d/p/q/r methods. At the moment the imputation strategy cannot be chosen but in future updates this will be a function argument that can be selected. Again these use numeric strategies and highlight these with messages to alert you to less precise results. We will return to the FunctionImputation decorator in a future tutorial which looks at constructing custom distributions.

How to Decorate

There are two main ways to decorate distributions in distr6, either in construction or after construction. We show these by example:

In Construction

Normal$new(decorators = CoreStatistics)
#> Norm(mean = 0, var = 1)
Normal$new(decorators = list(CoreStatistics, ExoticStatistics))
#> Norm(mean = 0, var = 1)

After Construction

N <- Normal$new()
decorate(N, CoreStatistics)
#> N is now decorated with CoreStatistics
#> Norm(mean = 0, var = 1)

# Or piping as in the last tutorial
library(magrittr)
Normal$new() %>% decorate(list(CoreStatistics, ExoticStatistics))
#> Norm is now decorated with CoreStatistics,ExoticStatistics
#> Norm(mean = 0, var = 1)

And that’s all there is to it! If you forget which decorators are available, just run listDecorators(), or listDecorators(simplify = FALSE) if you want to see which methods they include at the same time.

Summary

In this tutorial we looked at decorators in distr6 to obtain numeric results, methods for statistical modelling and imputation methods for missing d/p/q/r functions. In the next tutorial we look at the next big feature in distr6, wrappers.

References

Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. 1994. “Design Patterns: Elements of Reusable Object-Oriented Software.” Addison-Wesley.