Skip to content

Commit

Permalink
Documentation and coverage cleanup (#292)
Browse files Browse the repository at this point in the history
* documentation cleanup
* cov-fix for descriptives
  • Loading branch information
jonathf authored Nov 5, 2020
1 parent 3838276 commit 272f235
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Master Branch
=============


Version 4.1.0 (2020-11-05)
==========================

Expand Down
28 changes: 16 additions & 12 deletions chaospy/descriptives/conditional.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from itertools import product

import numpy
import chaospy
import numpoly

from . import expected
Expand All @@ -17,7 +18,7 @@ def E_cond(poly, freeze, dist, **kws):
Args:
poly (numpoly.ndpoly):
Polynomial to find conditional expected value on.
freeze (numpy.ndarray):
freeze (numpy.ndpoly):
Boolean values defining the conditional variables. True values
implies that the value is conditioned on, e.g. frozen during the
expected value calculation.
Expand All @@ -35,27 +36,31 @@ def E_cond(poly, freeze, dist, **kws):
>>> poly
polynomial([1, q0, q1, 10*q0*q1-1])
>>> dist = chaospy.J(chaospy.Gamma(1, 1), chaospy.Normal(0, 2))
>>> chaospy.E_cond(poly, [1, 0], dist)
>>> chaospy.E_cond(poly, q0, dist)
polynomial([1.0, q0, 0.0, -1.0])
>>> chaospy.E_cond(poly, [0, 1], dist)
>>> chaospy.E_cond(poly, q1, dist)
polynomial([1.0, 1.0, q1, 10.0*q1-1.0])
>>> chaospy.E_cond(poly, [1, 1], dist)
>>> chaospy.E_cond(poly, [q0, q1], dist)
polynomial([1, q0, q1, 10*q0*q1-1])
>>> chaospy.E_cond(poly, [0, 0], dist)
>>> chaospy.E_cond(poly, [], dist)
polynomial([1.0, 1.0, 0.0, -1.0])
>>> chaospy.E_cond(4, [], dist)
array(4)
"""
poly = numpoly.set_dimensions(poly, len(dist))
if not poly.isconstant:
if poly.isconstant():
return poly.tonumpy()
assert not dist.stochastic_dependent, dist

freeze = numpoly.polynomial(freeze)
if freeze.isconstant():
freeze = freeze.tonumpy().astype(bool)
freeze = numpoly.aspolynomial(freeze)
if not freeze.size:
return numpoly.polynomial(chaospy.E(poly, dist))
if not freeze.isconstant():
freeze = [name in freeze.names for name in poly.names]
else:
poly, freeze = numpoly.align_exponents(poly, freeze)
freeze = numpy.isin(poly.keys, freeze.keys)
freeze = freeze.tonumpy()
freeze = numpy.asarray(freeze, dtype=bool)

# decompose into frozen and unfrozen part
poly = numpoly.decompose(poly)
Expand All @@ -71,5 +76,4 @@ def E_cond(poly, freeze, dist, **kws):
# Remove frozen coefficients, such that poly == sum(frozen*unfrozen) holds
for key in unfrozen.keys:
unfrozen[key] = unfrozen[key] != 0

return numpoly.sum(frozen*expected.E(unfrozen, dist), 0)
3 changes: 0 additions & 3 deletions chaospy/descriptives/correlation/pearson.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ def Corr(poly, dist=None, **kws):
else:
poly = numpoly.polynomial(poly)

if not poly.shape:
return numpy.ones((1, 1))

cov = chaospy.Cov(poly, dist, **kws)
var = numpy.diag(cov)
vvar = numpy.sqrt(numpy.outer(var, var))
Expand Down
39 changes: 34 additions & 5 deletions chaospy/descriptives/correlation/spearman.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""Spearman's correlation coefficient."""
import numpy
from scipy.stats import spearmanr

import numpoly
import chaospy

def Spearman(poly, dist, sample=10000, retall=False, **kws):

def Spearman(poly, dist=None, sample=10000, retall=False, **kws):
"""
Calculate Spearman's rank-order correlation coefficient.
Expand All @@ -24,10 +28,35 @@ def Spearman(poly, dist, sample=10000, retall=False, **kws):
The two-sided p-value for a hypothesis test whose null hypothesis
is that two sets of data are uncorrelated, has same dimension as
``rho``.
Examples:
>>> distribution = chaospy.MvNormal(
... [3, 4], [[2, .5], [.5, 1]])
>>> corr, pvalue = chaospy.Spearman(distribution, sample=50, retall=True)
>>> corr.round(4)
array([[1. , 0.603],
[0.603, 1. ]])
>>> pvalue.round(8)
array([[0.00e+00, 3.58e-06],
[3.58e-06, 0.00e+00]])
"""
if isinstance(poly, chaospy.Distribution):
poly, dist = numpoly.variable(len(poly)), poly
else:
poly = numpoly.polynomial(poly)
samples = dist.sample(sample, **kws)
poly = polynomials.flatten(poly)
Y = poly(*samples)
corr = numpy.eye(len(poly))
pval = numpy.zeros((len(poly), len(poly)))
evals = poly.ravel()(*samples)
assert len(poly) == len(evals)
for idx in range(len(poly)):
for idy in range(idx+1, len(poly)):
if idx == idy:
pass
spear = spearmanr(evals[idx], evals[idy])
pval[idx, idy] = pval[idy, idx] = spear.pvalue
corr[idx, idy] = corr[idy, idx] = spear.correlation
if retall:
return spearmanr(Y.T)
return spearmanr(Y.T)[0]
return corr, pval
return corr
8 changes: 6 additions & 2 deletions chaospy/descriptives/covariance.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ def Cov(poly, dist=None, **kws):
[ 0. , 2. , 0.5, 0. ],
[ 0. , 0.5, 1. , 0. ],
[ 0. , 0. , 0. , 225. ]])
>>> chaospy.Cov([1, 2, 3], dist)
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
"""
if dist is None:
dist, poly = poly, numpoly.variable(len(poly))
poly = numpoly.set_dimensions(poly, len(dist))
if not poly.isconstant:
return poly.tonumpy()**2
if poly.isconstant():
return numpy.zeros((len(poly), len(poly)))
poly = poly-E(poly, dist)
poly = numpoly.outer(poly, poly)
return E(poly, dist)
7 changes: 5 additions & 2 deletions chaospy/descriptives/kurtosis.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,17 @@ def Kurt(poly, dist=None, fisher=True, **kws):
>>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1])
>>> chaospy.Kurt(poly, dist).round(4)
array([nan, 6., 0., 15.])
>>> chaospy.Kurt(4., dist)
array(nan)
"""
adjust = 3 if fisher else 0

if dist is None:
dist, poly = poly, numpoly.variable(len(poly))
poly = numpoly.set_dimensions(poly, len(dist))
if not poly.isconstant:
return poly.tonumpy()**4-adjust
if poly.isconstant():
return numpy.full(poly.shape, numpy.nan)

poly = poly-E(poly, dist, **kws)
poly = numpoly.true_divide(poly, Std(poly, dist, **kws))
Expand Down
7 changes: 2 additions & 5 deletions chaospy/descriptives/percentile.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,11 @@ def Perc(poly, q, dist, sample=10000, **kws):
shape = poly.shape
poly = poly.flatten()

q = numpy.array(q)/100.
q = numpy.asarray(q).ravel()/100.
dim = len(dist)

# Interior
Z = dist.sample(sample, **kws)
if dim==1:
Z = (Z,)
q = numpy.array([q])
Z = dist.sample(sample, **kws).reshape(len(dist), sample)
poly1 = poly(*Z)

# Min/max
Expand Down
8 changes: 6 additions & 2 deletions chaospy/descriptives/skewness.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Skewness operator."""
import numpy
import numpoly

from .expected import E
Expand Down Expand Up @@ -31,12 +32,15 @@ def Skew(poly, dist=None, **kws):
>>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1-1])
>>> chaospy.Skew(poly, dist)
array([nan, 2., 0., 0.])
>>> chaospy.Skew(2., dist)
array(nan)
"""
if dist is None:
dist, poly = poly, numpoly.variable(len(poly))
poly = numpoly.set_dimensions(poly, len(dist))
if not poly.isconstant:
return poly.tonumpy()**3
if poly.isconstant():
return numpy.full(poly.shape, numpy.nan)

poly = poly-E(poly, dist, **kws)
poly = numpoly.true_divide(poly, Std(poly, dist, **kws))
Expand Down
8 changes: 5 additions & 3 deletions chaospy/descriptives/variance.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ def Var(poly, dist=None, **kws):
>>> poly = chaospy.polynomial([1, q0, q1, 10*q0*q1])
>>> chaospy.Var(poly, dist)
array([ 0., 1., 4., 800.])
>>> chaospy.Var(2., dist)
array(0.)
"""
if dist is None:
dist, poly = poly, numpoly.variable(len(poly))
poly = numpoly.set_dimensions(poly, len(dist))
if not poly.isconstant:

return poly.tonumpy()**2
if poly.isconstant():
return numpy.zeros(poly.shape)
poly = poly-E(poly, dist, **kws)
poly = numpoly.square(poly)
return E(poly, dist, **kws)
34 changes: 17 additions & 17 deletions chaospy/quadrature/gauss_patterson.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ def quad_gauss_patterson(order, domain):
Example:
>>> abscissas, weights = chaospy.quad_gauss_patterson(
... 2, chaospy.Uniform(0, 1))
>>> abscissas.round(4)
array([[0.0198, 0.1127, 0.2829, 0.5 , 0.7171, 0.8873, 0.9802]])
>>> weights.round(4)
array([0.0523, 0.1342, 0.2007, 0.2255, 0.2007, 0.1342, 0.0523])
... 1, chaospy.Iid(chaospy.Uniform(0, 1), 2))
>>> abscissas.round(3)
array([[0.113, 0.113, 0.113, 0.5 , 0.5 , 0.5 , 0.887, 0.887, 0.887],
[0.113, 0.5 , 0.887, 0.113, 0.5 , 0.887, 0.113, 0.5 , 0.887]])
>>> weights.round(3)
array([0.077, 0.123, 0.077, 0.123, 0.198, 0.123, 0.077, 0.123, 0.077])
"""
if isinstance(domain, chaospy.Distribution):
abscissas, weights = quad_gauss_patterson(
Expand All @@ -75,24 +77,22 @@ def quad_gauss_patterson(order, domain):
weights /= numpy.sum(weights)
return abscissas, weights

lower, upper = numpy.array(domain)
lower = numpy.asarray(lower).flatten()
upper = numpy.asarray(upper).flatten()

if len(lower) > 1:
domain = numpy.array(numpy.broadcast_arrays(*domain))
lower, upper = numpy.atleast_2d(domain.T).T
lower, upper, order = numpy.broadcast_arrays(lower, upper, order)

values = [quad_gauss_patterson(order[i], (domain[0][i], domain[1][i]))
for i in range(len(domain[0]))]
if lower.size > 1:
values = [quad_gauss_patterson(order_, (lower_, upper_))
for order_, lower_, upper_ in zip(order, lower, upper)]

abscissas = [_[0][0] for _ in values]
weights = [_[1] for _ in values]
abscissas = [value[0][0] for value in values]
weights = [value[1] for value in values]
abscissas = combine(abscissas).T
weights = numpy.prod(combine(weights), -1)

return abscissas, weights

order = sorted(PATTERSON_VALUES.keys())[order]
abscissas, weights = PATTERSON_VALUES[order]
order = sorted(PATTERSON_VALUES.keys())[int(order)]
abscissas, weights = PATTERSON_VALUES[int(order)]

abscissas = .5*(abscissas*(upper-lower)+upper+lower)
abscissas = abscissas.reshape(1, abscissas.size)
Expand Down
15 changes: 8 additions & 7 deletions chaospy/quadrature/genz_keister/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ def quad_genz_keister(order, dist, rule=24):
"""
Genz-Keister quadrature rule.
Eabsicassample:
Examples:
>>> abscissas, weights = quad_genz_keister(
... order=1, dist=chaospy.Uniform(0, 1))
>>> abscissas.round(4)
array([[0.0416, 0.5 , 0.9584]])
>>> weights.round(4)
array([0.1667, 0.6667, 0.1667])
... order=1, dist=chaospy.Iid(chaospy.Uniform(0, 1), 2))
>>> abscissas.round(2)
array([[0.04, 0.04, 0.04, 0.5 , 0.5 , 0.5 , 0.96, 0.96, 0.96],
[0.04, 0.5 , 0.96, 0.04, 0.5 , 0.96, 0.04, 0.5 , 0.96]])
>>> weights.round(2)
array([0.03, 0.11, 0.03, 0.11, 0.44, 0.11, 0.03, 0.11, 0.03])
"""
assert isinstance(rule, int)

Expand All @@ -51,5 +53,4 @@ def quad_genz_keister(order, dist, rule=24):
abscissas, weights = foo(order)
abscissas = dist.inv(scipy.special.ndtr(abscissas))
abscissas = abscissas.reshape(1, abscissas.size)

return abscissas, weights
4 changes: 0 additions & 4 deletions chaospy/quadrature/genz_keister/gk16.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,3 @@ def quad_genz_keister_16(order):
1.8684014894510604E-18,
)),
}

if __name__ == "__main__":
import doctest
doctest.testmod()
41 changes: 22 additions & 19 deletions chaospy/quadrature/leja.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,21 @@ def quad_leja(
The quadrature weights with ``weights.shape == (N,)``.
Example:
>>> abscissas, weights = quad_leja(3, chaospy.Normal(0, 1))
>>> abscissas.round(4)
array([[-2.7173, -1.4142, 0. , 1.7635]])
>>> weights.round(4)
array([0.022 , 0.1629, 0.6506, 0.1645])
>>> abscissas, weights = quad_leja(
... 2, chaospy.Iid(chaospy.Normal(0, 1), 2))
>>> abscissas.round(2)
array([[-1.41, -1.41, -1.41, 0. , 0. , 0. , 1.76, 1.76, 1.76],
[-1.41, 0. , 1.76, -1.41, 0. , 1.76, -1.41, 0. , 1.76]])
>>> weights.round(3)
array([0.05 , 0.133, 0.04 , 0.133, 0.359, 0.107, 0.04 , 0.107, 0.032])
"""
if len(dist) > 1:
if dist.stochastic_depedent:
if dist.stochastic_dependent:
raise chaospy.StochasticallyDependentError(
"Leja quadrature do not supper distribution with dependencies.")
if isinstance(order, int):
out = [quad_leja(order, _) for _ in dist]
else:
out = [quad_leja(order[_], dist[_]) for _ in range(len(dist))]

order = numpy.broadcast_to(order, len(dist))
out = [quad_leja(order[_], dist[_]) for _ in range(len(dist))]
abscissas = [_[0][0] for _ in out]
weights = [_[1] for _ in out]
abscissas = combine(abscissas).T
Expand All @@ -90,11 +90,14 @@ def objective(abscissas_):
def fmin(idx):
"""Bound minimization."""
try:
x, fx = fminbound(objective, abscissas[idx], abscissas[idx+1], full_output=1)[:2]
except UnboundLocalError:
x = abscissas[idx] + 0.5*(3-5**0.5) * (abscissas[idx+1]-abscissas[idx])
fx = objective(x)
return x, fx
xopt, fval, _, _ = fminbound(objective, abscissas[idx],
abscissas[idx+1], full_output=True)
# Hard coded solution to scipy/scipy#11207 for scipy < 1.5.0.
except UnboundLocalError: # pragma: no cover
xopt = abscissas[idx]+0.5*(3-5**0.5)*(
abscissas[idx+1]-abscissas[idx])
fx = objective(xopt)
return xopt, fval

opts, vals = zip(*[fmin(idx) for idx in range(len(abscissas)-1)])
index = numpy.argmin(vals)
Expand All @@ -110,10 +113,10 @@ def fmin(idx):
def create_weights(
nodes,
dist,
rule="fejer",
rule="clenshaw_curtis",
):
"""Create weights for the Laja method."""
_, poly, _ = chaospy.stieltjes(len(nodes)-1, dist)
poly = poly.flatten()
_, poly, _ = chaospy.stieltjes(len(nodes)-1, dist, rule=rule)
poly = poly.ravel()
weights = numpy.linalg.inv(poly(nodes))
return weights[:, 0]
Loading

0 comments on commit 272f235

Please sign in to comment.