From f45102e242838c2fdde41b2aa45238d3e188dc45 Mon Sep 17 00:00:00 2001
From: jorenham GEV
distribution functions:
Here, \( \boxcox{\cdot}{\alpha} \) is the -Box-Cox transformation function.
+Here, \( \qexp{q}{y} \) and \( \qlog{q}{y} \) are the +Tsallis +\( q \)-exponential and the \( q \)-logarithm, +respectively.
An alternative parametrization is sometimes used, e.g. on Wikipedia, where \( \xi = -\alpha \). @@ -1220,6 +1222,10 @@
Note that the GEV is effectively a reparametrized +\( q \)-Gumbel +Tsallis distribution, with +\( q = 1 - \alpha \).
The generalized logistic distribution (GLO), also known as the shifted
log-logistic distribution
@@ -1228,8 +1234,8 @@ GLO
distribution functions:
For \( -1 < \alpha < 1 \), the general trimmed L-moments of the GLO are:
@@ -1259,6 +1265,10 @@The corresponding scipy.stats
implementation is
kappa4
, with h = -1
and k
set to \( \alpha \);
not genlogistic
.
Note that the GLO is effectively a reparametrized +\( q \)-logistic +Tsallis distribution, with +\( q = 1 - \alpha \).
The generalized Pareto distribution (GPD), with @@ -1266,8 +1276,8 @@
Note that this distribution is standard uniform if \( \alpha = 1 \), and @@ -1312,6 +1322,10 @@
See scipy.stats.genpareto
for the implementation of
the GPD.
Note that the GPD is a reparametrized \( q \)-exponential distribution +, where +\( q = (2 \alpha + 1) / (\alpha + 1) \) and \( \lambda = 1 / (2 - q) \) s.t. +\( \alpha \neq -1 \) and \( q < 2 \).
The Pareto Type IV has two shape parameters \( \alpha \in \mathbb{R} \) and @@ -1498,9 +1512,7 @@
Like the Wakeby distribution, the generalized lambda has no closed-form PDF or CDF. Instead, it is defined through its PPF:
Although its central product moments have no closed-form expression, when \( \beta > -1 \) and \( \delta > -1 \), the general trimmed L-moments can be @@ -1771,22 +1783,24 @@
scipy.special.boxcox
+ scipy.special.inv_boxcox(x, 1 - q)
scipy.special.inv_boxcox
+ scipy.special.boxcox(x, 1 - q)
Is your tail too heavy?\nCan't find a moment?\nAre the swans black?\nThe distribution pathological?\n\n... then look no further: Lmo's got you covered!\n\nUniform or multi-dimensional, Lmo can summarize it all with one quick glance!\n
Unlike the legacy moments, L-moments uniquely describe a probability distribution, and are more robust and efficient. The \u201cL\u201d stands for Linear; it is a linear combination of order statistics. So Lmo is as fast as sorting your samples (in terms of time-complexity).
"},{"location":"#key-features","title":"Key Features","text":"scipy.stats
distribution.lmo.l_moment(..., trim=(1/137, 3.1416))
.lmo.l_rv_nonparametric
Even if your data is pathological like Cauchy, and the L-moments are not defined, the trimmed L-moments (TL-moments) can be used instead. Let\u2019s calculate the TL-location and TL-scale of a small amount of samples:
>>> import numpy as np\n>>> import lmo\n>>> rng = np.random.default_rng(1980)\n>>> x = rng.standard_cauchy(96) # pickle me, Lmo\n>>> lmo.l_moment(x, [1, 2], trim=(1, 1)).\narray([-0.17937038, 0.68287665])\n
Now compare with the theoretical standard Cauchy TL-moments:
>>> from scipy.stats import cauchy\n>>> cauchy.l_moment([1, 2], trim=(1, 1))\narray([0. , 0.69782723])\n
See the documentation for more examples and the API reference.
"},{"location":"#roadmap","title":"Roadmap","text":"Lmo is on PyPI, so you can do something like:
pip install lmo\n
"},{"location":"#required-dependencies","title":"Required dependencies","text":"These are automatically installed by your package manager, alongside lmo
.
3.10
NumPy 1.22
SciPy 1.9
"},{"location":"#optional-dependencies","title":"Optional dependencies","text":"Package Minimum version Notes Pandas 1.4
Lmo extends pd.Series
and pd.DataFrame
with convenient methods, e.g. df.l_scale(trim=1)
. Install as pip install lmo[pandas]
to ensure compatibility."},{"location":"#foundational-literature","title":"Foundational Literature","text":"Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_kurtosis","title":"lmo.l_kurtosis(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"L-kurtosis coefficient; the 4th sample L-moment ratio.
\\[ \\tau^{(s, t)}_4 = \\frac{ \\lambda^{(s, t)}_4 }{ \\lambda^{(s, t)}_2 } \\]Alias for lmo.l_ratio(a, 4, 2, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_t(2, 99)\n>>> lmo.l_kurtosis(x)\n0.28912787...\n>>> lmo.l_kurtosis(x, trim=(1, 1))\n0.19928182...\n
Notes The L-kurtosis \\(\\tau_4\\) lies within the interval \\([-\\frac{1}{4}, 1)\\), and by the L-skewness \\(\\\\tau_3\\) as \\(5 \\tau_3^2 - 1 \\le 4 \\tau_4\\).
See Alsolmo.l_ratio
scipy.stats.kurtosis
lmo.l_loc(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"L-location (or L-loc): unbiased estimator of the first L-moment, \\(\\lambda^{(s, t)}_1\\).
Alias for lmo.l_moment(a, 1, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_cauchy(99)\n>>> x.mean()\n-7.5648...\n>>> lmo.l_loc(x) # no trim; equivalent to the (arithmetic) mean\n-7.5648...\n>>> lmo.l_loc(x, trim=(1, 1)) # TL-location\n-0.15924...\n>>> lmo.l_loc(x, trim=(3/2, 3/2)) # Fractional trimming (only in Lmo)\n-0.085845...\n
Notes If trim = (0, 0)
(default), the L-location is equivalent to the arithmetic mean.
lmo.l_moment
numpy.average
lmo.l_moment(a, r, /, trim=(0, 0), *, axis=None, dtype=np.float64, fweights=None, aweights=None, sort=None, cache=False)
","text":"Estimates the generalized trimmed L-moment \\(\\lambda^{(s, t)}_r\\) from the samples along the specified axis. By default, this will be the regular L-moment, \\(\\lambda_r = \\lambda^{(0, 0)}_r\\).
PARAMETER DESCRIPTIONa
Array containing numbers whose L-moments is desired. If a
is not an array, a conversion is attempted.
TYPE: npt.ArrayLike
r
The L-moment order(s), non-negative integer or array.
TYPE: IntVector | AnyInt
trim
Left- and right-trim orders \\((s, t)\\), non-negative ints or floats that are bound by \\(s + t < n - r\\). A single scalar \\(t\\) can be proivided as well, as alias for \\((t, t)\\).
Some special cases include:
TYPE: AnyTrim
DEFAULT: (0, 0)
axis
Axis along which to calculate the moments. If None
(default), all samples in the array will be used.
TYPE: int | None
DEFAULT: None
dtype
Floating type to use in computing the L-moments. Default is numpy.float64
.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
fweights
1-D array of integer frequency weights; the number of times each observation vector should be repeated.
TYPE: IntVector | None
DEFAULT: None
aweights
An array of weights associated with the values in a
. Each value in a
contributes to the average according to its associated weight. The weights array can either be 1-D (in which case its length must be the size of a along the given axis) or of the same shape as a
. If aweights=None
(default), then all data in a
are assumed to have a weight equal to one.
All aweights
must be >=0
, and the sum must be nonzero.
The algorithm is similar to that for weighted quantiles.
TYPE: npt.ArrayLike | None
DEFAULT: None
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
DEFAULT: None
cache
Set to True
to speed up future L-moment calculations that have the same number of observations in a
, equal trim
, and equal or smaller r
.
TYPE: bool
DEFAULT: False
l
The L-moment(s) of the input This is a scalar iff a is 1-d and r is a scalar. Otherwise, this is an array with np.ndim(r) + np.ndim(a) - 1
dimensions and shape like (*np.shape(r), *(d for d in np.shape(a) if d != axis))
.
TYPE: npt.NDArray[T] | T
Examples:
Calculate the L-location and L-scale from student-T(2) samples, for different (symmetric) trim-lengths.
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_t(2, 99)\n>>> lmo.l_moment(x, [1, 2], trim=(0, 0))\narray([-0.01412282, 0.94063132])\n>>> lmo.l_moment(x, [1, 2], trim=(1/2, 1/2))\narray([-0.02158858, 0.5796519 ])\n>>> lmo.l_moment(x, [1, 2], trim=(1, 1))\narray([-0.0124483 , 0.40120115])\n
The theoretical L-locations are all 0, and the the L-scale are 1.1107
, 0.6002
and 0.4165
, respectively.
scipy.stats.moment
lmo.l_moment_cov(a, r_max, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Non-parmateric auto-covariance matrix of the generalized trimmed L-moment point estimates with orders r = 1, ..., r_max
.
S_l
Variance-covariance matrix/tensor of shape (r_max, r_max, ...)
TYPE: npt.NDArray[T]
Examples:
Fitting of the cauchy distribution with TL-moments. The location is equal to the TL-location, and scale should be \\(0.698\\) times the TL(1)-scale, see Elamir & Seheult (2003).
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.standard_cauchy(1337)\n>>> lmo.l_moment(x, [1, 2], trim=(1, 1))\narray([0.08142405, 0.68884917])\n
The L-moment estimates seem to make sense. Let\u2019s check their standard errors, by taking the square root of the variances (the diagonal of the covariance matrix):
>>> lmo.l_moment_cov(x, 2, trim=(1, 1))\narray([[ 4.89407076e-03, -4.26419310e-05],\n [-4.26419310e-05, 1.30898414e-03]])\n>>> np.sqrt(_.diagonal())\narray([0.06995764, 0.03617989])\n
See Also lmo.l_moment
lmo.l_moment_influence(a, r, /, trim=(0, 0), *, sort=None, tol=1e-08)
","text":"Empirical Influence Function (EIF) of a sample L-moment.
NotesThis function is not vectorized.
PARAMETER DESCRIPTIONa
1-D array-like containing observed samples.
TYPE: npt.ArrayLike
r
L-moment order. Must be a non-negative integer.
TYPE: SupportsIndex
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The (vectorized) empirical influence function.
TYPE: Callable[[V], V]
lmo.l_ratio(a, r, s, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Estimates the generalized L-moment ratio:
\\[ \\tau^{(s, t)}_{rs} = \\frac{ \\lambda^{(s, t)}_r }{ \\lambda^{(s, t)}_s } \\]Equivalent to lmo.l_moment(a, r, *, **) / lmo.l_moment(a, s, *, **)
.
The L-moment with r=0
is 1
, so the l_ratio(a, r, 0, *, **)
is equivalent to l_moment(a, r, *, **)
.
Often, when referring to the \\(r\\)th L-ratio, the L-moment ratio with \\(k=2\\) is implied, i.e. \\(\\tau^{(s, t)}_r\\) is short-hand notation for \\(\\tau^{(s, t)}_{r,2}\\).
The L-variation (L-moment Coefficient of Variation, or L-CB) is another special case of the L-moment ratio, \\(\\tau^{(s, t)}_{2,1}\\). It is sometimes denoted in the literature by dropping the subscript indices: \\(\\tau^{(s, t)}\\). Note that this should only be used with strictly positive distributions.
Examples:
Estimate the L-location, L-scale, L-skewness and L-kurtosis simultaneously:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).lognormal(size=99)\n>>> lmo.l_ratio(x, [1, 2, 3, 4], [0, 0, 2, 2])\narray([1.53196368, 0.77549561, 0.4463163 , 0.29752178])\n>>> lmo.l_ratio(x, [1, 2, 3, 4], [0, 0, 2, 2], trim=(0, 1))\narray([0.75646807, 0.32203446, 0.23887609, 0.07917904])\n
See Also lmo.l_moment
lmo.l_ratio_influence(a, r, k=2, /, trim=(0, 0), *, sort=None, tol=1e-08)
","text":"Empirical Influence Function (EIF) of a sample L-moment ratio.
NotesThis function is not vectorized.
PARAMETER DESCRIPTIONa
1-D array-like containing observed samples.
TYPE: npt.ArrayLike
r
L-moment ratio order. Must be a non-negative integer.
TYPE: SupportsIndex
k
Denominator L-moment order, defaults to 2.
TYPE: SupportsIndex
DEFAULT: 2
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The (vectorized) empirical influence function.
TYPE: Callable[[V], V]
lmo.l_ratio_se(a, r, s, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Non-parametric estimates of the Standard Error (SE) in the L-ratio estimates from lmo.l_ratio
.
Examples:
Estimate the values and errors of the TL-loc, scale, skew and kurtosis for Cauchy-distributed samples. The theoretical values are [0.0, 0.698, 0.0, 0.343]
(Elamir & Seheult, 2003), respectively.
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.standard_cauchy(42)\n>>> lmo.l_ratio(x, [1, 2, 3, 4], [0, 0, 2, 2], trim=(1, 1))\narray([-0.25830513, 0.61738638, -0.03069701, 0.25550176])\n>>> lmo.l_ratio_se(x, [1, 2, 3, 4], [0, 0, 2, 2], trim=(1, 1))\narray([0.32857302, 0.12896501, 0.13835403, 0.07188138])\n
See Also lmo.l_ratio
lmo.l_moment_cov
lmo.l_scale(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"L-scale: unbiased estimator of the second L-moment, \\(\\lambda^{(s, t)}_2\\).
Alias for lmo.l_moment(a, 2, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_cauchy(99)\n>>> x.std()\n72.87715244...\n>>> lmo.l_scale(x)\n9.501123995...\n>>> lmo.l_scale(x, trim=(1, 1))\n0.658993279...\n
Notes If trim = (0, 0)
(default), the L-scale is equivalent to half the Gini mean difference (GMD).
lmo.l_moment
numpy.std
lmo.l_skew(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Unbiased sample estimator of the coefficient of L-skewness, or L-skew for short:
\\[ \\tau^{(s, t)}_3 = \\frac{ \\lambda^{(s, t)}_3 }{ \\lambda^{(s, t)}_2 } \\]Alias for lmo.l_ratio(a, 3, 2, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_exponential(99)\n>>> lmo.l_skew(x)\n0.38524343...\n>>> lmo.l_skew(x, trim=(0, 1))\n0.27116139...\n
See Also lmo.l_ratio
scipy.stats.skew
lmo.l_stats(a, /, trim=(0, 0), num=4, *, axis=None, dtype=np.float64, **kwargs)
","text":"Calculates the L-loc(ation), L-scale, L-skew(ness) and L-kurtosis.
Equivalent to lmo.l_ratio(a, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
by default.
Examples:
>>> import lmo, scipy.stats\n>>> x = scipy.stats.gumbel_r.rvs(size=99, random_state=12345)\n>>> lmo.l_stats(x)\narray([0.79014773, 0.68346357, 0.12207413, 0.12829047])\n
The theoretical L-stats of the standard Gumbel distribution are [0.577, 0.693, 0.170, 0.150]
.
lmo.l_stats_se
lmo.l_ratio
lmo.l_costats
lmo.l_stats_se(a, /, num=4, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Calculates the standard errors (SE\u2019s) of the L-stats
.
Equivalent to lmo.l_ratio_se(a, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
by default.
Examples:
>>> import lmo, scipy.stats\n>>> x = scipy.stats.gumbel_r.rvs(size=99, random_state=12345)\n>>> lmo.l_stats(x)\narray([0.79014773, 0.68346357, 0.12207413, 0.12829047])\n>>> lmo.l_stats_se(x)\narray([0.12305147, 0.05348839, 0.04472984, 0.03408495])\n
The theoretical L-stats of the standard Gumbel distribution are [0.577, 0.693, 0.170, 0.150]
. The corresponding relative z-scores are [-1.730, 0.181, 1.070, 0.648]
.
lmo.l_stats
lmo.l_ratio_se
lmo.l_variation(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"The coefficient of L-variation (or L-CV) unbiased sample estimator:
\\[ \\tau^{(s, t)} = \\frac{ \\lambda^{(s, t)}_2 }{ \\lambda^{(s, t)}_1 } \\]Alias for lmo.l_ratio(a, 2, 1, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).pareto(4.2, 99)\n>>> x.std() / x.mean()\n1.32161112...\n>>> lmo.l_variation(x)\n0.59073639...\n>>> lmo.l_variation(x, trim=(0, 1))\n0.55395044...\n
Notes If trim = (0, 0)
(default), this is equivalent to the Gini coefficient, and lies within the interval \\((0, 1)\\).
lmo.l_ratio
scipy.stats.variation.l_ratio
Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_cokurtosis","title":"lmo.l_cokurtosis(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Sample L-cokurtosis coefficient matrix \\(\\tilde\\Lambda^{(t_1, t_2)}_4\\).
Alias for lmo.l_coratio(a, 4, 2, *, **)
.
lmo.l_coratio
lmo.l_kurtosis
lmo.l_coloc(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"L-colocation matrix of 1st L-comoment estimates, \\(\\Lambda^{(t_1, t_2)}_1\\).
Alias for lmo.l_comoment(a, 1, *, **)
.
If trim = (0, 0)
(default), the L-colocation for \\([ij]\\) is the L-location \\(\\lambda_1\\) of \\(x_i\\), independent of \\(x_j\\).
Examples:
Without trimming, the L-colocation only provides marginal information:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_loc(x, axis=-1)\narray([-0.02678225, 0.03008309])\n>>> lmo.l_coloc(x)\narray([[-0.02678225, -0.02678225],\n [ 0.03008309, 0.03008309]])\n
But the trimmed L-locations are a different story\u2026
>>> lmo.l_loc(x, trim=(1, 1), axis=-1)\narray([-0.10488868, -0.00625729])\n>>> lmo.l_coloc(x, trim=(1, 1))\narray([[-0.10488868, -0.03797989],\n [ 0.03325074, -0.00625729]])\n
What this tells us, is somewhat of a mystery: trimmed L-comoments have been only been briefly mentioned once or twice in the literature.
See Alsolmo.l_comoment
lmo.l_loc
numpy.mean
lmo.l_comoment(a, r, /, trim=(0, 0), *, dtype=np.float64, rowvar=True, sort=None, cache=False)
","text":"Multivariate extension of lmo.l_moment
.
Estimates the L-comoment matrix:
\\[ \\Lambda_{r}^{(t_1, t_2)} = \\left[ \\lambda_{r [ij]}^{(t_1, t_2)} \\right]_{m \\times m} \\]Whereas the L-moments are calculated using the order statistics of the observations, i.e. by sorting, the L-comoment sorts \\(x_i\\) using the order of \\(x_j\\). This means that in general, \\(\\lambda_{r [ij]}^{(t_1, t_2)} \\neq \\lambda_{r [ji]}^{(t_1, t_2)}\\), i.e. \\(\\Lambda_{r}^{(t_1, t_2)}\\) is not symmetric.
The \\(r\\)-th L-comoment \\(\\lambda_{r [ij]}^{(t_1, t_2)}\\) reduces to the L-moment if \\(i=j\\), and can therefore be seen as a generalization of the (univariate) L-moments. Similar to how the diagonal of a covariance matrix contains the variances, the diagonal of the L-comoment matrix contains the L-moments.
Based on the proposed definition by Serfling & Xiao (2007) for L-comoments. Extended to allow for generalized trimming.
PARAMETER DESCRIPTIONa
1-D or 2-D array-like containing m
variables and n
observations. Each row of a
represents a variable, and each column a single observation of all those variables. Also see rowvar
below. If a
is not an array, a conversion is attempted.
TYPE: npt.ArrayLike
r
The L-moment order(s), non-negative integer or array.
TYPE: AnyInt | IntVector
trim
Left- and right-trim orders \\((t_1, t_2)\\), non-negative ints or floats that are bound by \\(t_1 + t_2 < n - r\\).
Some special cases include:
TYPE: AnyTrim
DEFAULT: (0, 0)
rowvar
If rowvar
is True (default), then each row (axis 0) represents a variable, with observations in the columns (axis 1). Otherwise, the relationship is transposed: each column represents a variable, while the rows contain observations.
TYPE: bool
DEFAULT: True
dtype
Floating type to use in computing the L-moments. Default is numpy.float64
.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
DEFAULT: None
cache
Set to True
to speed up future L-moment calculations that have the same number of observations in a
, equal trim
, and equal or smaller r
.
TYPE: bool
DEFAULT: False
L
Array of shape (*r.shape, m, m)
with r-th L-comoments.
TYPE: npt.NDArray[T]
Examples:
Estimation of the second L-comoment (the L-coscale) from biviariate normal samples:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_comoment(x, 2)\narray([[ 1.2766793 , -0.83299947],\n [-0.71547941, 1.05990727]])\n
The diagonal contains the univariate L-moments:
>>> lmo.l_moment(x, 2, axis=-1)\narray([1.2766793 , 1.05990727])\n
References lmo.l_coratio(a, r, s, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Estimate the generalized matrix of L-comoment ratio\u2019s.
\\[ \\tilde \\Lambda_{rs}^{(t_1, t_2)} = \\left[ \\left. \\lambda_{r [ij]}^{(t_1, t_2)} \\right/ \\lambda_{s [ii]}^{(t_1, t_2)} \\right]_{m \\times m} \\] See Alsolmo.l_comoment
lmo.l_ratio
lmo.l_corr(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Sample L-correlation coefficient matrix \\(\\tilde\\Lambda^{(t_1, t_2)}_2\\); the ratio of the L-coscale matrix over the L-scale column-vectors.
Alias for lmo.l_coratio(a, 2, 2, *, **)
.
The diagonal consists of all 1\u2019s.
Where the pearson correlation coefficient measures linearity, the (T)L-correlation coefficient measures monotonicity.
Examples:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> cov = np.array([[6, -3], [-3, 3.5]])\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_corr(x)\narray([[ 1. , -0.65247355],\n [-0.67503962, 1. ]])\n
Let\u2019s compare this with the theoretical correlation
>>> cov[0, 1] / np.sqrt(cov[0, 0] * cov[1, 1])\n-0.6546536707079772\n
and the (Pearson) correlation coefficient matrix:
>>> np.corrcoef(x)\narray([[ 1. , -0.66383285],\n [-0.66383285, 1. ]])\n
See Also lmo.l_coratio
numpy.corrcoef
lmo.l_coscale(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"L-coscale matrix of 2nd L-comoment estimates, \\(\\Lambda^{(t_1, t_2)}_2\\).
Alias for lmo.l_comoment(a, 2, *, **)
.
Analogous to the (auto-) variance-covariance matrix, the L-coscale matrix is positive semi-definite, and its main diagonal contains the L-scale\u2019s. conversely, the L-coscale matrix is inherently asymmetric, thus yielding more information.
Examples:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_scale(x, trim=(1, 1), axis=-1)\narray([0.66698774, 0.54440895])\n>>> lmo.l_coscale(x, trim=(1, 1))\narray([[ 0.66698774, -0.41025416],\n [-0.37918065, 0.54440895]])\n
See Also lmo.l_comoment
lmo.l_scale
numpy.cov
lmo.l_coskew(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Sample L-coskewness coefficient matrix \\(\\tilde\\Lambda^{(t_1, t_2)}_3\\).
Alias for lmo.l_coratio(a, 3, 2, *, **)
.
lmo.l_coratio
lmo.l_skew
lmo.l_costats(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Calculates the L-coscale, L-corr(elation), L-coskew(ness) and L-cokurtosis.
Equivalent to lmo.l_coratio(a, [2, 2, 3, 4], [0, 2, 2, 2], *, **)
.
lmo.l_stats
lmo.l_coratio
scipy.stats
extensions","text":"Extensions for scipy.stats
distributions.
lmo.contrib.scipy_stats.l_rv_generic
","text":"Additional methods that are patched into scipy.stats.rv_continuous
and scipy.stats.rv_discrete
.
l_moment(r, /, *args, trim=(0, 0), quad_opts=None, **kwds)
","text":"Population L-moment(s) \\(\\lambda^{(s,t)}_r\\).
\\[ \\lambda^{(s, t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\mathbb{E}_X \\left[ U^s \\left(1 - U\\right)^t \\,\\tilde{P}^{(t, s)}_{r-1}(U) \\,X \\right] \\;, \\]with \\(U = F_X(X)\\) the rank of \\(X\\), and \\(\\tilde{P}^{(a,b)}_n(x)\\) the shifted (\\(x \\mapsto 2x-1\\)) Jacobi polynomial.
Examples:
Evaluate the population L-moments of the normally-distributed IQ test:
>>> import lmo\n>>> from scipy.stats import norm\n>>> norm(100, 15).l_moment([1, 2, 3, 4]).round(6)\narray([100. , 8.462844, 0. , 1.037559])\n>>> _[1] * np.sqrt(np.pi)\n15.000000...\n
Discrete distributions are also supported, e.g. the Binomial distribution:
>>> from scipy.stats import binom\n>>> binom(10, .6).l_moment([1, 2, 3, 4]).round(6)\narray([ 6. , 0.862238, -0.019729, 0.096461])\n
PARAMETER DESCRIPTION r
L-moment order(s), non-negative integer or array-like of integers.
TYPE: AnyInt | IntVector
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
TypeError
r
is not integer-valued
ValueError
r
is empty or negative
lmbda
The population L-moment(s), a scalar or float array like r
.
TYPE: np.float64 | npt.NDArray[np.float64]
lmo.l_moment
: sample L-momentl_ratio(r, k, /, *args, trim=(0, 0), quad_opts=None, **kwds)
","text":"L-moment ratio(\u2019s) \\(\\tau^{(s,t)}_{r,k}\\).
\\[ \\tau^{(s,t)}_{r,k} = \\frac{\\lambda^{(s,t)}_r}{\\lambda^{(s,t)}_k} \\]Unless explicitly specified, the r-th (\\(r>2\\)) L-ratio, \\(\\tau^{(s,t)}_r\\) refers to \\(\\tau^{(s,t)}_{r, 2}\\). Another special case is the L-variation, or the L-CV, \\(\\tau^{(s,t)} = \\tau^{(s,t)}_{2, 1}\\). This is the L-moment analogue of the coefficient of variation.
Examples:
Evaluate the population L-CV and LL-CV (CV = coefficient of variation) of the standard Rayleigh distribution.
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.rayleigh()\n>>> X.std() / X.mean() # legacy CV\n0.5227232...\n>>> X.l_ratio(2, 1)\n0.2928932...\n>>> X.l_ratio(2, 1, trim=(0, 1))\n0.2752551...\n
And similarly, for the (discrete) Poisson distribution with rate parameter set to 2, the L-CF and LL-CV evaluate to:
>>> X = distributions.poisson(2)\n>>> X.std() / X.mean()\n0.7071067...\n>>> X.l_ratio(2, 1)\n0.3857527...\n>>> X.l_ratio(2, 1, trim=(0, 1))\n0.4097538...\n
Note that (untrimmed) L-CV requires a higher (subdivision) limit in the integration routine, otherwise it\u2019ll complain that it didn\u2019t converge (enough) yet. This is because it\u2019s effectively integrating a non-smooth function, which is mathematically iffy, but works fine in this numerical application.
PARAMETER DESCRIPTIONr
L-moment ratio order(s), non-negative integer or array-like of integers.
TYPE: AnyInt | IntVector
k
L-moment order of the denominator, e.g. 2.
TYPE: AnyInt | IntVector
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
l_rv_generic.l_moment
lmo.l_ratio
- Sample L-moment ratio estimatorl_stats(*args, trim=(0, 0), moments=4, quad_opts=None, **kwds)
","text":"The L-moments (for \\(r \\le 2\\)) and L-ratio\u2019s (for \\(r > 2\\)).
By default, the first moments = 4
population L-stats are calculated:
This method is equivalent to X.l_ratio([1, 2, 3, 4], [0, 0, 2, 2], *, **)
, for with default moments = 4
.
Examples:
Summarize the standard exponential distribution for different trim-orders.
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.expon()\n>>> X.l_stats().round(6)\narray([1. , 0.5 , 0.333333, 0.166667])\n>>> X.l_stats(trim=(0, 1/2)).round(6)\narray([0.666667, 0.333333, 0.266667, 0.114286])\n>>> X.l_stats(trim=(0, 1)).round(6)\narray([0.5 , 0.25 , 0.222222, 0.083333])\n
Note This should not be confused with the term L-statistic, which is sometimes used to describe any linear combination of order statistics.
PARAMETER DESCRIPTION*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
moments
The amount of L-moments to return. Defaults to 4.
TYPE: int
DEFAULT: 4
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
l_rv_generic.l_ratio
lmo.l_stats
- Unbiased sample estimation of L-stats.l_loc(*args, trim=(0, 0), **kwds)
","text":"L-location of the distribution, i.e. the 1st L-moment.
Alias for X.l_moment(1, ...)
.
l_scale(*args, trim=(0, 0), **kwds)
","text":"L-scale of the distribution, i.e. the 2nd L-moment.
Alias for X.l_moment(2, ...)
.
l_skew(*args, trim=(0, 0), **kwds)
","text":"L-skewness coefficient of the distribution; the 3rd L-moment ratio.
Alias for X.l_ratio(3, 2, ...)
.
l_kurtosis(*args, trim=(0, 0), **kwds)
","text":"L-kurtosis coefficient of the distribution; the 4th L-moment ratio.
Alias for X.l_ratio(4, 2, ...)
.
l_moments_cov(r_max, /, *args, trim=(0, 0), quad_opts=None, **kwds)
","text":"Variance/covariance matrix of the L-moment estimators.
L-moments that are estimated from \\(n\\) samples of a distribution with CDF \\(F\\), converge to the multivariate normal distribution as the sample size \\(n \\rightarrow \\infty\\).
\\[ \\sqrt{n} \\left( \\vec{l}^{(s, t)} - \\vec{\\lambda}^{(s, t)} \\right) \\sim \\mathcal{N}( \\vec{0}, \\mathbf{\\Lambda}^{(s, t)} ) \\]Here, \\(\\vec{l}^{(s, t)} = \\left[l^{(s,t)}_r, \\dots, l^{(s,t)}_{r_{max}}\\right]^T\\) is a vector of estimated sample L-moments, and \\(\\vec{\\lambda}^{(s, t)}\\) its theoretical (\u201ctrue\u201d) counterpart.
This function calculates the covariance matrix
\\[ \\begin{align} \\bf{\\Lambda}^{(s,t)}_{k, r} &= \\mathrm{Cov}[l^{(s, t)}_k, l^{(s, t)}_r] \\\\ &= c_k c_r \\iint\\limits_{x < y} \\Big[ p_k\\big(F(x)\\big) \\, p_r\\big(F(y)\\big) + p_r\\big(F(x)\\big) \\, p_k\\big(F(y)\\big) \\Big] w^{(s+1,\\, t)}\\big(F(x)\\big) \\, w^{(s,\\, t+1)}\\big(F(y)\\big) \\, \\mathrm{d}x \\, \\mathrm{d}y \\;, \\end{align} \\]where
\\[ c_n = \\frac{\\Gamma(n) \\Gamma(n+s+t+1)}{n \\Gamma(n+s) \\Gamma(n+t)}\\;, \\]the shifted Jacobi polynomial \\(p_n(u) = P^{(t, s)}_{n-1}(2u - 1)\\), \\(P^{(t, s)}_m\\), and \\(w^{(s,t)}(u) = u^s (1-u)^t\\) its weight function.
NotesThis function is not vectorized or parallelized.
For small sample sizes (\\(n < 100\\)), the covariances of the higher-order L-moments (\\(r > 2\\)) can be biased. But this bias quickly disappears at roughly \\(n > 200\\) (depending on the trim- and L-moment orders).
Examples:
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.expon() # standard exponential distribution\n>>> X.l_moments_cov(4).round(6)\narray([[1. , 0.5 , 0.166667, 0.083333],\n [0.5 , 0.333333, 0.166667, 0.083333],\n [0.166667, 0.166667, 0.133333, 0.083333],\n [0.083333, 0.083333, 0.083333, 0.071429]])\n
>>> X.l_moments_cov(4, trim=(0, 1)).round(6)\narray([[0.333333, 0.125 , 0. , 0. ],\n [0.125 , 0.075 , 0.016667, 0. ],\n [0. , 0.016667, 0.016931, 0.00496 ],\n [0. , 0. , 0.00496 , 0.0062 ]])\n
PARAMETER DESCRIPTION r_max
The amount of L-moment orders to consider. If for example r_max = 4
, the covariance matrix will be of shape (4, 4)
, and the columns and rows correspond to the L-moments of order \\(r = 1, \\dots, r_{max}\\).
TYPE: int
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
cov
Covariance matrix, with shape (r_max, r_max)
.
TYPE: npt.NDArray[np.float64]
RuntimeError
If the covariance matrix is invalid.
Referencesl_stats_cov(*args, moments=4, trim=(0, 0), quad_opts=None, **kwds)
","text":"Similar to l_moments_cov
, but for the l_rv_generic.l_stats
.
As the sample size \\(n \\rightarrow \\infty\\), the L-moment ratio\u2019s are also distributed (multivariate) normally. The L-stats are defined to be L-moments for \\(r\\le 2\\), and L-ratio coefficients otherwise.
The corresponding covariance matrix has been found to be
\\[ \\bf{T}^{(s, t)}_{k, r} = \\begin{cases} \\bf{\\Lambda}^{(s, t)}_{k, r} & k \\le 2 \\wedge r \\le 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} }{ \\lambda^{(s,t)}_{2} } & k \\le 2 \\wedge r > 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_k \\bf{\\Lambda}^{(s, t)}_{2, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} + \\tau_k \\tau_r \\bf{\\Lambda}^{(s, t)}_{2, 2} }{ \\Big( \\lambda^{(s,t)}_{2} \\Big)^2 } & k > 2 \\wedge r > 2 \\end{cases} \\]where \\(\\bf{\\Lambda}^{(s, t)}\\) is the covariance matrix of the L-moments from l_moment_cov_from_cdf
, and \\(\\tau^{(s,t)}_r = \\lambda^{(s,t)}_r / \\lambda^{(s,t)}_2\\) the population L-ratio.
Examples:
Evaluate the LL-stats covariance matrix of the standard exponential distribution, for 0, 1, and 2 degrees of trimming.
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.expon() # standard exponential distribution\n>>> X.l_stats_cov().round(6)\narray([[1. , 0.5 , 0. , 0. ],\n [0.5 , 0.333333, 0.111111, 0.055556],\n [0. , 0.111111, 0.237037, 0.185185],\n [0. , 0.055556, 0.185185, 0.21164 ]])\n>>> X.l_stats_cov(trim=(0, 1)).round(6)\narray([[ 0.333333, 0.125 , -0.111111, -0.041667],\n [ 0.125 , 0.075 , 0. , -0.025 ],\n [-0.111111, 0. , 0.21164 , 0.079365],\n [-0.041667, -0.025 , 0.079365, 0.10754 ]])\n>>> X.l_stats_cov(trim=(0, 2)).round(6)\narray([[ 0.2 , 0.066667, -0.114286, -0.02 ],\n [ 0.066667, 0.038095, -0.014286, -0.023333],\n [-0.114286, -0.014286, 0.228571, 0.04 ],\n [-0.02 , -0.023333, 0.04 , 0.086545]])\n
Note that with 0 trim the L-location is independent of the L-skewness and L-kurtosis. With 1 trim, the L-scale and L-skewness are independent. And with 2 trim, all L-stats depend on each other.
PARAMETER DESCRIPTION*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
moments
The amount of L-statistics to consider. Defaults to 4.
TYPE: int
DEFAULT: 4
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
l_moment_influence(r, /, *args, trim=(0, 0), quad_opts=None, tol=1e-08, **kwds)
","text":"Returns the influence function (IF) of an L-moment.
\\[ \\psi_{\\lambda^{(s, t)}_r | F}(x) = c^{(s,t)}_r \\, F(x)^s \\, \\big( 1-{F}(x) \\big)^t \\, \\tilde{P}^{(s,t)}_{r-1} \\big( F(x) \\big) \\, x - \\lambda^{(s,t)}_r \\;, \\]with \\(F\\) the CDF, \\(\\tilde{P}^{(s,t)}_{r-1}\\) the shifted Jacobi polynomial, and
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r, \\, r+s+t)}{B(r+s, \\, r+t)} \\;, \\]where \\(B\\) is the (complete) Beta function.
The proof is trivial, because population L-moments are linear functionals.
NotesThe order parameter r
is not vectorized.
r
The L-moment order \\(r \\in \\mathbb{N}^+\\)..
TYPE: AnyInt
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
tol
Values that are absolutely smaller than this will be rounded to zero.
TYPE: float
DEFAULT: 1e-08
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
influence_function
The (vectorized) influence function, \\(\\psi_{\\lambda^{(s, t)}_r | F}(x)\\).
TYPE: Callable[[V], V]
l_rv_generic.l_moment
lmo.l_moment
l_ratio_influence(r, k, /, *args, trim=(0, 0), quad_opts=None, tol=1e-08, **kwds)
","text":"Returns the influence function (IF) of an L-moment ratio.
\\[ \\psi_{\\tau^{(s, t)}_{r,k}|F}(x) = \\frac{ \\psi_{\\lambda^{(s, t)}_r|F}(x) - \\tau^{(s, t)}_{r,k} \\, \\psi_{\\lambda^{(s, t)}_k|F}(x) }{ \\lambda^{(s,t)}_k } \\;, \\]where the L-moment ratio is defined as
\\[ \\tau^{(s, t)}_{r,k} = \\frac{ \\lambda^{(s, t)}_r }{ \\lambda^{(s, t)}_k } \\;. \\]Because IF\u2019s are a special case of the general G\u00e2teuax derivative, the L-ratio IF is derived by applying the chain rule to the L-moment IF.
PARAMETER DESCRIPTIONr
L-moment ratio order, i.e. the order of the numerator L-moment.
TYPE: AnyInt
k
Denominator L-moment order, defaults to 2.
TYPE: AnyInt
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
tol
Values that are absolutely smaller than this will be rounded to zero.
TYPE: float
DEFAULT: 1e-08
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
influence_function
The influence function, with vectorized signature () -> ()
.
TYPE: Callable[[V], V]
l_rv_generic.l_ratio
lmo.l_ratio
l_fit(data, *args, n_extra=0, trim=(0, 0), full_output=False, fit_kwargs=None, random_state=None, **kwds)
","text":"Return estimates of shape (if applicable), location, and scale parameters from data. The default estimation method is Method of L-moments (L-MM), but the Generalized Method of L-Moments (L-GMM) is also available (see the n_extra
parameter).
See \u2018lmo.inference.fit\u2019 for details.
Examples:
Fitting standard normal samples Using scipy\u2019s default MLE (Maximum Likelihood Estimation) method:
>>> import lmo\n>>> import numpy as np\n>>> from scipy.stats import norm\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.standard_normal(200)\n>>> norm.fit(x)\n(0.0033..., 0.9555...)\n
Better results can be obtained different by using Lmo\u2019s L-MM (Method of L-moment):
>>> norm.l_fit(x, random_state=rng)\nFitArgs(loc=0.0033..., scale=0.9617...)\n>>> norm.l_fit(x, trim=1, random_state=rng)\nFitArgs(loc=0.0197..., scale=0.9674...)\n
To use more L-moments than the number of parameters, two in this case, n_extra
can be used. This will use the L-GMM (Generalized Method of L-Moments), which results in slightly better estimates:
>>> norm.l_fit(x, n_extra=1, random_state=rng)\nFitArgs(loc=0.0039..., scale=0.9623...)\n>>> norm.l_fit(x, trim=1, n_extra=1, random_state=rng)\nFitArgs(loc=-0.0012..., scale=0.9685...)\n
PARAMETER DESCRIPTION data
1-D array-like data to use in estimating the distribution parameters.
TYPE: npt.ArrayLike
*args
Starting value(s) for any shape-characterizing arguments ( those not provided will be determined by a call to fit(data)
).
TYPE: float
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
n_extra
The amount of extra L-moment conditions to use than the amount of parameters. If 0 (default), L-MM will be used. If >0, \\(k\\)-step L-GMM will be used.
TYPE: int
DEFAULT: 0
full_output
If set to True, a LGMMResult
instance will be returned, instead of only a tuple with parameters.
TYPE: bool
DEFAULT: False
fit_kwargs
Additional keyword arguments to be passed to \u2018lmo.inference.fit\u2019 or \u2018scipy.optimize.minimize\u2019.
TYPE: Mapping[str, Any] | None
DEFAULT: None
random_state
Integer or numpy.random.Generator
instance, used for Monte-Carlo simulation when n_extra > 0
. If None
(default), the random_state
of this distribution will be used.
TYPE: int | np.random.Generator | None
DEFAULT: None
**kwds
Special keyword arguments are recognized as holding certain parameters fixed:
- `f0...fn`: hold respective shape parameters fixed.\nAlternatively, shape parameters to fix can be specified by\nname. For example, if `self.shapes == \"a, b\"`, `fa` and\n`fix_a` are equivalent to `f0`, and `fb` and `fix_b` are\nequivalent to `f1`.\n- `floc`: hold location parameter fixed to specified value.\n- `fscale`: hold scale parameter fixed to specified value.\n
TYPE: Any
DEFAULT: {}
result
Named tuple with estimates for any shape parameters (if applicable), followed by those for location and scale. For most random variables, shape statistics will be returned, but there are exceptions (e.g. norm
). If full_output=True
, an instance of LGMMResult
will be returned instead.
TYPE: tuple[float, ...] | inference.GMMResult
l_fit_loc_scale(data, *args, trim=(0, 0), **kwds)
","text":"Estimate loc and scale parameters from data using the first two L-moments.
NotesThe implementation mimics that of fit_loc_scale()
data
Data to fit.
TYPE: npt.ArrayLike
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information).
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
loc_hat
Estimated location parameter for the data.
TYPE: float
scale_hat
Estimated scale parameter for the data.
TYPE: float
pandas
extensions (optional)","text":"Extension methods for pandas.Series
and pandas.DataFrame
.
Pandas is an optional dependency, and can be installed using pip install lmo[pandas]
.
Examples:
Univariate summary statistics:
>>> df = pd.DataFrame({'a': [1, 2, 2, 3, 4], 'b': [3, 4, 4, 4, 4]})\n>>> df.l_stats()\n a b\nr\n1 2.400000 3.8\n2 0.700000 0.2\n3 0.142857 -1.0\n4 0.285714 1.0\n>>> df.aggregate(['mean', 'std', 'skew', 'kurt'])\n a b\nmean 2.400000 3.800000\nstd 1.140175 0.447214\nskew 0.404796 -2.236068\nkurt -0.177515 5.000000\n
Comparison of L-correlation, and Pearson correlation matrices:
>>> df = pd.DataFrame({'dogs': [.2, .0, .5, .4], 'cats': [.3, .2, .0, .1]})\n>>> df.l_corr()\n dogs cats\ndogs 1.0 -0.764706\ncats -0.8 1.000000\n>>> df.corr()\n dogs cats\ndogs 1.000000 -0.756889\ncats -0.756889 1.000000\n
"},{"location":"api/#lmo.contrib.pandas.Series","title":"lmo.contrib.pandas.Series
","text":"Extension methods for pandas.Series
.
This class is not meant to be used directly. These methods are curried and registered as series accessors.
"},{"location":"api/#lmo.contrib.pandas.Series.l_moment","title":"l_moment(r, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_moment
.
out
A scalar, or a pd.Series[float]
, indexed by r
.
TYPE: _FloatOrSeries
l_ratio(r, k, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_ratio
.
out
A scalar, or pd.Series[float]
, with a MultiIndex
of r
and k
.
TYPE: _FloatOrSeries
l_stats(trim=(0, 0), num=4, **kwargs)
","text":"See lmo.l_stats
.
out
A pd.Series[float]
with index r = 1, ..., num
.
TYPE: pd.Series[float]
l_loc(trim=(0, 0), **kwargs)
","text":"See lmo.l_loc
.
out
A scalar.
TYPE: float
l_scale(trim=(0, 0), **kwargs)
","text":"See lmo.l_scale
.
out
A scalar.
TYPE: float
l_variation(trim=(0, 0), **kwargs)
","text":"See lmo.l_variation
.
out
A scalar.
TYPE: float
l_skew(trim=(0, 0), **kwargs)
","text":"See lmo.l_skew
.
out
A scalar.
TYPE: float
l_kurtosis(trim=(0, 0), **kwargs)
","text":"See lmo.l_kurtosis
.
out
A scalar.
TYPE: float
lmo.contrib.pandas.DataFrame
","text":"Extension methods for pandas.DataFrame
.
This class is not meant to be used directly. These methods are curried and registered as dataframe accessors.
"},{"location":"api/#lmo.contrib.pandas.DataFrame.l_moment","title":"l_moment(r, /, trim=(0, 0), axis=0, **kwargs)
","text":"See lmo.l_moment
.
out
A Series[float]
, or a DataFrame
with r
as index along the specified axis.
TYPE: _SeriesOrFrame
l_ratio(r, k, /, trim=(0, 0), axis=0, **kwargs)
","text":"See lmo.l_ratio
.
out
A Series[float]
, or a DataFrame
, with a MultiIndex
of r
and k
along the specified axis.
TYPE: _SeriesOrFrame
l_stats(trim=(0, 0), num=4, axis=0, **kwargs)
","text":"See lmo.l_stats
.
out
A DataFrame
with r = 1, ..., num
as index along the specified axis.
TYPE: pd.DataFrame
l_loc(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_moment(1, ...)
. See lmo.l_loc
for details.
l_scale(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_moment(2, ...)
. See lmo.l_scale
for details.
l_variation(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_ratio(2, 1, ...)
. See lmo.l_variation
for details.
l_skew(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_ratio(3, 2, ...)
. See lmo.l_skew
for details.
l_kurtosis(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_ratio(4, 2, ...)
. See lmo.l_kurtosis
for details.
l_kurt(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_kurtosis
.
l_comoment(r, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_comoment
.
r
The L-moment order, as a non-negative scalar.
TYPE: AnyInt
trim
Left- and right-trim orders.
TYPE: AnyTrim
DEFAULT: (0, 0)
**kwargs
Additional options to pass to lmo.l_comoment
.
TYPE: Unpack[LComomentOptions]
DEFAULT: {}
out
A DataFrame
of the column-to-column L-comoment matrix.
TYPE: pd.DataFrame
TypeError
If rowvar=True
, use df.T.l_comoment
instead.
l_coratio(r, k=2, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_coratio
.
r
The L-moment order of the numerator, a non-negative scalar.
TYPE: AnyInt
k
The L-moment order of the denominator, a non-negative scalar. Defaults to 2. If set to 0, this is equivalent to l_comoment
.
TYPE: AnyInt
DEFAULT: 2
trim
Left- and right-trim orders.
TYPE: AnyTrim
DEFAULT: (0, 0)
**kwargs
Additional options to pass to lmo.l_comoment
.
TYPE: Unpack[LComomentOptions]
DEFAULT: {}
out
A DataFrame
of the column-to-column matrix of L-comoment ratio\u2019s.
TYPE: pd.DataFrame
TypeError
If rowvar=True
, use df.T.l_comoment
instead.
l_coloc(trim=(0, 0), **kwargs)
","text":"Alias for l_comoment(1, trim, **kwargs)
. See lmo.l_coloc
for details.
l_coscale(trim=(0, 0), **kwargs)
","text":"Alias for l_comoment(2, trim, **kwargs)
. See lmo.l_coscale
for details.
l_corr(trim=(0, 0), **kwargs)
","text":"Alias for l_coratio(2, 2, trim, **kwargs)
. See lmo.l_corr
for details.
l_coskew(trim=(0, 0), **kwargs)
","text":"Alias for l_coratio(3, 2, trim, **kwargs)
. See lmo.l_coskew
for details.
l_cokurtosis(trim=(0, 0), **kwargs)
","text":"Alias for l_coratio(4, 2, trim, **kwargs)
. See lmo.l_cokurtosis
for details.
l_cokurt(trim=(0, 0), **kwargs)
","text":"Alias for l_cokurtosis
.
Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_rv_nonparametric","title":"lmo.l_rv_nonparametric(l_moments, trim=(0, 0), a=None, b=None, **kwargs)
","text":" Bases: rv_continuous
Estimate a distribution using the given L-moments. See scipy.stats.rv_continuous
for the available method.
The PPF (quantile function) is estimated using generalized Fourier series, with the (shifted) Jacobi orthogonal polynomials as basis, and the (scaled) L-moments as coefficients.
The corrected version of theorem 3 from Hosking (2007) states that
\\[ \\hat{Q}(q) = \\sum_{r=1}^{R} \\frac{(r + 1) (2r + s + t - 1)}{r + s + t + 1} \\lambda^{(s, t)}_r P^{(t, s)}_{r - 1}(2u - 1) \\; , \\]converges almost everywhere as \\(R \\rightarrow \\infty\\), for any sufficiently smooth (quantile) function \\(Q(u)\\) with \\(0 < u < 1\\).
Referencesl_moments
Vector containing the first \\(R\\) consecutive L-moments \\(\\left[ \\lambda^{(s, t)}_1 \\; \\lambda^{(s, t)}_2 \\; \\dots \\; \\lambda^{(s, t)}_R \\right]\\), where \\(R \\ge 2\\).
Sample L-moments can be estimated using e.g. lmo.l_moment(x, np.mgrid[:R] + 1, trim=(s, t))
.
The trim-lengths \\((s, t)\\) should be the same for all L-moments.
TYPE: FloatVector
trim
The left and right trim-lengths \\((s, t)\\), that correspond to the provided l_moments
.
TYPE: AnyTrim
DEFAULT: (0, 0)
a
Lower bound of the support of the distribution. By default it is estimated from the L-moments.
TYPE: float | None
DEFAULT: None
b
Upper bound of the support of the distribution. By default it is estimated from the L-moments.
TYPE: float | None
DEFAULT: None
**kwargs
Optional params for scipy.stats.rv_continuous
.
TYPE: Any
DEFAULT: {}
ValueError
If len(l_moments) < 2
, l_moments.ndim != 1
, or there are invalid L-moments / trim-lengths.
l_moments: npt.NDArray[np.float64]
property
","text":"Initial L-moments, for orders \\(r = 1, 2, \\dots, R\\).
"},{"location":"api/#lmo.l_rv_nonparametric.trim","title":"trim: tuple[int, int] | tuple[float, float]
property
","text":"The provided trim-lengths \\((s, t)\\).
"},{"location":"api/#lmo.l_rv_nonparametric.ppf_poly","title":"ppf_poly: PolySeries
property
","text":"Polynomial estimate of the percent point function (PPF), a.k.a. the quantile function (QF), or the inverse cumulative distribution function (ICDF).
NoteConverges to the \u201ctrue\u201d PPF in the mean-squared sense, with weight function \\(q^s (1 - q)^t\\) of quantile \\(q \\in \\[0, 1\\]\\), and trim-lengths \\((t_1, t_2) \\in \\mathbb{R^+} \\times \\mathbb{R^+}\\).
RETURNS DESCRIPTIONPolySeries
A numpy.polynomial.Legendre
orthogonal polynomial series instance.
cdf_poly: PolySeries
cached
property
","text":"Polynomial least-squares interpolation of the CDF.
RETURNS DESCRIPTIONPolySeries
A numpy.polynomial.Legendre
orthogonal polynomial series instance.
pdf_poly: PolySeries
cached
property
","text":"Derivative of the polynomial interpolation of the CDF, i.e. the polynomial estimate of the PDF.
RETURNS DESCRIPTIONPolySeries
A numpy.polynomial.Legendre
orthogonal polynomial series instance.
fit(data, /, rmax=None, trim=(0, 0))
classmethod
","text":"Estimate L-moment from the samples, and return a new l_rv_nonparametric
instance.
data
1d array-like with univariate sample observations.
TYPE: npt.ArrayLike
rmax
The (maximum) amount of L-moment orders to use. Defaults to \\(\\lceil 4 \\log_{10} N \\rceil\\). The quantile polynomial will be of degree rmax - 1
.
TYPE: SupportsIndex | None
DEFAULT: None
trim
The left and right trim-lengths \\((s, t)\\), that correspond to the provided l_moments
.
TYPE: AnyTrim
DEFAULT: (0, 0)
l_rv_nonparametric
A fitted l_rv_nonparametric
instance.
rmax
selection (the error appears to be periodic..?)trim
selectionHypothesis tests, estimator properties, and performance metrics.
"},{"location":"api/#lmo.diagnostic.HypothesisTestResult","title":"lmo.diagnostic.HypothesisTestResult
","text":" Bases: NamedTuple
Results of a hypothesis test.
ATTRIBUTE DESCRIPTIONstatistic
The raw test statistic. Its distribution depends on the specific test implementation.
TYPE: float | npt.NDArray[np.float64]
pvalue
Two-sided probability value corresponding to the the null hypothesis, \\(H_0\\).
TYPE: float | npt.NDArray[np.float64]
is_valid: bool | npt.NDArray[np.bool_]
property
","text":"Check if the statistic is finite and not nan
.
is_significant(level=0.05)
","text":"Whether or not the null hypothesis can be rejected, with a certain confidence level (5% by default).
"},{"location":"api/#lmo.diagnostic.normaltest","title":"lmo.diagnostic.normaltest(a, /, *, axis=None)
","text":"Statistical hypothesis test for non-normality, using the L-skewness and L-kurtosis coefficients on the sample data..
Adapted from Harri & Coble (2011), and includes Hosking\u2019s correction.
DefinitionExamples:
Compare the testing power with scipy.stats.normaltest
given 10.000 samples from a contaminated normal distribution.
>>> import numpy as np\n>>> from lmo.diagnostic import normaltest\n>>> from scipy.stats import normaltest as normaltest_scipy\n>>> rng = np.random.default_rng(12345)\n>>> n = 10_000\n>>> x = 0.9 * rng.normal(0, 1, n) + 0.1 * rng.normal(0, 9, n)\n>>> normaltest(x)[1]\n0.04806618...\n>>> normaltest_scipy(x)[1]\n0.08435627...\n
At a 5% significance level, Lmo\u2019s test is significant (i.e. indicating non-normality), whereas scipy\u2019s test isn\u2019t (i.e. inconclusive).
PARAMETER DESCRIPTIONa
Array-like of sample data.
TYPE: npt.ArrayLike
axis
Axis along which to compute the test.
TYPE: int | None
DEFAULT: None
HypothesisTestResult
A named tuple with:
statistic
: The \\(\\tau^2_{3, 4}\\) test statistic.pvalue
: Two-sided chi squared probability for \\(H_0\\).A. Harri & K.H. Coble (2011) - Normality testing: Two new tests using L-moments
"},{"location":"api/#lmo.diagnostic.l_moment_gof","title":"lmo.diagnostic.l_moment_gof(rv_or_cdf, l_moments, n_obs, /, trim=(0, 0), **kwargs)
","text":"Goodness-of-fit (GOF) hypothesis test for the null hypothesis that the observed L-moments come from a distribution with the given scipy.stats
distribution or cumulative distribution function (CDF).
H0
: The theoretical probability distribution, with the given CDF, is a good fit for the observed L-moments.H1
: The distribution is not a good fit for the observed L-moments.The test statistic is the squared Mahalanobis distance between the \\(n\\) observed L-moments, and the theoretical L-moments. It asymptically (in sample size) follows the \\(\\chi^2\\) distribution, with \\(n\\) degrees of freedom.
The sample L-moments are expected to be of consecutive orders \\(r = 1, 2, \\dots, n\\). Generally, the amount of L-moments \\(n\\) should not be less than the amount of parameters of the distribution, including the location and scale parameters. Therefore, it is required to have \\(n \\ge 2\\).
NotesThe theoretical L-moments and their covariance matrix are calculated from the CDF using numerical integration (scipy.integrate.quad
and scipy.integrate.nquad
). Undefined or infinite integrals cannot be detected, in which case the results might be incorrect.
If an IntegrationWarning
is issued, or the function is very slow, then the results are probably incorrect, and larger degrees of trimming should be used.
Examples:
Test if the samples are drawn from a normal distribution.
>>> import lmo\n>>> import numpy as np\n>>> from lmo.diagnostic import l_moment_gof\n>>> from scipy.stats import norm\n>>> rng = np.random.default_rng(12345)\n>>> X = norm(13.12, 1.66)\n>>> n = 1_000\n>>> x = X.rvs(n, random_state=rng)\n>>> x_lm = lmo.l_moment(x, [1, 2, 3, 4])\n>>> l_moment_gof(X, x_lm, n).pvalue\n0.8259...\n
Contaminated samples:
>>> y = 0.9 * x + 0.1 * rng.normal(X.mean(), X.std() * 10, n)\n>>> y_lm = lmo.l_moment(y, [1,2,3,4])\n>>> y_lm.round(3)\narray([1.3193e+01, 1.2860e+00, 6.0000e-03, 1.6800e-01])\n>>> l_moment_gof(X, y_lm, n).pvalue\n1.2668...e-60\n
See Also l_moment_from_cdf
lmo.diagnostic.l_stats_gof(rv_or_cdf, l_stats, n_obs, /, trim=(0, 0), **kwargs)
","text":"Analogous to lmo.diagnostic.l_moment_gof
, but using the L-stats (see lmo.l_stats
).
lmo.diagnostic.l_moment_bounds(r, /, trim=(0, 0), scale=1.0)
","text":"Returns the absolute upper bounds \\(L^{(s,t)}_r\\) on L-moments \\(\\lambda^{(s,t)}_r\\), proportional to the scale \\(\\sigma_X\\) (standard deviation) of the probability distribution of random variable \\(X\\). So \\(\\left| \\lambda^{(s,t)}_r(X) \\right| \\le \\sigma_X \\, L^{(s,t)}_r\\), given that standard deviation \\(\\sigma_X\\) of \\(X\\) exists and is finite.
WarningThese bounds do not apply to distributions with undefined variance, e.g. the Cauchy distribution, even if trimmed L-moments are used. Distributions with infinite variance (e.g. Student\u2019s t with \\(\\nu=2\\)) are a grey area:
For the L-scale (\\(r=2\\)), the corresponding bound will not be a valid one. However, it can still be used to find the L-ratio bounds, because the \\(\\sigma_X\\) terms will cancel out. Doing this is not for the faint of heart, as it requires dividing infinity by infinity. So be sure to wear safety glasses.
The bounds are derived by applying the Cauchy-Schwarz inequality to the covariance-based definition of generalized trimmed L-moment, for \\(r > 1\\):
\\[ \\lambda^{(s,t)}_r(X) = \\frac{r+s+t}{r} \\frac{B(r,\\, r+s+t)}{B(r+s,\\, r+t)} \\mathrm{Cov}\\left[ X,\\; F(X)^s \\big(1 - F(X)\\big)^t P^{(\\alpha, \\beta)}_r(X) \\right] \\;, \\]where \\(B\\) is the Beta function, \\(P^{(\\alpha, \\beta)}_r\\) the Jacobi polynomial, and \\(F\\) the cumulative distribution function of random variable \\(X\\).
After a lot of work, one can (and one did) derive the closed-form inequality:
\\[ \\left| \\lambda^{(s,t)}_r(X) \\right| \\le \\frac{\\sigma_X}{\\sqrt{2 \\pi}} \\frac{\\Gamma(r+s+t+1)}{r} \\sqrt{\\frac{ B(r-\\frac{1}{2}, s+\\frac{1}{2}, t+\\frac{1}{2}) }{ \\Gamma(s+t+1) \\Gamma(r+s) \\Gamma(r+t) }} \\]for \\(r \\in \\mathbb{N}_{\\ge 2}\\) and \\(s, t \\in \\mathbb{R}_{\\ge 0}\\), where \\(\\Gamma\\) is the Gamma function, and \\(B\\) the multivariate Beta function
For the untrimmed L-moments, this simplifies to
\\[ \\left| \\lambda_r(X) \\right| \\le \\frac{\\sigma_X}{\\sqrt{2 r - 1}} \\,. \\] NotesFor \\(r=1\\) there are no bounds, i.e. float('inf')
is returned.
There are no references; this novel finding is not (yet..?) published by the author, @jorenham.
PARAMETER DESCRIPTIONr
The L-moment order(s), non-negative integer or array-like of integers.
TYPE: IntVector | AnyInt
trim
Left- and right-trim orders \\((s, t)\\), as a tuple of non-negative ints or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
scale
The standard deviation \\(\\sigma_X\\) of the random variable \\(X\\). Defaults to 1.
TYPE: float
DEFAULT: 1.0
out
float array or scalar like r
.
TYPE: float | npt.NDArray[np.float64]
l_ratio_bounds
lmo.l_moment
lmo.diagnostic.l_ratio_bounds(r, /, trim=(0, 0), *, has_variance=True)
","text":"Returns the absolute upper bounds \\(T^{(s,t)}_r\\) on L-moment ratio\u2019s \\(\\tau^{(s,t)}_r = \\lambda^{(s,t)}_r / \\lambda^{(s,t)}_r\\), for \\(r \\ge 2\\). So \\(\\left| \\tau^{(s,t)}_r(X) \\right| \\le T^{(s,t)}_r\\), given that \\(\\mathrm{Var}[X] = \\sigma^2_X\\) exists.
If the variance of the distribution is not defined, e.g. in case of the Cauchy distribution, this method will not work. In this case, the looser bounds from Hosking (2007) can be used instead, by passing has_variance=False
.
Examples:
Calculate the bounds for different degrees of trimming:
>>> l_ratio_bounds([1, 2, 3, 4])\narray([ inf, 1. , 0.77459667, 0.65465367])\n>>> # the bounds for r=1,2 are the same for all trimmings; skip them\n>>> l_ratio_bounds([3, 4], trim=(1, 1))\narray([0.61475926, 0.4546206 ])\n>>> l_ratio_bounds([3, 4], trim=(.5, 1.5))\narray([0.65060005, 0.49736473])\n
In case of undefined variance, the bounds become a lot looser:
>>> l_ratio_bounds([3, 4], has_variance=False)\narray([1., 1.])\n>>> l_ratio_bounds([3, 4], trim=(1, 1), has_variance=False)\narray([1.11111111, 1.25 ])\n>>> l_ratio_bounds([3, 4], trim=(.5, 1.5), has_variance=False)\narray([1.33333333, 1.71428571])\n
PARAMETER DESCRIPTION r
Order of the L-moment ratio(s), as positive integer scalar or array-like.
TYPE: IntVector | AnyInt
trim
Tuple of left- and right- trim-lengths, matching those of the relevant L-moment ratio\u2019s.
TYPE: AnyTrim
DEFAULT: (0, 0)
has_variance
Set to False if the distribution has undefined variance, in which case the (looser) bounds from J.R.M. Hosking (2007) will be used.
TYPE: bool
DEFAULT: True
float | npt.NDArray[np.float64]
Array or scalar with shape like \\(r\\).
See Alsol_ratio
l_ratio_se
lmo.diagnostic.rejection_point(influence_fn, /, rho_min=0, rho_max=np.inf)
","text":"Evaluate the approximate rejection point of an influence function \\(\\psi_{T|F}(x)\\) given a statistical functional \\(T\\) (e.g. an L-moment) and cumulative distribution function \\(F(x)\\).
\\[ \\rho^*_{T|F} = \\inf_{r>0} \\left\\{ r: | \\psi_{T|F}(x) | \\le \\epsilon, \\, |x| > r \\right\\} \\; \\]with a \\(\\epsilon\\) a small positive number, corresponding to the tol
param of e.g. l_moment_influence , which defaults to 1e-8
.
Examples:
The untrimmed L-location isn\u2019t robust, e.g. with the standard normal distribution:
>>> import numpy as np\n>>> from scipy.stats import distributions as dists\n>>> from lmo.diagnostic import rejection_point\n>>> if_l_loc_norm = dists.norm.l_moment_influence(1, trim=0)\n>>> if_l_loc_norm(np.inf)\ninf\n>>> rejection_point(if_l_loc_norm)\nnan\n
For the TL-location of the Gaussian distribution, and even for the Student\u2019s t distribution with 4 degrees of freedom (3 also works, but is very slow), they exist.
>>> if_tl_loc_norm = dists.norm.l_moment_influence(1, trim=1)\n>>> if_tl_loc_t4 = dists.t(4).l_moment_influence(1, trim=1)\n>>> if_tl_loc_norm(np.inf), if_tl_loc_t4(np.inf)\n(0.0, 0.0)\n>>> rejection_point(if_tl_loc_norm), rejection_point(if_tl_loc_t4)\n(6.0, 206.0)\n
Notes Large rejection points (e.g. >1000) are unlikely to be found.
For instance, that of the TL-location of the Student\u2019s t distribution with 2 degrees of freedom lies between somewhere 1e4
and 1e5
, but will not be found. In this case, using trim=2
will return 166.0
.
influence_fn
Univariate influence function.
TYPE: Callable[[float], float]
rho_min
The minimum \\(\\rho^*_{T|F}\\) of the search space. Must be finite and non-negative. Defaults to \\(0\\).
TYPE: float
DEFAULT: 0
rho_max
The maximum \\(\\rho^*_{T|F}\\) of the search space. Must be larger than rho_min
. Defaults to \\(\\infty\\).
TYPE: float
DEFAULT: np.inf
float
A finite or infinite scalar.
See Alsolmo.contrib.scipy_stats.l_rv_generic.l_moment_influence
error_sensitivity
lmo.diagnostic.error_sensitivity(influence_fn, /, domain=(float('-inf'), float('inf')))
","text":"Evaluate the gross-error sensitivity of an influence function \\(\\psi_{T|F}(x)\\) given a statistical functional \\(T\\) (e.g. an L-moment) and cumulative distribution function \\(F(x)\\).
\\[ \\gamma^*_{T|F} = \\max_{x} \\left| \\psi_{T|F}(x) \\right| \\]Examples:
Evaluate the gross-error sensitivity of the standard exponential distribution\u2019s LL-skewness (\\(\\tau^{(0, 1)}_3\\)) and LL-kurtosis (\\(\\tau^{(0, 1)}_4\\)) coefficients:
>>> from lmo.diagnostic import error_sensitivity\n>>> from scipy.stats import expon\n>>> ll_skew_if = expon.l_ratio_influence(3, 2, trim=(0, 1))\n>>> ll_kurt_if = expon.l_ratio_influence(4, 2, trim=(0, 1))\n>>> error_sensitivity(ll_skew_if, domain=(0, float('inf')))\n1.814657...\n>>> error_sensitivity(ll_kurt_if, domain=(0, float('inf')))\n1.377743...\n
PARAMETER DESCRIPTION influence_fn
Univariate influence function.
TYPE: Callable[[float], float]
domain
Domain of the CDF. Defaults to \\((-\\infty, \\infty)\\).
TYPE: tuple[float, float]
DEFAULT: (float('-inf'), float('inf'))
float
Gross-error sensitivity \\(\\gamma^*_{T|F}\\) .
See Alsolmo.contrib.scipy_stats.l_rv_generic.l_moment_influence
rejection_point
lmo.diagnostic.shift_sensitivity(influence_fn, /, domain=(float('-inf'), float('inf')))
","text":"Evaluate the local-shift sensitivity of an influence function \\(\\psi_{T|F}(x)\\) given a statistical functional \\(T\\) (e.g. an L-moment) and cumulative distribution function \\(F(x)\\).
\\[ \\lambda^*_{T|F} = \\max_{x \\neq y} \\left| \\frac{ \\psi_{T|F}(y) - \\psi_{T|F}(x) }{ y - x } \\right| \\]Represents the effect of shifting an observation slightly from \\(x\\), to a neighbouring point \\(y\\). For instance, adding an observation at \\(y\\) and removing one at \\(x\\).
Examples:
Evaluate the local-shift sensitivity of the standard exponential distribution\u2019s LL-skewness (\\(\\tau^{(0, 1)}_3\\)) and LL-kurtosis (\\(\\tau^{(0, 1)}_4\\)) coefficients:
>>> from lmo.diagnostic import shift_sensitivity\n>>> from scipy.stats import expon\n>>> ll_skew_if = expon.l_ratio_influence(3, 2, trim=(0, 1))\n>>> ll_kurt_if = expon.l_ratio_influence(4, 2, trim=(0, 1))\n>>> domain = 0, float('inf')\n>>> shift_sensitivity(ll_skew_if, domain)\n0.837735...\n>>> shift_sensitivity(ll_kurt_if, domain)\n1.442062...\n
Let\u2019s compare these with the untrimmed ones:
>>> shift_sensitivity(expon.l_ratio_influence(3, 2), domain)\n1.920317...\n>>> shift_sensitivity(expon.l_ratio_influence(4, 2), domain)\n1.047565...\n
PARAMETER DESCRIPTION influence_fn
Univariate influence function.
TYPE: Callable[[float], float]
domain
Domain of the CDF. Defaults to \\((-\\infty, \\infty)\\).
TYPE: tuple[float, float]
DEFAULT: (float('-inf'), float('inf'))
float
Local-shift sensitivity \\(\\lambda^*_{T|F}\\) .
See Alsolmo.contrib.scipy_stats.l_rv_generic.l_moment_influence
error_sensitivity
Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_weights","title":"lmo.l_weights(r, n, /, trim=(0, 0), dtype=np.float64, *, cache=False)
","text":"Projection matrix of the first \\(r\\) (T)L-moments for \\(n\\) samples.
For integer trim is the matrix is a linear combination of the Power Weighted Moment (PWM) weights (the sample estimator of \\(\\beta_{r_1}\\)), and the shifted Legendre polynomials.
If the trimmings are nonzero and integers, a linearized (and corrected) adaptation of the recurrence relations from Hosking (2007) are applied, as well.
\\[ (2k + s + t - 1) \\lambda^{(s, t)}_k = (k + s + t) \\lambda^{(s - 1, t)}_k + \\frac{1}{k} (k + 1) (k + t) \\lambda^{(s - 1, t)}_{k+1} \\]for \\(s > 0\\), and
\\[ (2k + s + t - 1) \\lambda^{(s, t)}_k = (k + s + t) \\lambda^{(s, t - 1)}_k - \\frac{1}{k} (k + 1) (k + s) \\lambda^{(s, t - 1)}_{k+1} \\]for \\(t > 0\\).
If the trim values are floats instead, the weights are calculated directly from the (generalized) order statistics. At the time of writing (07-2023), these \u201cgeneralized trimmed L-moments\u201d have not been discussed in the literature or the R-packages. It\u2019s probably a good idea to publish this\u2026
TLDRThis matrix (linearly) transforms \\(x_{i:n}\\) (i.e. the sorted observation vector(s) of size \\(n\\)), into (an unbiased estimate of) the generalized trimmed L-moments, with orders \\(\\le r\\).
RETURNS DESCRIPTIONP_r
2-D array of shape (r, n)
.
TYPE: npt.NDArray[T]
Examples:
>>> import lmo\n>>> lmo.l_weights(3, 4)\narray([[ 0.25 , 0.25 , 0.25 , 0.25 ],\n [-0.25 , -0.08333333, 0.08333333, 0.25 ],\n [ 0.25 , -0.25 , -0.25 , 0.25 ]])\n>>> _ @ [-1, 0, 1 / 2, 3 / 2]\narray([0.25 , 0.66666667, 0. ])\n
References inference
","text":"Parametric inference.
"},{"location":"api/#lmo.inference.GMMResult","title":"lmo.inference.GMMResult
","text":" Bases: NamedTuple
Represents the Generalized Method of L-Moments (L-GMM) results. See lmo.inference.fit
for details.
args
The estimated distribution arguments, as (*shapes, loc, scale)
.
TYPE: tuple[float | int, ...]
success
Whether or not the optimizer exited successfully.
TYPE: bool
eps
Final relative difference in the (natural) L-moment conditions.
TYPE: npt.NDArray[np.float64]
statistic
The minimized objective value, corresponding to the weights
.
TYPE: float
n_samp
Amount of samples used to calculate the sample L-moment (after trimming).
TYPE: int
n_step
Number of GMM steps (the amount of times the weight matrix has been estimated).
TYPE: int
n_iter
Number of evaluations of the objective function (the theoretical L-moments).
TYPE: int
weights
The final weight (precision, inverse covariance) matrix.
TYPE: npt.NDArray[np.float64]
n_arg: int
property
","text":"The number of model parameters.
"},{"location":"api/#lmo.inference.GMMResult.n_con","title":"n_con: int
property
","text":"The amount of L-moment conditions of the model.
"},{"location":"api/#lmo.inference.GMMResult.n_extra","title":"n_extra: int
property
","text":"The number of over-identifying L-moment conditions. For L-MM this is zero, otherwise, for L-GMM, it is strictly positive.
"},{"location":"api/#lmo.inference.GMMResult.j_test","title":"j_test: HypothesisTestResult
property
","text":"Sargan-Hansen J-test for over-identifying restrictions; a hypothesis test for the invalidity of the model.
The test is defined through two hypotheses:
AIC: float
property
","text":"Akaike Information Criterion, based on the p-value of the J-test. Requires over-identified L-moment conditions, i.e. n_extra > 0
.
The AIC is useful for model selection, e.g. for finding the most appropriate probability distribution from the data (smaller is better).
ReferencesAICc: float
property
","text":"A modification of the AIC that includes a bias-correction small sample sizes.
Referenceslmo.inference.fit(ppf, args0, n_obs, l_moments, r=None, trim=(0, 0), *, k=None, k_max=50, l_tol=0.0001, l_moment_fn=None, n_mc_samples=9999, random_state=None, **kwds)
","text":"Fit the distribution parameters using the (Generalized) Method of L-Moments (L-(G)MM).
The goal is to find the \u201ctrue\u201d parameters \\(\\theta_0\\) of the distribution. In practise, this is done using a reasonably close estimate, \\(\\theta\\).
In the (non-Generalized) Method of L-moments (L-MM), this is done by solving the system of equations \\(l^{(s, t)}_r = \\lambda^{(s, t)}_r\\), for \\(r = 1, \\dots, k\\), with \\(n = |\\theta|\\) the number of parameters. Because the amount of parameters matches the amount of L-moment conditions, the solution is point-defined, and can be found using simple least squares.
L-GMM extends L-MM by allowing more L-moment conditions than there are free parameters, \\(m > n\\). This requires solving an over-identified system of \\(m\\) equations:
\\[ \\hat{\\theta} = \\mathop{\\arg \\min} \\limits_{\\theta \\in \\Theta} (\\vec{\\lambda}^{(s, t)}_r - \\vec{l}^{(s, t)})^T W_m (\\vec{\\lambda}^{(s, t)}_r - \\vec{l}^{(s, t)}) \\, , \\]where \\(W_m\\) is a \\(m \\times m\\) weight matrix.
The weight matrix is initially chosen as the matrix inverse of the non-parametric L-moment covariance matrix, see lmo.l_moment_cov
. These weights are then plugged into the the equation above, and fed into scipy.optimize.minimize
, to obtain the initial parameter estimates.
In the next step(s), Monte-Carlo sampling is used to draw samples from the distribution (using the current parameter estimates), with sample sizes matching that of the data. The L-moments of these samples are consequently used to to calculate the new weight matrix.
Todointegrality
kwarg with boolean mask for integral params._loss_cue()
, then run with k=1
).ppf
The (vectorized) quantile function of the probability distribution, with signature (*args: float, q: T) -> T
.
TYPE: DistributionFunction[...]
args0
Initial estimate of the distribution\u2019s parameter values.
TYPE: npt.ArrayLike
n_obs
Amount of observations.
TYPE: int
l_moments
Estimated sample L-moments. Must be a 1-d array-like s.t. len(l_moments) >= len(args0)
.
TYPE: npt.ArrayLike
r
The orders of l_moments
. Defaults to [1, ..., len(l_moments)]
.
TYPE: IntVector | None
DEFAULT: None
trim
The L-moment trim-length(s) to use. Currently, only integral trimming is supported.
TYPE: AnyTrim
DEFAULT: (0, 0)
k
If set to a positive integer, exactly \\(k\\) steps will be run. Will be ignored if n_extra=0
.
TYPE: int | None
k_max
Maximum amount of steps to run while not reaching convergence. Will be ignored if \\(k\\) is specified or if n_extra=0
.
TYPE: int
l_tol
Error tolerance in the parametric L-moments (unit-standardized). Will be ignored if \\(k\\) is specified or if n_extra=0
.
TYPE: float
l_moment_fn
Function for parametric L-moment calculation, with signature: (r: int64[], *args, trim: float[2] | int[2]) -> float64[]
.
TYPE: Callable[..., npt.NDArray[np.float64]] | None
n_mc_samples
The number of Monte-Carlo (MC) samples drawn from the distribution to to form the weight matrix in step \\(k > 1\\). Will be ignored if n_extra=0
.
TYPE: int
random_state
A seed value or numpy.random.Generator
instance, used for weight matrix estimation in step \\(k > 1\\). Will be ignored if n_extra=0
.
TYPE: int | np.random.Generator | np.random.RandomState | None
**kwds
Additional keyword arguments to be passed to scipy.optimize.minimize
.
TYPE: Any
ValueError
Invalid arguments.
RETURNS DESCRIPTIONresult
An instance of [GMMResult
][lmo.inference.GMMResult
].
TYPE: GMMResult
theoretical
","text":"Theoretical (population) L-moments of known univariate probability distributions.
"},{"location":"api/#lmo.theoretical.l_moment_from_cdf","title":"lmo.theoretical.l_moment_from_cdf(cdf, r, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Evaluate the population L-moment of a continuous probability distribution, using its Cumulative Distribution Function (CDF) \\(F_X(x) = P(X \\le x)\\).
\\[ \\lambda^{(s, t)}_r = \\begin{cases} 1 & r = 0 \\\\ \\int_{-\\infty}^{\\infty} \\left(H(x) - I_{F(x)}(s+1, \\,t+1)\\right) \\,\\mathrm{d} x & r = 1 \\\\ \\frac{c^{(s,t)}_r}{r} \\int_{-\\infty}^{\\infty} F(x)^{s+1} \\left(1 - F(x)\\right)^{t+1} \\,\\tilde{P}^{(t+1, s+1)}_{r-2}\\big(F(x)\\big) \\,\\mathrm{d} x & r > 1 \\;, \\end{cases} \\]where
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\;, \\]\\(\\tilde{P}^{(a,b)}_n(x)\\) the shifted (\\(x \\mapsto 2x-1\\)) Jacobi polynomial, \\(H(x)\\) the Heaviside step function, and \\(I_x(\\alpha, \\beta)\\) the regularized incomplete gamma function.
NotesNumerical integration is performed with scipy.integrate.quad
, which cannot verify whether the integral exists and is finite. If it returns an error message, an IntegrationWarning
is issues, and nan
is returned (even if quad
returned a finite result).
Examples:
Evaluate the first 4 L- and TL-moments of the standard normal distribution:
>>> from scipy.special import ndtr # standard normal CDF\n>>> l_moment_from_cdf(ndtr, [1, 2, 3, 4])\narray([0. , 0.56418958, 0. , 0.06917061])\n>>> l_moment_from_cdf(ndtr, [1, 2, 3, 4], trim=1)\narray([0. , 0.29701138, 0. , 0.01855727])\n
Evaluate the first 4 TL-moments of the standard Cauchy distribution:
>>> def cdf_cauchy(x: float) -> float:\n... return np.arctan(x) / np.pi + 1 / 2\n>>> l_moment_from_cdf(cdf_cauchy, [1, 2, 3, 4], trim=1)\narray([0. , 0.69782723, 0. , 0.23922105])\n
PARAMETER DESCRIPTION cdf
Cumulative Distribution Function (CDF), \\(F_X(x) = P(X \\le x)\\). Must be a continuous monotone increasing function with signature (float) -> float
, whose return value lies in \\([0, 1]\\).
TYPE: UnivariateCDF
r
L-moment order(s), non-negative integer or array-like of integers.
TYPE: AnyInt | IntVector
trim
Left- and right- trim, either as a \\((s, t)\\) tuple with \\(s, t > -1/2\\), or \\(t\\) as alias for \\((t, t)\\).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
. Generally it\u2019s not needed to provide this, as it will be guessed automatically.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Split the integral into integrals with limits \\([a, F^{-1}(\\alpha)]\\), \\([F(\\alpha), F^{-1}(1 - \\alpha)]\\) and \\([F^{-1}(1 - \\alpha), b]\\) to improve numerical stability. So \\(\\alpha\\) can be consideresd the size of the tail. Numerical experiments have found 0.05 to give good results for different distributions.
TYPE: float
ppf
The inverse of the cdf, used with alpha
to calculate the integral split points (if provided).
TYPE: UnivariatePPF | None
TypeError
r
is not integer-valued or negative
ValueError
r
is negative
lmbda
The population L-moment(s), a scalar or float array like r
. If nan
, consult the related IntegrationWarning
message.
TYPE: np.float64 | npt.NDArray[np.float64]
theoretical.l_moment_from_ppf
: population L-moment, using the inverse CDFl_moment
: sample L-momentlmo.theoretical.l_moment_from_ppf(ppf, r, /, trim=(0, 0), *, support=(0, 1), quad_opts=None, alpha=ALPHA)
","text":"Evaluate the population L-moment of a univariate probability distribution, using its Percentile Function (PPF) \\(x(F)\\), also commonly known as the quantile function, which is the inverse of the Cumulative Distribution Function (CDF).
\\[ \\lambda^{(s, t)}_r = c^{(s,t)}_r \\int_0^1 F^s (1 - F)^t \\,\\tilde{P}^{(t, s)}_{r-1}(F) \\,x(F) \\,\\mathrm{d} F \\;, \\]where
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\;, \\]and \\(\\tilde{P}^{(a,b)}_n(x)\\) the shifted (\\(x \\mapsto 2x-1\\)) Jacobi polynomial.
NotesNumerical integration is performed with scipy.integrate.quad
, which cannot verify whether the integral exists and is finite. If it returns an error message, an IntegrationWarning
is issues, and nan
is returned (even if quad
returned a finite result).
Examples:
Evaluate the first 4 L- and TL-moments of the standard normal distribution:
>>> from scipy.special import ndtri # standard normal inverse CDF\n>>> l_moment_from_ppf(ndtri, [1, 2, 3, 4])\narray([0. , 0.56418958, 0. , 0.06917061])\n>>> l_moment_from_ppf(ndtri, [1, 2, 3, 4], trim=1)\narray([0. , 0.29701138, 0. , 0.01855727])\n
PARAMETER DESCRIPTION ppf
The quantile function \\(x(F)\\), a monotonically continuous increasing function with signature (float) -> float
, that maps a probability in \\([0, 1]\\), to the domain of the distribution.
TYPE: UnivariatePPF
r
L-moment order(s), non-negative integer or array-like of integers. E.g. 0 gives 1, 1 the L-location, 2 the L-scale, etc.
TYPE: AnyInt | IntVector
trim
Left- and right- trim, either as a \\((s, t)\\) tuple with \\(s, t > -1/2\\), or \\(t\\) as alias for \\((t, t)\\).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
Integration limits. Defaults to (0, 1), as it should. There is no need to change this to anything else, and only exists to make the function signature consistent with the *_from_cdf
analogue.
TYPE: Pair[float]
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Split the integral into integrals with limits \\([0, \\alpha]\\), \\([\\alpha, 1-\\alpha]\\) and \\([1-\\alpha, 0]\\) to improve numerical stability. So \\(\\alpha\\) can be consideresd the size of the tail. Numerical experiments have found 0.1 to give good results for different distributions.
TYPE: float
TypeError
Invalid r
or trim
types.
ValueError
Invalid r
or trim
values.
lmbda
The population L-moment(s), a scalar or float array like r
. If nan
, consult the related IntegrationWarning
message.
TYPE: np.float64 | npt.NDArray[np.float64]
theoretical.l_moment_from_cdf
: population L-moment, using the CDF (i.e. the inverse PPF)l_moment
: sample L-momentlmo.theoretical.l_ratio_from_cdf(cdf, r, s, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Population L-ratio\u2019s from a CDF.
See Alsol_ratio_from_ppf
lmo.l_ratio
lmo.theoretical.l_ratio_from_ppf(ppf, r, s, /, trim=(0, 0), *, support=(0, 1), quad_opts=None, alpha=ALPHA)
","text":"Population L-ratio\u2019s from a PPF.
See Alsol_ratio_from_cdf
lmo.l_ratio
lmo.theoretical.l_stats_from_cdf(cdf, num=4, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Calculates the theoretical- / population- L-moments (for \\(r \\le 2\\)) and L-ratio\u2019s (for \\(r > 2\\)) of a distribution, from its CDF.
By default, the first num = 4
population L-stats are calculated:
This function is equivalent to l_ratio_from_cdf(cdf, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
.
This should not be confused with the term L-statistic, which is sometimes used to describe any linear combination of order statistics.
See Alsol_stats_from_ppf
- Population L-stats from the quantile function.l_ratio_from_cdf
- Generalized population L-ratio\u2019s from the CDF.lmo.l_stats
- Unbiased sample estimation of L-stats.lmo.theoretical.l_stats_from_ppf(ppf, num=4, /, trim=(0, 0), *, support=(0, 1), quad_opts=None, alpha=ALPHA)
","text":"Calculates the theoretical- / population- L-moments (for \\(r \\le 2\\)) and L-ratio\u2019s (for \\(r > 2\\)) of a distribution, from its quantile function.
By default, the first num = 4
population L-stats are calculated:
This function is equivalent to l_ratio_from_cdf(cdf, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
.
This should not be confused with the term L-statistic, which is sometimes used to describe any linear combination of order statistics.
See Alsol_stats_from_cdf
- Population L-stats from the CDF.l_ratio_from_ppf
- Generalized population L-ratio\u2019s from the quantile function.lmo.l_stats
- Unbiased sample estimation of L-stats.lmo.theoretical.l_moment_cov_from_cdf(cdf, r_max, /, trim=(0, 0), *, support=None, quad_opts=None)
","text":"L-moments that are estimated from \\(n\\) samples of a distribution with CDF \\(F\\), converge to the multivariate normal distribution as the sample size \\(n \\rightarrow \\infty\\).
\\[ \\sqrt{n} \\left( \\vec{l}^{(s, t)} - \\vec{\\lambda}^{(s, t)} \\right) \\sim \\mathcal{N}( \\vec{0}, \\mathbf{\\Lambda}^{(s, t)} ) \\]Here, \\(\\vec{l}^{(s, t)} = \\left[l^{(s, t)}_r, \\dots, l^{(s, t)}_{r_{max}} \\right]^T\\) is a vector of estimated sample L-moments, and \\(\\vec{\\lambda}^{(s, t)}\\) its theoretical (\u201ctrue\u201d) counterpart.
This function calculates the covariance matrix
\\[ \\begin{align} \\bf{\\Lambda}^{(s,t)}_{k, r} &= \\mathrm{Cov}[l^{(s, t)}_k, l^{(s, t)}_r] \\\\ &= c_k c_r \\iint\\limits_{x < y} \\Big[ p_k\\big(F(x)\\big) \\, p_r\\big(F(y)\\big) + p_r\\big(F(x)\\big) \\, p_k\\big(F(y)\\big) \\Big] w^{(s+1,\\, t)}\\big(F(x)\\big) \\, w^{(s,\\, t+1)}\\big(F(y)\\big) \\, \\mathrm{d}x \\, \\mathrm{d}y \\;, \\end{align} \\]where
\\[ c_n = \\frac{\\Gamma(n) \\Gamma(n+s+t+1)}{n \\Gamma(n+s) \\Gamma(n+t)}\\;, \\]the shifted Jacobi polynomial \\(p_n(u) = P^{(t, s)}_{n-1}(2u - 1)\\), \\(P^{(t, s)}_m\\), and \\(w^{(s,t)}(u) = u^s (1-u)^t\\) its weight function.
NotesThis function uses scipy.integrate.nquad
for numerical integration. Unexpected results may be returned if the integral does not exist, or does not converge. The results are rounded to match the order of magnitude of the absolute error of scipy.integrate.nquad
.
This function is not vectorized or parallelized.
For small sample sizes (\\(n < 100\\)), the covariances of the higher-order L-moments (\\(r > 2\\)) can be biased. But this bias quickly disappears at roughly \\(n > 200\\) (depending on the trim- and L-moment orders).
PARAMETER DESCRIPTIONcdf
Cumulative Distribution Function (CDF), \\(F_X(x) = P(X \\le x)\\). Must be a continuous monotone increasing function with signature (float) -> float
, whose return value lies in \\([0, 1]\\).
TYPE: UnivariateCDF
r_max
The amount of L-moment orders to consider. If for example r_max = 4
, the covariance matrix will be of shape (4, 4)
, and the columns and rows correspond to the L-moments of order \\(r = 1, \\dots, r_{max}\\).
TYPE: int
trim
Left- and right- trim. Must be a tuple of two non-negative ints or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
. Generally it\u2019s not needed to provide this, as it will be guessed automatically.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
cov
Covariance matrix, with shape (r_max, r_max)
.
TYPE: npt.NDArray[np.float64]
RuntimeError
If the covariance matrix is invalid.
See Alsol_moment_from_cdf
- Population L-moments from the cumulative distribution functionl_moment_from_ppf
- Population L-moments from the quantile functionlmo.l_moment
- Unbiased L-moment estimation from sampleslmo.l_moment_cov
- Distribution-free exact L-moment exact covariance estimate.lmo.theoretical.l_stats_cov_from_cdf(cdf, num=4, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Similar to l_moment_from_cdf
, but for the lmo.l_stats
.
As the sample size \\(n \\rightarrow \\infty\\), the L-moment ratio\u2019s are also distributed (multivariate) normally. The L-stats are defined to be L-moments for \\(r\\le 2\\), and L-ratio coefficients otherwise.
The corresponding covariance matrix has been found to be
\\[ \\bf{T}^{(s, t)}_{k, r} = \\begin{cases} \\bf{\\Lambda}^{(s, t)}_{k, r} & k \\le 2 \\wedge r \\le 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} }{ \\lambda^{(s,t)}_{2} } & k \\le 2 \\wedge r > 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_k \\bf{\\Lambda}^{(s, t)}_{2, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} + \\tau_k \\tau_r \\bf{\\Lambda}^{(s, t)}_{2, 2} }{ \\Big( \\lambda^{(s,t)}_{2} \\Big)^2 } & k > 2 \\wedge r > 2 \\end{cases} \\]where \\(\\bf{\\Lambda}^{(s, t)}\\) is the covariance matrix of the L-moments from l_moment_cov_from_cdf
, and \\(\\tau^{(s,t)}_r = \\lambda^{(s,t)}_r / \\lambda^{(s,t)}_2\\) the population L-ratio.
cdf
Cumulative Distribution Function (CDF), \\(F_X(x) = P(X \\le x)\\). Must be a continuous monotone increasing function with signature (float) -> float
, whose return value lies in \\([0, 1]\\).
TYPE: UnivariateCDF
num
The amount of L-statistics to return. Defaults to 4.
TYPE: int
DEFAULT: 4
trim
Left- and right- trim. Must be a tuple of two non-negative ints or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
. Generally it\u2019s not needed to provide this, as it will be guessed automatically.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Two-sided quantile to split the integral at.
TYPE: float
ppf
Quantile function, for calculating the split integral limits.
TYPE: UnivariatePPF | None
lmo.theoretical.l_moment_influence_from_cdf(cdf, r, /, trim=(0, 0), *, support=None, l_moment=None, quad_opts=None, alpha=ALPHA, tol=1e-08)
","text":"Influence Function (IF) of a theoretical L-moment.
\\[ \\psi_{\\lambda^{(s, t)}_r | F}(x) = c^{(s,t)}_r \\, F(x)^s \\, \\big( 1-{F}(x) \\big)^t \\, \\tilde{P}^{(s,t)}_{r-1} \\big( F(x) \\big) \\, x - \\lambda^{(s,t)}_r \\;, \\]with \\(F\\) the CDF, \\(\\tilde{P}^{(s,t)}_{r-1}\\) the shifted Jacobi polynomial, and
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r, \\, r+s+t)}{B(r+s, \\, r+t)} \\;, \\]where \\(B\\) is the (complete) Beta function.
The proof is trivial, because population L-moments are linear functionals.
NotesThe order parameter r
is not vectorized.
cdf
Vectorized cumulative distribution function (CDF).
TYPE: Callable[[npt.NDArray[np.float64]], npt.NDArray[np.float64]]
r
The L-moment order. Must be a non-negative integer.
TYPE: AnyInt
trim
Left- and right- trim lengths. Defaults to (0, 0).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
l_moment
The relevant L-moment to use. If not provided, it is calculated from the CDF.
TYPE: float | np.float64 | None
alpha
Two-sided quantile to split the integral at.
TYPE: float
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The influence function, with vectorized signature () -> ()
.
TYPE: Callable[[V], V]
l_moment_from_cdf
lmo.l_moment
lmo.theoretical.l_ratio_influence_from_cdf(cdf, r, k=2, /, trim=(0, 0), *, support=None, l_moments=None, quad_opts=None, alpha=ALPHA, tol=1e-08)
","text":"Construct the influence function of a theoretical L-moment ratio.
\\[ \\psi_{\\tau^{(s, t)}_{r,k}|F}(x) = \\frac{ \\psi_{\\lambda^{(s, t)}_r|F}(x) - \\tau^{(s, t)}_{r,k} \\, \\psi_{\\lambda^{(s, t)}_k|F}(x) }{ \\lambda^{(s,t)}_k } \\;, \\]where the generalized L-moment ratio is defined as
\\[ \\tau^{(s, t)}_{r,k} = \\frac{ \\lambda^{(s, t)}_r }{ \\lambda^{(s, t)}_k } \\;. \\]Because IF\u2019s are a special case of the general G\u00e2teuax derivative, the L-ratio IF is derived by applying the chain rule to the L-moment IF.
PARAMETER DESCRIPTIONcdf
Vectorized cumulative distribution function (CDF).
TYPE: Callable[[npt.NDArray[np.float64]], npt.NDArray[np.float64]]
r
L-moment ratio order, i.e. the order of the numerator L-moment.
TYPE: AnyInt
k
Denominator L-moment order, defaults to 2.
TYPE: AnyInt
DEFAULT: 2
trim
Left- and right- trim lengths. Defaults to (0, 0).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
.
TYPE: Pair[float] | None
l_moments
The L-moments corresponding to \\(r\\) and \\(k\\). If not provided, they are calculated from the CDF.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Two-sided quantile to split the integral at.
TYPE: float
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The influence function, with vectorized signature () -> ()
.
TYPE: Callable[[V], V]
l_ratio_from_cdf
lmo.l_ratio
lmo.theoretical.l_comoment_from_pdf(pdf, cdfs, r, /, trim=(0, 0), *, supports=None, quad_opts=None)
","text":"Evaluate the theoretical L-comoment matrix of a multivariate probability distribution, using the joint PDF \\(f_{\\vec{X}}(\\vec{x})\\) and \\(n\\) marginal CDFs \\(F_X(x)\\) of random vector \\(\\vec{X}\\).
The L-comoment matrix is defined as
\\[ \\Lambda_{r}^{(s, t)} = \\left[ \\lambda_{r [ij]}^{(s, t)} \\right]_{n \\times n} \\;, \\]with elements
\\[ \\begin{align} \\lambda_{r [ij]}^{(s, t)} &= c^{(s,t)}_r \\int_{\\mathbb{R^n}} x_i \\,F_j(x_j)^s \\,\\bar{F}_j(x_j)^t \\,\\tilde{P}^{(s, t)}_r \\big(F_j(x_j) \\big) \\,f(\\vec{x}) \\, d \\vec{x} \\\\ &= c^{(s,t)}_r \\, \\mathbb{E}_{\\vec{X}} \\left[ X_i \\,F_j(X_j)^s \\,\\bar{F}_j(X_j)^t \\,\\tilde{P}^{(s, t)}_r \\big(F_j(X_j) \\big) \\,f(\\vec{X}) \\right] \\;, \\end{align} \\]with vector \\(\\vec{x} = \\begin{bmatrix} x_1 & \\cdots & x_n \\end{bmatrix}^T\\), \\(f\\) the joint PDF, \\(F_i\\) the marginal CDF of \\(X_i\\) and \\(\\bar{F}_i\\) its complement (the Survival Function), \\(\\tilde{P}^{(s, t)}_n\\) the shifted Jacobi polynomial, and
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\;, \\]a positive constant.
For \\(r \\ge 2\\), it can also be expressed as
\\[ \\lambda_{r [ij]}^{(s, t)} = c^{(s,t)}_r \\mathrm{Cov} \\left[ X_i ,\\; F_j(X_j)^s \\,\\bar{F}_j(X_j)^t \\,\\tilde{P}^{(s, t)}_r \\big(F_j(X_j) \\big) \\,f(\\vec{X}) \\right] \\;, \\]and without trim (\\(s = t = 0\\)), this simplifies to
\\[ \\lambda_{r [ij]} = \\mathrm{Cov} \\left[ X_i ,\\; \\,\\tilde{P}_r \\big(F_j(X_j) \\big) \\,f(\\vec{X}) \\right] \\;, \\]with \\(\\tilde{P}_n\\) the shifted Legendre polynomial. This last form is precisely the definition introduced by Serfling & Xiao (2007).
Note that the L-comoments along the diagonal, are equivalent to the (univariate) L-moments, i.e.
\\[ \\lambda_{r [ii]}^{(s, t)}\\big( \\vec{X} \\big) = \\lambda_{r}^{(s, t)}\\big( X_i \\big) \\;. \\] NotesAt the time of writing, trimmed L-comoments have not been explicitly defined in the literature. Instead, the author (@jorenham) derived it by generizing the (untrimmed) L-comoment definition by Serfling & Xiao (2007), analogous to the generalization of L-moments into TL-moments by Elamir & Seheult (2003).
Examples:
Find the L-coscale and TL-coscale matrices of the multivariate Student\u2019s t distribution with 4 degrees of freedom:
>>> from lmo.theoretical import l_comoment_from_pdf\n>>> from scipy.stats import multivariate_t, t\n>>> df = 4\n>>> loc = np.array([0.5, -0.2])\n>>> cov = np.array([[2.0, 0.3], [0.3, 0.5]])\n>>> X = multivariate_t(loc, cov, df)\n>>> cdfs = [t(df, loc[i], np.sqrt(cov[i, i])).cdf for i in range(2)]\n>>> l_cov = l_comoment_from_pdf(X.pdf, cdfs, 2)\n>>> l_cov.round(4)\narray([[1.0413, 0.3124],\n [0.1562, 0.5207]])\n>>> tl_cov = l_comoment_from_pdf(X.pdf, cdfs, 2, trim=1)\n>>> tl_cov.round(4)\narray([[0.4893, 0.1468],\n [0.0734, 0.2447]])\n
The correlation coefficient can be recovered in several ways:
>>> cov[0, 1] / np.sqrt(cov[0, 0] * cov[1, 1]) # \"true\" correlation\n0.3\n>>> np.round(l_cov[0, 1] / l_cov[0, 0], 4)\n0.3\n>>> np.round(l_cov[1, 0] / l_cov[1, 1], 4)\n0.3\n>>> np.round(tl_cov[0, 1] / tl_cov[0, 0], 4)\n0.3\n>>> np.round(tl_cov[1, 0] / tl_cov[1, 1], 4)\n0.3\n
PARAMETER DESCRIPTION pdf
Joint Probability Distribution Function (PDF), that accepts a float vector of size \\(n\\), and returns a scalar in \\([0, 1]\\).
TYPE: Callable[[npt.NDArray[np.float64]], float]
cdfs
Sequence with \\(n\\) marginal CDF\u2019s.
TYPE: Sequence[Callable[[float], float]]
r
Non-negative integer \\(r\\) with the L-moment order.
TYPE: AnyInt
trim
Left- and right- trim, either as a \\((s, t)\\) tuple with \\(s, t > -1/2\\), or \\(t\\) as alias for \\((t, t)\\).
TYPE: AnyTrim
DEFAULT: (0, 0)
supports
A sequence with \\(n\\) 2-tuples, corresponding to the marginal integration limits. Defaults to \\([(-\\infty, \\infty), \\dots]\\).
TYPE: Sequence[Pair[float]] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
lmbda
The population L-comoment matrix with shape \\(n \\times n\\).
TYPE: npt.NDArray[np.float64]
lmo.theoretical.l_coratio_from_pdf(pdf, cdfs, r, r0=2, /, trim=(0, 0), *, supports=None, quad_opts=None)
","text":"Evaluate the theoretical L-comoment ratio matrix of a multivariate probability distribution, using the joint PDF \\(f_{\\vec{X}}(\\vec{x})\\) and \\(n\\) marginal CDFs \\(F_X(x)\\) of random vector \\(\\vec{X}\\).
\\[ \\tilde \\Lambda_{r,r_0}^{(s, t)} = \\left[ \\left. \\lambda_{r [ij]}^{(s, t)} \\right/ \\lambda_{r_0 [ii]}^{(s, t)} \\right]_{n \\times n} \\] See Alsol_comoment_from_pdf
lmo.l_coratio
linalg
","text":"Linear algebra and linearized orthogonal polynomials.
"},{"location":"api/#lmo.linalg.sandwich","title":"lmo.linalg.sandwich(A, X, /, dtype=np.float64)
","text":"Calculates the \u201csandwich\u201d matrix product (A @ X @ A.T
) along the specified X
axis.
A
2-D array of shape (s, r)
, the \u201cbread\u201d.
TYPE: npt.NDArray[np.number[Any]]
dtype
The data type of the result.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
X
Array of shape (r, r, ...)
.
TYPE: npt.NDArray[T | np.number[Any]]
C
Array of shape (s, s, ...)
.
TYPE: npt.NDArray[T]
lmo.linalg.pascal(k, /, dtype=np.int64, *, inv=False)
","text":"Construct the lower-diagonal Pascal matrix \\(L_{k \\times k\\)}$, or its matrix inverse \\(L^{-1}\\).
\\[ \\begin{align} L_{ij} &= \\binom{i}{j} \\\\ L^{-1}_{ij} &= (-1)^{i - j} L_{ij} \\end{align} \\]Implemented using recursion, unlike the slow naive implementation from the equivalent scipy.linalg.pascal
and scipy.linalg.invpascal
functions using kind='lower'
. By using the binomial recurrence relation, assuming \\(0 < j < i\\), \\(\\binom{i}{j} = \\frac{i}{j} \\binom{i-1}{j-1}\\), the following recursive definition is obtained:
Examples:
>>> import numpy as np\n>>> from lmo.linalg import pascal\n>>> pascal(4, dtype=np.int_)\narray([[1, 0, 0, 0],\n [1, 1, 0, 0],\n [1, 2, 1, 0],\n [1, 3, 3, 1]])\n>>> pascal(4, dtype=np.int_, inv=True)\narray([[ 1, 0, 0, 0],\n [-1, 1, 0, 0],\n [ 1, -2, 1, 0],\n [-1, 3, -3, 1]])\n>>> np.rint(np.linalg.inv(pascal(4))).astype(int)\narray([[ 1, 0, 0, 0],\n [-1, 1, 0, 0],\n [ 1, -2, 1, 0],\n [-1, 3, -3, 1]])\n
Now, let\u2019s compare with scipy:
>>> from scipy.linalg import invpascal\n>>> invpascal(4, kind='lower').astype(int)\narray([[ 1, 0, 0, 0],\n [-1, 1, 0, 0],\n [ 1, -2, 1, 0],\n [-1, 3, -3, 1]])\n
"},{"location":"api/#lmo.linalg.ir_pascal","title":"lmo.linalg.ir_pascal(k, /, dtype=np.float64)
","text":"Inverse regulatized lower-diagonal Pascal matrix, \\(\\bar{L}_{ij} = L^{-1}_ij / i\\).
Used to linearly combine order statistics order statistics into L-moments.
"},{"location":"api/#lmo.linalg.sh_legendre","title":"lmo.linalg.sh_legendre(k, /, dtype=np.int64)
","text":"Shifted Legendre polynomial coefficient matrix \\(\\widetilde{P}\\) of shape (k, k)
.
The \\(j\\)-th coefficient of the shifted Legendre polynomial of degree \\(k\\) is at \\((k, j)\\):
\\[ \\widetilde{p}_{k, j} = (-1)^{k - j} \\binom{k}{j} \\binom{k + j}{j} \\]Useful for transforming probability-weighted moments into L-moments.
DangerFor \\(k \\ge 29\\), all 64-bits dtypes (default is int64) will overflow, which results in either an OverflowError
(if you\u2019re lucky), or will give incorrect results. Similarly, all 32-bits dtypes (e.g. np.int_
on Windows) already overflow when \\(k \\ge 16\\).
This is not explicitly checked \u2013 so be sure to select the right dtype
depending on k
.
One option is to use dtype=np.object_
, which will use Python-native int
. However, this is a lot slower, and is likely to fail. For instance, when multiplied together with some float64
array, a TypeError
is raised.
k
The size of the matrix, and the max degree of the shifted Legendre polynomial.
TYPE: int
dtype
Desired output data type, e.g, numpy.float64
. Must be signed. The default is numpy.int64
.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.int64
P
2-D array of the lower-triangular square matrix of size \\(k^2\\)`.
TYPE: npt.NDArray[T]
Examples:
Calculate \\(\\widetilde{P}_{4 \\times 4}\\):
>>> from lmo.linalg import sh_legendre\n>>> sh_legendre(4, dtype=int)\narray([[ 1, 0, 0, 0],\n [ -1, 2, 0, 0],\n [ 1, -6, 6, 0],\n [ -1, 12, -30, 20]])\n
See Also lmo.linalg.sh_jacobi(k, a, b, /, dtype=None)
","text":"Shifted Jacobi polynomial coefficient matrix \\(\\widetilde{P}^{(a,b)}\\) of shape (k, k)
.
The \\(j\\)-th coefficient of the shifted Jacobi polynomial of degree \\(k\\) is at \\((k, j)\\):
The \u201cshift\u201d refers to the change of variables \\(x \\mapsto 2x - 1\\) in the (unshifted) Jacobi (or hypergeometric) polynomials.
The (shifted) Jacobi polynomials \\(\\widetilde{P}^{(a,b)}\\) generalize the (shifted) Legendre polynomials \\(\\widetilde{P}\\): \\(\\widetilde{P}^{(0, 0)} = \\widetilde{P}\\)
PARAMETER DESCRIPTIONk
The size of the matrix, and the max degree of the polynomial.
TYPE: AnyInt
a
The \\(\\alpha\\) parameter, must be \\(\\ge 0\\).
TYPE: AnyFloat
b
The \\(\\beta\\) parameter, must be \\(\\ge 0\\).
TYPE: AnyFloat
dtype
Desired output data type, e.g, numpy.float64
. Default is numpy.int64
if a
and b
are integers, otherwise np.float64
.
TYPE: np.dtype[T] | type[T] | None
DEFAULT: None
P
2-D array of the lower-triangular square matrix of size \\(k^2\\)`.
TYPE: npt.NDArray[T | np.int64]
Examples:
Calculate \\(\\widetilde{P}^{(1, 1)}_{4 \\times 4}\\):
>>> from lmo.linalg import sh_jacobi\n>>> sh_jacobi(4, 1, 1, dtype=int)\narray([[ 1, 0, 0, 0],\n [ -2, 4, 0, 0],\n [ 3, -15, 15, 0],\n [ -4, 36, -84, 56]])\n
Let\u2019s compare \\(\\widetilde{P}^(1, \\pi)_3\\) with the scipy Jacobi poly1d. This requires manual shifting \\(x \\mapsto f(x)\\), with \\(f(x) = 2x - 1\\):
>>> import numpy as np\n>>> import scipy.special as sc\n>>> f_x = np.poly1d([2, -1]) # f(x) = 2*x + 1\n>>> sc.jacobi(3, 1, np.pi)(f_x)\npoly1d([ 125.80159497, -228.55053774, 128.54584648, -21.79690371])\n>>> sh_jacobi(4, 1, np.pi)[3]\narray([ -21.79690371, 128.54584648, -228.55053774, 125.80159497])\n
Apart from the reversed coefficients of numpy.poly1d
(an awkward design choice, but it\u2019s fixed in the new numpy.polynomial
module.)
scipy.special.jacobi
lmo.linalg.succession_matrix(c)
","text":"A toeplitz-like transformation matrix construction, that prepends \\(i\\) zeroes to \\(i\\)-th row, so that the input shape is mapped from (n, k)
to (n, k + n)
.
So all values \\(i > j \\vee i + j \\ge k\\) are zero in the succession matrix.
PARAMETER DESCRIPTIONc
Dense matrix of shape (n, k)
.
TYPE: npt.NDArray[T]
S
Matrix of shape (n, k + n)
TYPE: npt.NDArray[T]
Examples:
>>> from lmo.linalg import succession_matrix\n>>> c = np.arange(1, 9).reshape(4, 2)\n>>> c\narray([[1, 2],\n [3, 4],\n [5, 6],\n [7, 8]])\n>>> succession_matrix(c)\narray([[1, 2, 0, 0, 0],\n [0, 3, 4, 0, 0],\n [0, 0, 5, 6, 0],\n [0, 0, 0, 7, 8]])\n
"},{"location":"api/#lmo.linalg.trim_matrix","title":"lmo.linalg.trim_matrix(r, /, trim, dtype=np.float64)
","text":"Linearization of the trimmed L-moment recurrence relations, following the (corrected) derivation by Hosking (2007) from the (shifted) Jacobi Polynomials.
This constructs a \\(r \\times r + t_1 + t_2\\) matrix \\(T^{(t_1, t_2)}\\) that \u201ctrims\u201d conventional L-moments. E.g. the first 3 \\((1, 1)\\) trimmed L-moments can be obtained from the first \\(3+1+1=5\\) (untrimmed) L-moments (assuming they exist) with trim_matrix(3, (1, 1)) @ l_moment(x, np.ogrid[:5] + 1)
.
The big \u201cL\u201d in \u201cL-moment\u201d, referring to it being a Linear combination of order statistics, has been prominently put in the name by Hosking (1990) for a good reason. It means that transforming order statistics to a bunch of L-moments, can be done using a single matrix multiplication (see lmo.linalg.sh_legendre
). By exploiting liniarity, it can easily be chained with this trim matrix, to obtain a reusable order-statistics -> trimmed L-moments transformation (matrix).
Note that these linear transformations can be used in exactly the same way to e.g. calculate several population TL-moments of some random varianble, using nothing but its theoretical probablity-weighted moments (PWMs).
PARAMETER DESCRIPTIONr
The max (trimmed) L-moment order.
TYPE: int
trim
Left- and right-trim orders \\((t_1, t_2)\\), integers \\(\\ge 0\\). If set to (0, 0), the identity matrix is returned.
TYPE: tuple[int, int]
dtype
Desired output data type, e.g, numpy.float64
(default).
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
npt.NDArray[T]
Toeplitz-like matrix of shape \\((r, r + t_1 + t_2)\\).
Examples:
>>> from lmo.linalg import trim_matrix\n>>> trim_matrix(3, (0, 1))\narray([[ 1. , -1. , 0. , 0. ],\n [ 0. , 0.75 , -0.75 , 0. ],\n [ 0. , 0. , 0.66666667, -0.66666667]])\n>>> trim_matrix(3, (1, 0))\narray([[1. , 1. , 0. , 0. ],\n [0. , 0.75 , 0.75 , 0. ],\n [0. , 0. , 0.66666667, 0.66666667]])\n
References Order statistics \\(X_{i:n}\\), with \\(i \\in [0, n)\\).
Primarily used as an intermediate step for L-moment estimation.
ReferencesH.A. David & H.N. Nagaraja (2004) \u2013 Order statistics
"},{"location":"api/#lmo.ostats.weights","title":"lmo.ostats.weights(i, n, N, /, *, cached=False)
","text":"Compute the linear weights \\(w_{i:n|j:N}\\) for \\(j = 0, \\dots, N-1\\).
The unbiased sample estimator \\(\\mu_{i:n}\\) is then derived from
\\[ E[X_{i:n}] = \\sum_{j=0}^{N-1} w_{i:n|j:N} X_{j:N} \\, , \\]where
\\[ \\begin{aligned} w_{i:n|j:N} &= \\binom{j}{i} \\binom{N - j - 1}{n - i - 1} / \\binom{N}{n} \\\\ &= \\frac{1}{N - n + 1} \\frac{ B(j + 1, N - j) }{ B(i + 1, n - i) B(j - i + 1, N - j - n + i + 1) } \\end{aligned} \\]Here, \\(B\\) denotes the Beta function, \\(B(a,b) = \\Gamma(a) \\Gamma(b) / \\Gamma(a + b)\\).
NotesThis function uses \u201cPython-style\u201d 0-based indexing for \\(i\\), instead of the conventional 1-based indexing that is generally used in the literature.
PARAMETER DESCRIPTIONi
0-indexed sample (fractional) index, \\(0 \\le i \\lt n\\). Negative indexing is allowed.
TYPE: float
n
Subsample size, optionally fractional, \\(0 \\le n0\\)
TYPE: float
N
Sample size, i.e. the observation count.
TYPE: int
cached
Cache the result for (i, n, n0)
. Defaults to False.
TYPE: bool
DEFAULT: False
npt.NDArray[np.float64]
1d array of size \\(N\\) with (ordered) sample weights.
"},{"location":"api/#lmo.ostats.from_cdf","title":"lmo.ostats.from_cdf(F, i, n)
","text":"Transform \\(F(X)\\) to \\(F_{i:n}(X)\\), of the \\(i\\)th variate within subsamples of size, i.e. \\(0 \\le i \\le n - 1\\).
PARAMETER DESCRIPTIONF
Scalar or array-like with the returned value of some cdf, i.e. \\(F_X(x) = P(X \\le x)\\). Must be between 0 and 1.
TYPE: npt.ArrayLike
i
0-indexed sample (fractional) index, \\(0 \\le i < n\\).
TYPE: float
n
Subsample size, optionally fractional, \\(0 \\le n0\\)
TYPE: float
Power-Weighted Moment (PWM) \\(\\beta_k = M_{1,k,0}\\).
Primarily used as an intermediate step for L-moment estimation.
"},{"location":"api/#lmo.pwm_beta.weights","title":"lmo.pwm_beta.weights(r, n, /, dtype=np.float64)
","text":"Probability Weighted moment (PWM) projection matrix \\(B\\) of the unbiased estimator for \\(\\beta_k = M_{1,k,0}\\) for \\(k = 0, \\dots, r - 1\\).
The PWM\u2019s are estimated by linear projection of the sample of order statistics, i.e. \\(b = B x_{i:n}\\)
PARAMETER DESCRIPTIONr
The amount of orders to evaluate, i.e. \\(k = 0, \\dots, r - 1\\).
TYPE: int
n
Sample count.
TYPE: int
dtype
Desired output floating data type.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
P_b
Upper-triangular projection matrix of shape (r, n)
.
TYPE: npt.NDArray[T]
Examples:
>>> from lmo import pwm_beta\n>>> pwm_beta.weights(4, 5)\narray([[0.2 , 0.2 , 0.2 , 0.2 , 0.2 ],\n [0. , 0.05 , 0.1 , 0.15 , 0.2 ],\n [0. , 0. , 0.03333333, 0.1 , 0.2 ],\n [0. , 0. , 0. , 0.05 , 0.2 ]])\n
"},{"location":"api/#lmo.pwm_beta.cov","title":"lmo.pwm_beta.cov(a, r, /, axis=None, dtype=np.float64, **kwargs)
","text":"Distribution-free variance-covariance matrix of the probability weighted moment (PWM) point estimates \\(\\beta_k = M_{1,k,0}\\), with orders \\(k = 0, \\dots, r - 1\\).
PARAMETER DESCRIPTIONa
Array-like with observations.
TYPE: npt.ArrayLike
r
The amount of orders to evaluate, i.e. \\(k = 0, \\dots, r - 1\\).
TYPE: int
axis
The axis along which to calculate the covariance matrices.
TYPE: int | None
DEFAULT: None
dtype
Desired output floating data type.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
**kwargs
Additional keywords to pass to lmo.stats.ordered
.
TYPE: Any
DEFAULT: {}
S_b
Variance-covariance matrix/tensor of shape (r, ...)
TYPE: npt.NDArray[T]
Any contributions to Lmo are appreciated!
"},{"location":"contributing/#issues","title":"Issues","text":"Questions, feature requests and bug reports are all welcome as issues.
When reporting a bug, make sure to include the versions of lmo
, python
, numpy
and scipy
you are using, and provide a reproducible example of the bug.
Ensure you have poetry installed, then clone your fork, and install with
poetry install --sync\n
It can help to use Lmo\u2019s lowest-supported Python version, so that you don\u2019t accidentally use those bleeding-edge Python features that you shouldn\u2019t, poetry env use python3.x
Now you can go ahead and do your thing. And don\u2019t forget the type annotations, add tests, and to lint it all.
If you\u2019re a 10x developer that doesn\u2019t wait on CI workflows, you can use the following 1337 shellscript (keep in mind that the CI runs this on all supported Python versions):
poetry run codespell check lmo\npoetry run ruff check lmo\npoetry run pyright\npoetry run py.test\n
If your change involves documentation updates, you can conjure up a live preview:
poetry run mkdocs serve\n
But don\u2019t worry about building the docs, or bumping the version; Lmo\u2019s personal assistant will do that on release.
"},{"location":"distributions/","title":"L-moments of common probability distributions","text":"This page lists L-moment statistics (L -location, scale, skewness, and kurtosis) of common univariate probability distributions, most of them continuous.
Each of the listed expressions have been validated, both numerically and symbolically (with either Wolfram Alpha, SymPy, or pen and paper).
Most of the closed-form expressions that are listed here, have been previously reported in the literature. But for the sake of interpretability, several have been algebraically rearranged.
Due to the exploratory use of symbolic computation software, this listing is likely to include some novel solutions. This is also the reason for the lack of references. But this should pose no problems in practise, since Lmo makes it trivial to check if they aren\u2019t incorrect.
Tip
Numerical calculation of these L-statistics using scipy.stats
distributions, refer to rv_continuous.l_stats
.
For direct calculation of the L-stats from a CDF or PPF (quantile function, inverse CDF), see l_stats_from_cdf
or l_stats_from_ppf
, respectively.
An overview of the untrimmed L-location, L-scale, L-skewness and L-kurtosis, of a bunch of popular univariate probability distributions, for which they exist (in closed form).
Name /scipy.stats
Params \\( \\lmoment{1} \\) \\( \\lmoment{2} \\) \\( \\lratio{3} = \\lmoment{3}/\\lmoment{2} \\) \\( \\lratio{4} = \\lmoment{4}/\\lmoment{2} \\) Uniform uniform
\\( a < b \\) \\[ \\frac{a + b}{2} \\] \\[ \\frac{b - a}{6} \\] \\( 0 \\) \\( 0 \\) Normal norm
\\( \\mu \\) \\( \\sigma>0 \\) \\( \\mu \\) \\[ \\frac{\\sigma}{\\sqrt \\pi} \\] \\( \\approx 0.5642 \\ \\sigma \\) \\( 0 \\) \\[ 30 \\ \\frac{\\arctan{\\sqrt 2}}{\\pi} - 9 \\] \\( \\approx 0.1226 \\) Logistic logistic(\u03bc, s)
\\( \\mu \\) \\( s>0 \\) \\( \\mu \\) \\( s \\) \\( 0 \\) \\[ 1 / 6 \\] \\( = 0.16\\overline{6}\\dots \\) Laplace laplace
\\( \\mu \\) \\( b > 0 \\) \\( \\mu \\) \\[ \\frac 3 4 b \\] \\( = 0.75 \\ b\\) \\( 0 \\) \\[ \\frac{17}{72} \\] \\( \\approx 0.2361 \\) Student's t (2 d.f.) t(2)
\\( \\nu = 2 \\) \\( 0 \\) \\[ \\frac{\\pi}{2 \\sqrt{2}} \\] \\( \\approx 1.1107 \\) \\( 0 \\) \\[ \\frac 3 8 \\] \\( = 0.375 \\) Student's t (3 d.f.) t(3)
\\( \\nu = 3 \\) \\( 0 \\) \\[ \\frac{3 \\sqrt 3}{\\vphantom{\\pi^2}2 \\pi} \\] \\( \\approx 0.8270 \\) \\( 0 \\) \\[ 1 - \\frac{\\vphantom{\\sqrt 3}175}{24 \\pi^2} \\] \\( \\approx 0.2612 \\) Student's t (4 d.f.) t(4)
\\( \\nu = 4 \\) \\( 0 \\) \\[ \\frac{15}{64} \\pi \\] \\( \\approx 0.7363 \\) \\( 0 \\) \\[ \\frac{111}{512} \\] \\( \\approx 0.2168 \\) Exponential expon
\\( \\lambda>0 \\) \\[ \\frac 1 \\lambda \\] \\[ \\frac{1}{2 \\lambda} \\] \\[ \\frac 1 3 \\] \\( = 0.3\\overline{3}\\dots \\) \\[ \\frac 1 6 \\] \\( = 0.16\\overline{6}\\dots \\) Rayleigh rayleigh
\\( \\sigma > 0 \\) \\[ \\frac 1 2 \\sqrt{2 \\pi} \\ \\sigma \\] \\( \\approx 1.253 \\ \\sigma \\) \\[ \\frac {\\sqrt 2 - 1}{2} \\sqrt{\\pi} \\ \\sigma \\] \\( \\approx 0.3671 \\ \\sigma \\) \\[ 2 \\frac{2 + \\sqrt 2}{\\sqrt 3} - \\frac{4 + \\sqrt{2}}{\\sqrt 2} \\] \\( \\approx 0.1140 \\) \\[ 10 \\frac{2 + \\sqrt 2}{\\sqrt 3} - 3 \\frac{5 + 3 \\sqrt 2}{\\sqrt 2} \\] \\( \\approx 0.1054 \\) Gumbel gumbel_r
see eq. \\( \\eqref{eq:lr_gev} \\) for \\( \\lmoment{r} \\) \\[ \\gamma_e \\] \\( \\approx 0.5772 \\) \\[ \\ln{2} \\] \\( \\approx 0.6931 \\) \\[ 2 \\log_2(3) - 3 \\] \\( \\approx 0.1699 \\) \\[ 16 - 10 \\log_2(3) \\] \\( \\approx 0.1504 \\) Pareto pareto
see eq. \\( \\eqref{eq:lr_pareto4} \\) for \\( \\lmoment{r} \\) \\[ \\begin{align*} \\alpha &> 0 \\quad \\text{(shape)} \\\\ \\beta &> 0 \\quad \\text{(scale)} \\end{align*} \\] \\[ \\frac{\\alpha}{\\alpha - 1} \\ \\beta \\] \\[ \\frac{\\alpha}{\\alpha - 1} \\frac{1}{2 \\alpha - 1} \\ \\beta \\] \\[ \\frac{\\alpha + 1}{3 \\alpha - 1} \\] \\[ \\frac{\\alpha + 1}{3 \\alpha - 1} \\frac{2 \\alpha + 1}{4 \\alpha - 1} \\]"},{"location":"distributions/#tl-stats","title":"TL-stats","text":"Collection of TL-location, -scale, -skewness, -kurtosis coefficients, with symmetric trimming of order 1, i.e. trim=(1, 1)
.
scipy.stats
Params \\( \\tlmoment{1}{1} \\) \\( \\tlmoment{1}{2} \\) \\( \\tlratio{1}{3} \\) \\( \\tlratio{1}{4} \\) Uniform uniform
\\( a < b \\) \\[ (a + b) / 2 \\] \\[ (a - b) / 10 \\] \\( 0 \\) \\( 0 \\) Normal norm
\\( \\mu \\) \\( \\sigma>0 \\) \\( \\mu \\) \\[ \\left( 6 - 18 \\ \\frac{\\arctan{\\sqrt 2}}{\\pi} \\right) \\frac{\\sigma}{\\sqrt \\pi} \\] \\( \\approx 0.2970 \\ \\sigma \\) \\( 0 \\) \\( \\approx 0.06248 \\) Logistic logistic(\u03bc, s)
\\( \\mu \\)\\( s>0 \\) \\( \\mu \\) \\( s / 2 \\) \\( 0 \\) \\[ 1 / 12 \\] \\( = 0.083\\overline{3} \\dots \\) Laplace laplace
\\( \\mu \\) \\( b > 0 \\) \\( \\mu \\) \\[ 11b / 32 \\] \\( = 0.34375 \\ b\\) \\( 0 \\) \\[ 3 / 22 \\] \\( = 0.136\\overline{36} \\dots \\) Cauchy / Student's t (1 d.f.) cauchy
/ t(2)
\\( \\nu = 1 \\) \\( 0 \\) \\[ \\frac{18 \\vphantom{)}}{\\pi^3 \\vphantom{)}} \\ \\zeta(3) \\] \\( \\approx 0.6978 \\ b \\) \\( 0 \\) \\[ \\frac{25}{6} - \\frac{175}{4 \\pi^2} \\frac{\\zeta(5)}{\\zeta(3)} \\] \\( \\approx 0.3428 \\) Student's t (2 d.f.) t(2)
\\( \\nu = 2 \\) \\( 0 \\) \\[ \\frac{3 \\pi}{16 \\sqrt{2}} \\] \\( \\approx 0.4165 \\) \\( 0 \\) \\[ \\frac{5}{32} \\] \\( = 0.15625 \\) Student's t (3 d.f.) t(3)
\\( \\nu = 3 \\) \\( 0 \\) \\[ \\frac{105 \\sqrt 3}{16 \\pi^3} \\] \\( \\approx 0.3666 \\) \\( 0 \\) \\[ \\frac{25}{6} - \\frac{23 \\ 023}{(24 \\pi)^2} \\] \\( \\approx 0.1168 \\) Student's t (4 d.f.) t(4)
\\( \\nu = 4 \\) \\( 0 \\) \\[ \\frac{3 \\ 609 \\ \\pi}{32 \\ 768} \\] \\( \\approx 0.3460 \\) \\( 0 \\) \\[ \\frac{164 \\ 975}{1 \\ 642 \\ 496} \\] \\( \\approx 0.1004 \\) Exponential expon
\\( \\lambda>0 \\) \\[ \\frac{5}{6 \\lambda} \\] \\[ \\frac{1}{4 \\lambda} \\] \\[ \\frac 2 9 \\] \\( = 0.2\\overline{2}\\dots \\) \\[ \\frac{1}{12} \\] \\( = 0.083\\overline{3}\\dots \\) Rayleigh rayleigh
\\[ \\frac 1 6 \\bigl( 9 - 2 \\sqrt 6 \\bigr) \\sqrt \\pi \\ \\] \\( \\approx 1.211 \\) \\[ \\frac 1 4 \\bigl( 6 - 4 \\sqrt 6 + 3 \\sqrt 2 \\bigr) \\sqrt \\pi \\ \\] \\( \\approx 0.1970 \\) \\[ \\frac{10}{9} - \\frac{8}{9} \\frac {3 \\sqrt{10} + 5 \\sqrt 6 - 15 \\sqrt 2} {6 - 4 \\sqrt 6 + 3 \\sqrt 2} \\] \\( \\approx 0.06951 \\) \\[ \\frac 5 4 - \\frac 7 6 \\frac {18 \\sqrt{10} + 10 \\sqrt 6 - 10 \\sqrt 3 - 45 \\sqrt 2} {6 - 4 \\sqrt 6 + 3 \\sqrt 2} \\] \\( \\approx 0.05422 \\) Gumbel gumbel_r
see eq. \\( \\eqref{eq:lr_gev} \\) for \\( \\tlmoment{s,t}{r} \\) \\[ \\gamma_e + 3 \\ln{2} - 2 \\ln{3} \\] \\( \\approx 0.4594 \\) \\[ 6 \\ln{3} - 9 \\ln{2} \\] \\( \\approx 0.3533 \\) \\[ -\\frac{10}{9} \\frac {2 \\log_2(5) - 5} {2 \\log_2(3) - 3} - \\frac{20}{9} \\] \\( \\approx 0.1065 \\) \\[ \\frac{35}{6} \\frac {3 \\log_2(5) - 7} {2 \\log_2(3) - 3} + \\frac{5}{4} \\] \\( \\approx 0.07541 \\)"},{"location":"distributions/#general-distribution-l-moments","title":"General distribution L-moments","text":"Lmo derived a bunch of closed-form solutions for L-moments of several distributions. The proofs are not published, but it isn\u2019t difficult to validate their correctness, e.g. numerically, or symbolically with sympy or wolfram alpha / mathematica.
"},{"location":"distributions/#gev","title":"GEV","text":"The generalized extreme value (GEV) distribution unifies the Gumbel, Fr\u00e9chet, and Weibull distributions. It has one shape parameter \\( \\alpha \\in \\mathbb{R} \\), and the following distribution functions:
\\[ \\begin{align*} F(x) &= e^{-\\coxbox{-x}{\\alpha}} \\\\ x(F) &= -\\boxcox{-\\ln(F)}{\\alpha} \\end{align*} \\]Here, \\( \\boxcox{\\cdot}{\\alpha} \\) is the Box-Cox transformation function.
An alternative parametrization is sometimes used, e.g. on Wikipedia, where \\( \\xi = -\\alpha \\). The convention that is used here, is the same as in scipy.stats.genextreme
, where c
corresponds to \\( \\alpha \\).
The trimmed L-moments of the GEV are
\\[ \\begin{equation} \\tlmoment{s, t}{r} = \\frac{(-1)^{r}}{r} \\sum_{k = s + 1}^{r + s + t} (-1)^{k - s} \\binom{r + k - 2}{r + s - 1} \\binom{r + s + t}{k} \\left( \\begin{cases} \\gamma_e + \\ln(k) & \\text{if } \\alpha = 0 \\\\ 1 / \\alpha - \\Gamma(\\alpha) \\ k^{-\\alpha} & \\text{if } \\alpha \\neq 0 \\end{cases} \\right) \\label{eq:lr_gev} \\end{equation} \\]"},{"location":"distributions/#glo","title":"GLO","text":"The generalized logistic distribution (GLO), also known as the shifted log-logistic distribution , with shape parameter \\( \\alpha \\in \\mathbb{R} \\), is characterized by the following distribution functions:
\\[ \\begin{align*} F(x) &= \\frac{1}{1 + \\coxbox{x}{\\alpha}} \\\\ x(F) &= -\\boxcox{\\frac{1 - F}{F}}{\\alpha} \\end{align*} \\]For \\( -1 < \\alpha < 1 \\), the general trimmed L-moments of the GLO are:
\\[ \\begin{equation} \\tlmoment{s, t}{r} = \\begin{cases} \\displaystyle \\psi(s + 1) - \\psi(t + 1) & \\text{if } \\alpha = 0 \\wedge r = 1 \\\\ \\displaystyle \\frac{(-1)^r}{r} \\B(r - 1,\\ s + 1) + \\frac 1 r \\B(r - 1,\\ t + 1) & \\text{if } \\alpha = 0 \\\\ \\displaystyle \\frac{\\ffact{1}{r}}{\\alpha} + \\sum_{k = s + 1}^{r + s + t} (-1)^{r + s - k } \\binom{r + k - 2}{r + s - 1} \\binom{r + s + t}{k} \\B(\\alpha,\\ k - \\alpha) & \\text{if } -1 < \\alpha < 1 \\end{cases} \\label{eq:lr_glo} \\end{equation} \\]Where \\( \\psi(z) \\) is the digamma function.
The corresponding scipy.stats
implementation is kappa4
, with h = -1
and k
set to \\( \\alpha \\); not genlogistic
.
The generalized Pareto distribution (GPD), with shape parameter \\( \\alpha \\in \\mathbb{R} \\), has for \\( x \\ge 0 \\) the distribution functions:
\\[ \\begin{align*} F(x) &= 1 - 1 / \\coxbox{x}{\\alpha} \\\\ x(F) &= \\boxcox{1 / (1 - F)}{\\alpha} \\end{align*} \\]Note that this distribution is standard uniform if \\( \\alpha = 1 \\), and standard exponential if \\( \\alpha = 0 \\).
The general trimmed L-moments of the GPD are:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\begin{cases} \\displaystyle \\sum_{k = 1}^{s + 1} \\frac{1}{t + k} & \\text{if } \\alpha = 0 \\wedge r = 1 \\\\ \\frac{1}{r} \\B(r - 1,\\ t + 1) & \\text{if } \\alpha = 0 \\\\ \\displaystyle \\frac{r + s + t}{\\alpha \\ r} \\sum_{k = 0}^{r + t - 1} \\frac{(-1)^{r - k}}{k} \\binom{r + s + t - 1}{k + s} \\binom{r + s + k - 1}{k} \\left( 1 - \\frac{(k + 1)!}{\\rfact{1 - \\alpha}{k + 1}} \\right) & \\text{if } \\alpha < 1 \\end{cases} \\label{eq:lr_gpd} \\end{equation} \\]Apparently left-trimming the exponential distribution does not influence any of the L-moments, besides the L-location.
For the general LH-moments, this simplifies to:
\\[ \\begin{equation} \\tlmoment{0,t}{r} = \\begin{cases} \\displaystyle \\frac{1}{t + 1} & \\text{if } \\alpha = 0 \\wedge r = 1 \\\\ \\frac{1}{r} \\B(r - 1,\\ t + 1) & \\text{if } \\alpha = 0 \\wedge r > 1 \\\\ \\displaystyle \\frac{r + t}{r} \\frac{\\rfact{1 + \\alpha}{r - 2}}{\\rfact{1 - \\alpha + t}{r}} - \\frac{\\ffact{1}{r}}{\\alpha} & \\text{if } \\alpha < 1 \\end{cases} \\label{eq:lhr_gpd} \\end{equation} \\]See scipy.stats.genpareto
for the implementation of the GPD.
The Pareto Type IV has two shape parameters \\( \\alpha \\in \\mathbb{R} \\) and \\( \\gamma \\in \\mathbb{R}_{>0} \\), and scale parameter \\( \\beta \\). For \\( x \\ge 0 \\), the CDF and its inverse (the PPF) are
\\[ \\begin{align*} F(x) &= 1 - \\left( 1 + \\left(\\frac x \\beta\\right)^{\\frac 1 \\gamma} \\right)^{-\\alpha} \\\\ x(F) &= \\beta \\left( (1 - F)^{-1 / \\alpha} - 1 \\right)^\\gamma \\end{align*} \\]When \\( \\alpha > \\gamma \\), the trimmed L-moments are found to be:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\frac{\\beta \\gamma}{r} \\sum_{k = t + 1}^{r + s + t} (-1)^{k - t - 1} \\binom{r + k - 2}{r + t - 1} \\binom{r + s + t}{k} \\B(\\gamma,\\ k \\alpha - \\gamma) \\label{eq:lr_pareto4} \\end{equation} \\]This distribution is currently not implemented in scipy.stats
.
For Kumaraswamy\u2019s distribution with parameters \\( \\alpha \\in \\mathbb{R}_{>0} \\) and \\( \\beta \\in \\mathbb{R}_{>0} \\), the general solution for the \\( r \\)th L-moment has been derived by Jones (2009). This can be extended for the general trimmed L-moments.
The distribution functions are for \\( 0 \\le x \\le 1 \\) defined as:
\\[ \\begin{align*} F(x) &= 1 - (1 - x^\\alpha)^\\beta \\\\ x(F) &= \\bigl(1 - (1 - F)^{1/\\beta} \\bigr)^{1/\\alpha} \\end{align*} \\]Its general \\( r \\)-th trimmed L-moment are:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\beta \\ \\frac{r + s + t}{r} \\sum_{k = t}^{r + s + t - 1} (-1)^k \\binom{k + r - 1}{k - t} \\binom{r + s + t - 1}{k} \\B\\bigl(1 + 1 / \\alpha,\\ \\beta + k \\beta \\bigr) \\label{eq:lr_kum} \\end{equation} \\]Unfortunately, the Kumaraswamy distribution is not implemented in scipy.stats
.
The Burr type III distribution, also known as the Dagum distribution, has two shape parameters \\( \\alpha \\) and \\( \\beta \\), both restricted to the positive reals
For \\( x > 0 \\), the distribution functions are:
\\[ \\begin{align*} F(x) &= (1 + x^{-\\alpha})^{-\\beta} \\\\ x(F) &= (F^{-1 / \\beta} - 1)^{-1 / \\alpha} \\end{align*} \\]For \\( \\alpha > 1 \\), the general L-moments are:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = (-1)^{t - 1 / \\alpha} \\ \\beta \\ \\frac{r + s + t}{r} \\sum_{k = s}^{r + s + t - 1} (-1)^{k} \\binom{k + r - 1}{k - s} \\binom{r + s + t - 1}{k} \\B(1 - 1 / \\alpha, -\\beta - k \\beta) \\label{eq:lr_burr3} \\end{equation} \\]The Burr Type III distribution is implemented in scipy.stats.burr
, where the shape parameters c
and d
correspond to \\( \\alpha \\) and \\( \\beta \\), respectively.
Just like Kumaraswamy\u2019s distribution, the Burr Type XII distribution has two shape parameters \\( \\alpha \\) and \\( \\beta \\), both restricted to the positive reals.
The distribution functions are for \\( x > 0 \\) defined as:
\\[ \\begin{align*} F(x) &= 1 - (1 - x^\\alpha)^{-\\beta} \\\\ x(F) &= \\bigl(1 - (1 - F)^{-1/\\beta} \\bigr)^{1/\\alpha} \\end{align*} \\]When \\( \\beta > 1 / \\alpha \\), the general \\( r \\)-th trimmed L-moment is:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\beta \\ \\frac{r + s + t}{r} \\sum_{k = t}^{r + s + t - 1} (-1)^k \\binom{k + r - 1}{k - t} \\binom{r + s + t - 1}{k} \\B\\bigl(1 + 1 / \\alpha,\\ \\beta + k \\beta - 1 / \\alpha \\bigr) \\label{eq:lr_burr12} \\end{equation} \\]The Burr Type XII distribution is implemented in scipy.stats.burr12
, where the shape parameters c
and d
correspond to \\( \\alpha \\) and \\( \\beta \\), respectively.
The Wakeby distribution is quantile-based, without closed-form expressions for the PDF and CDF, whose quantile function (PPF) is defined to be
\\[ x(F) = \\frac \\alpha \\beta \\bigl(1 - (1 - F)^\\beta\\bigr) - \\frac \\gamma \\delta \\bigl(1 - (1 - F)^{-\\delta}\\bigr) + \\mu \\]Each of the scale- \\( \\alpha, \\gamma \\) and shape parameters \\( \\beta, \\delta \\), are assumed to be positive real numbers.
Lmo figured out that the L-moments with any order \\( r \\in \\mathbb{N}_{\\ge 1} \\) and trim \\( s, t \\in \\mathbb{N}^2_{\\ge 1} \\) can be expressed as
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\frac{\\rfact{r + t}{s + 1}}{r} \\left[ \\alpha \\frac {\\rfact{1 - \\beta}{r - 2}} {\\rfact{1 + \\beta + t}{r + s}} + \\gamma \\frac {\\rfact{1 + \\delta}{r - 2}} {\\rfact{1 - \\delta + t}{r + s}} \\right] + \\underbrace{ \\ffact{1}{r} \\left( \\frac \\alpha \\beta - \\frac \\gamma \\delta \\right) }_{\\text{will be } 0 \\text{ if } r>1} \\end{equation} \\]Unfortunately, the Wakeby distribution has currently no scipy.stats
implementation.
The Tukey lambda distribution can be extended to the generalized lambda distribution, which has two scale parameters \\( \\alpha, \\gamma \\), and two shape parameters \\( \\beta, \\delta \\).
Like the Wakeby distribution, the generalized lambda has no closed-form PDF or CDF. Instead, it is defined through its PPF:
\\[ x(F) = \\alpha \\boxcox{F}{\\beta} - \\gamma \\boxcox{1 - F}{\\delta} \\]Although its central product moments have no closed-form expression, when \\( \\beta > -1 \\) and \\( \\delta > -1 \\), the general trimmed L-moments can be compactly expressed as:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\alpha \\frac {\\rfact{r + s}{t + 1} \\ \\ffact{\\beta + s}{r + s - 1}} {r \\ \\rfact{\\beta}{r + s + t + 1}} + (-1)^r \\gamma \\ \\frac {\\rfact{r + t}{s + t} \\ \\ffact{\\delta + t}{r + t - 1}} {r \\ \\rfact{\\delta}{r + s + t + 1}} - \\underbrace{ \\ffact{1}{r} \\left( \\frac \\alpha \\beta - \\frac \\gamma \\delta \\right) }_{\\text{will be } 0 \\text{ if } r>1} \\end{equation} \\]When \\( \\alpha = \\gamma \\) and \\( \\beta = \\delta \\), this is the (non-generalized) Tukey-lambda distribution, which has been implemented as scipy.stats.tukeylambda
. Currently, this 4-parameter generalization has no scipy.stats
implementation.
An overview of the (non-obvious) mathematical notation of special functions and constants.
Name Notation Definition Python Euler\u2013Mascheroni constant \\[ \\gamma_e \\] \\[ \\begin{align*} &= \\int_1^\\infty \\left( \\frac{1}{\\lfloor x \\rfloor} - \\frac 1 x \\right) \\ \\mathrm{d} x \\\\ &= \\lim_{x \\to 0} \\left( \\frac 1 x - \\Gamma(x) \\right) \\\\ &\\approx 0.5772 \\vphantom{\\frac 1 1} \\end{align*} \\]numpy.euler_gamma
Factorial \\[ n! \\vphantom{\\prod_{k=1}^n k} \\] \\[ \\begin{align*} &= \\prod_{k=1}^n k \\\\ &= \\underbrace {1 \\times 2 \\times \\ldots \\times n} _{n{\\text{ factors}}} \\end{align*} \\] scipy.special.factorial
Falling factorial \\[ \\ffact{x}{n} \\] \\[ \\begin{align*} &= \\prod_{k=0}^{n-1} (x - k) \\\\ &= \\underbrace {x \\ (x - 1) \\ldots (x - n + 1)} _{n{\\text{ factors}}} \\end{align*} \\] Rising factorial \\[ \\rfact{x}{n} \\] \\[ \\begin{align*} &= \\prod_{k=0}^{n-1} (x + k) \\\\ &= \\underbrace {x \\ (x + 1) \\ldots (x + n - 1)} _{n{\\text{ factors}}} \\end{align*} \\] scipy.special.poch
Binomial coefficient \\[ \\binom{n}{k} \\] \\[ \\begin{align*} &= \\frac{n!}{k! \\ (n - k)!} \\\\ &= \\frac{\\ffact{n}{k}}{k!} \\end{align*} \\] scipy.special.comb
Gamma function \\[ \\Gamma(z) \\] \\[ = \\int_0^\\infty t^{z-1} e^{-t} \\, \\mathrm{d} t \\] scipy.special.gamma
Digamma function \\[ \\psi(z) \\] \\[ = \\frac{\\mathrm{d}}{\\mathrm{d}z} \\ln \\Gamma(z) \\] scipy.special.digamma
Beta function \\[ \\B(z_1,\\ z_2) \\] \\[ = \\frac{\\Gamma(z_1) \\Gamma(z_2)}{\\Gamma(z_1 + z_2)} \\] scipy.special.beta
Riemann zeta function \\[ \\zeta(z) \\vphantom{\\sum_{n = 1}^{\\infty}} \\] \\[ = \\sum_{n = 1}^{\\infty} n^{-z} \\] scipy.special.zeta
Box\u2013Cox transform \\[ \\boxcox{y}{\\lambda} \\] \\[ = \\begin{cases} (y^\\lambda - 1) / \\lambda & \\text{if } \\lambda \\neq 0 \\\\ \\ln(y) & \\text{if } \\lambda = 0 \\end{cases} \\] scipy.special.boxcox
Inverse Box\u2013Cox transform \\[ \\coxbox{x}{\\lambda} \\] \\[ = \\begin{cases} (\\lambda x + 1)^{1 / \\lambda} & \\text{if } \\lambda \\neq 0 \\\\ e^{x} & \\text{if } \\lambda = 0 \\end{cases} \\] scipy.special.inv_boxcox
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":""},{"location":"#lmo-trimmed-l-moments-and-l-comoments","title":"Lmo - Trimmed L-moments and L-comoments","text":"Is your tail too heavy?\nCan't find a moment?\nAre the swans black?\nThe distribution pathological?\n\n... then look no further: Lmo's got you covered!\n\nUniform or multi-dimensional, Lmo can summarize it all with one quick glance!\n
Unlike the legacy moments, L-moments uniquely describe a probability distribution, and are more robust and efficient. The \u201cL\u201d stands for Linear; it is a linear combination of order statistics. So Lmo is as fast as sorting your samples (in terms of time-complexity).
"},{"location":"#key-features","title":"Key Features","text":"scipy.stats
distribution.lmo.l_moment(..., trim=(1/137, 3.1416))
.lmo.l_rv_nonparametric
Even if your data is pathological like Cauchy, and the L-moments are not defined, the trimmed L-moments (TL-moments) can be used instead. Let\u2019s calculate the TL-location and TL-scale of a small amount of samples:
>>> import numpy as np\n>>> import lmo\n>>> rng = np.random.default_rng(1980)\n>>> x = rng.standard_cauchy(96) # pickle me, Lmo\n>>> lmo.l_moment(x, [1, 2], trim=(1, 1)).\narray([-0.17937038, 0.68287665])\n
Now compare with the theoretical standard Cauchy TL-moments:
>>> from scipy.stats import cauchy\n>>> cauchy.l_moment([1, 2], trim=(1, 1))\narray([0. , 0.69782723])\n
See the documentation for more examples and the API reference.
"},{"location":"#roadmap","title":"Roadmap","text":"Lmo is on PyPI, so you can do something like:
pip install lmo\n
"},{"location":"#required-dependencies","title":"Required dependencies","text":"These are automatically installed by your package manager, alongside lmo
.
3.10
NumPy 1.22
SciPy 1.9
"},{"location":"#optional-dependencies","title":"Optional dependencies","text":"Package Minimum version Notes Pandas 1.4
Lmo extends pd.Series
and pd.DataFrame
with convenient methods, e.g. df.l_scale(trim=1)
. Install as pip install lmo[pandas]
to ensure compatibility."},{"location":"#foundational-literature","title":"Foundational Literature","text":"Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_kurtosis","title":"lmo.l_kurtosis(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"L-kurtosis coefficient; the 4th sample L-moment ratio.
\\[ \\tau^{(s, t)}_4 = \\frac{ \\lambda^{(s, t)}_4 }{ \\lambda^{(s, t)}_2 } \\]Alias for lmo.l_ratio(a, 4, 2, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_t(2, 99)\n>>> lmo.l_kurtosis(x)\n0.28912787...\n>>> lmo.l_kurtosis(x, trim=(1, 1))\n0.19928182...\n
Notes The L-kurtosis \\(\\tau_4\\) lies within the interval \\([-\\frac{1}{4}, 1)\\), and by the L-skewness \\(\\\\tau_3\\) as \\(5 \\tau_3^2 - 1 \\le 4 \\tau_4\\).
See Alsolmo.l_ratio
scipy.stats.kurtosis
lmo.l_loc(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"L-location (or L-loc): unbiased estimator of the first L-moment, \\(\\lambda^{(s, t)}_1\\).
Alias for lmo.l_moment(a, 1, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_cauchy(99)\n>>> x.mean()\n-7.5648...\n>>> lmo.l_loc(x) # no trim; equivalent to the (arithmetic) mean\n-7.5648...\n>>> lmo.l_loc(x, trim=(1, 1)) # TL-location\n-0.15924...\n>>> lmo.l_loc(x, trim=(3/2, 3/2)) # Fractional trimming (only in Lmo)\n-0.085845...\n
Notes If trim = (0, 0)
(default), the L-location is equivalent to the arithmetic mean.
lmo.l_moment
numpy.average
lmo.l_moment(a, r, /, trim=(0, 0), *, axis=None, dtype=np.float64, fweights=None, aweights=None, sort=None, cache=False)
","text":"Estimates the generalized trimmed L-moment \\(\\lambda^{(s, t)}_r\\) from the samples along the specified axis. By default, this will be the regular L-moment, \\(\\lambda_r = \\lambda^{(0, 0)}_r\\).
PARAMETER DESCRIPTIONa
Array containing numbers whose L-moments is desired. If a
is not an array, a conversion is attempted.
TYPE: npt.ArrayLike
r
The L-moment order(s), non-negative integer or array.
TYPE: IntVector | AnyInt
trim
Left- and right-trim orders \\((s, t)\\), non-negative ints or floats that are bound by \\(s + t < n - r\\). A single scalar \\(t\\) can be proivided as well, as alias for \\((t, t)\\).
Some special cases include:
TYPE: AnyTrim
DEFAULT: (0, 0)
axis
Axis along which to calculate the moments. If None
(default), all samples in the array will be used.
TYPE: int | None
DEFAULT: None
dtype
Floating type to use in computing the L-moments. Default is numpy.float64
.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
fweights
1-D array of integer frequency weights; the number of times each observation vector should be repeated.
TYPE: IntVector | None
DEFAULT: None
aweights
An array of weights associated with the values in a
. Each value in a
contributes to the average according to its associated weight. The weights array can either be 1-D (in which case its length must be the size of a along the given axis) or of the same shape as a
. If aweights=None
(default), then all data in a
are assumed to have a weight equal to one.
All aweights
must be >=0
, and the sum must be nonzero.
The algorithm is similar to that for weighted quantiles.
TYPE: npt.ArrayLike | None
DEFAULT: None
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
DEFAULT: None
cache
Set to True
to speed up future L-moment calculations that have the same number of observations in a
, equal trim
, and equal or smaller r
.
TYPE: bool
DEFAULT: False
l
The L-moment(s) of the input This is a scalar iff a is 1-d and r is a scalar. Otherwise, this is an array with np.ndim(r) + np.ndim(a) - 1
dimensions and shape like (*np.shape(r), *(d for d in np.shape(a) if d != axis))
.
TYPE: npt.NDArray[T] | T
Examples:
Calculate the L-location and L-scale from student-T(2) samples, for different (symmetric) trim-lengths.
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_t(2, 99)\n>>> lmo.l_moment(x, [1, 2], trim=(0, 0))\narray([-0.01412282, 0.94063132])\n>>> lmo.l_moment(x, [1, 2], trim=(1/2, 1/2))\narray([-0.02158858, 0.5796519 ])\n>>> lmo.l_moment(x, [1, 2], trim=(1, 1))\narray([-0.0124483 , 0.40120115])\n
The theoretical L-locations are all 0, and the the L-scale are 1.1107
, 0.6002
and 0.4165
, respectively.
scipy.stats.moment
lmo.l_moment_cov(a, r_max, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Non-parmateric auto-covariance matrix of the generalized trimmed L-moment point estimates with orders r = 1, ..., r_max
.
S_l
Variance-covariance matrix/tensor of shape (r_max, r_max, ...)
TYPE: npt.NDArray[T]
Examples:
Fitting of the cauchy distribution with TL-moments. The location is equal to the TL-location, and scale should be \\(0.698\\) times the TL(1)-scale, see Elamir & Seheult (2003).
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.standard_cauchy(1337)\n>>> lmo.l_moment(x, [1, 2], trim=(1, 1))\narray([0.08142405, 0.68884917])\n
The L-moment estimates seem to make sense. Let\u2019s check their standard errors, by taking the square root of the variances (the diagonal of the covariance matrix):
>>> lmo.l_moment_cov(x, 2, trim=(1, 1))\narray([[ 4.89407076e-03, -4.26419310e-05],\n [-4.26419310e-05, 1.30898414e-03]])\n>>> np.sqrt(_.diagonal())\narray([0.06995764, 0.03617989])\n
See Also lmo.l_moment
lmo.l_moment_influence(a, r, /, trim=(0, 0), *, sort=None, tol=1e-08)
","text":"Empirical Influence Function (EIF) of a sample L-moment.
NotesThis function is not vectorized.
PARAMETER DESCRIPTIONa
1-D array-like containing observed samples.
TYPE: npt.ArrayLike
r
L-moment order. Must be a non-negative integer.
TYPE: SupportsIndex
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The (vectorized) empirical influence function.
TYPE: Callable[[V], V]
lmo.l_ratio(a, r, s, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Estimates the generalized L-moment ratio:
\\[ \\tau^{(s, t)}_{rs} = \\frac{ \\lambda^{(s, t)}_r }{ \\lambda^{(s, t)}_s } \\]Equivalent to lmo.l_moment(a, r, *, **) / lmo.l_moment(a, s, *, **)
.
The L-moment with r=0
is 1
, so the l_ratio(a, r, 0, *, **)
is equivalent to l_moment(a, r, *, **)
.
Often, when referring to the \\(r\\)th L-ratio, the L-moment ratio with \\(k=2\\) is implied, i.e. \\(\\tau^{(s, t)}_r\\) is short-hand notation for \\(\\tau^{(s, t)}_{r,2}\\).
The L-variation (L-moment Coefficient of Variation, or L-CB) is another special case of the L-moment ratio, \\(\\tau^{(s, t)}_{2,1}\\). It is sometimes denoted in the literature by dropping the subscript indices: \\(\\tau^{(s, t)}\\). Note that this should only be used with strictly positive distributions.
Examples:
Estimate the L-location, L-scale, L-skewness and L-kurtosis simultaneously:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).lognormal(size=99)\n>>> lmo.l_ratio(x, [1, 2, 3, 4], [0, 0, 2, 2])\narray([1.53196368, 0.77549561, 0.4463163 , 0.29752178])\n>>> lmo.l_ratio(x, [1, 2, 3, 4], [0, 0, 2, 2], trim=(0, 1))\narray([0.75646807, 0.32203446, 0.23887609, 0.07917904])\n
See Also lmo.l_moment
lmo.l_ratio_influence(a, r, k=2, /, trim=(0, 0), *, sort=None, tol=1e-08)
","text":"Empirical Influence Function (EIF) of a sample L-moment ratio.
NotesThis function is not vectorized.
PARAMETER DESCRIPTIONa
1-D array-like containing observed samples.
TYPE: npt.ArrayLike
r
L-moment ratio order. Must be a non-negative integer.
TYPE: SupportsIndex
k
Denominator L-moment order, defaults to 2.
TYPE: SupportsIndex
DEFAULT: 2
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The (vectorized) empirical influence function.
TYPE: Callable[[V], V]
lmo.l_ratio_se(a, r, s, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Non-parametric estimates of the Standard Error (SE) in the L-ratio estimates from lmo.l_ratio
.
Examples:
Estimate the values and errors of the TL-loc, scale, skew and kurtosis for Cauchy-distributed samples. The theoretical values are [0.0, 0.698, 0.0, 0.343]
(Elamir & Seheult, 2003), respectively.
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.standard_cauchy(42)\n>>> lmo.l_ratio(x, [1, 2, 3, 4], [0, 0, 2, 2], trim=(1, 1))\narray([-0.25830513, 0.61738638, -0.03069701, 0.25550176])\n>>> lmo.l_ratio_se(x, [1, 2, 3, 4], [0, 0, 2, 2], trim=(1, 1))\narray([0.32857302, 0.12896501, 0.13835403, 0.07188138])\n
See Also lmo.l_ratio
lmo.l_moment_cov
lmo.l_scale(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"L-scale: unbiased estimator of the second L-moment, \\(\\lambda^{(s, t)}_2\\).
Alias for lmo.l_moment(a, 2, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_cauchy(99)\n>>> x.std()\n72.87715244...\n>>> lmo.l_scale(x)\n9.501123995...\n>>> lmo.l_scale(x, trim=(1, 1))\n0.658993279...\n
Notes If trim = (0, 0)
(default), the L-scale is equivalent to half the Gini mean difference (GMD).
lmo.l_moment
numpy.std
lmo.l_skew(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Unbiased sample estimator of the coefficient of L-skewness, or L-skew for short:
\\[ \\tau^{(s, t)}_3 = \\frac{ \\lambda^{(s, t)}_3 }{ \\lambda^{(s, t)}_2 } \\]Alias for lmo.l_ratio(a, 3, 2, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).standard_exponential(99)\n>>> lmo.l_skew(x)\n0.38524343...\n>>> lmo.l_skew(x, trim=(0, 1))\n0.27116139...\n
See Also lmo.l_ratio
scipy.stats.skew
lmo.l_stats(a, /, trim=(0, 0), num=4, *, axis=None, dtype=np.float64, **kwargs)
","text":"Calculates the L-loc(ation), L-scale, L-skew(ness) and L-kurtosis.
Equivalent to lmo.l_ratio(a, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
by default.
Examples:
>>> import lmo, scipy.stats\n>>> x = scipy.stats.gumbel_r.rvs(size=99, random_state=12345)\n>>> lmo.l_stats(x)\narray([0.79014773, 0.68346357, 0.12207413, 0.12829047])\n
The theoretical L-stats of the standard Gumbel distribution are [0.577, 0.693, 0.170, 0.150]
.
lmo.l_stats_se
lmo.l_ratio
lmo.l_costats
lmo.l_stats_se(a, /, num=4, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"Calculates the standard errors (SE\u2019s) of the L-stats
.
Equivalent to lmo.l_ratio_se(a, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
by default.
Examples:
>>> import lmo, scipy.stats\n>>> x = scipy.stats.gumbel_r.rvs(size=99, random_state=12345)\n>>> lmo.l_stats(x)\narray([0.79014773, 0.68346357, 0.12207413, 0.12829047])\n>>> lmo.l_stats_se(x)\narray([0.12305147, 0.05348839, 0.04472984, 0.03408495])\n
The theoretical L-stats of the standard Gumbel distribution are [0.577, 0.693, 0.170, 0.150]
. The corresponding relative z-scores are [-1.730, 0.181, 1.070, 0.648]
.
lmo.l_stats
lmo.l_ratio_se
lmo.l_variation(a, /, trim=(0, 0), *, axis=None, dtype=np.float64, **kwargs)
","text":"The coefficient of L-variation (or L-CV) unbiased sample estimator:
\\[ \\tau^{(s, t)} = \\frac{ \\lambda^{(s, t)}_2 }{ \\lambda^{(s, t)}_1 } \\]Alias for lmo.l_ratio(a, 2, 1, *, **)
.
Examples:
>>> import lmo, numpy as np\n>>> x = np.random.default_rng(12345).pareto(4.2, 99)\n>>> x.std() / x.mean()\n1.32161112...\n>>> lmo.l_variation(x)\n0.59073639...\n>>> lmo.l_variation(x, trim=(0, 1))\n0.55395044...\n
Notes If trim = (0, 0)
(default), this is equivalent to the Gini coefficient, and lies within the interval \\((0, 1)\\).
lmo.l_ratio
scipy.stats.variation.l_ratio
Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_cokurtosis","title":"lmo.l_cokurtosis(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Sample L-cokurtosis coefficient matrix \\(\\tilde\\Lambda^{(t_1, t_2)}_4\\).
Alias for lmo.l_coratio(a, 4, 2, *, **)
.
lmo.l_coratio
lmo.l_kurtosis
lmo.l_coloc(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"L-colocation matrix of 1st L-comoment estimates, \\(\\Lambda^{(t_1, t_2)}_1\\).
Alias for lmo.l_comoment(a, 1, *, **)
.
If trim = (0, 0)
(default), the L-colocation for \\([ij]\\) is the L-location \\(\\lambda_1\\) of \\(x_i\\), independent of \\(x_j\\).
Examples:
Without trimming, the L-colocation only provides marginal information:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_loc(x, axis=-1)\narray([-0.02678225, 0.03008309])\n>>> lmo.l_coloc(x)\narray([[-0.02678225, -0.02678225],\n [ 0.03008309, 0.03008309]])\n
But the trimmed L-locations are a different story\u2026
>>> lmo.l_loc(x, trim=(1, 1), axis=-1)\narray([-0.10488868, -0.00625729])\n>>> lmo.l_coloc(x, trim=(1, 1))\narray([[-0.10488868, -0.03797989],\n [ 0.03325074, -0.00625729]])\n
What this tells us, is somewhat of a mystery: trimmed L-comoments have been only been briefly mentioned once or twice in the literature.
See Alsolmo.l_comoment
lmo.l_loc
numpy.mean
lmo.l_comoment(a, r, /, trim=(0, 0), *, dtype=np.float64, rowvar=True, sort=None, cache=False)
","text":"Multivariate extension of lmo.l_moment
.
Estimates the L-comoment matrix:
\\[ \\Lambda_{r}^{(t_1, t_2)} = \\left[ \\lambda_{r [ij]}^{(t_1, t_2)} \\right]_{m \\times m} \\]Whereas the L-moments are calculated using the order statistics of the observations, i.e. by sorting, the L-comoment sorts \\(x_i\\) using the order of \\(x_j\\). This means that in general, \\(\\lambda_{r [ij]}^{(t_1, t_2)} \\neq \\lambda_{r [ji]}^{(t_1, t_2)}\\), i.e. \\(\\Lambda_{r}^{(t_1, t_2)}\\) is not symmetric.
The \\(r\\)-th L-comoment \\(\\lambda_{r [ij]}^{(t_1, t_2)}\\) reduces to the L-moment if \\(i=j\\), and can therefore be seen as a generalization of the (univariate) L-moments. Similar to how the diagonal of a covariance matrix contains the variances, the diagonal of the L-comoment matrix contains the L-moments.
Based on the proposed definition by Serfling & Xiao (2007) for L-comoments. Extended to allow for generalized trimming.
PARAMETER DESCRIPTIONa
1-D or 2-D array-like containing m
variables and n
observations. Each row of a
represents a variable, and each column a single observation of all those variables. Also see rowvar
below. If a
is not an array, a conversion is attempted.
TYPE: npt.ArrayLike
r
The L-moment order(s), non-negative integer or array.
TYPE: AnyInt | IntVector
trim
Left- and right-trim orders \\((t_1, t_2)\\), non-negative ints or floats that are bound by \\(t_1 + t_2 < n - r\\).
Some special cases include:
TYPE: AnyTrim
DEFAULT: (0, 0)
rowvar
If rowvar
is True (default), then each row (axis 0) represents a variable, with observations in the columns (axis 1). Otherwise, the relationship is transposed: each column represents a variable, while the rows contain observations.
TYPE: bool
DEFAULT: True
dtype
Floating type to use in computing the L-moments. Default is numpy.float64
.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
sort
Sorting algorithm, see numpy.sort
.
TYPE: quick | stable | heap
DEFAULT: None
cache
Set to True
to speed up future L-moment calculations that have the same number of observations in a
, equal trim
, and equal or smaller r
.
TYPE: bool
DEFAULT: False
L
Array of shape (*r.shape, m, m)
with r-th L-comoments.
TYPE: npt.NDArray[T]
Examples:
Estimation of the second L-comoment (the L-coscale) from biviariate normal samples:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_comoment(x, 2)\narray([[ 1.2766793 , -0.83299947],\n [-0.71547941, 1.05990727]])\n
The diagonal contains the univariate L-moments:
>>> lmo.l_moment(x, 2, axis=-1)\narray([1.2766793 , 1.05990727])\n
References lmo.l_coratio(a, r, s, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Estimate the generalized matrix of L-comoment ratio\u2019s.
\\[ \\tilde \\Lambda_{rs}^{(t_1, t_2)} = \\left[ \\left. \\lambda_{r [ij]}^{(t_1, t_2)} \\right/ \\lambda_{s [ii]}^{(t_1, t_2)} \\right]_{m \\times m} \\] See Alsolmo.l_comoment
lmo.l_ratio
lmo.l_corr(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Sample L-correlation coefficient matrix \\(\\tilde\\Lambda^{(t_1, t_2)}_2\\); the ratio of the L-coscale matrix over the L-scale column-vectors.
Alias for lmo.l_coratio(a, 2, 2, *, **)
.
The diagonal consists of all 1\u2019s.
Where the pearson correlation coefficient measures linearity, the (T)L-correlation coefficient measures monotonicity.
Examples:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> cov = np.array([[6, -3], [-3, 3.5]])\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_corr(x)\narray([[ 1. , -0.65247355],\n [-0.67503962, 1. ]])\n
Let\u2019s compare this with the theoretical correlation
>>> cov[0, 1] / np.sqrt(cov[0, 0] * cov[1, 1])\n-0.6546536707079772\n
and the (Pearson) correlation coefficient matrix:
>>> np.corrcoef(x)\narray([[ 1. , -0.66383285],\n [-0.66383285, 1. ]])\n
See Also lmo.l_coratio
numpy.corrcoef
lmo.l_coscale(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"L-coscale matrix of 2nd L-comoment estimates, \\(\\Lambda^{(t_1, t_2)}_2\\).
Alias for lmo.l_comoment(a, 2, *, **)
.
Analogous to the (auto-) variance-covariance matrix, the L-coscale matrix is positive semi-definite, and its main diagonal contains the L-scale\u2019s. conversely, the L-coscale matrix is inherently asymmetric, thus yielding more information.
Examples:
>>> import lmo, numpy as np\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.multivariate_normal([0, 0], [[6, -3], [-3, 3.5]], 99).T\n>>> lmo.l_scale(x, trim=(1, 1), axis=-1)\narray([0.66698774, 0.54440895])\n>>> lmo.l_coscale(x, trim=(1, 1))\narray([[ 0.66698774, -0.41025416],\n [-0.37918065, 0.54440895]])\n
See Also lmo.l_comoment
lmo.l_scale
numpy.cov
lmo.l_coskew(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Sample L-coskewness coefficient matrix \\(\\tilde\\Lambda^{(t_1, t_2)}_3\\).
Alias for lmo.l_coratio(a, 3, 2, *, **)
.
lmo.l_coratio
lmo.l_skew
lmo.l_costats(a, /, trim=(0, 0), *, dtype=np.float64, **kwargs)
","text":"Calculates the L-coscale, L-corr(elation), L-coskew(ness) and L-cokurtosis.
Equivalent to lmo.l_coratio(a, [2, 2, 3, 4], [0, 2, 2, 2], *, **)
.
lmo.l_stats
lmo.l_coratio
scipy.stats
extensions","text":"Extensions for scipy.stats
distributions.
lmo.contrib.scipy_stats.l_rv_generic
","text":"Additional methods that are patched into scipy.stats.rv_continuous
and scipy.stats.rv_discrete
.
l_moment(r, /, *args, trim=(0, 0), quad_opts=None, **kwds)
","text":"Population L-moment(s) \\(\\lambda^{(s,t)}_r\\).
\\[ \\lambda^{(s, t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\mathbb{E}_X \\left[ U^s \\left(1 - U\\right)^t \\,\\tilde{P}^{(t, s)}_{r-1}(U) \\,X \\right] \\;, \\]with \\(U = F_X(X)\\) the rank of \\(X\\), and \\(\\tilde{P}^{(a,b)}_n(x)\\) the shifted (\\(x \\mapsto 2x-1\\)) Jacobi polynomial.
Examples:
Evaluate the population L-moments of the normally-distributed IQ test:
>>> import lmo\n>>> from scipy.stats import norm\n>>> norm(100, 15).l_moment([1, 2, 3, 4]).round(6)\narray([100. , 8.462844, 0. , 1.037559])\n>>> _[1] * np.sqrt(np.pi)\n15.000000...\n
Discrete distributions are also supported, e.g. the Binomial distribution:
>>> from scipy.stats import binom\n>>> binom(10, .6).l_moment([1, 2, 3, 4]).round(6)\narray([ 6. , 0.862238, -0.019729, 0.096461])\n
PARAMETER DESCRIPTION r
L-moment order(s), non-negative integer or array-like of integers.
TYPE: AnyInt | IntVector
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
TypeError
r
is not integer-valued
ValueError
r
is empty or negative
lmbda
The population L-moment(s), a scalar or float array like r
.
TYPE: np.float64 | npt.NDArray[np.float64]
lmo.l_moment
: sample L-momentl_ratio(r, k, /, *args, trim=(0, 0), quad_opts=None, **kwds)
","text":"L-moment ratio(\u2019s) \\(\\tau^{(s,t)}_{r,k}\\).
\\[ \\tau^{(s,t)}_{r,k} = \\frac{\\lambda^{(s,t)}_r}{\\lambda^{(s,t)}_k} \\]Unless explicitly specified, the r-th (\\(r>2\\)) L-ratio, \\(\\tau^{(s,t)}_r\\) refers to \\(\\tau^{(s,t)}_{r, 2}\\). Another special case is the L-variation, or the L-CV, \\(\\tau^{(s,t)} = \\tau^{(s,t)}_{2, 1}\\). This is the L-moment analogue of the coefficient of variation.
Examples:
Evaluate the population L-CV and LL-CV (CV = coefficient of variation) of the standard Rayleigh distribution.
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.rayleigh()\n>>> X.std() / X.mean() # legacy CV\n0.5227232...\n>>> X.l_ratio(2, 1)\n0.2928932...\n>>> X.l_ratio(2, 1, trim=(0, 1))\n0.2752551...\n
And similarly, for the (discrete) Poisson distribution with rate parameter set to 2, the L-CF and LL-CV evaluate to:
>>> X = distributions.poisson(2)\n>>> X.std() / X.mean()\n0.7071067...\n>>> X.l_ratio(2, 1)\n0.3857527...\n>>> X.l_ratio(2, 1, trim=(0, 1))\n0.4097538...\n
Note that (untrimmed) L-CV requires a higher (subdivision) limit in the integration routine, otherwise it\u2019ll complain that it didn\u2019t converge (enough) yet. This is because it\u2019s effectively integrating a non-smooth function, which is mathematically iffy, but works fine in this numerical application.
PARAMETER DESCRIPTIONr
L-moment ratio order(s), non-negative integer or array-like of integers.
TYPE: AnyInt | IntVector
k
L-moment order of the denominator, e.g. 2.
TYPE: AnyInt | IntVector
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
l_rv_generic.l_moment
lmo.l_ratio
- Sample L-moment ratio estimatorl_stats(*args, trim=(0, 0), moments=4, quad_opts=None, **kwds)
","text":"The L-moments (for \\(r \\le 2\\)) and L-ratio\u2019s (for \\(r > 2\\)).
By default, the first moments = 4
population L-stats are calculated:
This method is equivalent to X.l_ratio([1, 2, 3, 4], [0, 0, 2, 2], *, **)
, for with default moments = 4
.
Examples:
Summarize the standard exponential distribution for different trim-orders.
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.expon()\n>>> X.l_stats().round(6)\narray([1. , 0.5 , 0.333333, 0.166667])\n>>> X.l_stats(trim=(0, 1/2)).round(6)\narray([0.666667, 0.333333, 0.266667, 0.114286])\n>>> X.l_stats(trim=(0, 1)).round(6)\narray([0.5 , 0.25 , 0.222222, 0.083333])\n
Note This should not be confused with the term L-statistic, which is sometimes used to describe any linear combination of order statistics.
PARAMETER DESCRIPTION*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
moments
The amount of L-moments to return. Defaults to 4.
TYPE: int
DEFAULT: 4
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
l_rv_generic.l_ratio
lmo.l_stats
- Unbiased sample estimation of L-stats.l_loc(*args, trim=(0, 0), **kwds)
","text":"L-location of the distribution, i.e. the 1st L-moment.
Alias for X.l_moment(1, ...)
.
l_scale(*args, trim=(0, 0), **kwds)
","text":"L-scale of the distribution, i.e. the 2nd L-moment.
Alias for X.l_moment(2, ...)
.
l_skew(*args, trim=(0, 0), **kwds)
","text":"L-skewness coefficient of the distribution; the 3rd L-moment ratio.
Alias for X.l_ratio(3, 2, ...)
.
l_kurtosis(*args, trim=(0, 0), **kwds)
","text":"L-kurtosis coefficient of the distribution; the 4th L-moment ratio.
Alias for X.l_ratio(4, 2, ...)
.
l_moments_cov(r_max, /, *args, trim=(0, 0), quad_opts=None, **kwds)
","text":"Variance/covariance matrix of the L-moment estimators.
L-moments that are estimated from \\(n\\) samples of a distribution with CDF \\(F\\), converge to the multivariate normal distribution as the sample size \\(n \\rightarrow \\infty\\).
\\[ \\sqrt{n} \\left( \\vec{l}^{(s, t)} - \\vec{\\lambda}^{(s, t)} \\right) \\sim \\mathcal{N}( \\vec{0}, \\mathbf{\\Lambda}^{(s, t)} ) \\]Here, \\(\\vec{l}^{(s, t)} = \\left[l^{(s,t)}_r, \\dots, l^{(s,t)}_{r_{max}}\\right]^T\\) is a vector of estimated sample L-moments, and \\(\\vec{\\lambda}^{(s, t)}\\) its theoretical (\u201ctrue\u201d) counterpart.
This function calculates the covariance matrix
\\[ \\begin{align} \\bf{\\Lambda}^{(s,t)}_{k, r} &= \\mathrm{Cov}[l^{(s, t)}_k, l^{(s, t)}_r] \\\\ &= c_k c_r \\iint\\limits_{x < y} \\Big[ p_k\\big(F(x)\\big) \\, p_r\\big(F(y)\\big) + p_r\\big(F(x)\\big) \\, p_k\\big(F(y)\\big) \\Big] w^{(s+1,\\, t)}\\big(F(x)\\big) \\, w^{(s,\\, t+1)}\\big(F(y)\\big) \\, \\mathrm{d}x \\, \\mathrm{d}y \\;, \\end{align} \\]where
\\[ c_n = \\frac{\\Gamma(n) \\Gamma(n+s+t+1)}{n \\Gamma(n+s) \\Gamma(n+t)}\\;, \\]the shifted Jacobi polynomial \\(p_n(u) = P^{(t, s)}_{n-1}(2u - 1)\\), \\(P^{(t, s)}_m\\), and \\(w^{(s,t)}(u) = u^s (1-u)^t\\) its weight function.
NotesThis function is not vectorized or parallelized.
For small sample sizes (\\(n < 100\\)), the covariances of the higher-order L-moments (\\(r > 2\\)) can be biased. But this bias quickly disappears at roughly \\(n > 200\\) (depending on the trim- and L-moment orders).
Examples:
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.expon() # standard exponential distribution\n>>> X.l_moments_cov(4).round(6)\narray([[1. , 0.5 , 0.166667, 0.083333],\n [0.5 , 0.333333, 0.166667, 0.083333],\n [0.166667, 0.166667, 0.133333, 0.083333],\n [0.083333, 0.083333, 0.083333, 0.071429]])\n
>>> X.l_moments_cov(4, trim=(0, 1)).round(6)\narray([[0.333333, 0.125 , 0. , 0. ],\n [0.125 , 0.075 , 0.016667, 0. ],\n [0. , 0.016667, 0.016931, 0.00496 ],\n [0. , 0. , 0.00496 , 0.0062 ]])\n
PARAMETER DESCRIPTION r_max
The amount of L-moment orders to consider. If for example r_max = 4
, the covariance matrix will be of shape (4, 4)
, and the columns and rows correspond to the L-moments of order \\(r = 1, \\dots, r_{max}\\).
TYPE: int
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
cov
Covariance matrix, with shape (r_max, r_max)
.
TYPE: npt.NDArray[np.float64]
RuntimeError
If the covariance matrix is invalid.
Referencesl_stats_cov(*args, moments=4, trim=(0, 0), quad_opts=None, **kwds)
","text":"Similar to l_moments_cov
, but for the l_rv_generic.l_stats
.
As the sample size \\(n \\rightarrow \\infty\\), the L-moment ratio\u2019s are also distributed (multivariate) normally. The L-stats are defined to be L-moments for \\(r\\le 2\\), and L-ratio coefficients otherwise.
The corresponding covariance matrix has been found to be
\\[ \\bf{T}^{(s, t)}_{k, r} = \\begin{cases} \\bf{\\Lambda}^{(s, t)}_{k, r} & k \\le 2 \\wedge r \\le 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} }{ \\lambda^{(s,t)}_{2} } & k \\le 2 \\wedge r > 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_k \\bf{\\Lambda}^{(s, t)}_{2, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} + \\tau_k \\tau_r \\bf{\\Lambda}^{(s, t)}_{2, 2} }{ \\Big( \\lambda^{(s,t)}_{2} \\Big)^2 } & k > 2 \\wedge r > 2 \\end{cases} \\]where \\(\\bf{\\Lambda}^{(s, t)}\\) is the covariance matrix of the L-moments from l_moment_cov_from_cdf
, and \\(\\tau^{(s,t)}_r = \\lambda^{(s,t)}_r / \\lambda^{(s,t)}_2\\) the population L-ratio.
Examples:
Evaluate the LL-stats covariance matrix of the standard exponential distribution, for 0, 1, and 2 degrees of trimming.
>>> import lmo\n>>> from scipy.stats import distributions\n>>> X = distributions.expon() # standard exponential distribution\n>>> X.l_stats_cov().round(6)\narray([[1. , 0.5 , 0. , 0. ],\n [0.5 , 0.333333, 0.111111, 0.055556],\n [0. , 0.111111, 0.237037, 0.185185],\n [0. , 0.055556, 0.185185, 0.21164 ]])\n>>> X.l_stats_cov(trim=(0, 1)).round(6)\narray([[ 0.333333, 0.125 , -0.111111, -0.041667],\n [ 0.125 , 0.075 , 0. , -0.025 ],\n [-0.111111, 0. , 0.21164 , 0.079365],\n [-0.041667, -0.025 , 0.079365, 0.10754 ]])\n>>> X.l_stats_cov(trim=(0, 2)).round(6)\narray([[ 0.2 , 0.066667, -0.114286, -0.02 ],\n [ 0.066667, 0.038095, -0.014286, -0.023333],\n [-0.114286, -0.014286, 0.228571, 0.04 ],\n [-0.02 , -0.023333, 0.04 , 0.086545]])\n
Note that with 0 trim the L-location is independent of the L-skewness and L-kurtosis. With 1 trim, the L-scale and L-skewness are independent. And with 2 trim, all L-stats depend on each other.
PARAMETER DESCRIPTION*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
moments
The amount of L-statistics to consider. Defaults to 4.
TYPE: int
DEFAULT: 4
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
l_moment_influence(r, /, *args, trim=(0, 0), quad_opts=None, tol=1e-08, **kwds)
","text":"Returns the influence function (IF) of an L-moment.
\\[ \\psi_{\\lambda^{(s, t)}_r | F}(x) = c^{(s,t)}_r \\, F(x)^s \\, \\big( 1-{F}(x) \\big)^t \\, \\tilde{P}^{(s,t)}_{r-1} \\big( F(x) \\big) \\, x - \\lambda^{(s,t)}_r \\;, \\]with \\(F\\) the CDF, \\(\\tilde{P}^{(s,t)}_{r-1}\\) the shifted Jacobi polynomial, and
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r, \\, r+s+t)}{B(r+s, \\, r+t)} \\;, \\]where \\(B\\) is the (complete) Beta function.
The proof is trivial, because population L-moments are linear functionals.
NotesThe order parameter r
is not vectorized.
r
The L-moment order \\(r \\in \\mathbb{N}^+\\)..
TYPE: AnyInt
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
tol
Values that are absolutely smaller than this will be rounded to zero.
TYPE: float
DEFAULT: 1e-08
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
influence_function
The (vectorized) influence function, \\(\\psi_{\\lambda^{(s, t)}_r | F}(x)\\).
TYPE: Callable[[V], V]
l_rv_generic.l_moment
lmo.l_moment
l_ratio_influence(r, k, /, *args, trim=(0, 0), quad_opts=None, tol=1e-08, **kwds)
","text":"Returns the influence function (IF) of an L-moment ratio.
\\[ \\psi_{\\tau^{(s, t)}_{r,k}|F}(x) = \\frac{ \\psi_{\\lambda^{(s, t)}_r|F}(x) - \\tau^{(s, t)}_{r,k} \\, \\psi_{\\lambda^{(s, t)}_k|F}(x) }{ \\lambda^{(s,t)}_k } \\;, \\]where the L-moment ratio is defined as
\\[ \\tau^{(s, t)}_{r,k} = \\frac{ \\lambda^{(s, t)}_r }{ \\lambda^{(s, t)}_k } \\;. \\]Because IF\u2019s are a special case of the general G\u00e2teuax derivative, the L-ratio IF is derived by applying the chain rule to the L-moment IF.
PARAMETER DESCRIPTIONr
L-moment ratio order, i.e. the order of the numerator L-moment.
TYPE: AnyInt
k
Denominator L-moment order, defaults to 2.
TYPE: AnyInt
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information)
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float. or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
DEFAULT: None
tol
Values that are absolutely smaller than this will be rounded to zero.
TYPE: float
DEFAULT: 1e-08
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
influence_function
The influence function, with vectorized signature () -> ()
.
TYPE: Callable[[V], V]
l_rv_generic.l_ratio
lmo.l_ratio
l_fit(data, *args, n_extra=0, trim=(0, 0), full_output=False, fit_kwargs=None, random_state=None, **kwds)
","text":"Return estimates of shape (if applicable), location, and scale parameters from data. The default estimation method is Method of L-moments (L-MM), but the Generalized Method of L-Moments (L-GMM) is also available (see the n_extra
parameter).
See \u2018lmo.inference.fit\u2019 for details.
Examples:
Fitting standard normal samples Using scipy\u2019s default MLE (Maximum Likelihood Estimation) method:
>>> import lmo\n>>> import numpy as np\n>>> from scipy.stats import norm\n>>> rng = np.random.default_rng(12345)\n>>> x = rng.standard_normal(200)\n>>> norm.fit(x)\n(0.0033..., 0.9555...)\n
Better results can be obtained different by using Lmo\u2019s L-MM (Method of L-moment):
>>> norm.l_fit(x, random_state=rng)\nFitArgs(loc=0.0033..., scale=0.9617...)\n>>> norm.l_fit(x, trim=1, random_state=rng)\nFitArgs(loc=0.0197..., scale=0.9674...)\n
To use more L-moments than the number of parameters, two in this case, n_extra
can be used. This will use the L-GMM (Generalized Method of L-Moments), which results in slightly better estimates:
>>> norm.l_fit(x, n_extra=1, random_state=rng)\nFitArgs(loc=0.0039..., scale=0.9623...)\n>>> norm.l_fit(x, trim=1, n_extra=1, random_state=rng)\nFitArgs(loc=-0.0012..., scale=0.9685...)\n
PARAMETER DESCRIPTION data
1-D array-like data to use in estimating the distribution parameters.
TYPE: npt.ArrayLike
*args
Starting value(s) for any shape-characterizing arguments ( those not provided will be determined by a call to fit(data)
).
TYPE: float
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
n_extra
The amount of extra L-moment conditions to use than the amount of parameters. If 0 (default), L-MM will be used. If >0, \\(k\\)-step L-GMM will be used.
TYPE: int
DEFAULT: 0
full_output
If set to True, a LGMMResult
instance will be returned, instead of only a tuple with parameters.
TYPE: bool
DEFAULT: False
fit_kwargs
Additional keyword arguments to be passed to \u2018lmo.inference.fit\u2019 or \u2018scipy.optimize.minimize\u2019.
TYPE: Mapping[str, Any] | None
DEFAULT: None
random_state
Integer or numpy.random.Generator
instance, used for Monte-Carlo simulation when n_extra > 0
. If None
(default), the random_state
of this distribution will be used.
TYPE: int | np.random.Generator | None
DEFAULT: None
**kwds
Special keyword arguments are recognized as holding certain parameters fixed:
- `f0...fn`: hold respective shape parameters fixed.\nAlternatively, shape parameters to fix can be specified by\nname. For example, if `self.shapes == \"a, b\"`, `fa` and\n`fix_a` are equivalent to `f0`, and `fb` and `fix_b` are\nequivalent to `f1`.\n- `floc`: hold location parameter fixed to specified value.\n- `fscale`: hold scale parameter fixed to specified value.\n
TYPE: Any
DEFAULT: {}
result
Named tuple with estimates for any shape parameters (if applicable), followed by those for location and scale. For most random variables, shape statistics will be returned, but there are exceptions (e.g. norm
). If full_output=True
, an instance of LGMMResult
will be returned instead.
TYPE: tuple[float, ...] | inference.GMMResult
l_fit_loc_scale(data, *args, trim=(0, 0), **kwds)
","text":"Estimate loc and scale parameters from data using the first two L-moments.
NotesThe implementation mimics that of fit_loc_scale()
data
Data to fit.
TYPE: npt.ArrayLike
*args
The shape parameter(s) for the distribution (see docstring of the instance object for more information).
TYPE: Any
DEFAULT: ()
trim
Left- and right- trim. Can be scalar or 2-tuple of non-negative int or float.
TYPE: AnyTrim
DEFAULT: (0, 0)
**kwds
Additional keyword arguments to pass to the distribution.
TYPE: Any
DEFAULT: {}
loc_hat
Estimated location parameter for the data.
TYPE: float
scale_hat
Estimated scale parameter for the data.
TYPE: float
pandas
extensions (optional)","text":"Extension methods for pandas.Series
and pandas.DataFrame
.
Pandas is an optional dependency, and can be installed using pip install lmo[pandas]
.
Examples:
Univariate summary statistics:
>>> df = pd.DataFrame({'a': [1, 2, 2, 3, 4], 'b': [3, 4, 4, 4, 4]})\n>>> df.l_stats()\n a b\nr\n1 2.400000 3.8\n2 0.700000 0.2\n3 0.142857 -1.0\n4 0.285714 1.0\n>>> df.aggregate(['mean', 'std', 'skew', 'kurt'])\n a b\nmean 2.400000 3.800000\nstd 1.140175 0.447214\nskew 0.404796 -2.236068\nkurt -0.177515 5.000000\n
Comparison of L-correlation, and Pearson correlation matrices:
>>> df = pd.DataFrame({'dogs': [.2, .0, .5, .4], 'cats': [.3, .2, .0, .1]})\n>>> df.l_corr()\n dogs cats\ndogs 1.0 -0.764706\ncats -0.8 1.000000\n>>> df.corr()\n dogs cats\ndogs 1.000000 -0.756889\ncats -0.756889 1.000000\n
"},{"location":"api/#lmo.contrib.pandas.Series","title":"lmo.contrib.pandas.Series
","text":"Extension methods for pandas.Series
.
This class is not meant to be used directly. These methods are curried and registered as series accessors.
"},{"location":"api/#lmo.contrib.pandas.Series.l_moment","title":"l_moment(r, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_moment
.
out
A scalar, or a pd.Series[float]
, indexed by r
.
TYPE: _FloatOrSeries
l_ratio(r, k, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_ratio
.
out
A scalar, or pd.Series[float]
, with a MultiIndex
of r
and k
.
TYPE: _FloatOrSeries
l_stats(trim=(0, 0), num=4, **kwargs)
","text":"See lmo.l_stats
.
out
A pd.Series[float]
with index r = 1, ..., num
.
TYPE: pd.Series[float]
l_loc(trim=(0, 0), **kwargs)
","text":"See lmo.l_loc
.
out
A scalar.
TYPE: float
l_scale(trim=(0, 0), **kwargs)
","text":"See lmo.l_scale
.
out
A scalar.
TYPE: float
l_variation(trim=(0, 0), **kwargs)
","text":"See lmo.l_variation
.
out
A scalar.
TYPE: float
l_skew(trim=(0, 0), **kwargs)
","text":"See lmo.l_skew
.
out
A scalar.
TYPE: float
l_kurtosis(trim=(0, 0), **kwargs)
","text":"See lmo.l_kurtosis
.
out
A scalar.
TYPE: float
lmo.contrib.pandas.DataFrame
","text":"Extension methods for pandas.DataFrame
.
This class is not meant to be used directly. These methods are curried and registered as dataframe accessors.
"},{"location":"api/#lmo.contrib.pandas.DataFrame.l_moment","title":"l_moment(r, /, trim=(0, 0), axis=0, **kwargs)
","text":"See lmo.l_moment
.
out
A Series[float]
, or a DataFrame
with r
as index along the specified axis.
TYPE: _SeriesOrFrame
l_ratio(r, k, /, trim=(0, 0), axis=0, **kwargs)
","text":"See lmo.l_ratio
.
out
A Series[float]
, or a DataFrame
, with a MultiIndex
of r
and k
along the specified axis.
TYPE: _SeriesOrFrame
l_stats(trim=(0, 0), num=4, axis=0, **kwargs)
","text":"See lmo.l_stats
.
out
A DataFrame
with r = 1, ..., num
as index along the specified axis.
TYPE: pd.DataFrame
l_loc(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_moment(1, ...)
. See lmo.l_loc
for details.
l_scale(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_moment(2, ...)
. See lmo.l_scale
for details.
l_variation(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_ratio(2, 1, ...)
. See lmo.l_variation
for details.
l_skew(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_ratio(3, 2, ...)
. See lmo.l_skew
for details.
l_kurtosis(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_ratio(4, 2, ...)
. See lmo.l_kurtosis
for details.
l_kurt(trim=(0, 0), axis=0, **kwargs)
","text":"Alias for l_kurtosis
.
l_comoment(r, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_comoment
.
r
The L-moment order, as a non-negative scalar.
TYPE: AnyInt
trim
Left- and right-trim orders.
TYPE: AnyTrim
DEFAULT: (0, 0)
**kwargs
Additional options to pass to lmo.l_comoment
.
TYPE: Unpack[LComomentOptions]
DEFAULT: {}
out
A DataFrame
of the column-to-column L-comoment matrix.
TYPE: pd.DataFrame
TypeError
If rowvar=True
, use df.T.l_comoment
instead.
l_coratio(r, k=2, /, trim=(0, 0), **kwargs)
","text":"See lmo.l_coratio
.
r
The L-moment order of the numerator, a non-negative scalar.
TYPE: AnyInt
k
The L-moment order of the denominator, a non-negative scalar. Defaults to 2. If set to 0, this is equivalent to l_comoment
.
TYPE: AnyInt
DEFAULT: 2
trim
Left- and right-trim orders.
TYPE: AnyTrim
DEFAULT: (0, 0)
**kwargs
Additional options to pass to lmo.l_comoment
.
TYPE: Unpack[LComomentOptions]
DEFAULT: {}
out
A DataFrame
of the column-to-column matrix of L-comoment ratio\u2019s.
TYPE: pd.DataFrame
TypeError
If rowvar=True
, use df.T.l_comoment
instead.
l_coloc(trim=(0, 0), **kwargs)
","text":"Alias for l_comoment(1, trim, **kwargs)
. See lmo.l_coloc
for details.
l_coscale(trim=(0, 0), **kwargs)
","text":"Alias for l_comoment(2, trim, **kwargs)
. See lmo.l_coscale
for details.
l_corr(trim=(0, 0), **kwargs)
","text":"Alias for l_coratio(2, 2, trim, **kwargs)
. See lmo.l_corr
for details.
l_coskew(trim=(0, 0), **kwargs)
","text":"Alias for l_coratio(3, 2, trim, **kwargs)
. See lmo.l_coskew
for details.
l_cokurtosis(trim=(0, 0), **kwargs)
","text":"Alias for l_coratio(4, 2, trim, **kwargs)
. See lmo.l_cokurtosis
for details.
l_cokurt(trim=(0, 0), **kwargs)
","text":"Alias for l_cokurtosis
.
Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_rv_nonparametric","title":"lmo.l_rv_nonparametric(l_moments, trim=(0, 0), a=None, b=None, **kwargs)
","text":" Bases: rv_continuous
Estimate a distribution using the given L-moments. See scipy.stats.rv_continuous
for the available method.
The PPF (quantile function) is estimated using generalized Fourier series, with the (shifted) Jacobi orthogonal polynomials as basis, and the (scaled) L-moments as coefficients.
The corrected version of theorem 3 from Hosking (2007) states that
\\[ \\hat{Q}(q) = \\sum_{r=1}^{R} \\frac{(r + 1) (2r + s + t - 1)}{r + s + t + 1} \\lambda^{(s, t)}_r P^{(t, s)}_{r - 1}(2u - 1) \\; , \\]converges almost everywhere as \\(R \\rightarrow \\infty\\), for any sufficiently smooth (quantile) function \\(Q(u)\\) with \\(0 < u < 1\\).
Referencesl_moments
Vector containing the first \\(R\\) consecutive L-moments \\(\\left[ \\lambda^{(s, t)}_1 \\; \\lambda^{(s, t)}_2 \\; \\dots \\; \\lambda^{(s, t)}_R \\right]\\), where \\(R \\ge 2\\).
Sample L-moments can be estimated using e.g. lmo.l_moment(x, np.mgrid[:R] + 1, trim=(s, t))
.
The trim-lengths \\((s, t)\\) should be the same for all L-moments.
TYPE: FloatVector
trim
The left and right trim-lengths \\((s, t)\\), that correspond to the provided l_moments
.
TYPE: AnyTrim
DEFAULT: (0, 0)
a
Lower bound of the support of the distribution. By default it is estimated from the L-moments.
TYPE: float | None
DEFAULT: None
b
Upper bound of the support of the distribution. By default it is estimated from the L-moments.
TYPE: float | None
DEFAULT: None
**kwargs
Optional params for scipy.stats.rv_continuous
.
TYPE: Any
DEFAULT: {}
ValueError
If len(l_moments) < 2
, l_moments.ndim != 1
, or there are invalid L-moments / trim-lengths.
l_moments: npt.NDArray[np.float64]
property
","text":"Initial L-moments, for orders \\(r = 1, 2, \\dots, R\\).
"},{"location":"api/#lmo.l_rv_nonparametric.trim","title":"trim: tuple[int, int] | tuple[float, float]
property
","text":"The provided trim-lengths \\((s, t)\\).
"},{"location":"api/#lmo.l_rv_nonparametric.ppf_poly","title":"ppf_poly: PolySeries
property
","text":"Polynomial estimate of the percent point function (PPF), a.k.a. the quantile function (QF), or the inverse cumulative distribution function (ICDF).
NoteConverges to the \u201ctrue\u201d PPF in the mean-squared sense, with weight function \\(q^s (1 - q)^t\\) of quantile \\(q \\in \\[0, 1\\]\\), and trim-lengths \\((t_1, t_2) \\in \\mathbb{R^+} \\times \\mathbb{R^+}\\).
RETURNS DESCRIPTIONPolySeries
A numpy.polynomial.Legendre
orthogonal polynomial series instance.
cdf_poly: PolySeries
cached
property
","text":"Polynomial least-squares interpolation of the CDF.
RETURNS DESCRIPTIONPolySeries
A numpy.polynomial.Legendre
orthogonal polynomial series instance.
pdf_poly: PolySeries
cached
property
","text":"Derivative of the polynomial interpolation of the CDF, i.e. the polynomial estimate of the PDF.
RETURNS DESCRIPTIONPolySeries
A numpy.polynomial.Legendre
orthogonal polynomial series instance.
fit(data, /, rmax=None, trim=(0, 0))
classmethod
","text":"Estimate L-moment from the samples, and return a new l_rv_nonparametric
instance.
data
1d array-like with univariate sample observations.
TYPE: npt.ArrayLike
rmax
The (maximum) amount of L-moment orders to use. Defaults to \\(\\lceil 4 \\log_{10} N \\rceil\\). The quantile polynomial will be of degree rmax - 1
.
TYPE: SupportsIndex | None
DEFAULT: None
trim
The left and right trim-lengths \\((s, t)\\), that correspond to the provided l_moments
.
TYPE: AnyTrim
DEFAULT: (0, 0)
l_rv_nonparametric
A fitted l_rv_nonparametric
instance.
rmax
selection (the error appears to be periodic..?)trim
selectionHypothesis tests, estimator properties, and performance metrics.
"},{"location":"api/#lmo.diagnostic.HypothesisTestResult","title":"lmo.diagnostic.HypothesisTestResult
","text":" Bases: NamedTuple
Results of a hypothesis test.
ATTRIBUTE DESCRIPTIONstatistic
The raw test statistic. Its distribution depends on the specific test implementation.
TYPE: float | npt.NDArray[np.float64]
pvalue
Two-sided probability value corresponding to the the null hypothesis, \\(H_0\\).
TYPE: float | npt.NDArray[np.float64]
is_valid: bool | npt.NDArray[np.bool_]
property
","text":"Check if the statistic is finite and not nan
.
is_significant(level=0.05)
","text":"Whether or not the null hypothesis can be rejected, with a certain confidence level (5% by default).
"},{"location":"api/#lmo.diagnostic.normaltest","title":"lmo.diagnostic.normaltest(a, /, *, axis=None)
","text":"Statistical hypothesis test for non-normality, using the L-skewness and L-kurtosis coefficients on the sample data..
Adapted from Harri & Coble (2011), and includes Hosking\u2019s correction.
DefinitionExamples:
Compare the testing power with scipy.stats.normaltest
given 10.000 samples from a contaminated normal distribution.
>>> import numpy as np\n>>> from lmo.diagnostic import normaltest\n>>> from scipy.stats import normaltest as normaltest_scipy\n>>> rng = np.random.default_rng(12345)\n>>> n = 10_000\n>>> x = 0.9 * rng.normal(0, 1, n) + 0.1 * rng.normal(0, 9, n)\n>>> normaltest(x)[1]\n0.04806618...\n>>> normaltest_scipy(x)[1]\n0.08435627...\n
At a 5% significance level, Lmo\u2019s test is significant (i.e. indicating non-normality), whereas scipy\u2019s test isn\u2019t (i.e. inconclusive).
PARAMETER DESCRIPTIONa
Array-like of sample data.
TYPE: npt.ArrayLike
axis
Axis along which to compute the test.
TYPE: int | None
DEFAULT: None
HypothesisTestResult
A named tuple with:
statistic
: The \\(\\tau^2_{3, 4}\\) test statistic.pvalue
: Two-sided chi squared probability for \\(H_0\\).A. Harri & K.H. Coble (2011) - Normality testing: Two new tests using L-moments
"},{"location":"api/#lmo.diagnostic.l_moment_gof","title":"lmo.diagnostic.l_moment_gof(rv_or_cdf, l_moments, n_obs, /, trim=(0, 0), **kwargs)
","text":"Goodness-of-fit (GOF) hypothesis test for the null hypothesis that the observed L-moments come from a distribution with the given scipy.stats
distribution or cumulative distribution function (CDF).
H0
: The theoretical probability distribution, with the given CDF, is a good fit for the observed L-moments.H1
: The distribution is not a good fit for the observed L-moments.The test statistic is the squared Mahalanobis distance between the \\(n\\) observed L-moments, and the theoretical L-moments. It asymptically (in sample size) follows the \\(\\chi^2\\) distribution, with \\(n\\) degrees of freedom.
The sample L-moments are expected to be of consecutive orders \\(r = 1, 2, \\dots, n\\). Generally, the amount of L-moments \\(n\\) should not be less than the amount of parameters of the distribution, including the location and scale parameters. Therefore, it is required to have \\(n \\ge 2\\).
NotesThe theoretical L-moments and their covariance matrix are calculated from the CDF using numerical integration (scipy.integrate.quad
and scipy.integrate.nquad
). Undefined or infinite integrals cannot be detected, in which case the results might be incorrect.
If an IntegrationWarning
is issued, or the function is very slow, then the results are probably incorrect, and larger degrees of trimming should be used.
Examples:
Test if the samples are drawn from a normal distribution.
>>> import lmo\n>>> import numpy as np\n>>> from lmo.diagnostic import l_moment_gof\n>>> from scipy.stats import norm\n>>> rng = np.random.default_rng(12345)\n>>> X = norm(13.12, 1.66)\n>>> n = 1_000\n>>> x = X.rvs(n, random_state=rng)\n>>> x_lm = lmo.l_moment(x, [1, 2, 3, 4])\n>>> l_moment_gof(X, x_lm, n).pvalue\n0.8259...\n
Contaminated samples:
>>> y = 0.9 * x + 0.1 * rng.normal(X.mean(), X.std() * 10, n)\n>>> y_lm = lmo.l_moment(y, [1,2,3,4])\n>>> y_lm.round(3)\narray([1.3193e+01, 1.2860e+00, 6.0000e-03, 1.6800e-01])\n>>> l_moment_gof(X, y_lm, n).pvalue\n1.2668...e-60\n
See Also l_moment_from_cdf
lmo.diagnostic.l_stats_gof(rv_or_cdf, l_stats, n_obs, /, trim=(0, 0), **kwargs)
","text":"Analogous to lmo.diagnostic.l_moment_gof
, but using the L-stats (see lmo.l_stats
).
lmo.diagnostic.l_moment_bounds(r, /, trim=(0, 0), scale=1.0)
","text":"Returns the absolute upper bounds \\(L^{(s,t)}_r\\) on L-moments \\(\\lambda^{(s,t)}_r\\), proportional to the scale \\(\\sigma_X\\) (standard deviation) of the probability distribution of random variable \\(X\\). So \\(\\left| \\lambda^{(s,t)}_r(X) \\right| \\le \\sigma_X \\, L^{(s,t)}_r\\), given that standard deviation \\(\\sigma_X\\) of \\(X\\) exists and is finite.
WarningThese bounds do not apply to distributions with undefined variance, e.g. the Cauchy distribution, even if trimmed L-moments are used. Distributions with infinite variance (e.g. Student\u2019s t with \\(\\nu=2\\)) are a grey area:
For the L-scale (\\(r=2\\)), the corresponding bound will not be a valid one. However, it can still be used to find the L-ratio bounds, because the \\(\\sigma_X\\) terms will cancel out. Doing this is not for the faint of heart, as it requires dividing infinity by infinity. So be sure to wear safety glasses.
The bounds are derived by applying the Cauchy-Schwarz inequality to the covariance-based definition of generalized trimmed L-moment, for \\(r > 1\\):
\\[ \\lambda^{(s,t)}_r(X) = \\frac{r+s+t}{r} \\frac{B(r,\\, r+s+t)}{B(r+s,\\, r+t)} \\mathrm{Cov}\\left[ X,\\; F(X)^s \\big(1 - F(X)\\big)^t P^{(\\alpha, \\beta)}_r(X) \\right] \\;, \\]where \\(B\\) is the Beta function, \\(P^{(\\alpha, \\beta)}_r\\) the Jacobi polynomial, and \\(F\\) the cumulative distribution function of random variable \\(X\\).
After a lot of work, one can (and one did) derive the closed-form inequality:
\\[ \\left| \\lambda^{(s,t)}_r(X) \\right| \\le \\frac{\\sigma_X}{\\sqrt{2 \\pi}} \\frac{\\Gamma(r+s+t+1)}{r} \\sqrt{\\frac{ B(r-\\frac{1}{2}, s+\\frac{1}{2}, t+\\frac{1}{2}) }{ \\Gamma(s+t+1) \\Gamma(r+s) \\Gamma(r+t) }} \\]for \\(r \\in \\mathbb{N}_{\\ge 2}\\) and \\(s, t \\in \\mathbb{R}_{\\ge 0}\\), where \\(\\Gamma\\) is the Gamma function, and \\(B\\) the multivariate Beta function
For the untrimmed L-moments, this simplifies to
\\[ \\left| \\lambda_r(X) \\right| \\le \\frac{\\sigma_X}{\\sqrt{2 r - 1}} \\,. \\] NotesFor \\(r=1\\) there are no bounds, i.e. float('inf')
is returned.
There are no references; this novel finding is not (yet..?) published by the author, @jorenham.
PARAMETER DESCRIPTIONr
The L-moment order(s), non-negative integer or array-like of integers.
TYPE: IntVector | AnyInt
trim
Left- and right-trim orders \\((s, t)\\), as a tuple of non-negative ints or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
scale
The standard deviation \\(\\sigma_X\\) of the random variable \\(X\\). Defaults to 1.
TYPE: float
DEFAULT: 1.0
out
float array or scalar like r
.
TYPE: float | npt.NDArray[np.float64]
l_ratio_bounds
lmo.l_moment
lmo.diagnostic.l_ratio_bounds(r, /, trim=(0, 0), *, has_variance=True)
","text":"Returns the absolute upper bounds \\(T^{(s,t)}_r\\) on L-moment ratio\u2019s \\(\\tau^{(s,t)}_r = \\lambda^{(s,t)}_r / \\lambda^{(s,t)}_r\\), for \\(r \\ge 2\\). So \\(\\left| \\tau^{(s,t)}_r(X) \\right| \\le T^{(s,t)}_r\\), given that \\(\\mathrm{Var}[X] = \\sigma^2_X\\) exists.
If the variance of the distribution is not defined, e.g. in case of the Cauchy distribution, this method will not work. In this case, the looser bounds from Hosking (2007) can be used instead, by passing has_variance=False
.
Examples:
Calculate the bounds for different degrees of trimming:
>>> l_ratio_bounds([1, 2, 3, 4])\narray([ inf, 1. , 0.77459667, 0.65465367])\n>>> # the bounds for r=1,2 are the same for all trimmings; skip them\n>>> l_ratio_bounds([3, 4], trim=(1, 1))\narray([0.61475926, 0.4546206 ])\n>>> l_ratio_bounds([3, 4], trim=(.5, 1.5))\narray([0.65060005, 0.49736473])\n
In case of undefined variance, the bounds become a lot looser:
>>> l_ratio_bounds([3, 4], has_variance=False)\narray([1., 1.])\n>>> l_ratio_bounds([3, 4], trim=(1, 1), has_variance=False)\narray([1.11111111, 1.25 ])\n>>> l_ratio_bounds([3, 4], trim=(.5, 1.5), has_variance=False)\narray([1.33333333, 1.71428571])\n
PARAMETER DESCRIPTION r
Order of the L-moment ratio(s), as positive integer scalar or array-like.
TYPE: IntVector | AnyInt
trim
Tuple of left- and right- trim-lengths, matching those of the relevant L-moment ratio\u2019s.
TYPE: AnyTrim
DEFAULT: (0, 0)
has_variance
Set to False if the distribution has undefined variance, in which case the (looser) bounds from J.R.M. Hosking (2007) will be used.
TYPE: bool
DEFAULT: True
float | npt.NDArray[np.float64]
Array or scalar with shape like \\(r\\).
See Alsol_ratio
l_ratio_se
lmo.diagnostic.rejection_point(influence_fn, /, rho_min=0, rho_max=np.inf)
","text":"Evaluate the approximate rejection point of an influence function \\(\\psi_{T|F}(x)\\) given a statistical functional \\(T\\) (e.g. an L-moment) and cumulative distribution function \\(F(x)\\).
\\[ \\rho^*_{T|F} = \\inf_{r>0} \\left\\{ r: | \\psi_{T|F}(x) | \\le \\epsilon, \\, |x| > r \\right\\} \\; \\]with a \\(\\epsilon\\) a small positive number, corresponding to the tol
param of e.g. l_moment_influence , which defaults to 1e-8
.
Examples:
The untrimmed L-location isn\u2019t robust, e.g. with the standard normal distribution:
>>> import numpy as np\n>>> from scipy.stats import distributions as dists\n>>> from lmo.diagnostic import rejection_point\n>>> if_l_loc_norm = dists.norm.l_moment_influence(1, trim=0)\n>>> if_l_loc_norm(np.inf)\ninf\n>>> rejection_point(if_l_loc_norm)\nnan\n
For the TL-location of the Gaussian distribution, and even for the Student\u2019s t distribution with 4 degrees of freedom (3 also works, but is very slow), they exist.
>>> if_tl_loc_norm = dists.norm.l_moment_influence(1, trim=1)\n>>> if_tl_loc_t4 = dists.t(4).l_moment_influence(1, trim=1)\n>>> if_tl_loc_norm(np.inf), if_tl_loc_t4(np.inf)\n(0.0, 0.0)\n>>> rejection_point(if_tl_loc_norm), rejection_point(if_tl_loc_t4)\n(6.0, 206.0)\n
Notes Large rejection points (e.g. >1000) are unlikely to be found.
For instance, that of the TL-location of the Student\u2019s t distribution with 2 degrees of freedom lies between somewhere 1e4
and 1e5
, but will not be found. In this case, using trim=2
will return 166.0
.
influence_fn
Univariate influence function.
TYPE: Callable[[float], float]
rho_min
The minimum \\(\\rho^*_{T|F}\\) of the search space. Must be finite and non-negative. Defaults to \\(0\\).
TYPE: float
DEFAULT: 0
rho_max
The maximum \\(\\rho^*_{T|F}\\) of the search space. Must be larger than rho_min
. Defaults to \\(\\infty\\).
TYPE: float
DEFAULT: np.inf
float
A finite or infinite scalar.
See Alsolmo.contrib.scipy_stats.l_rv_generic.l_moment_influence
error_sensitivity
lmo.diagnostic.error_sensitivity(influence_fn, /, domain=(float('-inf'), float('inf')))
","text":"Evaluate the gross-error sensitivity of an influence function \\(\\psi_{T|F}(x)\\) given a statistical functional \\(T\\) (e.g. an L-moment) and cumulative distribution function \\(F(x)\\).
\\[ \\gamma^*_{T|F} = \\max_{x} \\left| \\psi_{T|F}(x) \\right| \\]Examples:
Evaluate the gross-error sensitivity of the standard exponential distribution\u2019s LL-skewness (\\(\\tau^{(0, 1)}_3\\)) and LL-kurtosis (\\(\\tau^{(0, 1)}_4\\)) coefficients:
>>> from lmo.diagnostic import error_sensitivity\n>>> from scipy.stats import expon\n>>> ll_skew_if = expon.l_ratio_influence(3, 2, trim=(0, 1))\n>>> ll_kurt_if = expon.l_ratio_influence(4, 2, trim=(0, 1))\n>>> error_sensitivity(ll_skew_if, domain=(0, float('inf')))\n1.814657...\n>>> error_sensitivity(ll_kurt_if, domain=(0, float('inf')))\n1.377743...\n
PARAMETER DESCRIPTION influence_fn
Univariate influence function.
TYPE: Callable[[float], float]
domain
Domain of the CDF. Defaults to \\((-\\infty, \\infty)\\).
TYPE: tuple[float, float]
DEFAULT: (float('-inf'), float('inf'))
float
Gross-error sensitivity \\(\\gamma^*_{T|F}\\) .
See Alsolmo.contrib.scipy_stats.l_rv_generic.l_moment_influence
rejection_point
lmo.diagnostic.shift_sensitivity(influence_fn, /, domain=(float('-inf'), float('inf')))
","text":"Evaluate the local-shift sensitivity of an influence function \\(\\psi_{T|F}(x)\\) given a statistical functional \\(T\\) (e.g. an L-moment) and cumulative distribution function \\(F(x)\\).
\\[ \\lambda^*_{T|F} = \\max_{x \\neq y} \\left| \\frac{ \\psi_{T|F}(y) - \\psi_{T|F}(x) }{ y - x } \\right| \\]Represents the effect of shifting an observation slightly from \\(x\\), to a neighbouring point \\(y\\). For instance, adding an observation at \\(y\\) and removing one at \\(x\\).
Examples:
Evaluate the local-shift sensitivity of the standard exponential distribution\u2019s LL-skewness (\\(\\tau^{(0, 1)}_3\\)) and LL-kurtosis (\\(\\tau^{(0, 1)}_4\\)) coefficients:
>>> from lmo.diagnostic import shift_sensitivity\n>>> from scipy.stats import expon\n>>> ll_skew_if = expon.l_ratio_influence(3, 2, trim=(0, 1))\n>>> ll_kurt_if = expon.l_ratio_influence(4, 2, trim=(0, 1))\n>>> domain = 0, float('inf')\n>>> shift_sensitivity(ll_skew_if, domain)\n0.837735...\n>>> shift_sensitivity(ll_kurt_if, domain)\n1.442062...\n
Let\u2019s compare these with the untrimmed ones:
>>> shift_sensitivity(expon.l_ratio_influence(3, 2), domain)\n1.920317...\n>>> shift_sensitivity(expon.l_ratio_influence(4, 2), domain)\n1.047565...\n
PARAMETER DESCRIPTION influence_fn
Univariate influence function.
TYPE: Callable[[float], float]
domain
Domain of the CDF. Defaults to \\((-\\infty, \\infty)\\).
TYPE: tuple[float, float]
DEFAULT: (float('-inf'), float('inf'))
float
Local-shift sensitivity \\(\\lambda^*_{T|F}\\) .
See Alsolmo.contrib.scipy_stats.l_rv_generic.l_moment_influence
error_sensitivity
Lmo: Robust statistics with trimmed L-moments and L-comoments.
"},{"location":"api/#lmo.l_weights","title":"lmo.l_weights(r, n, /, trim=(0, 0), dtype=np.float64, *, cache=False)
","text":"Projection matrix of the first \\(r\\) (T)L-moments for \\(n\\) samples.
For integer trim is the matrix is a linear combination of the Power Weighted Moment (PWM) weights (the sample estimator of \\(\\beta_{r_1}\\)), and the shifted Legendre polynomials.
If the trimmings are nonzero and integers, a linearized (and corrected) adaptation of the recurrence relations from Hosking (2007) are applied, as well.
\\[ (2k + s + t - 1) \\lambda^{(s, t)}_k = (k + s + t) \\lambda^{(s - 1, t)}_k + \\frac{1}{k} (k + 1) (k + t) \\lambda^{(s - 1, t)}_{k+1} \\]for \\(s > 0\\), and
\\[ (2k + s + t - 1) \\lambda^{(s, t)}_k = (k + s + t) \\lambda^{(s, t - 1)}_k - \\frac{1}{k} (k + 1) (k + s) \\lambda^{(s, t - 1)}_{k+1} \\]for \\(t > 0\\).
If the trim values are floats instead, the weights are calculated directly from the (generalized) order statistics. At the time of writing (07-2023), these \u201cgeneralized trimmed L-moments\u201d have not been discussed in the literature or the R-packages. It\u2019s probably a good idea to publish this\u2026
TLDRThis matrix (linearly) transforms \\(x_{i:n}\\) (i.e. the sorted observation vector(s) of size \\(n\\)), into (an unbiased estimate of) the generalized trimmed L-moments, with orders \\(\\le r\\).
RETURNS DESCRIPTIONP_r
2-D array of shape (r, n)
.
TYPE: npt.NDArray[T]
Examples:
>>> import lmo\n>>> lmo.l_weights(3, 4)\narray([[ 0.25 , 0.25 , 0.25 , 0.25 ],\n [-0.25 , -0.08333333, 0.08333333, 0.25 ],\n [ 0.25 , -0.25 , -0.25 , 0.25 ]])\n>>> _ @ [-1, 0, 1 / 2, 3 / 2]\narray([0.25 , 0.66666667, 0. ])\n
References inference
","text":"Parametric inference.
"},{"location":"api/#lmo.inference.GMMResult","title":"lmo.inference.GMMResult
","text":" Bases: NamedTuple
Represents the Generalized Method of L-Moments (L-GMM) results. See lmo.inference.fit
for details.
args
The estimated distribution arguments, as (*shapes, loc, scale)
.
TYPE: tuple[float | int, ...]
success
Whether or not the optimizer exited successfully.
TYPE: bool
eps
Final relative difference in the (natural) L-moment conditions.
TYPE: npt.NDArray[np.float64]
statistic
The minimized objective value, corresponding to the weights
.
TYPE: float
n_samp
Amount of samples used to calculate the sample L-moment (after trimming).
TYPE: int
n_step
Number of GMM steps (the amount of times the weight matrix has been estimated).
TYPE: int
n_iter
Number of evaluations of the objective function (the theoretical L-moments).
TYPE: int
weights
The final weight (precision, inverse covariance) matrix.
TYPE: npt.NDArray[np.float64]
n_arg: int
property
","text":"The number of model parameters.
"},{"location":"api/#lmo.inference.GMMResult.n_con","title":"n_con: int
property
","text":"The amount of L-moment conditions of the model.
"},{"location":"api/#lmo.inference.GMMResult.n_extra","title":"n_extra: int
property
","text":"The number of over-identifying L-moment conditions. For L-MM this is zero, otherwise, for L-GMM, it is strictly positive.
"},{"location":"api/#lmo.inference.GMMResult.j_test","title":"j_test: HypothesisTestResult
property
","text":"Sargan-Hansen J-test for over-identifying restrictions; a hypothesis test for the invalidity of the model.
The test is defined through two hypotheses:
AIC: float
property
","text":"Akaike Information Criterion, based on the p-value of the J-test. Requires over-identified L-moment conditions, i.e. n_extra > 0
.
The AIC is useful for model selection, e.g. for finding the most appropriate probability distribution from the data (smaller is better).
ReferencesAICc: float
property
","text":"A modification of the AIC that includes a bias-correction small sample sizes.
Referenceslmo.inference.fit(ppf, args0, n_obs, l_moments, r=None, trim=(0, 0), *, k=None, k_max=50, l_tol=0.0001, l_moment_fn=None, n_mc_samples=9999, random_state=None, **kwds)
","text":"Fit the distribution parameters using the (Generalized) Method of L-Moments (L-(G)MM).
The goal is to find the \u201ctrue\u201d parameters \\(\\theta_0\\) of the distribution. In practise, this is done using a reasonably close estimate, \\(\\theta\\).
In the (non-Generalized) Method of L-moments (L-MM), this is done by solving the system of equations \\(l^{(s, t)}_r = \\lambda^{(s, t)}_r\\), for \\(r = 1, \\dots, k\\), with \\(n = |\\theta|\\) the number of parameters. Because the amount of parameters matches the amount of L-moment conditions, the solution is point-defined, and can be found using simple least squares.
L-GMM extends L-MM by allowing more L-moment conditions than there are free parameters, \\(m > n\\). This requires solving an over-identified system of \\(m\\) equations:
\\[ \\hat{\\theta} = \\mathop{\\arg \\min} \\limits_{\\theta \\in \\Theta} (\\vec{\\lambda}^{(s, t)}_r - \\vec{l}^{(s, t)})^T W_m (\\vec{\\lambda}^{(s, t)}_r - \\vec{l}^{(s, t)}) \\, , \\]where \\(W_m\\) is a \\(m \\times m\\) weight matrix.
The weight matrix is initially chosen as the matrix inverse of the non-parametric L-moment covariance matrix, see lmo.l_moment_cov
. These weights are then plugged into the the equation above, and fed into scipy.optimize.minimize
, to obtain the initial parameter estimates.
In the next step(s), Monte-Carlo sampling is used to draw samples from the distribution (using the current parameter estimates), with sample sizes matching that of the data. The L-moments of these samples are consequently used to to calculate the new weight matrix.
Todointegrality
kwarg with boolean mask for integral params._loss_cue()
, then run with k=1
).ppf
The (vectorized) quantile function of the probability distribution, with signature (*args: float, q: T) -> T
.
TYPE: DistributionFunction[...]
args0
Initial estimate of the distribution\u2019s parameter values.
TYPE: npt.ArrayLike
n_obs
Amount of observations.
TYPE: int
l_moments
Estimated sample L-moments. Must be a 1-d array-like s.t. len(l_moments) >= len(args0)
.
TYPE: npt.ArrayLike
r
The orders of l_moments
. Defaults to [1, ..., len(l_moments)]
.
TYPE: IntVector | None
DEFAULT: None
trim
The L-moment trim-length(s) to use. Currently, only integral trimming is supported.
TYPE: AnyTrim
DEFAULT: (0, 0)
k
If set to a positive integer, exactly \\(k\\) steps will be run. Will be ignored if n_extra=0
.
TYPE: int | None
k_max
Maximum amount of steps to run while not reaching convergence. Will be ignored if \\(k\\) is specified or if n_extra=0
.
TYPE: int
l_tol
Error tolerance in the parametric L-moments (unit-standardized). Will be ignored if \\(k\\) is specified or if n_extra=0
.
TYPE: float
l_moment_fn
Function for parametric L-moment calculation, with signature: (r: int64[], *args, trim: float[2] | int[2]) -> float64[]
.
TYPE: Callable[..., npt.NDArray[np.float64]] | None
n_mc_samples
The number of Monte-Carlo (MC) samples drawn from the distribution to to form the weight matrix in step \\(k > 1\\). Will be ignored if n_extra=0
.
TYPE: int
random_state
A seed value or numpy.random.Generator
instance, used for weight matrix estimation in step \\(k > 1\\). Will be ignored if n_extra=0
.
TYPE: int | np.random.Generator | np.random.RandomState | None
**kwds
Additional keyword arguments to be passed to scipy.optimize.minimize
.
TYPE: Any
ValueError
Invalid arguments.
RETURNS DESCRIPTIONresult
An instance of [GMMResult
][lmo.inference.GMMResult
].
TYPE: GMMResult
theoretical
","text":"Theoretical (population) L-moments of known univariate probability distributions.
"},{"location":"api/#lmo.theoretical.l_moment_from_cdf","title":"lmo.theoretical.l_moment_from_cdf(cdf, r, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Evaluate the population L-moment of a continuous probability distribution, using its Cumulative Distribution Function (CDF) \\(F_X(x) = P(X \\le x)\\).
\\[ \\lambda^{(s, t)}_r = \\begin{cases} 1 & r = 0 \\\\ \\int_{-\\infty}^{\\infty} \\left(H(x) - I_{F(x)}(s+1, \\,t+1)\\right) \\,\\mathrm{d} x & r = 1 \\\\ \\frac{c^{(s,t)}_r}{r} \\int_{-\\infty}^{\\infty} F(x)^{s+1} \\left(1 - F(x)\\right)^{t+1} \\,\\tilde{P}^{(t+1, s+1)}_{r-2}\\big(F(x)\\big) \\,\\mathrm{d} x & r > 1 \\;, \\end{cases} \\]where
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\;, \\]\\(\\tilde{P}^{(a,b)}_n(x)\\) the shifted (\\(x \\mapsto 2x-1\\)) Jacobi polynomial, \\(H(x)\\) the Heaviside step function, and \\(I_x(\\alpha, \\beta)\\) the regularized incomplete gamma function.
NotesNumerical integration is performed with scipy.integrate.quad
, which cannot verify whether the integral exists and is finite. If it returns an error message, an IntegrationWarning
is issues, and nan
is returned (even if quad
returned a finite result).
Examples:
Evaluate the first 4 L- and TL-moments of the standard normal distribution:
>>> from scipy.special import ndtr # standard normal CDF\n>>> l_moment_from_cdf(ndtr, [1, 2, 3, 4])\narray([0. , 0.56418958, 0. , 0.06917061])\n>>> l_moment_from_cdf(ndtr, [1, 2, 3, 4], trim=1)\narray([0. , 0.29701138, 0. , 0.01855727])\n
Evaluate the first 4 TL-moments of the standard Cauchy distribution:
>>> def cdf_cauchy(x: float) -> float:\n... return np.arctan(x) / np.pi + 1 / 2\n>>> l_moment_from_cdf(cdf_cauchy, [1, 2, 3, 4], trim=1)\narray([0. , 0.69782723, 0. , 0.23922105])\n
PARAMETER DESCRIPTION cdf
Cumulative Distribution Function (CDF), \\(F_X(x) = P(X \\le x)\\). Must be a continuous monotone increasing function with signature (float) -> float
, whose return value lies in \\([0, 1]\\).
TYPE: UnivariateCDF
r
L-moment order(s), non-negative integer or array-like of integers.
TYPE: AnyInt | IntVector
trim
Left- and right- trim, either as a \\((s, t)\\) tuple with \\(s, t > -1/2\\), or \\(t\\) as alias for \\((t, t)\\).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
. Generally it\u2019s not needed to provide this, as it will be guessed automatically.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Split the integral into integrals with limits \\([a, F^{-1}(\\alpha)]\\), \\([F(\\alpha), F^{-1}(1 - \\alpha)]\\) and \\([F^{-1}(1 - \\alpha), b]\\) to improve numerical stability. So \\(\\alpha\\) can be consideresd the size of the tail. Numerical experiments have found 0.05 to give good results for different distributions.
TYPE: float
ppf
The inverse of the cdf, used with alpha
to calculate the integral split points (if provided).
TYPE: UnivariatePPF | None
TypeError
r
is not integer-valued or negative
ValueError
r
is negative
lmbda
The population L-moment(s), a scalar or float array like r
. If nan
, consult the related IntegrationWarning
message.
TYPE: np.float64 | npt.NDArray[np.float64]
theoretical.l_moment_from_ppf
: population L-moment, using the inverse CDFl_moment
: sample L-momentlmo.theoretical.l_moment_from_ppf(ppf, r, /, trim=(0, 0), *, support=(0, 1), quad_opts=None, alpha=ALPHA)
","text":"Evaluate the population L-moment of a univariate probability distribution, using its Percentile Function (PPF) \\(x(F)\\), also commonly known as the quantile function, which is the inverse of the Cumulative Distribution Function (CDF).
\\[ \\lambda^{(s, t)}_r = c^{(s,t)}_r \\int_0^1 F^s (1 - F)^t \\,\\tilde{P}^{(t, s)}_{r-1}(F) \\,x(F) \\,\\mathrm{d} F \\;, \\]where
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\;, \\]and \\(\\tilde{P}^{(a,b)}_n(x)\\) the shifted (\\(x \\mapsto 2x-1\\)) Jacobi polynomial.
NotesNumerical integration is performed with scipy.integrate.quad
, which cannot verify whether the integral exists and is finite. If it returns an error message, an IntegrationWarning
is issues, and nan
is returned (even if quad
returned a finite result).
Examples:
Evaluate the first 4 L- and TL-moments of the standard normal distribution:
>>> from scipy.special import ndtri # standard normal inverse CDF\n>>> l_moment_from_ppf(ndtri, [1, 2, 3, 4])\narray([0. , 0.56418958, 0. , 0.06917061])\n>>> l_moment_from_ppf(ndtri, [1, 2, 3, 4], trim=1)\narray([0. , 0.29701138, 0. , 0.01855727])\n
PARAMETER DESCRIPTION ppf
The quantile function \\(x(F)\\), a monotonically continuous increasing function with signature (float) -> float
, that maps a probability in \\([0, 1]\\), to the domain of the distribution.
TYPE: UnivariatePPF
r
L-moment order(s), non-negative integer or array-like of integers. E.g. 0 gives 1, 1 the L-location, 2 the L-scale, etc.
TYPE: AnyInt | IntVector
trim
Left- and right- trim, either as a \\((s, t)\\) tuple with \\(s, t > -1/2\\), or \\(t\\) as alias for \\((t, t)\\).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
Integration limits. Defaults to (0, 1), as it should. There is no need to change this to anything else, and only exists to make the function signature consistent with the *_from_cdf
analogue.
TYPE: Pair[float]
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Split the integral into integrals with limits \\([0, \\alpha]\\), \\([\\alpha, 1-\\alpha]\\) and \\([1-\\alpha, 0]\\) to improve numerical stability. So \\(\\alpha\\) can be consideresd the size of the tail. Numerical experiments have found 0.1 to give good results for different distributions.
TYPE: float
TypeError
Invalid r
or trim
types.
ValueError
Invalid r
or trim
values.
lmbda
The population L-moment(s), a scalar or float array like r
. If nan
, consult the related IntegrationWarning
message.
TYPE: np.float64 | npt.NDArray[np.float64]
theoretical.l_moment_from_cdf
: population L-moment, using the CDF (i.e. the inverse PPF)l_moment
: sample L-momentlmo.theoretical.l_ratio_from_cdf(cdf, r, s, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Population L-ratio\u2019s from a CDF.
See Alsol_ratio_from_ppf
lmo.l_ratio
lmo.theoretical.l_ratio_from_ppf(ppf, r, s, /, trim=(0, 0), *, support=(0, 1), quad_opts=None, alpha=ALPHA)
","text":"Population L-ratio\u2019s from a PPF.
See Alsol_ratio_from_cdf
lmo.l_ratio
lmo.theoretical.l_stats_from_cdf(cdf, num=4, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Calculates the theoretical- / population- L-moments (for \\(r \\le 2\\)) and L-ratio\u2019s (for \\(r > 2\\)) of a distribution, from its CDF.
By default, the first num = 4
population L-stats are calculated:
This function is equivalent to l_ratio_from_cdf(cdf, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
.
This should not be confused with the term L-statistic, which is sometimes used to describe any linear combination of order statistics.
See Alsol_stats_from_ppf
- Population L-stats from the quantile function.l_ratio_from_cdf
- Generalized population L-ratio\u2019s from the CDF.lmo.l_stats
- Unbiased sample estimation of L-stats.lmo.theoretical.l_stats_from_ppf(ppf, num=4, /, trim=(0, 0), *, support=(0, 1), quad_opts=None, alpha=ALPHA)
","text":"Calculates the theoretical- / population- L-moments (for \\(r \\le 2\\)) and L-ratio\u2019s (for \\(r > 2\\)) of a distribution, from its quantile function.
By default, the first num = 4
population L-stats are calculated:
This function is equivalent to l_ratio_from_cdf(cdf, [1, 2, 3, 4], [0, 0, 2, 2], *, **)
.
This should not be confused with the term L-statistic, which is sometimes used to describe any linear combination of order statistics.
See Alsol_stats_from_cdf
- Population L-stats from the CDF.l_ratio_from_ppf
- Generalized population L-ratio\u2019s from the quantile function.lmo.l_stats
- Unbiased sample estimation of L-stats.lmo.theoretical.l_moment_cov_from_cdf(cdf, r_max, /, trim=(0, 0), *, support=None, quad_opts=None)
","text":"L-moments that are estimated from \\(n\\) samples of a distribution with CDF \\(F\\), converge to the multivariate normal distribution as the sample size \\(n \\rightarrow \\infty\\).
\\[ \\sqrt{n} \\left( \\vec{l}^{(s, t)} - \\vec{\\lambda}^{(s, t)} \\right) \\sim \\mathcal{N}( \\vec{0}, \\mathbf{\\Lambda}^{(s, t)} ) \\]Here, \\(\\vec{l}^{(s, t)} = \\left[l^{(s, t)}_r, \\dots, l^{(s, t)}_{r_{max}} \\right]^T\\) is a vector of estimated sample L-moments, and \\(\\vec{\\lambda}^{(s, t)}\\) its theoretical (\u201ctrue\u201d) counterpart.
This function calculates the covariance matrix
\\[ \\begin{align} \\bf{\\Lambda}^{(s,t)}_{k, r} &= \\mathrm{Cov}[l^{(s, t)}_k, l^{(s, t)}_r] \\\\ &= c_k c_r \\iint\\limits_{x < y} \\Big[ p_k\\big(F(x)\\big) \\, p_r\\big(F(y)\\big) + p_r\\big(F(x)\\big) \\, p_k\\big(F(y)\\big) \\Big] w^{(s+1,\\, t)}\\big(F(x)\\big) \\, w^{(s,\\, t+1)}\\big(F(y)\\big) \\, \\mathrm{d}x \\, \\mathrm{d}y \\;, \\end{align} \\]where
\\[ c_n = \\frac{\\Gamma(n) \\Gamma(n+s+t+1)}{n \\Gamma(n+s) \\Gamma(n+t)}\\;, \\]the shifted Jacobi polynomial \\(p_n(u) = P^{(t, s)}_{n-1}(2u - 1)\\), \\(P^{(t, s)}_m\\), and \\(w^{(s,t)}(u) = u^s (1-u)^t\\) its weight function.
NotesThis function uses scipy.integrate.nquad
for numerical integration. Unexpected results may be returned if the integral does not exist, or does not converge. The results are rounded to match the order of magnitude of the absolute error of scipy.integrate.nquad
.
This function is not vectorized or parallelized.
For small sample sizes (\\(n < 100\\)), the covariances of the higher-order L-moments (\\(r > 2\\)) can be biased. But this bias quickly disappears at roughly \\(n > 200\\) (depending on the trim- and L-moment orders).
PARAMETER DESCRIPTIONcdf
Cumulative Distribution Function (CDF), \\(F_X(x) = P(X \\le x)\\). Must be a continuous monotone increasing function with signature (float) -> float
, whose return value lies in \\([0, 1]\\).
TYPE: UnivariateCDF
r_max
The amount of L-moment orders to consider. If for example r_max = 4
, the covariance matrix will be of shape (4, 4)
, and the columns and rows correspond to the L-moments of order \\(r = 1, \\dots, r_{max}\\).
TYPE: int
trim
Left- and right- trim. Must be a tuple of two non-negative ints or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
. Generally it\u2019s not needed to provide this, as it will be guessed automatically.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
cov
Covariance matrix, with shape (r_max, r_max)
.
TYPE: npt.NDArray[np.float64]
RuntimeError
If the covariance matrix is invalid.
See Alsol_moment_from_cdf
- Population L-moments from the cumulative distribution functionl_moment_from_ppf
- Population L-moments from the quantile functionlmo.l_moment
- Unbiased L-moment estimation from sampleslmo.l_moment_cov
- Distribution-free exact L-moment exact covariance estimate.lmo.theoretical.l_stats_cov_from_cdf(cdf, num=4, /, trim=(0, 0), *, support=None, quad_opts=None, alpha=ALPHA, ppf=None)
","text":"Similar to l_moment_from_cdf
, but for the lmo.l_stats
.
As the sample size \\(n \\rightarrow \\infty\\), the L-moment ratio\u2019s are also distributed (multivariate) normally. The L-stats are defined to be L-moments for \\(r\\le 2\\), and L-ratio coefficients otherwise.
The corresponding covariance matrix has been found to be
\\[ \\bf{T}^{(s, t)}_{k, r} = \\begin{cases} \\bf{\\Lambda}^{(s, t)}_{k, r} & k \\le 2 \\wedge r \\le 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} }{ \\lambda^{(s,t)}_{2} } & k \\le 2 \\wedge r > 2 \\\\ \\frac{ \\bf{\\Lambda}^{(s, t)}_{k, r} - \\tau_k \\bf{\\Lambda}^{(s, t)}_{2, r} - \\tau_r \\bf{\\Lambda}^{(s, t)}_{k, 2} + \\tau_k \\tau_r \\bf{\\Lambda}^{(s, t)}_{2, 2} }{ \\Big( \\lambda^{(s,t)}_{2} \\Big)^2 } & k > 2 \\wedge r > 2 \\end{cases} \\]where \\(\\bf{\\Lambda}^{(s, t)}\\) is the covariance matrix of the L-moments from l_moment_cov_from_cdf
, and \\(\\tau^{(s,t)}_r = \\lambda^{(s,t)}_r / \\lambda^{(s,t)}_2\\) the population L-ratio.
cdf
Cumulative Distribution Function (CDF), \\(F_X(x) = P(X \\le x)\\). Must be a continuous monotone increasing function with signature (float) -> float
, whose return value lies in \\([0, 1]\\).
TYPE: UnivariateCDF
num
The amount of L-statistics to return. Defaults to 4.
TYPE: int
DEFAULT: 4
trim
Left- and right- trim. Must be a tuple of two non-negative ints or floats.
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
. Generally it\u2019s not needed to provide this, as it will be guessed automatically.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Two-sided quantile to split the integral at.
TYPE: float
ppf
Quantile function, for calculating the split integral limits.
TYPE: UnivariatePPF | None
lmo.theoretical.l_moment_influence_from_cdf(cdf, r, /, trim=(0, 0), *, support=None, l_moment=None, quad_opts=None, alpha=ALPHA, tol=1e-08)
","text":"Influence Function (IF) of a theoretical L-moment.
\\[ \\psi_{\\lambda^{(s, t)}_r | F}(x) = c^{(s,t)}_r \\, F(x)^s \\, \\big( 1-{F}(x) \\big)^t \\, \\tilde{P}^{(s,t)}_{r-1} \\big( F(x) \\big) \\, x - \\lambda^{(s,t)}_r \\;, \\]with \\(F\\) the CDF, \\(\\tilde{P}^{(s,t)}_{r-1}\\) the shifted Jacobi polynomial, and
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r, \\, r+s+t)}{B(r+s, \\, r+t)} \\;, \\]where \\(B\\) is the (complete) Beta function.
The proof is trivial, because population L-moments are linear functionals.
NotesThe order parameter r
is not vectorized.
cdf
Vectorized cumulative distribution function (CDF).
TYPE: Callable[[npt.NDArray[np.float64]], npt.NDArray[np.float64]]
r
The L-moment order. Must be a non-negative integer.
TYPE: AnyInt
trim
Left- and right- trim lengths. Defaults to (0, 0).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
l_moment
The relevant L-moment to use. If not provided, it is calculated from the CDF.
TYPE: float | np.float64 | None
alpha
Two-sided quantile to split the integral at.
TYPE: float
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The influence function, with vectorized signature () -> ()
.
TYPE: Callable[[V], V]
l_moment_from_cdf
lmo.l_moment
lmo.theoretical.l_ratio_influence_from_cdf(cdf, r, k=2, /, trim=(0, 0), *, support=None, l_moments=None, quad_opts=None, alpha=ALPHA, tol=1e-08)
","text":"Construct the influence function of a theoretical L-moment ratio.
\\[ \\psi_{\\tau^{(s, t)}_{r,k}|F}(x) = \\frac{ \\psi_{\\lambda^{(s, t)}_r|F}(x) - \\tau^{(s, t)}_{r,k} \\, \\psi_{\\lambda^{(s, t)}_k|F}(x) }{ \\lambda^{(s,t)}_k } \\;, \\]where the generalized L-moment ratio is defined as
\\[ \\tau^{(s, t)}_{r,k} = \\frac{ \\lambda^{(s, t)}_r }{ \\lambda^{(s, t)}_k } \\;. \\]Because IF\u2019s are a special case of the general G\u00e2teuax derivative, the L-ratio IF is derived by applying the chain rule to the L-moment IF.
PARAMETER DESCRIPTIONcdf
Vectorized cumulative distribution function (CDF).
TYPE: Callable[[npt.NDArray[np.float64]], npt.NDArray[np.float64]]
r
L-moment ratio order, i.e. the order of the numerator L-moment.
TYPE: AnyInt
k
Denominator L-moment order, defaults to 2.
TYPE: AnyInt
DEFAULT: 2
trim
Left- and right- trim lengths. Defaults to (0, 0).
TYPE: AnyTrim
DEFAULT: (0, 0)
support
The subinterval of the nonzero domain of cdf
.
TYPE: Pair[float] | None
l_moments
The L-moments corresponding to \\(r\\) and \\(k\\). If not provided, they are calculated from the CDF.
TYPE: Pair[float] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
alpha
Two-sided quantile to split the integral at.
TYPE: float
tol
Zero-roundoff absolute threshold.
TYPE: float
influence_function
The influence function, with vectorized signature () -> ()
.
TYPE: Callable[[V], V]
l_ratio_from_cdf
lmo.l_ratio
lmo.theoretical.l_comoment_from_pdf(pdf, cdfs, r, /, trim=(0, 0), *, supports=None, quad_opts=None)
","text":"Evaluate the theoretical L-comoment matrix of a multivariate probability distribution, using the joint PDF \\(f_{\\vec{X}}(\\vec{x})\\) and \\(n\\) marginal CDFs \\(F_X(x)\\) of random vector \\(\\vec{X}\\).
The L-comoment matrix is defined as
\\[ \\Lambda_{r}^{(s, t)} = \\left[ \\lambda_{r [ij]}^{(s, t)} \\right]_{n \\times n} \\;, \\]with elements
\\[ \\begin{align} \\lambda_{r [ij]}^{(s, t)} &= c^{(s,t)}_r \\int_{\\mathbb{R^n}} x_i \\,F_j(x_j)^s \\,\\bar{F}_j(x_j)^t \\,\\tilde{P}^{(s, t)}_r \\big(F_j(x_j) \\big) \\,f(\\vec{x}) \\, d \\vec{x} \\\\ &= c^{(s,t)}_r \\, \\mathbb{E}_{\\vec{X}} \\left[ X_i \\,F_j(X_j)^s \\,\\bar{F}_j(X_j)^t \\,\\tilde{P}^{(s, t)}_r \\big(F_j(X_j) \\big) \\,f(\\vec{X}) \\right] \\;, \\end{align} \\]with vector \\(\\vec{x} = \\begin{bmatrix} x_1 & \\cdots & x_n \\end{bmatrix}^T\\), \\(f\\) the joint PDF, \\(F_i\\) the marginal CDF of \\(X_i\\) and \\(\\bar{F}_i\\) its complement (the Survival Function), \\(\\tilde{P}^{(s, t)}_n\\) the shifted Jacobi polynomial, and
\\[ c^{(s,t)}_r = \\frac{r+s+t}{r} \\frac{B(r,\\,r+s+t)}{B(r+s,\\,r+t)} \\;, \\]a positive constant.
For \\(r \\ge 2\\), it can also be expressed as
\\[ \\lambda_{r [ij]}^{(s, t)} = c^{(s,t)}_r \\mathrm{Cov} \\left[ X_i ,\\; F_j(X_j)^s \\,\\bar{F}_j(X_j)^t \\,\\tilde{P}^{(s, t)}_r \\big(F_j(X_j) \\big) \\,f(\\vec{X}) \\right] \\;, \\]and without trim (\\(s = t = 0\\)), this simplifies to
\\[ \\lambda_{r [ij]} = \\mathrm{Cov} \\left[ X_i ,\\; \\,\\tilde{P}_r \\big(F_j(X_j) \\big) \\,f(\\vec{X}) \\right] \\;, \\]with \\(\\tilde{P}_n\\) the shifted Legendre polynomial. This last form is precisely the definition introduced by Serfling & Xiao (2007).
Note that the L-comoments along the diagonal, are equivalent to the (univariate) L-moments, i.e.
\\[ \\lambda_{r [ii]}^{(s, t)}\\big( \\vec{X} \\big) = \\lambda_{r}^{(s, t)}\\big( X_i \\big) \\;. \\] NotesAt the time of writing, trimmed L-comoments have not been explicitly defined in the literature. Instead, the author (@jorenham) derived it by generizing the (untrimmed) L-comoment definition by Serfling & Xiao (2007), analogous to the generalization of L-moments into TL-moments by Elamir & Seheult (2003).
Examples:
Find the L-coscale and TL-coscale matrices of the multivariate Student\u2019s t distribution with 4 degrees of freedom:
>>> from lmo.theoretical import l_comoment_from_pdf\n>>> from scipy.stats import multivariate_t, t\n>>> df = 4\n>>> loc = np.array([0.5, -0.2])\n>>> cov = np.array([[2.0, 0.3], [0.3, 0.5]])\n>>> X = multivariate_t(loc, cov, df)\n>>> cdfs = [t(df, loc[i], np.sqrt(cov[i, i])).cdf for i in range(2)]\n>>> l_cov = l_comoment_from_pdf(X.pdf, cdfs, 2)\n>>> l_cov.round(4)\narray([[1.0413, 0.3124],\n [0.1562, 0.5207]])\n>>> tl_cov = l_comoment_from_pdf(X.pdf, cdfs, 2, trim=1)\n>>> tl_cov.round(4)\narray([[0.4893, 0.1468],\n [0.0734, 0.2447]])\n
The correlation coefficient can be recovered in several ways:
>>> cov[0, 1] / np.sqrt(cov[0, 0] * cov[1, 1]) # \"true\" correlation\n0.3\n>>> np.round(l_cov[0, 1] / l_cov[0, 0], 4)\n0.3\n>>> np.round(l_cov[1, 0] / l_cov[1, 1], 4)\n0.3\n>>> np.round(tl_cov[0, 1] / tl_cov[0, 0], 4)\n0.3\n>>> np.round(tl_cov[1, 0] / tl_cov[1, 1], 4)\n0.3\n
PARAMETER DESCRIPTION pdf
Joint Probability Distribution Function (PDF), that accepts a float vector of size \\(n\\), and returns a scalar in \\([0, 1]\\).
TYPE: Callable[[npt.NDArray[np.float64]], float]
cdfs
Sequence with \\(n\\) marginal CDF\u2019s.
TYPE: Sequence[Callable[[float], float]]
r
Non-negative integer \\(r\\) with the L-moment order.
TYPE: AnyInt
trim
Left- and right- trim, either as a \\((s, t)\\) tuple with \\(s, t > -1/2\\), or \\(t\\) as alias for \\((t, t)\\).
TYPE: AnyTrim
DEFAULT: (0, 0)
supports
A sequence with \\(n\\) 2-tuples, corresponding to the marginal integration limits. Defaults to \\([(-\\infty, \\infty), \\dots]\\).
TYPE: Sequence[Pair[float]] | None
quad_opts
Optional dict of options to pass to scipy.integrate.quad
.
TYPE: QuadOptions | None
lmbda
The population L-comoment matrix with shape \\(n \\times n\\).
TYPE: npt.NDArray[np.float64]
lmo.theoretical.l_coratio_from_pdf(pdf, cdfs, r, r0=2, /, trim=(0, 0), *, supports=None, quad_opts=None)
","text":"Evaluate the theoretical L-comoment ratio matrix of a multivariate probability distribution, using the joint PDF \\(f_{\\vec{X}}(\\vec{x})\\) and \\(n\\) marginal CDFs \\(F_X(x)\\) of random vector \\(\\vec{X}\\).
\\[ \\tilde \\Lambda_{r,r_0}^{(s, t)} = \\left[ \\left. \\lambda_{r [ij]}^{(s, t)} \\right/ \\lambda_{r_0 [ii]}^{(s, t)} \\right]_{n \\times n} \\] See Alsol_comoment_from_pdf
lmo.l_coratio
linalg
","text":"Linear algebra and linearized orthogonal polynomials.
"},{"location":"api/#lmo.linalg.sandwich","title":"lmo.linalg.sandwich(A, X, /, dtype=np.float64)
","text":"Calculates the \u201csandwich\u201d matrix product (A @ X @ A.T
) along the specified X
axis.
A
2-D array of shape (s, r)
, the \u201cbread\u201d.
TYPE: npt.NDArray[np.number[Any]]
dtype
The data type of the result.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
X
Array of shape (r, r, ...)
.
TYPE: npt.NDArray[T | np.number[Any]]
C
Array of shape (s, s, ...)
.
TYPE: npt.NDArray[T]
lmo.linalg.pascal(k, /, dtype=np.int64, *, inv=False)
","text":"Construct the lower-diagonal Pascal matrix \\(L_{k \\times k\\)}$, or its matrix inverse \\(L^{-1}\\).
\\[ \\begin{align} L_{ij} &= \\binom{i}{j} \\\\ L^{-1}_{ij} &= (-1)^{i - j} L_{ij} \\end{align} \\]Implemented using recursion, unlike the slow naive implementation from the equivalent scipy.linalg.pascal
and scipy.linalg.invpascal
functions using kind='lower'
. By using the binomial recurrence relation, assuming \\(0 < j < i\\), \\(\\binom{i}{j} = \\frac{i}{j} \\binom{i-1}{j-1}\\), the following recursive definition is obtained:
Examples:
>>> import numpy as np\n>>> from lmo.linalg import pascal\n>>> pascal(4, dtype=np.int_)\narray([[1, 0, 0, 0],\n [1, 1, 0, 0],\n [1, 2, 1, 0],\n [1, 3, 3, 1]])\n>>> pascal(4, dtype=np.int_, inv=True)\narray([[ 1, 0, 0, 0],\n [-1, 1, 0, 0],\n [ 1, -2, 1, 0],\n [-1, 3, -3, 1]])\n>>> np.rint(np.linalg.inv(pascal(4))).astype(int)\narray([[ 1, 0, 0, 0],\n [-1, 1, 0, 0],\n [ 1, -2, 1, 0],\n [-1, 3, -3, 1]])\n
Now, let\u2019s compare with scipy:
>>> from scipy.linalg import invpascal\n>>> invpascal(4, kind='lower').astype(int)\narray([[ 1, 0, 0, 0],\n [-1, 1, 0, 0],\n [ 1, -2, 1, 0],\n [-1, 3, -3, 1]])\n
"},{"location":"api/#lmo.linalg.ir_pascal","title":"lmo.linalg.ir_pascal(k, /, dtype=np.float64)
","text":"Inverse regulatized lower-diagonal Pascal matrix, \\(\\bar{L}_{ij} = L^{-1}_ij / i\\).
Used to linearly combine order statistics order statistics into L-moments.
"},{"location":"api/#lmo.linalg.sh_legendre","title":"lmo.linalg.sh_legendre(k, /, dtype=np.int64)
","text":"Shifted Legendre polynomial coefficient matrix \\(\\widetilde{P}\\) of shape (k, k)
.
The \\(j\\)-th coefficient of the shifted Legendre polynomial of degree \\(k\\) is at \\((k, j)\\):
\\[ \\widetilde{p}_{k, j} = (-1)^{k - j} \\binom{k}{j} \\binom{k + j}{j} \\]Useful for transforming probability-weighted moments into L-moments.
DangerFor \\(k \\ge 29\\), all 64-bits dtypes (default is int64) will overflow, which results in either an OverflowError
(if you\u2019re lucky), or will give incorrect results. Similarly, all 32-bits dtypes (e.g. np.int_
on Windows) already overflow when \\(k \\ge 16\\).
This is not explicitly checked \u2013 so be sure to select the right dtype
depending on k
.
One option is to use dtype=np.object_
, which will use Python-native int
. However, this is a lot slower, and is likely to fail. For instance, when multiplied together with some float64
array, a TypeError
is raised.
k
The size of the matrix, and the max degree of the shifted Legendre polynomial.
TYPE: int
dtype
Desired output data type, e.g, numpy.float64
. Must be signed. The default is numpy.int64
.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.int64
P
2-D array of the lower-triangular square matrix of size \\(k^2\\)`.
TYPE: npt.NDArray[T]
Examples:
Calculate \\(\\widetilde{P}_{4 \\times 4}\\):
>>> from lmo.linalg import sh_legendre\n>>> sh_legendre(4, dtype=int)\narray([[ 1, 0, 0, 0],\n [ -1, 2, 0, 0],\n [ 1, -6, 6, 0],\n [ -1, 12, -30, 20]])\n
See Also lmo.linalg.sh_jacobi(k, a, b, /, dtype=None)
","text":"Shifted Jacobi polynomial coefficient matrix \\(\\widetilde{P}^{(a,b)}\\) of shape (k, k)
.
The \\(j\\)-th coefficient of the shifted Jacobi polynomial of degree \\(k\\) is at \\((k, j)\\):
The \u201cshift\u201d refers to the change of variables \\(x \\mapsto 2x - 1\\) in the (unshifted) Jacobi (or hypergeometric) polynomials.
The (shifted) Jacobi polynomials \\(\\widetilde{P}^{(a,b)}\\) generalize the (shifted) Legendre polynomials \\(\\widetilde{P}\\): \\(\\widetilde{P}^{(0, 0)} = \\widetilde{P}\\)
PARAMETER DESCRIPTIONk
The size of the matrix, and the max degree of the polynomial.
TYPE: AnyInt
a
The \\(\\alpha\\) parameter, must be \\(\\ge 0\\).
TYPE: AnyFloat
b
The \\(\\beta\\) parameter, must be \\(\\ge 0\\).
TYPE: AnyFloat
dtype
Desired output data type, e.g, numpy.float64
. Default is numpy.int64
if a
and b
are integers, otherwise np.float64
.
TYPE: np.dtype[T] | type[T] | None
DEFAULT: None
P
2-D array of the lower-triangular square matrix of size \\(k^2\\)`.
TYPE: npt.NDArray[T | np.int64]
Examples:
Calculate \\(\\widetilde{P}^{(1, 1)}_{4 \\times 4}\\):
>>> from lmo.linalg import sh_jacobi\n>>> sh_jacobi(4, 1, 1, dtype=int)\narray([[ 1, 0, 0, 0],\n [ -2, 4, 0, 0],\n [ 3, -15, 15, 0],\n [ -4, 36, -84, 56]])\n
Let\u2019s compare \\(\\widetilde{P}^(1, \\pi)_3\\) with the scipy Jacobi poly1d. This requires manual shifting \\(x \\mapsto f(x)\\), with \\(f(x) = 2x - 1\\):
>>> import numpy as np\n>>> import scipy.special as sc\n>>> f_x = np.poly1d([2, -1]) # f(x) = 2*x + 1\n>>> sc.jacobi(3, 1, np.pi)(f_x)\npoly1d([ 125.80159497, -228.55053774, 128.54584648, -21.79690371])\n>>> sh_jacobi(4, 1, np.pi)[3]\narray([ -21.79690371, 128.54584648, -228.55053774, 125.80159497])\n
Apart from the reversed coefficients of numpy.poly1d
(an awkward design choice, but it\u2019s fixed in the new numpy.polynomial
module.)
scipy.special.jacobi
lmo.linalg.succession_matrix(c)
","text":"A toeplitz-like transformation matrix construction, that prepends \\(i\\) zeroes to \\(i\\)-th row, so that the input shape is mapped from (n, k)
to (n, k + n)
.
So all values \\(i > j \\vee i + j \\ge k\\) are zero in the succession matrix.
PARAMETER DESCRIPTIONc
Dense matrix of shape (n, k)
.
TYPE: npt.NDArray[T]
S
Matrix of shape (n, k + n)
TYPE: npt.NDArray[T]
Examples:
>>> from lmo.linalg import succession_matrix\n>>> c = np.arange(1, 9).reshape(4, 2)\n>>> c\narray([[1, 2],\n [3, 4],\n [5, 6],\n [7, 8]])\n>>> succession_matrix(c)\narray([[1, 2, 0, 0, 0],\n [0, 3, 4, 0, 0],\n [0, 0, 5, 6, 0],\n [0, 0, 0, 7, 8]])\n
"},{"location":"api/#lmo.linalg.trim_matrix","title":"lmo.linalg.trim_matrix(r, /, trim, dtype=np.float64)
","text":"Linearization of the trimmed L-moment recurrence relations, following the (corrected) derivation by Hosking (2007) from the (shifted) Jacobi Polynomials.
This constructs a \\(r \\times r + t_1 + t_2\\) matrix \\(T^{(t_1, t_2)}\\) that \u201ctrims\u201d conventional L-moments. E.g. the first 3 \\((1, 1)\\) trimmed L-moments can be obtained from the first \\(3+1+1=5\\) (untrimmed) L-moments (assuming they exist) with trim_matrix(3, (1, 1)) @ l_moment(x, np.ogrid[:5] + 1)
.
The big \u201cL\u201d in \u201cL-moment\u201d, referring to it being a Linear combination of order statistics, has been prominently put in the name by Hosking (1990) for a good reason. It means that transforming order statistics to a bunch of L-moments, can be done using a single matrix multiplication (see lmo.linalg.sh_legendre
). By exploiting liniarity, it can easily be chained with this trim matrix, to obtain a reusable order-statistics -> trimmed L-moments transformation (matrix).
Note that these linear transformations can be used in exactly the same way to e.g. calculate several population TL-moments of some random varianble, using nothing but its theoretical probablity-weighted moments (PWMs).
PARAMETER DESCRIPTIONr
The max (trimmed) L-moment order.
TYPE: int
trim
Left- and right-trim orders \\((t_1, t_2)\\), integers \\(\\ge 0\\). If set to (0, 0), the identity matrix is returned.
TYPE: tuple[int, int]
dtype
Desired output data type, e.g, numpy.float64
(default).
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
npt.NDArray[T]
Toeplitz-like matrix of shape \\((r, r + t_1 + t_2)\\).
Examples:
>>> from lmo.linalg import trim_matrix\n>>> trim_matrix(3, (0, 1))\narray([[ 1. , -1. , 0. , 0. ],\n [ 0. , 0.75 , -0.75 , 0. ],\n [ 0. , 0. , 0.66666667, -0.66666667]])\n>>> trim_matrix(3, (1, 0))\narray([[1. , 1. , 0. , 0. ],\n [0. , 0.75 , 0.75 , 0. ],\n [0. , 0. , 0.66666667, 0.66666667]])\n
References Order statistics \\(X_{i:n}\\), with \\(i \\in [0, n)\\).
Primarily used as an intermediate step for L-moment estimation.
ReferencesH.A. David & H.N. Nagaraja (2004) \u2013 Order statistics
"},{"location":"api/#lmo.ostats.weights","title":"lmo.ostats.weights(i, n, N, /, *, cached=False)
","text":"Compute the linear weights \\(w_{i:n|j:N}\\) for \\(j = 0, \\dots, N-1\\).
The unbiased sample estimator \\(\\mu_{i:n}\\) is then derived from
\\[ E[X_{i:n}] = \\sum_{j=0}^{N-1} w_{i:n|j:N} X_{j:N} \\, , \\]where
\\[ \\begin{aligned} w_{i:n|j:N} &= \\binom{j}{i} \\binom{N - j - 1}{n - i - 1} / \\binom{N}{n} \\\\ &= \\frac{1}{N - n + 1} \\frac{ B(j + 1, N - j) }{ B(i + 1, n - i) B(j - i + 1, N - j - n + i + 1) } \\end{aligned} \\]Here, \\(B\\) denotes the Beta function, \\(B(a,b) = \\Gamma(a) \\Gamma(b) / \\Gamma(a + b)\\).
NotesThis function uses \u201cPython-style\u201d 0-based indexing for \\(i\\), instead of the conventional 1-based indexing that is generally used in the literature.
PARAMETER DESCRIPTIONi
0-indexed sample (fractional) index, \\(0 \\le i \\lt n\\). Negative indexing is allowed.
TYPE: float
n
Subsample size, optionally fractional, \\(0 \\le n0\\)
TYPE: float
N
Sample size, i.e. the observation count.
TYPE: int
cached
Cache the result for (i, n, n0)
. Defaults to False.
TYPE: bool
DEFAULT: False
npt.NDArray[np.float64]
1d array of size \\(N\\) with (ordered) sample weights.
"},{"location":"api/#lmo.ostats.from_cdf","title":"lmo.ostats.from_cdf(F, i, n)
","text":"Transform \\(F(X)\\) to \\(F_{i:n}(X)\\), of the \\(i\\)th variate within subsamples of size, i.e. \\(0 \\le i \\le n - 1\\).
PARAMETER DESCRIPTIONF
Scalar or array-like with the returned value of some cdf, i.e. \\(F_X(x) = P(X \\le x)\\). Must be between 0 and 1.
TYPE: npt.ArrayLike
i
0-indexed sample (fractional) index, \\(0 \\le i < n\\).
TYPE: float
n
Subsample size, optionally fractional, \\(0 \\le n0\\)
TYPE: float
Power-Weighted Moment (PWM) \\(\\beta_k = M_{1,k,0}\\).
Primarily used as an intermediate step for L-moment estimation.
"},{"location":"api/#lmo.pwm_beta.weights","title":"lmo.pwm_beta.weights(r, n, /, dtype=np.float64)
","text":"Probability Weighted moment (PWM) projection matrix \\(B\\) of the unbiased estimator for \\(\\beta_k = M_{1,k,0}\\) for \\(k = 0, \\dots, r - 1\\).
The PWM\u2019s are estimated by linear projection of the sample of order statistics, i.e. \\(b = B x_{i:n}\\)
PARAMETER DESCRIPTIONr
The amount of orders to evaluate, i.e. \\(k = 0, \\dots, r - 1\\).
TYPE: int
n
Sample count.
TYPE: int
dtype
Desired output floating data type.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
P_b
Upper-triangular projection matrix of shape (r, n)
.
TYPE: npt.NDArray[T]
Examples:
>>> from lmo import pwm_beta\n>>> pwm_beta.weights(4, 5)\narray([[0.2 , 0.2 , 0.2 , 0.2 , 0.2 ],\n [0. , 0.05 , 0.1 , 0.15 , 0.2 ],\n [0. , 0. , 0.03333333, 0.1 , 0.2 ],\n [0. , 0. , 0. , 0.05 , 0.2 ]])\n
"},{"location":"api/#lmo.pwm_beta.cov","title":"lmo.pwm_beta.cov(a, r, /, axis=None, dtype=np.float64, **kwargs)
","text":"Distribution-free variance-covariance matrix of the probability weighted moment (PWM) point estimates \\(\\beta_k = M_{1,k,0}\\), with orders \\(k = 0, \\dots, r - 1\\).
PARAMETER DESCRIPTIONa
Array-like with observations.
TYPE: npt.ArrayLike
r
The amount of orders to evaluate, i.e. \\(k = 0, \\dots, r - 1\\).
TYPE: int
axis
The axis along which to calculate the covariance matrices.
TYPE: int | None
DEFAULT: None
dtype
Desired output floating data type.
TYPE: np.dtype[T] | type[T]
DEFAULT: np.float64
**kwargs
Additional keywords to pass to lmo.stats.ordered
.
TYPE: Any
DEFAULT: {}
S_b
Variance-covariance matrix/tensor of shape (r, ...)
TYPE: npt.NDArray[T]
Any contributions to Lmo are appreciated!
"},{"location":"contributing/#issues","title":"Issues","text":"Questions, feature requests and bug reports are all welcome as issues.
When reporting a bug, make sure to include the versions of lmo
, python
, numpy
and scipy
you are using, and provide a reproducible example of the bug.
Ensure you have poetry installed, then clone your fork, and install with
poetry install --sync\n
It can help to use Lmo\u2019s lowest-supported Python version, so that you don\u2019t accidentally use those bleeding-edge Python features that you shouldn\u2019t, poetry env use python3.x
Now you can go ahead and do your thing. And don\u2019t forget the type annotations, add tests, and to lint it all.
If you\u2019re a 10x developer that doesn\u2019t wait on CI workflows, you can use the following 1337 shellscript (keep in mind that the CI runs this on all supported Python versions):
poetry run codespell check lmo\npoetry run ruff check lmo\npoetry run pyright\npoetry run py.test\n
If your change involves documentation updates, you can conjure up a live preview:
poetry run mkdocs serve\n
But don\u2019t worry about building the docs, or bumping the version; Lmo\u2019s personal assistant will do that on release.
"},{"location":"distributions/","title":"L-moments of common probability distributions","text":"This page lists L-moment statistics (L -location, scale, skewness, and kurtosis) of common univariate probability distributions, most of them continuous.
Each of the listed expressions have been validated, both numerically and symbolically (with either Wolfram Alpha, SymPy, or pen and paper).
Most of the closed-form expressions that are listed here, have been previously reported in the literature. But for the sake of interpretability, several have been algebraically rearranged.
Due to the exploratory use of symbolic computation software, this listing is likely to include some novel solutions. This is also the reason for the lack of references. But this should pose no problems in practise, since Lmo makes it trivial to check if they aren\u2019t incorrect.
Tip
Numerical calculation of these L-statistics using scipy.stats
distributions, refer to rv_continuous.l_stats
.
For direct calculation of the L-stats from a CDF or PPF (quantile function, inverse CDF), see l_stats_from_cdf
or l_stats_from_ppf
, respectively.
An overview of the untrimmed L-location, L-scale, L-skewness and L-kurtosis, of a bunch of popular univariate probability distributions, for which they exist (in closed form).
Name /scipy.stats
Params \\( \\lmoment{1} \\) \\( \\lmoment{2} \\) \\( \\lratio{3} = \\lmoment{3}/\\lmoment{2} \\) \\( \\lratio{4} = \\lmoment{4}/\\lmoment{2} \\) Uniform uniform
\\( a < b \\) \\[ \\frac{a + b}{2} \\] \\[ \\frac{b - a}{6} \\] \\( 0 \\) \\( 0 \\) Normal norm
\\( \\mu \\) \\( \\sigma>0 \\) \\( \\mu \\) \\[ \\frac{\\sigma}{\\sqrt \\pi} \\] \\( \\approx 0.5642 \\ \\sigma \\) \\( 0 \\) \\[ 30 \\ \\frac{\\arctan{\\sqrt 2}}{\\pi} - 9 \\] \\( \\approx 0.1226 \\) Logistic logistic(\u03bc, s)
\\( \\mu \\) \\( s>0 \\) \\( \\mu \\) \\( s \\) \\( 0 \\) \\[ 1 / 6 \\] \\( = 0.16\\overline{6}\\dots \\) Laplace laplace
\\( \\mu \\) \\( b > 0 \\) \\( \\mu \\) \\[ \\frac 3 4 b \\] \\( = 0.75 \\ b\\) \\( 0 \\) \\[ \\frac{17}{72} \\] \\( \\approx 0.2361 \\) Student's t (2 d.f.) t(2)
\\( \\nu = 2 \\) \\( 0 \\) \\[ \\frac{\\pi}{2 \\sqrt{2}} \\] \\( \\approx 1.1107 \\) \\( 0 \\) \\[ \\frac 3 8 \\] \\( = 0.375 \\) Student's t (3 d.f.) t(3)
\\( \\nu = 3 \\) \\( 0 \\) \\[ \\frac{3 \\sqrt 3}{\\vphantom{\\pi^2}2 \\pi} \\] \\( \\approx 0.8270 \\) \\( 0 \\) \\[ 1 - \\frac{\\vphantom{\\sqrt 3}175}{24 \\pi^2} \\] \\( \\approx 0.2612 \\) Student's t (4 d.f.) t(4)
\\( \\nu = 4 \\) \\( 0 \\) \\[ \\frac{15}{64} \\pi \\] \\( \\approx 0.7363 \\) \\( 0 \\) \\[ \\frac{111}{512} \\] \\( \\approx 0.2168 \\) Exponential expon
\\( \\lambda>0 \\) \\[ \\frac 1 \\lambda \\] \\[ \\frac{1}{2 \\lambda} \\] \\[ \\frac 1 3 \\] \\( = 0.3\\overline{3}\\dots \\) \\[ \\frac 1 6 \\] \\( = 0.16\\overline{6}\\dots \\) Rayleigh rayleigh
\\( \\sigma > 0 \\) \\[ \\frac 1 2 \\sqrt{2 \\pi} \\ \\sigma \\] \\( \\approx 1.253 \\ \\sigma \\) \\[ \\frac {\\sqrt 2 - 1}{2} \\sqrt{\\pi} \\ \\sigma \\] \\( \\approx 0.3671 \\ \\sigma \\) \\[ 2 \\frac{2 + \\sqrt 2}{\\sqrt 3} - \\frac{4 + \\sqrt{2}}{\\sqrt 2} \\] \\( \\approx 0.1140 \\) \\[ 10 \\frac{2 + \\sqrt 2}{\\sqrt 3} - 3 \\frac{5 + 3 \\sqrt 2}{\\sqrt 2} \\] \\( \\approx 0.1054 \\) Gumbel gumbel_r
see eq. \\( \\eqref{eq:lr_gev} \\) for \\( \\lmoment{r} \\) \\[ \\gamma_e \\] \\( \\approx 0.5772 \\) \\[ \\ln{2} \\] \\( \\approx 0.6931 \\) \\[ 2 \\log_2(3) - 3 \\] \\( \\approx 0.1699 \\) \\[ 16 - 10 \\log_2(3) \\] \\( \\approx 0.1504 \\) Pareto pareto
see eq. \\( \\eqref{eq:lr_pareto4} \\) for \\( \\lmoment{r} \\) \\[ \\begin{align*} \\alpha &> 0 \\quad \\text{(shape)} \\\\ \\beta &> 0 \\quad \\text{(scale)} \\end{align*} \\] \\[ \\frac{\\alpha}{\\alpha - 1} \\ \\beta \\] \\[ \\frac{\\alpha}{\\alpha - 1} \\frac{1}{2 \\alpha - 1} \\ \\beta \\] \\[ \\frac{\\alpha + 1}{3 \\alpha - 1} \\] \\[ \\frac{\\alpha + 1}{3 \\alpha - 1} \\frac{2 \\alpha + 1}{4 \\alpha - 1} \\]"},{"location":"distributions/#tl-stats","title":"TL-stats","text":"Collection of TL-location, -scale, -skewness, -kurtosis coefficients, with symmetric trimming of order 1, i.e. trim=(1, 1)
.
scipy.stats
Params \\( \\tlmoment{1}{1} \\) \\( \\tlmoment{1}{2} \\) \\( \\tlratio{1}{3} \\) \\( \\tlratio{1}{4} \\) Uniform uniform
\\( a < b \\) \\[ (a + b) / 2 \\] \\[ (a - b) / 10 \\] \\( 0 \\) \\( 0 \\) Normal norm
\\( \\mu \\) \\( \\sigma>0 \\) \\( \\mu \\) \\[ \\left( 6 - 18 \\ \\frac{\\arctan{\\sqrt 2}}{\\pi} \\right) \\frac{\\sigma}{\\sqrt \\pi} \\] \\( \\approx 0.2970 \\ \\sigma \\) \\( 0 \\) \\( \\approx 0.06248 \\) Logistic logistic(\u03bc, s)
\\( \\mu \\)\\( s>0 \\) \\( \\mu \\) \\( s / 2 \\) \\( 0 \\) \\[ 1 / 12 \\] \\( = 0.083\\overline{3} \\dots \\) Laplace laplace
\\( \\mu \\) \\( b > 0 \\) \\( \\mu \\) \\[ 11b / 32 \\] \\( = 0.34375 \\ b\\) \\( 0 \\) \\[ 3 / 22 \\] \\( = 0.136\\overline{36} \\dots \\) Cauchy / Student's t (1 d.f.) cauchy
/ t(2)
\\( \\nu = 1 \\) \\( 0 \\) \\[ \\frac{18 \\vphantom{)}}{\\pi^3 \\vphantom{)}} \\ \\zeta(3) \\] \\( \\approx 0.6978 \\ b \\) \\( 0 \\) \\[ \\frac{25}{6} - \\frac{175}{4 \\pi^2} \\frac{\\zeta(5)}{\\zeta(3)} \\] \\( \\approx 0.3428 \\) Student's t (2 d.f.) t(2)
\\( \\nu = 2 \\) \\( 0 \\) \\[ \\frac{3 \\pi}{16 \\sqrt{2}} \\] \\( \\approx 0.4165 \\) \\( 0 \\) \\[ \\frac{5}{32} \\] \\( = 0.15625 \\) Student's t (3 d.f.) t(3)
\\( \\nu = 3 \\) \\( 0 \\) \\[ \\frac{105 \\sqrt 3}{16 \\pi^3} \\] \\( \\approx 0.3666 \\) \\( 0 \\) \\[ \\frac{25}{6} - \\frac{23 \\ 023}{(24 \\pi)^2} \\] \\( \\approx 0.1168 \\) Student's t (4 d.f.) t(4)
\\( \\nu = 4 \\) \\( 0 \\) \\[ \\frac{3 \\ 609 \\ \\pi}{32 \\ 768} \\] \\( \\approx 0.3460 \\) \\( 0 \\) \\[ \\frac{164 \\ 975}{1 \\ 642 \\ 496} \\] \\( \\approx 0.1004 \\) Exponential expon
\\( \\lambda>0 \\) \\[ \\frac{5}{6 \\lambda} \\] \\[ \\frac{1}{4 \\lambda} \\] \\[ \\frac 2 9 \\] \\( = 0.2\\overline{2}\\dots \\) \\[ \\frac{1}{12} \\] \\( = 0.083\\overline{3}\\dots \\) Rayleigh rayleigh
\\[ \\frac 1 6 \\bigl( 9 - 2 \\sqrt 6 \\bigr) \\sqrt \\pi \\ \\] \\( \\approx 1.211 \\) \\[ \\frac 1 4 \\bigl( 6 - 4 \\sqrt 6 + 3 \\sqrt 2 \\bigr) \\sqrt \\pi \\ \\] \\( \\approx 0.1970 \\) \\[ \\frac{10}{9} - \\frac{8}{9} \\frac {3 \\sqrt{10} + 5 \\sqrt 6 - 15 \\sqrt 2} {6 - 4 \\sqrt 6 + 3 \\sqrt 2} \\] \\( \\approx 0.06951 \\) \\[ \\frac 5 4 - \\frac 7 6 \\frac {18 \\sqrt{10} + 10 \\sqrt 6 - 10 \\sqrt 3 - 45 \\sqrt 2} {6 - 4 \\sqrt 6 + 3 \\sqrt 2} \\] \\( \\approx 0.05422 \\) Gumbel gumbel_r
see eq. \\( \\eqref{eq:lr_gev} \\) for \\( \\tlmoment{s,t}{r} \\) \\[ \\gamma_e + 3 \\ln{2} - 2 \\ln{3} \\] \\( \\approx 0.4594 \\) \\[ 6 \\ln{3} - 9 \\ln{2} \\] \\( \\approx 0.3533 \\) \\[ -\\frac{10}{9} \\frac {2 \\log_2(5) - 5} {2 \\log_2(3) - 3} - \\frac{20}{9} \\] \\( \\approx 0.1065 \\) \\[ \\frac{35}{6} \\frac {3 \\log_2(5) - 7} {2 \\log_2(3) - 3} + \\frac{5}{4} \\] \\( \\approx 0.07541 \\)"},{"location":"distributions/#general-distribution-l-moments","title":"General distribution L-moments","text":"Lmo derived a bunch of closed-form solutions for L-moments of several distributions. The proofs are not published, but it isn\u2019t difficult to validate their correctness, e.g. numerically, or symbolically with sympy or wolfram alpha / mathematica.
"},{"location":"distributions/#gev","title":"GEV","text":"The generalized extreme value (GEV) distribution unifies the Gumbel, Fr\u00e9chet, and Weibull distributions. It has one shape parameter \\( \\alpha \\in \\mathbb{R} \\), and the following distribution functions:
\\[ \\begin{align*} F(x) &= e^{-\\qexp{1 - \\alpha}{-x}} \\\\ x(F) &= -\\qlog{1 - \\alpha}{-\\ln(F)} \\end{align*} \\]Here, \\( \\qexp{q}{y} \\) and \\( \\qlog{q}{y} \\) are the Tsallis \\( q \\)-exponential and the \\( q \\)-logarithm, respectively.
An alternative parametrization is sometimes used, e.g. on Wikipedia, where \\( \\xi = -\\alpha \\). The convention that is used here, is the same as in scipy.stats.genextreme
, where c
corresponds to \\( \\alpha \\).
The trimmed L-moments of the GEV are
\\[ \\begin{equation} \\tlmoment{s, t}{r} = \\frac{(-1)^{r}}{r} \\sum_{k = s + 1}^{r + s + t} (-1)^{k - s} \\binom{r + k - 2}{r + s - 1} \\binom{r + s + t}{k} \\left( \\begin{cases} \\gamma_e + \\ln(k) & \\text{if } \\alpha = 0 \\\\ 1 / \\alpha - \\Gamma(\\alpha) \\ k^{-\\alpha} & \\text{if } \\alpha \\neq 0 \\end{cases} \\right) \\label{eq:lr_gev} \\end{equation} \\]Note that the GEV is effectively a reparametrized \\( q \\)-Gumbel Tsallis distribution, with \\( q = 1 - \\alpha \\).
"},{"location":"distributions/#glo","title":"GLO","text":"The generalized logistic distribution (GLO), also known as the shifted log-logistic distribution , with shape parameter \\( \\alpha \\in \\mathbb{R} \\), is characterized by the following distribution functions:
\\[ \\begin{align*} F(x) &= \\frac{1}{1 + \\qexp{1 - \\alpha}{x}} \\\\ x(F) &= -\\qlog{1 - \\alpha}{\\frac{1 - F}{F}} \\end{align*} \\]For \\( -1 < \\alpha < 1 \\), the general trimmed L-moments of the GLO are:
\\[ \\begin{equation} \\tlmoment{s, t}{r} = \\begin{cases} \\displaystyle \\psi(s + 1) - \\psi(t + 1) & \\text{if } \\alpha = 0 \\wedge r = 1 \\\\ \\displaystyle \\frac{(-1)^r}{r} \\B(r - 1,\\ s + 1) + \\frac 1 r \\B(r - 1,\\ t + 1) & \\text{if } \\alpha = 0 \\\\ \\displaystyle \\frac{\\ffact{1}{r}}{\\alpha} + \\sum_{k = s + 1}^{r + s + t} (-1)^{r + s - k } \\binom{r + k - 2}{r + s - 1} \\binom{r + s + t}{k} \\B(\\alpha,\\ k - \\alpha) & \\text{if } -1 < \\alpha < 1 \\end{cases} \\label{eq:lr_glo} \\end{equation} \\]Where \\( \\psi(z) \\) is the digamma function.
The corresponding scipy.stats
implementation is kappa4
, with h = -1
and k
set to \\( \\alpha \\); not genlogistic
.
Note that the GLO is effectively a reparametrized \\( q \\)-logistic Tsallis distribution, with \\( q = 1 - \\alpha \\).
"},{"location":"distributions/#gpd","title":"GPD","text":"The generalized Pareto distribution (GPD), with shape parameter \\( \\alpha \\in \\mathbb{R} \\), has for \\( x \\ge 0 \\) the distribution functions:
\\[ \\begin{align*} F(x) &= 1 - 1 / \\qexp{1 - \\alpha}{x} \\\\ x(F) &= \\qlog{1 - \\alpha}{1 / (1 - F)} \\end{align*} \\]Note that this distribution is standard uniform if \\( \\alpha = 1 \\), and standard exponential if \\( \\alpha = 0 \\).
The general trimmed L-moments of the GPD are:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\begin{cases} \\displaystyle \\sum_{k = 1}^{s + 1} \\frac{1}{t + k} & \\text{if } \\alpha = 0 \\wedge r = 1 \\\\ \\frac{1}{r} \\B(r - 1,\\ t + 1) & \\text{if } \\alpha = 0 \\\\ \\displaystyle \\frac{r + s + t}{\\alpha \\ r} \\sum_{k = 0}^{r + t - 1} \\frac{(-1)^{r - k}}{k} \\binom{r + s + t - 1}{k + s} \\binom{r + s + k - 1}{k} \\left( 1 - \\frac{(k + 1)!}{\\rfact{1 - \\alpha}{k + 1}} \\right) & \\text{if } \\alpha < 1 \\end{cases} \\label{eq:lr_gpd} \\end{equation} \\]Apparently left-trimming the exponential distribution does not influence any of the L-moments, besides the L-location.
For the general LH-moments, this simplifies to:
\\[ \\begin{equation} \\tlmoment{0,t}{r} = \\begin{cases} \\displaystyle \\frac{1}{t + 1} & \\text{if } \\alpha = 0 \\wedge r = 1 \\\\ \\frac{1}{r} \\B(r - 1,\\ t + 1) & \\text{if } \\alpha = 0 \\wedge r > 1 \\\\ \\displaystyle \\frac{r + t}{r} \\frac{\\rfact{1 + \\alpha}{r - 2}}{\\rfact{1 - \\alpha + t}{r}} - \\frac{\\ffact{1}{r}}{\\alpha} & \\text{if } \\alpha < 1 \\end{cases} \\label{eq:lhr_gpd} \\end{equation} \\]See scipy.stats.genpareto
for the implementation of the GPD.
Note that the GPD is a reparametrized \\( q \\)-exponential distribution , where \\( q = (2 \\alpha + 1) / (\\alpha + 1) \\) and \\( \\lambda = 1 / (2 - q) \\) s.t. \\( \\alpha \\neq -1 \\) and \\( q < 2 \\).
"},{"location":"distributions/#pareto-type-iv","title":"Pareto Type IV","text":"The Pareto Type IV has two shape parameters \\( \\alpha \\in \\mathbb{R} \\) and \\( \\gamma \\in \\mathbb{R}_{>0} \\), and scale parameter \\( \\beta \\). For \\( x \\ge 0 \\), the CDF and its inverse (the PPF) are
\\[ \\begin{align*} F(x) &= 1 - \\left( 1 + \\left(\\frac x \\beta\\right)^{\\frac 1 \\gamma} \\right)^{-\\alpha} \\\\ x(F) &= \\beta \\left( (1 - F)^{-1 / \\alpha} - 1 \\right)^\\gamma \\end{align*} \\]When \\( \\alpha > \\gamma \\), the trimmed L-moments are found to be:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\frac{\\beta \\gamma}{r} \\sum_{k = t + 1}^{r + s + t} (-1)^{k - t - 1} \\binom{r + k - 2}{r + t - 1} \\binom{r + s + t}{k} \\B(\\gamma,\\ k \\alpha - \\gamma) \\label{eq:lr_pareto4} \\end{equation} \\]This distribution is currently not implemented in scipy.stats
.
For Kumaraswamy\u2019s distribution with parameters \\( \\alpha \\in \\mathbb{R}_{>0} \\) and \\( \\beta \\in \\mathbb{R}_{>0} \\), the general solution for the \\( r \\)th L-moment has been derived by Jones (2009). This can be extended for the general trimmed L-moments.
The distribution functions are for \\( 0 \\le x \\le 1 \\) defined as:
\\[ \\begin{align*} F(x) &= 1 - (1 - x^\\alpha)^\\beta \\\\ x(F) &= \\bigl(1 - (1 - F)^{1/\\beta} \\bigr)^{1/\\alpha} \\end{align*} \\]Its general \\( r \\)-th trimmed L-moment are:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\beta \\ \\frac{r + s + t}{r} \\sum_{k = t}^{r + s + t - 1} (-1)^k \\binom{k + r - 1}{k - t} \\binom{r + s + t - 1}{k} \\B\\bigl(1 + 1 / \\alpha,\\ \\beta + k \\beta \\bigr) \\label{eq:lr_kum} \\end{equation} \\]Unfortunately, the Kumaraswamy distribution is not implemented in scipy.stats
.
The Burr type III distribution, also known as the Dagum distribution, has two shape parameters \\( \\alpha \\) and \\( \\beta \\), both restricted to the positive reals
For \\( x > 0 \\), the distribution functions are:
\\[ \\begin{align*} F(x) &= (1 + x^{-\\alpha})^{-\\beta} \\\\ x(F) &= (F^{-1 / \\beta} - 1)^{-1 / \\alpha} \\end{align*} \\]For \\( \\alpha > 1 \\), the general L-moments are:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = (-1)^{t - 1 / \\alpha} \\ \\beta \\ \\frac{r + s + t}{r} \\sum_{k = s}^{r + s + t - 1} (-1)^{k} \\binom{k + r - 1}{k - s} \\binom{r + s + t - 1}{k} \\B(1 - 1 / \\alpha, -\\beta - k \\beta) \\label{eq:lr_burr3} \\end{equation} \\]The Burr Type III distribution is implemented in scipy.stats.burr
, where the shape parameters c
and d
correspond to \\( \\alpha \\) and \\( \\beta \\), respectively.
Just like Kumaraswamy\u2019s distribution, the Burr Type XII distribution has two shape parameters \\( \\alpha \\) and \\( \\beta \\), both restricted to the positive reals.
The distribution functions are for \\( x > 0 \\) defined as:
\\[ \\begin{align*} F(x) &= 1 - (1 - x^\\alpha)^{-\\beta} \\\\ x(F) &= \\bigl(1 - (1 - F)^{-1/\\beta} \\bigr)^{1/\\alpha} \\end{align*} \\]When \\( \\beta > 1 / \\alpha \\), the general \\( r \\)-th trimmed L-moment is:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\beta \\ \\frac{r + s + t}{r} \\sum_{k = t}^{r + s + t - 1} (-1)^k \\binom{k + r - 1}{k - t} \\binom{r + s + t - 1}{k} \\B\\bigl(1 + 1 / \\alpha,\\ \\beta + k \\beta - 1 / \\alpha \\bigr) \\label{eq:lr_burr12} \\end{equation} \\]The Burr Type XII distribution is implemented in scipy.stats.burr12
, where the shape parameters c
and d
correspond to \\( \\alpha \\) and \\( \\beta \\), respectively.
The Wakeby distribution is quantile-based, without closed-form expressions for the PDF and CDF, whose quantile function (PPF) is defined to be
\\[ x(F) = \\frac \\alpha \\beta \\bigl(1 - (1 - F)^\\beta\\bigr) - \\frac \\gamma \\delta \\bigl(1 - (1 - F)^{-\\delta}\\bigr) + \\mu \\]Each of the scale- \\( \\alpha, \\gamma \\) and shape parameters \\( \\beta, \\delta \\), are assumed to be positive real numbers.
Lmo figured out that the L-moments with any order \\( r \\in \\mathbb{N}_{\\ge 1} \\) and trim \\( s, t \\in \\mathbb{N}^2_{\\ge 1} \\) can be expressed as
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\frac{\\rfact{r + t}{s + 1}}{r} \\left[ \\alpha \\frac {\\rfact{1 - \\beta}{r - 2}} {\\rfact{1 + \\beta + t}{r + s}} + \\gamma \\frac {\\rfact{1 + \\delta}{r - 2}} {\\rfact{1 - \\delta + t}{r + s}} \\right] + \\underbrace{ \\ffact{1}{r} \\left( \\frac \\alpha \\beta - \\frac \\gamma \\delta \\right) }_{\\text{will be } 0 \\text{ if } r>1} \\end{equation} \\]Unfortunately, the Wakeby distribution has currently no scipy.stats
implementation.
The Tukey lambda distribution can be extended to the generalized lambda distribution, which has two scale parameters \\( \\alpha, \\gamma \\), and two shape parameters \\( \\beta, \\delta \\).
Like the Wakeby distribution, the generalized lambda has no closed-form PDF or CDF. Instead, it is defined through its PPF:
\\[ x(F) = \\alpha \\qlog{1 - \\beta}{F} - \\gamma \\qlog{1 - \\delta}{1 - F} \\]Although its central product moments have no closed-form expression, when \\( \\beta > -1 \\) and \\( \\delta > -1 \\), the general trimmed L-moments can be compactly expressed as:
\\[ \\begin{equation} \\tlmoment{s,t}{r} = \\alpha \\frac {\\rfact{r + s}{t + 1} \\ \\ffact{\\beta + s}{r + s - 1}} {r \\ \\rfact{\\beta}{r + s + t + 1}} + (-1)^r \\gamma \\ \\frac {\\rfact{r + t}{s + t} \\ \\ffact{\\delta + t}{r + t - 1}} {r \\ \\rfact{\\delta}{r + s + t + 1}} - \\underbrace{ \\ffact{1}{r} \\left( \\frac \\alpha \\beta - \\frac \\gamma \\delta \\right) }_{\\text{will be } 0 \\text{ if } r>1} \\end{equation} \\]When \\( \\alpha = \\gamma \\) and \\( \\beta = \\delta \\), this is the (non-generalized) Tukey-lambda distribution, which has been implemented as scipy.stats.tukeylambda
. Currently, this 4-parameter generalization has no scipy.stats
implementation.
An overview of the (non-obvious) mathematical notation of special functions and constants.
Name Notation Definition Python Euler\u2013Mascheroni constant \\[ \\gamma_e \\] \\[ \\begin{align*} &= \\int_1^\\infty \\left( \\frac{1}{\\lfloor x \\rfloor} - \\frac 1 x \\right) \\ \\mathrm{d} x \\\\ &= \\lim_{x \\to 0} \\left( \\frac 1 x - \\Gamma(x) \\right) \\\\ &\\approx 0.5772 \\vphantom{\\frac 1 1} \\end{align*} \\]numpy.euler_gamma
Factorial \\[ n! \\vphantom{\\prod_{k=1}^n k} \\] \\[ \\begin{align*} &= \\prod_{k=1}^n k \\\\ &= \\underbrace {1 \\times 2 \\times \\ldots \\times n} _{n{\\text{ factors}}} \\end{align*} \\] scipy.special.factorial
Falling factorial \\[ \\ffact{x}{n} \\] \\[ \\begin{align*} &= \\prod_{k=0}^{n-1} (x - k) \\\\ &= \\underbrace {x \\ (x - 1) \\ldots (x - n + 1)} _{n{\\text{ factors}}} \\end{align*} \\] Rising factorial \\[ \\rfact{x}{n} \\] \\[ \\begin{align*} &= \\prod_{k=0}^{n-1} (x + k) \\\\ &= \\underbrace {x \\ (x + 1) \\ldots (x + n - 1)} _{n{\\text{ factors}}} \\end{align*} \\] scipy.special.poch
Binomial coefficient \\[ \\binom{n}{k} \\] \\[ \\begin{align*} &= \\frac{n!}{k! \\ (n - k)!} \\\\ &= \\frac{\\ffact{n}{k}}{k!} \\end{align*} \\] scipy.special.comb
Gamma function \\[ \\Gamma(z) \\] \\[ = \\int_0^\\infty t^{z-1} e^{-t} \\, \\mathrm{d} t \\] scipy.special.gamma
Digamma function \\[ \\psi(z) \\] \\[ = \\frac{\\mathrm{d}}{\\mathrm{d}z} \\ln \\Gamma(z) \\] scipy.special.digamma
Beta function \\[ \\B(z_1,\\ z_2) \\] \\[ = \\frac{\\Gamma(z_1) \\Gamma(z_2)}{\\Gamma(z_1 + z_2)} \\] scipy.special.beta
Riemann zeta function \\[ \\zeta(z) \\vphantom{\\sum_{n = 1}^{\\infty}} \\] \\[ = \\sum_{n = 1}^{\\infty} n^{-z} \\] scipy.special.zeta
Tsallis \\( q \\)-exponential \\[ \\qexp{q}{x} \\] \\[ = \\begin{cases} \\displaystyle e^x & \\text{if } q = 1 \\\\ \\displaystyle \\sqrt[1 - q]{1 + (1 - q) \\ x} & \\text{otherwise} \\end{cases} \\] scipy.special.inv_boxcox(x, 1 - q)
Tsallis \\( q \\)-logarithm \\[ \\qlog{q}{x} \\] \\[ = \\begin{cases} \\displaystyle \\ln(x) & \\text{if } q = 1 \\\\ \\displaystyle \\frac{x^{1 - q} - 1}{1 - q} & \\text{otherwise} \\\\ \\end{cases} \\] scipy.special.boxcox(x, 1 - q)
"}]}
\ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
index e9263bf3..4383729a 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -2,22 +2,22 @@
sw7k+l>&Zg+_RwOm>#*L+|!!79&tm+W-q4&m$(1T0OV?HfhO zV<(%c*_U-)Hg%4y-X^B43cY!c=2Ldc4c`Jb&I1O1Y5u^aM)$OA8bgZiI;Fh6r-kes Xt&kY#|0(ZB{=D)B);-Nw>;eD)i-2h4