Skip to content

rerror

Description

rerror() is a random number generator (RNG) that generates random error from a population with μ=0\mu = 0 and σ=1\sigma = 1, and with user-defined amounts of skewness and (excess) kurtosis. It uses the Fleishman power method to achieve this.

Syntax

rerror([rows=1], [cols=1], [skew=0], [kurt=0], [seed_str]) where

  • rows and cols define the shape of the output
  • skew is the desired skewness, defaulting to zero. Negative values are possible.
  • kurt is the desired kurtosis, defaulting to zero. This must be a positive number.
  • seed_str is a memorable seed string. If omitted then a quasi-unique seed is used to generate a non-volatile output; see the documentation of runif for more detail about this. Use a memorable string of your choosing if you want to set an explicit seed for reproducibility purposes, and use GetSeed if you want a volatile output.

If rerror is called without specifying values for either skew or kurt then rerror will draw values from the standard normal distribution.

You can specify any value, positive or negative, for the skewness. The kurtosis must be positive (or zero) and its minimum possible value is given by

kmin≈−1.13168+1.58837⋅skew2 k_\text{min} \approx -1.13168 + 1.58837 \cdot \text{skew}^2

If you specify a skew but not a kurtosis, then a value near the minimum kurtosis will be used. If you specify both a skew and a kurtosis, but the kurtosis is too small, then an error message will be returned.

There are also some maximum values of skew and kurt for which no valid solution can be found, in which case an error message to that effect will be displayed. This is simply a limitation of the Fleishman power method; if you want a simulation with larger skew and kurtosis, try drawing samples from a lognormal distribution, which is famous for its long tail.

This function is well-suited for quick & dirty work but for simulation demos you will either want to set the seed with a memorable string or use the volatile version of this function, rerrorV. Also please read the caveats about choosing seed strings in the runif documentation; they apply to this function too.

Example

The example below (and the attached spreadsheet) shows how rerror can be used to generate random numbers with different skews and kurtosis, independent of the mean and standard deviation.

rerror columns with different skew and kurtosis (0/0, 1/2, -3, 0/40), all mean 2 and SD 1 except the first, with boxplots showing their shapes

Download: rerror.xlsx

Source Code

Helpers used: FSolve, FSolveBump, FSolveLadder, FSolveMonotonic

rerror = LAMBDA(
    [rows], [cols],   // size of matrix returned (default of 1 for both)
    [skew], [kurt],   // desired skewness and excess kurtosis (default 0 for both)
    [seed_str],       // optional seed string (default uses this workbook's CLS_SystemSeed)
    LET(
        // default values
        rows, IF(ISOMITTED(rows), 1, rows),
        cols, IF(ISOMITTED(cols), 1, cols),
        skew, IF(ISOMITTED(skew), 0, skew),
        // do not allow kurtosis below minimum or below zero
        kmin, MAX(0, Fkmin(skew)),
        // error if the user explicitly asked for a kurtosis below the feasible minimum
        kurtTooSmall, IF(ISOMITTED(kurt), FALSE, kurt < kmin),
        kurt, IF(ISOMITTED(kurt), ROUNDUP(kmin,0), kurt),
        // solve for coefficients, retrying across starting values and (if needed)
        // slightly bumped kurtosis until a valid monotonic solution is found
        coeffs, FSolveBump(skew, kurt),
        // general random numbers
        IF(
            kurtTooSmall,
            "ERROR: kurtosis too small for skew = " & skew & "; must be at least " & TEXT(kmin, "0.00"),
            IF(
                AND(skew = 0, kurt = 0),
                // use rnorm
                rnorm(rows, cols, 0, 1, seed_str),
                IF(
                    INDEX(coeffs, 1, 6) = 0,
                    "ERROR: no valid distribution found for skew = " & skew & ", kurt = " & kurt,
                    // use Flieshman method
                    LET(
                        z, NORM.S.INV(runif(rows, cols, ,,,seed_str)),
                        b, INDEX(coeffs, 1, 1),
                        c, INDEX(coeffs, 1, 2),
                        d, INDEX(coeffs, 1, 3),
                        -c + b*z + c*z^2 + d*z^3
                    )
                )
            )
        )
    )
);