404
+ +Page not found
+ + +diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..c9e542c --- /dev/null +++ b/404.html @@ -0,0 +1,143 @@ + + +
+ + + + +Page not found
+ + +LightGBMLSS - An extension of LightGBM to probabilistic forecasting
+ + + +datasets
+
+
+LightGBMLSS - An extension of LightGBM to probabilistic forecasting
+ + + +data_loader
+
+
+load_simulated_gaussian_data()
+
+Returns train/test dataframe of a simulated example.
+ +y int64: response +x int64: x-feature +X1:X10 int64: random noise features
+lightgbmlss/datasets/data_loader.py
5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 |
|
load_simulated_studentT_data()
+
+Returns train/test dataframe of a simulated example.
+ +y int64: response +x int64: x-feature +X1:X10 int64: random noise features
+lightgbmlss/datasets/data_loader.py
24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 |
|
distributions
+
+
+LightGBMLSS - An extension of LightGBM to probabilistic forecasting
+ + + +Beta
+
+
+Beta
+
+
+
+ Bases: DistributionClass
Beta distribution class.
+concentration1: torch.Tensor + 1st concentration parameter of the distribution (often referred to as alpha). +concentration0: torch.Tensor + 2nd concentration parameter of the distribution (often referred to as beta).
+https://pytorch.org/docs/stable/distributions.html#beta
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Beta.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
Cauchy
+
+
+Cauchy
+
+
+
+ Bases: DistributionClass
Cauchy distribution class.
+loc: torch.Tensor + Mode or median of the distribution. +scale: torch.Tensor + Half width at half maximum.
+https://pytorch.org/docs/stable/distributions.html#cauchy
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Cauchy.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
Expectile
+
+
+Expectile
+
+
+
+ Bases: DistributionClass
Expectile distribution class.
+expectile: List + List of specified expectiles.
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +expectiles: List + List of expectiles in increasing order. +penalize_crossing: bool + Whether to include a penalty term to discourage crossing of expectiles.
+ +lightgbmlss/distributions/Expectile.py
11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 |
|
Expectile_Torch
+
+
+
+ Bases: Distribution
PyTorch implementation of expectiles.
+expectiles : List[torch.Tensor] + List of expectiles. +penalize_crossing : bool + Whether to include a penalty term to discourage crossing of expectiles.
+ +lightgbmlss/distributions/Expectile.py
68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 |
|
log_prob(value, tau)
+
+Returns the log of the probability density function evaluated at value
.
value : torch.Tensor + Response for which log probability is to be calculated. +tau : List[torch.Tensor] + List of asymmetry parameters.
+torch.Tensor
+ Log probability of value
.
lightgbmlss/distributions/Expectile.py
88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 |
|
expectile_norm(tau=0.5, m=0, sd=1)
+
+Calculates expectiles from Normal distribution for given tau values. +For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html
+Arguments
+tau : np.ndarray + Vector of expectiles from the respective distribution. +m : np.ndarray + Mean of the Normal distribution. +sd : np.ndarray + Standard deviation of the Normal distribution.
+Returns
+np.ndarray
+ +lightgbmlss/distributions/Expectile.py
158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 |
|
expectile_pnorm(tau=0.5, m=0, sd=1)
+
+Normal Expectile Distribution Function. +For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html
+Arguments
+tau : np.ndarray + Vector of expectiles from the respective distribution. +m : np.ndarray + Mean of the Normal distribution. +sd : np.ndarray + Standard deviation of the Normal distribution.
+Returns
+tau : np.ndarray + Expectiles from the Normal distribution.
+ +lightgbmlss/distributions/Expectile.py
127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 |
|
Gamma
+
+
+Gamma
+
+
+
+ Bases: DistributionClass
Gamma distribution class.
+concentration: torch.Tensor + shape parameter of the distribution (often referred to as alpha) +rate: torch.Tensor + rate = 1 / scale of the distribution (often referred to as beta)
+https://pytorch.org/docs/stable/distributions.html#gamma
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Gamma.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
Gaussian
+
+
+Gaussian
+
+
+
+ Bases: DistributionClass
Gaussian distribution class.
+loc: torch.Tensor + Mean of the distribution (often referred to as mu). +scale: torch.Tensor + Standard deviation of the distribution (often referred to as sigma).
+https://pytorch.org/docs/stable/distributions.html#normal
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Gaussian.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
Gumbel
+
+
+Gumbel
+
+
+
+ Bases: DistributionClass
Gumbel distribution class.
+loc: torch.Tensor + Location parameter of the distribution. +scale: torch.Tensor + Scale parameter of the distribution.
+https://pytorch.org/docs/stable/distributions.html#gumbel
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Gumbel.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
Laplace
+
+
+Laplace
+
+
+
+ Bases: DistributionClass
Laplace distribution class.
+loc: torch.Tensor + Mean of the distribution. +scale: torch.Tensor + Scale of the distribution.
+https://pytorch.org/docs/stable/distributions.html#laplace
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Laplace.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
LogNormal
+
+
+LogNormal
+
+
+
+ Bases: DistributionClass
LogNormal distribution class.
+loc: torch.Tensor + Mean of log of distribution. +scale: torch.Tensor + Standard deviation of log of the distribution.
+https://pytorch.org/docs/stable/distributions.html#lognormal
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/LogNormal.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
Mixture
+
+
+Mixture
+
+
+
+ Bases: MixtureDistributionClass
Mixture-Density distribution class.
+Implements a mixture-density distribution for univariate targets, where all components are from different +parameterizations of the same distribution-type. A mixture-density distribution is a concept used to model a +complex distribution that arises from combining multiple simpler distributions. The Mixture-Density distribution +is parameterized by a categorical selecting distribution (over M components) and M-component distributions. For more +information on the Mixture-Density distribution, see:
+Bishop, C. M. (1994). Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK.
+
+Inherits the distributional parameters from the component distributions.
+https://pytorch.org/docs/stable/distributions.html#mixturesamefamily
+component_distribution: torch.distributions.Distribution + Distribution class for the components of the mixture distribution. Has to be one of the available + univariate distributions of the package. +M: int + Number of components in the mixture distribution. +hessian_mode: str + Mode for computing the Hessian. Must be one of the following:
+ - "individual": Each parameter is treated as a separate tensor. As a result, the Hessian corresponds to the
+ second-order derivative with respect to that specific parameter only. The resulting Hessians capture the
+ curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can
+ be more accurate.
+
+ - "grouped": Each tensor contains all parameters for a specific parameter-type, e.g., for a Gaussian-Mixture
+ with M=2, loc=[loc_1, loc_2], scale=[scale_1, scale_2], and mix_prob=[mix_prob_1, mix_prob_2]. When
+ computing the Hessian, the derivatives for all parameters in the respective tensor are calculated jointly.
+ The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter-type. This is usually
+ less runtime intensive, but can be less accurate.
+
+tau: float, non-negative scalar temperature. + The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a "soft" + version of a categorical distribution. It’s a way to draw samples from a categorical distribution in a + differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of + categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a + Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. + Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is + defined as:
+ s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}}
+
+where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness
+of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau
+approaches infty, the mixing probabilities become more uniform. For more information we refer to
+
+ Jang, E., Gu, Shixiang and Poole, B. "Categorical Reparameterization with Gumbel-Softmax", ICLR, 2017.
+
+
+ lightgbmlss/distributions/Mixture.py
6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 |
|
NegativeBinomial
+
+
+NegativeBinomial
+
+
+
+ Bases: DistributionClass
NegativeBinomial distribution class.
+total_count: torch.Tensor + Non-negative number of negative Bernoulli trials to stop. +probs: torch.Tensor + Event probabilities of success in the half open interval [0, 1). +logits: torch.Tensor + Event log-odds for probabilities of success.
+https://pytorch.org/docs/stable/distributions.html#negativebinomial
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn_total_count: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential), "softplus" (softplus) or "relu" (rectified linear unit). +response_fn_probs: str + Response function for transforming the distributional parameters to the correct support. Options are + "sigmoid" (sigmoid). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/NegativeBinomial.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 |
|
Poisson
+
+
+Poisson
+
+
+
+ Bases: DistributionClass
Poisson distribution class.
+rate: torch.Tensor + Rate parameter of the distribution (often referred to as lambda).
+https://pytorch.org/docs/stable/distributions.html#poisson
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential), "softplus" (softplus) or "relu" (rectified linear unit). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/Poisson.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 |
|
SplineFlow
+
+
+SplineFlow
+
+
+
+ Bases: NormalizingFlowClass
Spline Flow class.
+The spline flow is a normalizing flow based on element-wise rational spline bijections of linear and quadratic +order (Durkan et al., 2019; Dolatabadi et al., 2020). Rational splines are functions that are comprised of segments +that are the ratio of two polynomials. Rational splines offer an excellent combination of functional flexibility +whilst maintaining a numerically stable inverse.
+For more details, see: +- Durkan, C., Bekasov, A., Murray, I. and Papamakarios, G. Neural Spline Flows. NeurIPS 2019. +- Dolatabadi, H. M., Erfani, S. and Leckie, C., Invertible Generative Modeling using Linear Rational Splines. AISTATS 2020.
+https://docs.pyro.ai/en/stable/distributions.html#pyro.distributions.transforms.Spline
+target_support: str + The target support. Options are + - "real": [-inf, inf] + - "positive": [0, inf] + - "positive_integer": [0, 1, 2, 3, ...] + - "unit_interval": [0, 1] +count_bins: int + The number of segments comprising the spline. +bound: float + The quantity "K" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the + "K" value, you can control the size of the bounding box and consequently control the range of inputs that + the spline transform operates on. Larger values of "K" will result in a wider valid range for the spline + transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen + based on the range of the data. +order: str + The order of the spline. Options are "linear" or "quadratic". +stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD" or "L2". +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/SplineFlow.py
9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 |
|
StudentT
+
+
+StudentT
+
+
+
+ Bases: DistributionClass
Student-T Distribution Class
+df: torch.Tensor + Degrees of freedom. +loc: torch.Tensor + Mean of the distribution. +scale: torch.Tensor + Scale of the distribution.
+https://pytorch.org/docs/stable/distributions.html#studentt
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/StudentT.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 |
|
Weibull
+
+
+Weibull
+
+
+
+ Bases: DistributionClass
Weibull distribution class.
+scale: torch.Tensor + Scale parameter of distribution (lambda). +concentration: torch.Tensor + Concentration parameter of distribution (k/shape).
+https://pytorch.org/docs/stable/distributions.html#weibull
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/Weibull.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 |
|
ZABeta
+
+
+ZABeta
+
+
+
+ Bases: DistributionClass
Zero-Adjusted Beta distribution class.
+The zero-adjusted Beta distribution is similar to the Beta distribution but allows zeros as y values.
+concentration1: torch.Tensor + 1st concentration parameter of the distribution (often referred to as alpha). +concentration0: torch.Tensor + 2nd concentration parameter of the distribution (often referred to as beta). +gate: torch.Tensor + Probability of zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/ZABeta.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 |
|
ZAGamma
+
+
+ZAGamma
+
+
+
+ Bases: DistributionClass
Zero-Adjusted Gamma distribution class.
+The zero-adjusted Gamma distribution is similar to the Gamma distribution but allows zeros as y values.
+concentration: torch.Tensor + shape parameter of the distribution (often referred to as alpha) +rate: torch.Tensor + rate = 1 / scale of the distribution (often referred to as beta) +gate: torch.Tensor + Probability of zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/ZAGamma.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 |
|
ZALN
+
+
+ZALN
+
+
+
+ Bases: DistributionClass
Zero-Adjusted LogNormal distribution class.
+The zero-adjusted Log-Normal distribution is similar to the Log-Normal distribution but allows zeros as y values.
+loc: torch.Tensor + Mean of log of distribution. +scale: torch.Tensor + Standard deviation of log of the distribution. +gate: torch.Tensor + Probability of zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential) or "softplus" (softplus). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/ZALN.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 |
|
ZINB
+
+
+ZINB
+
+
+
+ Bases: DistributionClass
Zero-Inflated Negative Binomial distribution class.
+total_count: torch.Tensor + Non-negative number of negative Bernoulli trials to stop. +probs: torch.Tensor + Event probabilities of success in the half open interval [0, 1). +gate: torch.Tensor + Probability of extra zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn_total_count: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential), "softplus" (softplus) or "relu" (rectified linear unit). +response_fn_probs: str + Response function for transforming the distributional parameters to the correct support. Options are + "sigmoid" (sigmoid). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/ZINB.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 |
|
ZIPoisson
+
+
+ZIPoisson
+
+
+
+ Bases: DistributionClass
Zero-Inflated Poisson distribution class.
+rate: torch.Tensor + Rate parameter of the distribution (often referred to as lambda). +gate: torch.Tensor + Probability of extra zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121
+stabilization: str + Stabilization method for the Gradient and Hessian. Options are "None", "MAD", "L2". +response_fn: str + Response function for transforming the distributional parameters to the correct support. Options are + "exp" (exponential), "softplus" (softplus) or "relu" (rectified linear unit). +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/ZIPoisson.py
6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 |
|
distribution_utils
+
+
+DistributionClass
+
+
+Generic class that contains general functions for univariate distributions.
+distribution: torch.distributions.Distribution + PyTorch Distribution class. +univariate: bool + Whether the distribution is univariate or multivariate. +discrete: bool + Whether the support of the distribution is discrete or continuous. +n_dist_param: int + Number of distributional parameters. +stabilization: str + Stabilization method. +param_dict: Dict[str, Any] + Dictionary that maps distributional parameters to their response scale. +distribution_arg_names: List + List of distributional parameter names. +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function. +tau: List + List of expectiles. Only used for Expectile distributon. +penalize_crossing: bool + Whether to include a penalty term to discourage crossing of expectiles. Only used for Expectile distribution.
+ +lightgbmlss/distributions/distribution_utils.py
17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 |
|
calculate_start_values(target, max_iter=50)
+
+Function that calculates the starting values for each distributional parameter.
+target: np.ndarray + Data from which starting values are calculated. +max_iter: int + Maximum number of iterations.
+loss: float + Loss value. +start_values: np.ndarray + Starting values for each distributional parameter.
+ +lightgbmlss/distributions/distribution_utils.py
178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 |
|
compute_gradients_and_hessians(loss, predt, weights)
+
+Calculates gradients and hessians.
+Output gradients and hessians have shape (n_samples*n_outputs, 1).
+loss: torch.Tensor + Loss. +predt: torch.Tensor + List of predicted parameters. +weights: np.ndarray + Weights.
+grad: torch.Tensor + Gradients. +hess: torch.Tensor + Hessians.
+ +lightgbmlss/distributions/distribution_utils.py
423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 |
|
crps_score(y, yhat_dist)
+
+Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples.
+y: torch.Tensor + Response variable of shape (n_observations,1). +yhat_dist: torch.Tensor + Predicted samples of shape (n_samples, n_observations).
+crps: torch.Tensor + CRPS score.
+Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. +Journal of the American Statistical Association. 102. 359-378.
+https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549
+ +lightgbmlss/distributions/distribution_utils.py
544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 |
|
dist_select(target, candidate_distributions, max_iter=100, plot=False, figure_size=(10, 5))
+
+Function that selects the most suitable distribution among the candidate_distributions for the target variable, +based on the NegLogLikelihood (lower is better).
+target: np.ndarray + Response variable. +candidate_distributions: List + List of candidate distributions. +max_iter: int + Maximum number of iterations for the optimization. +plot: bool + If True, a density plot of the actual and fitted distribution is created. +figure_size: tuple + Figure size of the density plot.
+fit_df: pd.DataFrame + Dataframe with the loss values of the fitted candidate distributions.
+ +lightgbmlss/distributions/distribution_utils.py
598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 |
|
draw_samples(predt_params, n_samples=1000, seed=123)
+
+Function that draws n_samples from a predicted distribution.
+predt_params: pd.DataFrame + pd.DataFrame with predicted distributional parameters. +n_samples: int + Number of sample to draw from predicted response distribution. +seed: int + Manual seed.
+pred_dist: pd.DataFrame + DataFrame with n_samples drawn from predicted response distribution.
+ +lightgbmlss/distributions/distribution_utils.py
298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 |
|
get_params_loss(predt, target, start_values, requires_grad=False)
+
+Function that returns the predicted parameters and the loss.
+predt: np.ndarray + Predicted values. +target: torch.Tensor + Target values. +start_values: List + Starting values for each distributional parameter. +requires_grad: bool + Whether to add to the computational graph or not.
+predt: torch.Tensor + Predicted parameters. +loss: torch.Tensor + Loss value.
+ +lightgbmlss/distributions/distribution_utils.py
236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 |
|
loss_fn_start_values(params, target)
+
+Function that calculates the loss for a given set of distributional parameters. Only used for calculating +the loss for the start values.
+params: torch.Tensor + Distributional parameters. +target: torch.Tensor + Target values.
+loss: torch.Tensor + Loss value.
+ +lightgbmlss/distributions/distribution_utils.py
140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 |
|
metric_fn(predt, data)
+
+Function that evaluates the predictions using the negative log-likelihood.
+predt: np.ndarray + Predicted values. +data: lgb.Dataset + Data used for training.
+name: str + Name of the evaluation metric. +nll: float + Negative log-likelihood. +is_higher_better: bool + Whether a higher value of the metric is better or not.
+ +lightgbmlss/distributions/distribution_utils.py
108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 |
|
objective_fn(predt, data)
+
+Function to estimate gradients and hessians of distributional parameters.
+predt: np.ndarray + Predicted values. +data: lgb.DMatrix + Data used for training.
+grad: np.ndarray + Gradient. +hess: np.ndarray + Hessian.
+ +lightgbmlss/distributions/distribution_utils.py
70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 |
|
predict_dist(booster, data, start_values, pred_type='parameters', n_samples=1000, quantiles=[0.1, 0.5, 0.9], seed=123)
+
+Function that predicts from the trained model.
+booster : lgb.Booster + Trained model. +data : pd.DataFrame + Data to predict from. +start_values : np.ndarray. + Starting values for each distributional parameter. +pred_type : str + Type of prediction: + - "samples" draws n_samples from the predicted distribution. + - "quantiles" calculates the quantiles from the predicted distribution. + - "parameters" returns the predicted distributional parameters. + - "expectiles" returns the predicted expectiles. +n_samples : int + Number of samples to draw from the predicted distribution. +quantiles : List[float] + List of quantiles to calculate from the predicted distribution. +seed : int + Seed for random number generator used to draw samples from the predicted distribution.
+pred : pd.DataFrame + Predictions.
+ +lightgbmlss/distributions/distribution_utils.py
338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 |
|
stabilize_derivative(input_der, type='MAD')
+
+Function that stabilizes Gradients and Hessians.
+As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important +that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, +the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. +Another way to improve convergence might be to standardize the response variable. This is especially useful if the +range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and +the standardization of the response are not always advised but need to be carefully considered. +Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173
+input_der : torch.Tensor + Input derivative, either Gradient or Hessian. +type: str + Stabilization method. Can be either "None", "MAD" or "L2".
+stab_der : torch.Tensor + Stabilized Gradient or Hessian.
+ +lightgbmlss/distributions/distribution_utils.py
501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 |
|
flow_utils
+
+
+NormalizingFlowClass
+
+
+Generic class that contains general functions for normalizing flows.
+base_dist: torch.distributions.Distribution + PyTorch Distribution class. Currently only Normal is supported. +flow_transform: Transform + Specify the normalizing flow transform. +count_bins: Optional[int] + The number of segments comprising the spline. Only used if flow_transform is Spline. +bound: Optional[float] + The quantity "K" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the + "K" value, you can control the size of the bounding box and consequently control the range of inputs that + the spline transform operates on. Larger values of "K" will result in a wider valid range for the spline + transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen + based on the range of the data. Only used if flow_transform is Spline. +order: Optional[str] + The order of the spline. Options are "linear" or "quadratic". Only used if flow_transform is Spline. +n_dist_param: int + Number of parameters. +param_dict: Dict[str, Any] + Dictionary that maps parameters to their response scale. +target_transform: Transform + Specify the target transform. +discrete: bool + Whether the target is discrete or not. +univariate: bool + Whether the distribution is univariate or multivariate. +stabilization: str + Stabilization method. Options are "None", "MAD" or "L2". +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood) or "crps" (continuous ranked probability score). + Note that if "crps" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. + Hence, using the CRPS disregards any variation in the curvature of the loss function.
+ +lightgbmlss/distributions/flow_utils.py
19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 |
|
calculate_start_values(target, max_iter=50)
+
+Function that calculates starting values for each parameter.
+target: np.ndarray + Data from which starting values are calculated. +max_iter: int + Maximum number of iterations.
+loss: float + Loss value. +start_values: np.ndarray + Starting values for each parameter.
+ +lightgbmlss/distributions/flow_utils.py
152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 |
|
compute_gradients_and_hessians(loss, predt, weights)
+
+Calculates gradients and hessians.
+Output gradients and hessians have shape (n_samples*n_outputs, 1).
+loss: torch.Tensor + Loss. +predt: torch.Tensor + List of predicted parameters. +weights: np.ndarray + Weights.
+grad: torch.Tensor + Gradients. +hess: torch.Tensor + Hessians.
+ +lightgbmlss/distributions/flow_utils.py
483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 |
|
create_spline_flow(input_dim=None)
+
+Function that constructs a Normalizing Flow.
+input_dim: int + Input dimension.
+spline_flow: Transform + Normalizing Flow.
+ +lightgbmlss/distributions/flow_utils.py
287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 |
|
crps_score(y, yhat_dist)
+
+Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples.
+y: torch.Tensor + Response variable of shape (n_observations,1). +yhat_dist: torch.Tensor + Predicted samples of shape (n_samples, n_observations).
+crps: torch.Tensor + CRPS score.
+Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. +Journal of the American Statistical Association. 102. 359-378.
+https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549
+ +lightgbmlss/distributions/flow_utils.py
583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 |
|
draw_samples(predt_params, n_samples=1000, seed=123)
+
+Function that draws n_samples from a predicted distribution.
+predt_params: pd.DataFrame + pd.DataFrame with predicted distributional parameters. +n_samples: int + Number of sample to draw from predicted response distribution. +seed: int + Manual seed.
+pred_dist: pd.DataFrame + DataFrame with n_samples drawn from predicted response distribution.
+ +lightgbmlss/distributions/flow_utils.py
362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 |
|
flow_select(target, candidate_flows, max_iter=100, plot=False, figure_size=(10, 5))
+
+Function that selects the most suitable normalizing flow specification among the candidate_flow for the +target variable, based on the NegLogLikelihood (lower is better).
+target: np.ndarray + Response variable. +candidate_flows: List + List of candidate normalizing flow specifications. +max_iter: int + Maximum number of iterations for the optimization. +plot: bool + If True, a density plot of the actual and fitted distribution is created. +figure_size: tuple + Figure size of the density plot.
+fit_df: pd.DataFrame + Dataframe with the loss values of the fitted normalizing flow.
+ +lightgbmlss/distributions/flow_utils.py
637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 |
|
get_params_loss(predt, target, start_values, requires_grad=False)
+
+Function that returns the predicted parameters and the loss.
+predt: np.ndarray + Predicted values. +target: torch.Tensor + Target values. +start_values: List + Starting values for each parameter.
+predt: torch.Tensor + Predicted parameters. +loss: torch.Tensor + Loss value.
+ +lightgbmlss/distributions/flow_utils.py
231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 |
|
metric_fn(predt, data)
+
+Function that evaluates the predictions using the specified loss function.
+predt: np.ndarray + Predicted values. +data: lgb.Dataset + Data used for training.
+name: str + Name of the evaluation metric. +loss: float + Loss value.
+ +lightgbmlss/distributions/flow_utils.py
122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 |
|
objective_fn(predt, data)
+
+Function to estimate gradients and hessians of normalizing flow parameters.
+predt: np.ndarray + Predicted values. +data: lgb.Dataset + Data used for training.
+grad: np.ndarray + Gradient. +hess: np.ndarray + Hessian.
+ +lightgbmlss/distributions/flow_utils.py
84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 |
|
predict_dist(booster, data, start_values, pred_type='parameters', n_samples=1000, quantiles=[0.1, 0.5, 0.9], seed=123)
+
+Function that predicts from the trained model.
+booster : lgb.Booster + Trained model. +start_values : np.ndarray + Starting values for each distributional parameter. +data : pd.DataFrame + Data to predict from. +pred_type : str + Type of prediction: + - "samples" draws n_samples from the predicted distribution. + - "quantiles" calculates the quantiles from the predicted distribution. + - "parameters" returns the predicted distributional parameters. + - "expectiles" returns the predicted expectiles. +n_samples : int + Number of samples to draw from the predicted distribution. +quantiles : List[float] + List of quantiles to calculate from the predicted distribution. +seed : int + Seed for random number generator used to draw samples from the predicted distribution.
+pred : pd.DataFrame + Predictions.
+ +lightgbmlss/distributions/flow_utils.py
404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 |
|
replace_parameters(params, flow_dist)
+
+Replace parameters with estimated ones.
+params: torch.Tensor + Estimated parameters. +flow_dist: Transform + Normalizing Flow.
+params_list: List + List of estimated parameters. +flow_dist: Transform + Normalizing Flow with estimated parameters.
+ +lightgbmlss/distributions/flow_utils.py
321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 |
|
stabilize_derivative(input_der, type='MAD')
+
+Function that stabilizes Gradients and Hessians.
+Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable +in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable +so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve +convergence might be to standardize the response variable. This is especially useful if the range of the +response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the +standardization of the response are not always advised but need to be carefully considered.
+https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173
+input_der : torch.Tensor + Input derivative, either Gradient or Hessian. +type: str + Stabilization method. Can be either "None", "MAD" or "L2".
+stab_der : torch.Tensor + Stabilized Gradient or Hessian.
+ +lightgbmlss/distributions/flow_utils.py
537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 |
|
mixture_distribution_utils
+
+
+MixtureDistributionClass
+
+
+Generic class that contains general functions for mixed-density distributions.
+distribution: torch.distributions.Distribution + PyTorch Distribution class. +M: int + Number of components in the mixture distribution. +temperature: float + Temperature for the Gumbel-Softmax distribution. +hessian_mode: str + Mode for computing the Hessian. Must be one of the following:
+ - "individual": Each parameter is treated as a separate tensor. As a result, when the Hessian is calculated
+ for each gradient element, this corresponds to the second derivative with respect to that specific tensor
+ element only. This means the resulting Hessians capture the curvature of the loss w.r.t. each individual
+ parameter. This is usually more runtime intensive, but can also be more accurate.
+
+ - "grouped": Each parameter is a tensor containing all values for a specific parameter type,
+ e.g., loc, scale, or mixture probabilities for a Gaussian Mixture. When computing the Hessian for each
+ gradient element, the Hessian matrix for all the values in the respective tensor are calculated together.
+ The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter type tensor. This is
+ usually less runtime intensive, but can be less accurate.
+
+univariate: bool + Whether the distribution is univariate or multivariate. +discrete: bool + Whether the support of the distribution is discrete or continuous. +n_dist_param: int + Number of distributional parameters. +stabilization: str + Stabilization method. +param_dict: Dict[str, Any] + Dictionary that maps distributional parameters to their response scale. +distribution_arg_names: List + List of distributional parameter names. +loss_fn: str + Loss function. Options are "nll" (negative log-likelihood).
+ +lightgbmlss/distributions/mixture_distribution_utils.py
47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 |
|
calculate_start_values(target, max_iter=50)
+
+Function that calculates the starting values for each distributional parameter.
+target: np.ndarray + Data from which starting values are calculated. +max_iter: int + Maximum number of iterations.
+loss: float + Loss value. +start_values: np.ndarray + Starting values for each distributional parameter.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 |
|
compute_gradients_and_hessians(loss, predt, weights)
+
+Calculates gradients and hessians.
+Output gradients and hessians have shape (n_samples*n_outputs, 1).
+loss: torch.Tensor + Loss. +predt: torch.Tensor + List of predicted parameters. +weights: np.ndarray + Weights.
+grad: torch.Tensor + Gradients. +hess: torch.Tensor + Hessians.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 |
|
create_mixture_distribution(params)
+
+Function that creates a mixture distribution.
+params: torch.Tensor + Distributional parameters.
+dist: torch.distributions.Distribution + Mixture distribution.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 |
|
dist_select(target, candidate_distributions, max_iter=100, plot=False, figure_size=(8, 5))
+
+Function that selects the most suitable distribution among the candidate_distributions for the target variable, +based on the NegLogLikelihood (lower is better).
+target: np.ndarray + Response variable. +candidate_distributions: List + List of candidate distributions. +max_iter: int + Maximum number of iterations for the optimization. +plot: bool + If True, a density plot of the actual and fitted distribution is created. +figure_size: tuple + Figure size of the density plot.
+fit_df: pd.DataFrame + Dataframe with the loss values of the fitted candidate distributions.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 |
|
draw_samples(predt_params, n_samples=1000, seed=123)
+
+Function that draws n_samples from a predicted distribution.
+predt_params: pd.DataFrame + pd.DataFrame with predicted distributional parameters. +n_samples: int + Number of sample to draw from predicted response distribution. +seed: int + Manual seed.
+pred_dist: pd.DataFrame + DataFrame with n_samples drawn from predicted response distribution.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 |
|
get_params_loss(predt, target, start_values, requires_grad=False)
+
+Function that returns the predicted parameters and the loss.
+predt: np.ndarray + Predicted values. +target: torch.Tensor + Target values. +start_values: List + Starting values for each distributional parameter. +requires_grad: bool + Whether to add to the computational graph or not.
+predt: torch.Tensor + Predicted parameters. +loss: torch.Tensor + Loss value.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 |
|
loss_fn_start_values(params, target)
+
+Function that calculates the loss for a given set of distributional parameters. Only used for calculating +the loss for the start values.
+params: torch.Tensor + Distributional parameters. +target: torch.Tensor + Target values.
+loss: torch.Tensor + Loss value.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 |
|
metric_fn(predt, data)
+
+Function that evaluates the predictions using the negative log-likelihood.
+predt: np.ndarray + Predicted values. +data: lgb.Dataset + Data used for training.
+name: str + Name of the evaluation metric. +nll: float + Negative log-likelihood. +is_higher_better: bool + Whether a higher value of the metric is better or not.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 |
|
objective_fn(predt, data)
+
+Function to estimate gradients and hessians of distributional parameters.
+predt: np.ndarray + Predicted values. +data: lgb.DMatrix + Data used for training.
+grad: np.ndarray + Gradient. +hess: np.ndarray + Hessian.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 |
|
predict_dist(booster, data, start_values, pred_type='parameters', n_samples=1000, quantiles=[0.1, 0.5, 0.9], seed=123)
+
+Function that predicts from the trained model.
+booster : lgb.Booster + Trained model. +data : pd.DataFrame + Data to predict from. +start_values : np.ndarray. + Starting values for each distributional parameter. +pred_type : str + Type of prediction: + - "samples" draws n_samples from the predicted distribution. + - "quantiles" calculates the quantiles from the predicted distribution. + - "parameters" returns the predicted distributional parameters. +n_samples : int + Number of samples to draw from the predicted distribution. +quantiles : List[float] + List of quantiles to calculate from the predicted distribution. +seed : int + Seed for random number generator used to draw samples from the predicted distribution.
+pred : pd.DataFrame + Predictions.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 |
|
stabilize_derivative(input_der, type='MAD')
+
+Function that stabilizes Gradients and Hessians.
+As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important +that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, +the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. +Another way to improve convergence might be to standardize the response variable. This is especially useful if the +range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and +the standardization of the response are not always advised but need to be carefully considered. +Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173
+input_der : torch.Tensor + Input derivative, either Gradient or Hessian. +type: str + Stabilization method. Can be either "None", "MAD" or "L2".
+stab_der : torch.Tensor + Stabilized Gradient or Hessian.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 |
|
get_component_distributions()
+
+Function that returns component distributions for creating a mixing distribution.
+None
+distns: List + List of all available distributions.
+ +lightgbmlss/distributions/mixture_distribution_utils.py
19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 |
|
zero_inflated
+
+
+ZeroAdjustedBeta
+
+
+
+ Bases: ZeroInflatedDistribution
A Zero-Adjusted Beta distribution.
+concentration1: torch.Tensor + 1st concentration parameter of the distribution (often referred to as alpha). +concentration0: torch.Tensor + 2nd concentration parameter of the distribution (often referred to as beta). +gate: torch.Tensor + Probability of zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py
+ +lightgbmlss/distributions/zero_inflated.py
299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 |
|
ZeroAdjustedGamma
+
+
+
+ Bases: ZeroInflatedDistribution
A Zero-Adjusted Gamma distribution.
+concentration: torch.Tensor + shape parameter of the distribution (often referred to as alpha) +rate: torch.Tensor + rate = 1 / scale of the distribution (often referred to as beta) +gate: torch.Tensor + Probability of zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py
+ +lightgbmlss/distributions/zero_inflated.py
221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 |
|
ZeroAdjustedLogNormal
+
+
+
+ Bases: ZeroInflatedDistribution
A Zero-Adjusted Log-Normal distribution.
+loc: torch.Tensor + Mean of log of distribution. +scale: torch.Tensor + Standard deviation of log of the distribution. +gate: torch.Tensor + Probability of zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py
+ +lightgbmlss/distributions/zero_inflated.py
260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 |
|
ZeroInflatedDistribution
+
+
+
+ Bases: TorchDistribution
Generic Zero Inflated distribution.
+This can be used directly or can be used as a base class as e.g. for
+:class:ZeroInflatedPoisson
and :class:ZeroInflatedNegativeBinomial
.
gate : torch.Tensor + Probability of extra zeros given via a Bernoulli distribution. +base_dist : torch.distributions.Distribution + The base distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L18
+ +lightgbmlss/distributions/zero_inflated.py
20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 |
|
ZeroInflatedNegativeBinomial
+
+
+
+ Bases: ZeroInflatedDistribution
A Zero Inflated Negative Binomial distribution.
+total_count: torch.Tensor + Non-negative number of negative Bernoulli trial. +probs: torch.Tensor + Event probabilities of success in the half open interval [0, 1). +logits: torch.Tensor + Event log-odds of success (log(p/(1-p))). +gate: torch.Tensor + Probability of extra zeros given via a Bernoulli distribution.
+lightgbmlss/distributions/zero_inflated.py
174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 |
|
ZeroInflatedPoisson
+
+
+
+ Bases: ZeroInflatedDistribution
A Zero-Inflated Poisson distribution.
+rate: torch.Tensor + The rate of the Poisson distribution. +gate: torch.Tensor + Probability of extra zeros given via a Bernoulli distribution.
+https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121
+ +lightgbmlss/distributions/zero_inflated.py
142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 |
|
model
+
+
+LightGBMLSS
+
+
+LightGBMLSS model class
+dist : Distribution + DistributionClass object. + start_values : np.ndarray + Starting values for each distributional parameter.
+ +lightgbmlss/model.py
51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 |
|
cv(params, train_set, num_boost_round=100, folds=None, nfold=5, stratified=True, shuffle=True, init_model=None, feature_name='auto', categorical_feature='auto', fpreproc=None, seed=123, callbacks=None, eval_train_metric=False, return_cvbooster=False)
+
+Function to cross-validate a LightGBMLSS model with given parameters.
+params : dict
+ Parameters for training. Values passed through params
take precedence over those
+ supplied via arguments.
+train_set : Dataset
+ Data to be trained on.
+num_boost_round : int, optional (default=100)
+ Number of boosting iterations.
+folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None)
+ If generator or iterator, it should yield the train and test indices for each fold.
+ If object, it should be one of the scikit-learn splitter classes
+ (https://scikit-learn.org/stable/modules/classes.html#splitter-classes)
+ and have split
method.
+ This argument has highest priority over other data split arguments.
+nfold : int, optional (default=5)
+ Number of folds in CV.
+stratified : bool, optional (default=True)
+ Whether to perform stratified sampling.
+shuffle : bool, optional (default=True)
+ Whether to shuffle before splitting data.
+init_model : str, pathlib.Path, Booster or None, optional (default=None)
+ Filename of LightGBM model or Booster instance used for continue training.
+feature_name : list of str, or 'auto', optional (default="auto")
+ Feature names.
+ If 'auto' and data is pandas DataFrame, data columns names are used.
+categorical_feature : list of str or int, or 'auto', optional (default="auto")
+ Categorical features.
+ If list of int, interpreted as indices.
+ If list of str, interpreted as feature names (need to specify feature_name
as well).
+ If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used.
+ All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647).
+ Large values could be memory consuming. Consider using consecutive integers starting from zero.
+ All negative values in categorical features will be treated as missing values.
+ The output cannot be monotonically constrained with respect to a categorical feature.
+ Floating point numbers in categorical features will be rounded towards 0.
+fpreproc : callable or None, optional (default=None)
+ Preprocessing function that takes (dtrain, dtest, params)
+ and returns transformed versions of those.
+seed : int, optional (default=0)
+ Seed used to generate the folds (passed to numpy.random.seed).
+callbacks : list of callable, or None, optional (default=None)
+ List of callback functions that are applied at each iteration.
+ See Callbacks in Python API for more information.
+eval_train_metric : bool, optional (default=False)
+ Whether to display the train metric in progress.
+ The score of the metric is calculated again after each training step, so there is some impact on performance.
+return_cvbooster : bool, optional (default=False)
+ Whether to return Booster models trained on each fold through CVBooster
.
eval_hist : dict
+ Evaluation history.
+ The dictionary has the following format:
+ {'metric1-mean': [values], 'metric1-stdv': [values],
+ 'metric2-mean': [values], 'metric2-stdv': [values],
+ ...}.
+ If return_cvbooster=True
, also returns trained boosters wrapped in a CVBooster
object via cvbooster
key.
lightgbmlss/model.py
184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 |
|
expectile_plot(X, feature='x', expectile='0.05', plot_type='Partial_Dependence')
+
+LightGBMLSS function for plotting expectile SHapley values.
+ +Train/Test Data
+feature: str + Specifies which feature to use for plotting Partial_Dependence plot. +expectile: str + Specifies which expectile to plot. +plot_type: str + Specifies which SHapley-plot to visualize. Currently, "Partial_Dependence" and "Feature_Importance" + are supported.
+ +lightgbmlss/model.py
520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 |
|
hyper_opt(hp_dict, train_set, num_boost_round=500, nfold=10, early_stopping_rounds=20, max_minutes=10, n_trials=None, study_name=None, silence=False, seed=None, hp_seed=None)
+
+Function to tune hyperparameters using optuna.
+hp_dict: dict + Dictionary of hyperparameters to tune. +train_set: lgb.Dataset + Training data. +num_boost_round: int + Number of boosting iterations. +nfold: int + Number of folds in CV. +early_stopping_rounds: int + Activates early stopping. Cross-Validation metric (average of validation + metric computed over CV folds) needs to improve at least once in + every early_stopping_rounds round(s) to continue training. + The last entry in the evaluation history will represent the best iteration. + If there's more than one metric in the eval_metric parameter given in + params, the last metric will be used for early stopping. +max_minutes: int + Time budget in minutes, i.e., stop study after the given number of minutes. +n_trials: int + The number of trials. If this argument is set to None, there is no limitation on the number of trials. +study_name: str + Name of the hyperparameter study. +silence: bool + Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. +seed: int + Seed used to generate the folds (passed to numpy.random.seed). +hp_seed: int + Seed for random number generator used in the Bayesian hyper-parameter search.
+opt_params : dict + Optimal hyper-parameters.
+ +lightgbmlss/model.py
287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 |
|
load_model(model_path)
+
+
+ staticmethod
+
+
+Load the model from a file.
+model_path : str + The path to the saved model.
+The loaded model.
+ +lightgbmlss/model.py
601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 |
|
plot(X, feature='x', parameter='loc', max_display=15, plot_type='Partial_Dependence')
+
+LightGBMLSS SHap plotting function.
+X: pd.DataFrame + Train/Test Data +feature: str + Specifies which feature is to be plotted. +parameter: str + Specifies which distributional parameter is to be plotted. +max_display: int + Specifies the maximum number of features to be displayed. +plot_type: str + Specifies the type of plot: + "Partial_Dependence" plots the partial dependence of the parameter on the feature. + "Feature_Importance" plots the feature importance of the parameter.
+ +lightgbmlss/model.py
477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 |
|
predict(data, pred_type='parameters', n_samples=1000, quantiles=[0.1, 0.5, 0.9], seed=123)
+
+Function that predicts from the trained model.
+data : pd.DataFrame + Data to predict from. +pred_type : str + Type of prediction: + - "samples" draws n_samples from the predicted distribution. + - "quantiles" calculates the quantiles from the predicted distribution. + - "parameters" returns the predicted distributional parameters. + - "expectiles" returns the predicted expectiles. +n_samples : int + Number of samples to draw from the predicted distribution. +quantiles : List[float] + List of quantiles to calculate from the predicted distribution. +seed : int + Seed for random number generator used to draw samples from the predicted distribution.
+predt_df : pd.DataFrame + Predictions.
+ +lightgbmlss/model.py
434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 |
|
save_model(model_path)
+
+Save the model to a file.
+model_path : str + The path to save the model.
+None
+ +lightgbmlss/model.py
583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 |
|
set_init_score(dmatrix)
+
+Set init_score for distributions.
+dmatrix : Dataset + Dataset to set base margin for.
+None
+ +lightgbmlss/model.py
89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 +101 +102 +103 +104 +105 |
|
set_params(params)
+
+Set parameters for distributional model.
+params : Dict[str, Any] + Parameters for model.
+params : Dict[str, Any] + Updated Parameters for model.
+ +lightgbmlss/model.py
66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 |
|
set_valid_margin(valid_sets, start_values)
+
+Function that sets the base margin for the validation set.
+valid_sets : list + List of tuples containing the train and evaluation set. +valid_names: list + List of tuples containing the name of train and evaluation set. +start_values : np.ndarray + Array containing the start values for the distributional parameters.
+valid_sets : list + List of tuples containing the train and evaluation set.
+ +lightgbmlss/model.py
550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 |
|
train(params, train_set, num_boost_round=100, valid_sets=None, valid_names=None, init_model=None, feature_name='auto', categorical_feature='auto', keep_training_booster=False, callbacks=None)
+
+Function to perform the training of a LightGBMLSS model with given parameters.
+params : dict
+ Parameters for training. Values passed through params
take precedence over those
+ supplied via arguments.
+train_set : Dataset
+ Data to be trained on.
+num_boost_round : int, optional (default=100)
+ Number of boosting iterations.
+valid_sets : list of Dataset, or None, optional (default=None)
+ List of data to be evaluated on during training.
+valid_names : list of str, or None, optional (default=None)
+ Names of valid_sets
.
+init_model : str, pathlib.Path, Booster or None, optional (default=None)
+ Filename of LightGBM model or Booster instance used for continue training.
+feature_name : list of str, or 'auto', optional (default="auto")
+ Feature names.
+ If 'auto' and data is pandas DataFrame, data columns names are used.
+categorical_feature : list of str or int, or 'auto', optional (default="auto")
+ Categorical features.
+ If list of int, interpreted as indices.
+ If list of str, interpreted as feature names (need to specify feature_name
as well).
+ If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used.
+ All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647).
+ Large values could be memory consuming. Consider using consecutive integers starting from zero.
+ All negative values in categorical features will be treated as missing values.
+ The output cannot be monotonically constrained with respect to a categorical feature.
+ Floating point numbers in categorical features will be rounded towards 0.
+keep_training_booster : bool, optional (default=False)
+ Whether the returned Booster will be used to keep training.
+ If False, the returned value will be converted into _InnerPredictor before returning.
+ This means you won't be able to use eval
, eval_train
or eval_valid
methods of the returned Booster.
+ When your model is very large and cause the memory error,
+ you can try to set this param to True
to avoid the model conversion performed during the internal call of model_to_string
.
+ You can still use _InnerPredictor as init_model
for future continue training.
+callbacks : list of callable, or None, optional (default=None)
+ List of callback functions that are applied at each iteration.
+ See Callbacks in Python API for more information.
booster : Booster + The trained Booster model.
+ +lightgbmlss/model.py
107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 |
|
utils
+
+
+exp_fn(predt)
+
+Exponential function used to ensure predt is strictly positive.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 |
|
exp_fn_df(predt)
+
+Exponential function used for Student-T distribution.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 |
|
gumbel_softmax_fn(predt, tau=1.0)
+
+Gumbel-softmax function used to ensure predt is adding to one.
+The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a "soft" +version of a categorical distribution. It’s a way to draw samples from a categorical distribution in a +differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of +categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a +Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. +Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is +defined as:
+s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}}
+
+where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness +of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau +approaches infty, the mixing probabilities become more uniform. For more information we refer to
+Jang, E., Gu, Shixiang and Poole, B. "Categorical Reparameterization with Gumbel-Softmax", ICLR, 2017.
+
+predt: torch.tensor + Predicted values. +tau: float, non-negative scalar temperature. + Temperature parameter for the Gumbel-softmax distribution. As tau -> 0, the output becomes more discrete, and as + tau -> inf, the output becomes more uniform.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 |
|
identity_fn(predt)
+
+Identity mapping of predt.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 |
|
nan_to_num(predt)
+
+Replace nan, inf and -inf with the mean of predt.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 |
|
relu_fn(predt)
+
+Function used to ensure predt are scaled to max(0, predt).
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 |
|
sigmoid_fn(predt)
+
+Function used to ensure predt are scaled to (0,1).
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 |
|
softmax_fn(predt)
+
+Softmax function used to ensure predt is adding to one.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 |
|
softplus_fn(predt)
+
+Softplus function used to ensure predt is strictly positive.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 +100 |
|
softplus_fn_df(predt)
+
+Softplus function used for Student-T distribution.
+predt: torch.tensor + Predicted values.
+predt: torch.tensor + Predicted values.
+ +lightgbmlss/utils.py
103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 |
|
+''Practitioners expect forecasting to reduce future uncertainty by providing accurate predictions like those in hard sciences. However, this is a great misconception. A major purpose of forecasting is not to reduce uncertainty but reveal its full extent and implications by estimating it as precisely as possible. [...] The challenge for the forecasting field is how to persuade practitioners of the reality that all forecasts are uncertain and that this uncertainty cannot be ignored, as doing so could lead to catastrophic consequences.'' Makridakis (2022b) ++ +It has not been too long, though, that most regression models focused on estimating the conditional mean $\mathbb{E}(Y|\mathbf{X} = \mathbf{x})$ only, implicitly treating higher moments of the conditional distribution $F_{Y}(y|\mathbf{x})$ as fixed nuisance parameters. As such, models that minimize an $\ell_{2}$-type loss for the conditional mean are not able to fully exploit the information contained in the data, since this is equivalent to assuming a Normal distribution with constant variance. In real world situations, however, the data generating process is usually less well behaved, exhibiting characteristics such as heteroskedasticity, varying degrees of skewness and kurtosis or intermittent and sporadic behaviour. In recent years, however, there has been a clear shift in both academic and corporate research toward modelling the entire conditional distribution. This change in attention is most evident in the recent M5 forecasting competition (Makridakis et al., 2022a,b), which differed from previous ones in that it consisted of two parallel competitions: in addition to providing accurate point forecasts, participants were also asked to forecast nine different quantiles to approximate the distribution of future sales. + +# Distributional Gradient Boosting Machines + +This section introduces the general idea of distributional modelling. For a more thorough introduction, we refer the interested reader to Rigby and Stasinopoulos (2005); Klein et al. (2015); Stasinopoulos et al. (2017). + +## GAMLSS + +Probabilistic forecasts are predictions in the form of a probability distribution, rather than a single point estimate only. In this context, the introduction of Generalized Additive Models for Location Scale and Shape (GAMLSS) by Rigby and Stasinopoulos (2005) has stimulated a lot of research and culminated in a new research branch that focuses on modelling the entire conditional distribution in dependence of covariates. + +### Univariate Targets + +In its original formulation, GAMLSS assume a univariate response $y$ to follow a distribution $\mathcal{D}\bigl(\boldsymbol{\theta}(x)\bigr)$ that depends on up to four parameters, i.e., $y_{i} \stackrel{ind}{\sim} \mathcal{D}\bigl(\boldsymbol{\theta}_{i}(x_{i})\bigr)$ with $\boldsymbol{\theta}_{i}(x_{i}) = \bigl(\mu_{i}(x_{i}), \sigma^{2}_{i}(x_{i}), \nu_{i}(x_{i}), \tau_{i}(x_{i})\bigr), i=1,\ldots, N$, where $\mu_{i}(\cdot)$ and $\sigma^{2}_{i}(\cdot)$ are often location and scale parameters, respectively, while $\nu_{i}(\cdot)$ and $\tau_{i}(\cdot)$ correspond to shape parameters such as skewness and kurtosis. Hence, the framework allows to model not only the mean (or location) but all parameters as functions of explanatory variables. It is important to note that distributional modelling implies that observations are independent, but not necessarily identical realizations $y \stackrel{ind}{\sim} \mathcal{D}\big(\boldsymbol{\theta}(x)\big)$, since all distributional parameters $\boldsymbol{\theta}(x)$ are related to and allowed to change with covariates. In contrast to Generalized Linear (GLM) and Generalized Additive Models (GAM), the assumption of the response distribution belonging to an exponential family is relaxed in GAMLSS and replaced by a more general class of distributions, including highly skewed and/or kurtotic continuous, discrete and mixed discrete, as well as zero-inflated distributions. While the original formulation of GAMLSS in Rigby and Stasinopoulos (2005) suggests that any distribution can be described by location, scale and shape parameters, it is not necessarily true that the observed data distribution can actually be characterized by all of these parameters. Hence, we follow Klein et al. (2015b) and use the term distributional modelling and GAMLSS interchangeably. + +From a frequentist point of view, distributional modelling can be formulated as follows + +\begin{equation} +y_{i} \stackrel{ind}{\sim} \mathcal{D} + \begin{pmatrix} + h_{1}\bigl(\theta_{i1}(x_{i})\bigr) = \eta_{i1} \\ + h_{2}\bigl(\theta_{i2}(x_{i})\bigr) = \eta_{i2} \\ + \vdots \\ + h_{K}\bigl(\theta_{iK}(x_{i})\bigr) = \eta_{iK} +\end{pmatrix} +\end{equation} + +for $i = 1, \ldots, N$, where $\mathcal{D}$ denotes a parametric distribution for the response $\textbf{y} = (y_{1}, \ldots, y_{N})^{\prime}$ that depends on $K$ distributional parameters $\theta_{k}$, $k = 1, \ldots, K$, and with $h_{k}(\cdot)$ denoting a known function relating distributional parameters to predictors $\eta_{k}$. In its most generic form, the predictor $\eta_{k}$ is given by + +\begin{equation} +\eta_{k} = f_{k}(\mathbf{x}), \qquad k = 1, \ldots, K +\end{equation} + +Within the original distributional regression framework, the functions $f_{k}(\cdot)$ usually represent a combination of linear and GAM-type predictors, which allows to estimate linear effects or categorical variables, as well as highly non-linear and spatial effects using a Spline-based basis function approach. The predictor specification $\eta_{k}$ is generic enough to use tree-based models as well, which allows us to extend LightGBM to a probabilistic framework. + +## Mixture Distributions + +Mixture densities, or mixture distributions, offer an extension to the notion of traditional univariate distributions by allowing the observed data to be thought of as arising from multiple underlying processes. In its essence, a mixture distribution is a weighted combination of several component distributions, where each component contributes to the overall mixture distribution, with the weights indicating the importance of each component. For instance, if you imagine the observed data distribution having multiple modes, a mixture of Gaussians could be employed to capture each mode with a separate Gaussian distribution. + +
The development of modelling approaches that approximate and describe the data generating processes underlying the observed data in as much detail as possible is a guiding principle in both statistics and machine learning. We therefore strongly agree with the statement of Hothorn et al. (2014) that ''the ultimate goal of any regression analysis is to obtain information about the entire conditional distribution \(F_{Y}(y|\mathbf{x})\) of a response given a set of explanatory variables''.
++''Practitioners expect forecasting to reduce future uncertainty by providing accurate predictions like those in hard sciences. However, this is a great misconception. A major purpose of forecasting is not to reduce uncertainty but reveal its full extent and implications by estimating it as precisely as possible. [...] The challenge for the forecasting field is how to persuade practitioners of the reality that all forecasts are uncertain and that this uncertainty cannot be ignored, as doing so could lead to catastrophic consequences.'' Makridakis (2022b) ++ +
It has not been too long, though, that most regression models focused on estimating the conditional mean \(\mathbb{E}(Y|\mathbf{X} = \mathbf{x})\) only, implicitly treating higher moments of the conditional distribution \(F_{Y}(y|\mathbf{x})\) as fixed nuisance parameters. As such, models that minimize an \(\ell_{2}\)-type loss for the conditional mean are not able to fully exploit the information contained in the data, since this is equivalent to assuming a Normal distribution with constant variance. In real world situations, however, the data generating process is usually less well behaved, exhibiting characteristics such as heteroskedasticity, varying degrees of skewness and kurtosis or intermittent and sporadic behaviour. In recent years, however, there has been a clear shift in both academic and corporate research toward modelling the entire conditional distribution. This change in attention is most evident in the recent M5 forecasting competition (Makridakis et al., 2022a,b), which differed from previous ones in that it consisted of two parallel competitions: in addition to providing accurate point forecasts, participants were also asked to forecast nine different quantiles to approximate the distribution of future sales.
+This section introduces the general idea of distributional modelling. For a more thorough introduction, we refer the interested reader to Rigby and Stasinopoulos (2005); Klein et al. (2015); Stasinopoulos et al. (2017).
+Probabilistic forecasts are predictions in the form of a probability distribution, rather than a single point estimate only. In this context, the introduction of Generalized Additive Models for Location Scale and Shape (GAMLSS) by Rigby and Stasinopoulos (2005) has stimulated a lot of research and culminated in a new research branch that focuses on modelling the entire conditional distribution in dependence of covariates.
+In its original formulation, GAMLSS assume a univariate response \(y\) to follow a distribution \(\mathcal{D}\bigl(\boldsymbol{\theta}(x)\bigr)\) that depends on up to four parameters, i.e., \(y_{i} \stackrel{ind}{\sim} \mathcal{D}\bigl(\boldsymbol{\theta}_{i}(x_{i})\bigr)\) with \(\boldsymbol{\theta}_{i}(x_{i}) = \bigl(\mu_{i}(x_{i}), \sigma^{2}_{i}(x_{i}), \nu_{i}(x_{i}), \tau_{i}(x_{i})\bigr), i=1,\ldots, N\), where \(\mu_{i}(\cdot)\) and \(\sigma^{2}_{i}(\cdot)\) are often location and scale parameters, respectively, while \(\nu_{i}(\cdot)\) and \(\tau_{i}(\cdot)\) correspond to shape parameters such as skewness and kurtosis. Hence, the framework allows to model not only the mean (or location) but all parameters as functions of explanatory variables. It is important to note that distributional modelling implies that observations are independent, but not necessarily identical realizations \(y \stackrel{ind}{\sim} \mathcal{D}\big(\boldsymbol{\theta}(x)\big)\), since all distributional parameters \(\boldsymbol{\theta}(x)\) are related to and allowed to change with covariates. In contrast to Generalized Linear (GLM) and Generalized Additive Models (GAM), the assumption of the response distribution belonging to an exponential family is relaxed in GAMLSS and replaced by a more general class of distributions, including highly skewed and/or kurtotic continuous, discrete and mixed discrete, as well as zero-inflated distributions. While the original formulation of GAMLSS in Rigby and Stasinopoulos (2005) suggests that any distribution can be described by location, scale and shape parameters, it is not necessarily true that the observed data distribution can actually be characterized by all of these parameters. Hence, we follow Klein et al. (2015b) and use the term distributional modelling and GAMLSS interchangeably.
+From a frequentist point of view, distributional modelling can be formulated as follows
+for \(i = 1, \ldots, N\), where \(\mathcal{D}\) denotes a parametric distribution for the response \(\textbf{y} = (y_{1}, \ldots, y_{N})^{\prime}\) that depends on \(K\) distributional parameters \(\theta_{k}\), \(k = 1, \ldots, K\), and with \(h_{k}(\cdot)\) denoting a known function relating distributional parameters to predictors \(\eta_{k}\). In its most generic form, the predictor \(\eta_{k}\) is given by
+Within the original distributional regression framework, the functions \(f_{k}(\cdot)\) usually represent a combination of linear and GAM-type predictors, which allows to estimate linear effects or categorical variables, as well as highly non-linear and spatial effects using a Spline-based basis function approach. The predictor specification \(\eta_{k}\) is generic enough to use tree-based models as well, which allows us to extend LightGBM to a probabilistic framework.
+Mixture densities, or mixture distributions, offer an extension to the notion of traditional univariate distributions by allowing the observed data to be thought of as arising from multiple underlying processes. In its essence, a mixture distribution is a weighted combination of several component distributions, where each component contributes to the overall mixture distribution, with the weights indicating the importance of each component. For instance, if you imagine the observed data distribution having multiple modes, a mixture of Gaussians could be employed to capture each mode with a separate Gaussian distribution.
+For each component of the mixture, there would be a set of parameters that depend on covariates, and additional mixing coefficients which are also modeled as a function of covariates. This is particularly useful when a single parametric distribution cannot adequately capture the underlying data generating process. A mixture distribution can be represented as follows:
+where \(f(\cdot)\) represents the mixture density for the \(i\)-th observation, \(f_{m}(\cdot)\) is the \(m\)-th component density, each with its own set of parameters \(\boldsymbol{\theta}_{i,m}(\cdot)\), and \(w_{i,m}(\cdot)\) represent the weights of the \(m\)-th component in the mixture, subject to \(\sum_{j=1}^{M} w_{i,m} = 1\). The components can either be a combination of different parametric univariate distributions, such as a combination of a Normal and a StudentT, or, as in our implementation, a combination of the same distribution-type with different parameterizations, e.g., Gaussian-Mixture or StudentT-Mixture. The choice of the component distributions depends on the characteristics of the data and the underlying assumptions. Due to their high flexibility, mixture densities can portray a diverse range of shapes, making them adaptable to a plethora of datasets. By incorporating mixture densities within our framework, users can gain a more comprehensive understanding of the conditional distribution of the response variable, providing a more accurate representation of the data generating process. Hence, mixture densities greatly expand the expressive power of distributional modeling frameworks like GAMLSS, allowing one to capture a wider array of data distributions with enhanced flexibility by combining multiple members of this set.
+Although the GAMLSS framework offers considerable flexibility, parametric distributions may prove not flexible enough to provide a reasonable approximation for certain dataset, e.g., for multi-modal distributions. For such cases, it is preferable to relax the assumption of a parametric distribution and approximate the data non-parametrically. While there are several alternatives for learning conditional distributions, we propose to use Normalizing Flows for their ability to fit complex distributions with only a few parameters.
+The principle that underlies Normalizing Flows is to turn a simple base distribution, e.g., \(F_{Z}(\mathbf{z}) = N(0,1)\), into a more complex and realistic distribution of the target variable \(F_{Y}(\mathbf{y})\) by applying several bijective transformations \(h_{j}\), \(j = 1, \ldots, J\) to the variable of the base distribution
+Based on the complete transformation function \(h=h_{J}\circ\ldots\circ h_{1}\), the density of \(\mathbf{y}\) is then given by the change of variables theorem
+where scaling with the Jacobian determinant \(|h^{\prime}(\mathbf{y})| = |\partial h(\mathbf{y}) / \partial \mathbf{y}|\) ensures \(f_{Y}(\mathbf{y})\) to be a proper density integrating to one. The composition of these transformations is invertible, allowing one to sample from the complex distribution by transforming samples from the base distribution.
++
Our Normalizing Flow approach is based on element-wise rational splines of linear or quadratic order as introduced by Durkan (2019) and Dolatabadi (2020) and implemented in Pyro, since they offer a combination of functional flexibility and numerical stability. Despite this specific choice, our framework is generic enough to accommodate the use of other parametrizable Normalizing Flows.
+We draw inspiration from GAMLSS and label our model as LightGBM for Location, Scale and Shape (LightGBMLSS). Despite its nominal reference to GAMLSS, our framework is designed in such a way to accommodate the modeling of a wide range of parametrizable distributions that go beyond location, scale and shape. LightGBMLSS requires the specification of a suitable distribution from which Gradients and Hessians are derived. These represent the partial first and second order derivatives of the log-likelihood with respect to the parameter of interest. GBMLSS are based on multi-parameter optimization, where a separate tree is grown for each parameter. Estimation of Gradients and Hessians, as well as the evaluation of the loss function is done simultaneously for all parameters. Gradients and Hessians are derived using PyTorch's automatic differentiation capabilities. The flexibility offered by automatic differentiation allows users to easily implement novel or customized parametric distributions for which Gradients and Hessians are difficult to derive analytically. It also facilitates the usage of Normalizing Flows, or to add additional constraints to the loss function. To improve the convergence and stability of GBMLSS estimation, unconditional Maximum Likelihood estimates of the parameters are used as offset values. To enable a deeper understanding of the data generating process, GBMLSS also provide attribute importance and partial dependence plots using the Shapley-Value approach.
+LightGBMLSS is built upon PyTorch and Pyro, enabling users to harness a diverse set of distributional families and to leverage automatic differentiation capabilities. This greatly expands the options for probabilistic modeling and uncertainty estimation and allows users to tackle complex regression tasks.
+LightGBMLSS currently supports the following univariate distributions.
+Distribution | +Usage | +Type | +Support | +Number of Parameters | +
---|---|---|---|---|
Beta | +Beta() |
+Continuous (Univariate) |
+\(y \in (0, 1)\) | +2 | +
Cauchy | +Cauchy() |
+Continuous (Univariate) |
+\(y \in (-\infty,\infty)\) | +2 | +
Expectile | +Expectile() |
+Continuous (Univariate) |
+\(y \in (-\infty,\infty)\) | +Number of expectiles | +
Gamma | +Gamma() |
+Continuous (Univariate) |
+\(y \in (0, \infty)\) | +2 | +
Gaussian | +Gaussian() |
+Continuous (Univariate) |
+\(y \in (-\infty,\infty)\) | +2 | +
Gumbel | +Gumbel() |
+Continuous (Univariate) |
+\(y \in (-\infty,\infty)\) | +2 | +
Laplace | +Laplace() |
+Continuous (Univariate) |
+\(y \in (-\infty,\infty)\) | +2 | +
LogNormal | +LogNormal() |
+Continuous (Univariate) |
+\(y \in (0,\infty)\) | +2 | +
Mixture | +Mixture(CompDist(), M) |
+Continuous & Discrete Count (Univariate) |
+\(y \in (-\infty,\infty)\) \(y \in [0, \infty)\) \(y \in [0, 1]\) \(y \in (0, 1, 2, 3, \ldots)\) |
+CompDist + M | +
Negative Binomial | +NegativeBinomial() |
+Discrete Count (Univariate) |
+\(y \in (0, 1, 2, 3, \ldots)\) | +2 | +
Poisson | +Poisson() |
+Discrete Count (Univariate) |
+\(y \in (0, 1, 2, 3, \ldots)\) | +1 | +
Spline Flow | +SplineFlow() |
+Continuous & Discrete Count (Univariate) |
+\(y \in (-\infty,\infty)\) \(y \in [0, \infty)\) \(y \in [0, 1]\) \(y \in (0, 1, 2, 3, \ldots)\) |
+2xcount_bins + (count_bins-1) (order=quadratic) 3xcount_bins + (count_bins-1) (order=linear) |
+
Student-T | +StudentT() |
+Continuous (Univariate) |
+\(y \in (-\infty,\infty)\) | +3 | +
Weibull | +Weibull() |
+Continuous (Univariate) |
+\(y \in [0, \infty)\) | +2 | +
Zero-Adjusted Beta | +ZABeta() |
+Discrete-Continuous (Univariate) |
+\(y \in [0, 1)\) | +3 | +
Zero-Adjusted Gamma | +ZAGamma() |
+Discrete-Continuous (Univariate) |
+\(y \in [0, \infty)\) | +3 | +
Zero-Adjusted LogNormal | +ZALN() |
+Discrete-Continuous (Univariate) |
+\(y \in [0, \infty)\) | +3 | +
Zero-Inflated Negative Binomial | +ZINB() |
+Discrete-Count (Univariate) |
+\(y \in [0, 1, 2, 3, \ldots)\) | +3 | +
Zero-Inflated Poisson | +ZIPoisson() |
+Discrete-Count (Univariate) |
+\(y \in [0, 1, 2, 3, \ldots)\) | +2 | +
\n", + " | expectile_0.05 | \n", + "expectile_0.95 | \n", + "
---|---|---|
0 | \n", + "6.695340 | \n", + "13.277894 | \n", + "
1 | \n", + "6.615792 | \n", + "13.277894 | \n", + "
2 | \n", + "8.519470 | \n", + "11.511595 | \n", + "
3 | \n", + "4.557220 | \n", + "14.967069 | \n", + "
4 | \n", + "6.615792 | \n", + "13.367647 | \n", + "
from lightgbmlss.model import *
+from lightgbmlss.distributions.Expectile import *
+from lightgbmlss.datasets.data_loader import load_simulated_gaussian_data
+
+import plotnine
+from plotnine import *
+plotnine.options.figure_size = (20, 10)
+
# The data is a simulated Gaussian as follows, where x is the only true feature and all others are noise variables
+ # loc = 10
+ # scale = 1 + 4*((0.3 < x) & (x < 0.5)) + 2*(x > 0.7)
+
+train, test = load_simulated_gaussian_data()
+
+X_train, y_train = train.filter(regex="x"), train["y"].values
+X_test, y_test = test.filter(regex="x"), test["y"].values
+
+dtrain = lgb.Dataset(X_train, label=y_train)
+
lgblss = LightGBMLSS(
+ Expectile(stabilization="None", # Options are "None", "MAD", "L2".
+ expectiles = [0.05, 0.95], # List of expectiles to be estimated, in increasing order.
+ penalize_crossing = True # Whether to include a penalty term to discourage crossing of expectiles.
+ )
+)
+
Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows:
+- Float/Int sample_type
+ - {"param_name": ["sample_type", low, high, log]}
+ - sample_type: str, Type of sampling, e.g., "float" or "int"
+ - low: int, Lower endpoint of the range of suggested values
+ - high: int, Upper endpoint of the range of suggested values
+ - log: bool, Flag to sample the value from the log domain or not
+ - Example: {"eta": "float", low=1e-5, high=1, log=True]}
+
+- Categorical sample_type
+ - {"param_name": ["sample_type", ["choice1", "choice2", "choice3", "..."]]}
+ - sample_type: str, Type of sampling, either "categorical"
+ - choice1, choice2, choice3, ...: str, Possible choices for the parameter
+ - Example: {"boosting": ["categorical", ["gbdt", "dart"]]}
+
+- For parameters without tunable choice (this is needed if tree_method = "gpu_hist" and gpu_id needs to be specified)
+ - {"param_name": ["none", [value]]},
+ - param_name: str, Name of the parameter
+ - value: int, Value of the parameter
+ - Example: {"gpu_id": ["none", [0]]}
+param_dict = {
+ "eta": ["float", {"low": 1e-5, "high": 1, "log": True}],
+ "max_depth": ["int", {"low": 1, "high": 10, "log": False}],
+ "num_leaves": ["int", {"low": 255, "high": 255, "log": False}], # set to constant for this example
+ "min_data_in_leaf": ["int", {"low": 20, "high": 20, "log": False}], # set to constant for this example
+ "min_gain_to_split": ["float", {"low": 1e-8, "high": 40, "log": False}],
+ "min_sum_hessian_in_leaf": ["float", {"low": 1e-8, "high": 500, "log": True}],
+ "subsample": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "feature_fraction": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "boosting": ["categorical", ["gbdt"]],
+}
+
+np.random.seed(123)
+opt_param = lgblss.hyper_opt(param_dict,
+ dtrain,
+ num_boost_round=100, # Number of boosting iterations.
+ nfold=5, # Number of cv-folds.
+ early_stopping_rounds=20, # Number of early-stopping rounds
+ max_minutes=10, # Time budget in minutes, i.e., stop study after the given number of minutes.
+ n_trials=30, # The number of trials. If this argument is set to None, there is no limitation on the number of trials.
+ silence=False, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail.
+ seed=123, # Seed used to generate cv-folds.
+ hp_seed=None # Seed for random number generator used in the Bayesian hyperparameter search.
+ )
+
[I 2023-08-11 12:21:09,469] A new study created in memory with name: LightGBMLSS Hyper-Parameter Optimization ++
0%| | 0/30 [00:00<?, ?it/s]+
[I 2023-08-11 12:21:12,718] Trial 0 finished with value: 2455.671630859375 and parameters: {'eta': 4.999979903379203e-05, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 21.93993866573528, 'min_sum_hessian_in_leaf': 0.0003530133520827798, 'subsample': 0.4971819264686692, 'feature_fraction': 0.3707311537482785, 'boosting': 'gbdt'}. Best is trial 0 with value: 2455.671630859375. +[I 2023-08-11 12:21:14,662] Trial 1 finished with value: 1905.1077880859375 and parameters: {'eta': 0.031600943671035775, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 25.283240746368264, 'min_sum_hessian_in_leaf': 49.082392515255734, 'subsample': 0.6788944834474666, 'feature_fraction': 0.9258044091945574, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:18,260] Trial 2 finished with value: 2163.520751953125 and parameters: {'eta': 0.005894981780547752, 'max_depth': 9, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 10.409790461895193, 'min_sum_hessian_in_leaf': 0.0008141832901711874, 'subsample': 0.4070793729617024, 'feature_fraction': 0.6846602442537073, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:20,956] Trial 3 finished with value: 2340.4150390625 and parameters: {'eta': 0.001961322558956042, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 12.389445813281812, 'min_sum_hessian_in_leaf': 0.0001665823267805825, 'subsample': 0.8122180498006835, 'feature_fraction': 0.5881597651097203, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:23,663] Trial 4 finished with value: 2455.928955078125 and parameters: {'eta': 4.876502677739385e-05, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 4.557086477842732, 'min_sum_hessian_in_leaf': 8.971172534030456e-08, 'subsample': 0.43772569367787945, 'feature_fraction': 0.33757411361894973, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:26,413] Trial 5 finished with value: 2020.169677734375 and parameters: {'eta': 0.015583832782804402, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 17.990075586462332, 'min_sum_hessian_in_leaf': 9.047858492815616e-06, 'subsample': 0.2661853234410493, 'feature_fraction': 0.43711054797968024, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:29,603] Trial 6 finished with value: 2453.094970703125 and parameters: {'eta': 3.192833281012269e-05, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 4.179852781197093, 'min_sum_hessian_in_leaf': 0.0013367062405656063, 'subsample': 0.760623390889942, 'feature_fraction': 0.9918521803651483, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:33,061] Trial 7 finished with value: 2390.234375 and parameters: {'eta': 0.001099408166117131, 'max_depth': 7, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 5.928259874226358, 'min_sum_hessian_in_leaf': 22.801819887756512, 'subsample': 0.8539904207951285, 'feature_fraction': 0.5227409182953131, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:36,013] Trial 8 finished with value: 1914.283935546875 and parameters: {'eta': 0.2493102080752807, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 9.13540771750102, 'min_sum_hessian_in_leaf': 0.34110612176978133, 'subsample': 0.5308020126325235, 'feature_fraction': 0.8641969342663409, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:36,677] Trial 9 finished with value: 1937.4613037109375 and parameters: {'eta': 0.9636709157054849, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 32.67317336133683, 'min_sum_hessian_in_leaf': 1.2744267371826801e-08, 'subsample': 0.9935887353691604, 'feature_fraction': 0.943953338852964, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:38,579] Trial 10 finished with value: 1946.8072509765625 and parameters: {'eta': 0.04879973495349672, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 39.06107022051272, 'min_sum_hessian_in_leaf': 106.46715749293509, 'subsample': 0.6595408126514654, 'feature_fraction': 0.7942683208717877, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:40,307] Trial 11 finished with value: 1942.6409912109375 and parameters: {'eta': 0.3475471835643695, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 23.567503144962224, 'min_sum_hessian_in_leaf': 0.4888567766798536, 'subsample': 0.598911344176229, 'feature_fraction': 0.8378628459298021, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:43,076] Trial 12 finished with value: 1970.949951171875 and parameters: {'eta': 0.1355810372876077, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 27.308295812909954, 'min_sum_hessian_in_leaf': 0.45165961145216943, 'subsample': 0.5607270559851195, 'feature_fraction': 0.8192697272856723, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:43,791] Trial 13 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:45,120] Trial 14 finished with value: 1917.481201171875 and parameters: {'eta': 0.1918179723427333, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 0.5575780726770159, 'min_sum_hessian_in_leaf': 0.38144740122258586, 'subsample': 0.5276921773301158, 'feature_fraction': 0.8851651910106035, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:46,648] Trial 15 finished with value: 1908.2398681640625 and parameters: {'eta': 0.06814525677756796, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 13.416527851881755, 'min_sum_hessian_in_leaf': 1.5533574937270136, 'subsample': 0.6891248913559522, 'feature_fraction': 0.9718069324964819, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:47,454] Trial 16 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:48,239] Trial 17 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:49,033] Trial 18 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:49,857] Trial 19 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:51,393] Trial 20 finished with value: 1911.4632568359375 and parameters: {'eta': 0.06986238192969108, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 29.160727721810346, 'min_sum_hessian_in_leaf': 0.016442500462406705, 'subsample': 0.6023777335914249, 'feature_fraction': 0.9155248389222844, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:52,873] Trial 21 finished with value: 1911.5198974609375 and parameters: {'eta': 0.07161895441713878, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 29.27696506397222, 'min_sum_hessian_in_leaf': 0.020946061323593588, 'subsample': 0.6163027333377462, 'feature_fraction': 0.9137504739618006, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:53,660] Trial 22 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:55,121] Trial 23 finished with value: 1908.989501953125 and parameters: {'eta': 0.08734639812497531, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 30.42886083118784, 'min_sum_hessian_in_leaf': 0.03828710084297306, 'subsample': 0.7131459665462002, 'feature_fraction': 0.7904449014182882, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:55,893] Trial 24 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:21:57,012] Trial 25 finished with value: 1917.7242431640625 and parameters: {'eta': 0.1615054287282905, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 20.364960331001463, 'min_sum_hessian_in_leaf': 3.329890678595058, 'subsample': 0.7972962597807455, 'feature_fraction': 0.8464533630717106, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:57,915] Trial 26 finished with value: 1909.692626953125 and parameters: {'eta': 0.5158300039154515, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 25.112985606000265, 'min_sum_hessian_in_leaf': 0.05243689522362543, 'subsample': 0.6973560330154309, 'feature_fraction': 0.7889207482360956, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:21:58,767] Trial 27 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:22:00,393] Trial 28 finished with value: 1921.2425537109375 and parameters: {'eta': 0.09255361005893879, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 14.802111899924473, 'min_sum_hessian_in_leaf': 1.7943497012679166, 'subsample': 0.692797127315583, 'feature_fraction': 0.6813967545026932, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. +[I 2023-08-11 12:22:01,231] Trial 29 pruned. Trial was pruned at iteration 20. + +Hyper-Parameter Optimization successfully finished. + Number of finished trials: 30 + Best trial: + Value: 1905.1077880859375 + Params: + eta: 0.031600943671035775 + max_depth: 3 + num_leaves: 255 + min_data_in_leaf: 20 + min_gain_to_split: 25.283240746368264 + min_sum_hessian_in_leaf: 49.082392515255734 + subsample: 0.6788944834474666 + feature_fraction: 0.9258044091945574 + boosting: gbdt + opt_rounds: 55 ++
np.random.seed(123)
+
+opt_params = opt_param.copy()
+n_rounds = opt_params["opt_rounds"]
+del opt_params["opt_rounds"]
+
+# Train Model with optimized hyperparameters
+lgblss.train(opt_params,
+ dtrain,
+ num_boost_round=n_rounds
+ )
+
# Predicted expectiles
+pred_expectile = lgblss.predict(X_test, pred_type="expectiles")
+
pred_expectile.head()
+
+ | expectile_0.05 | +expectile_0.95 | +
---|---|---|
0 | +6.695340 | +13.277894 | +
1 | +6.615792 | +13.277894 | +
2 | +8.519470 | +11.511595 | +
3 | +4.557220 | +14.967069 | +
4 | +6.615792 | +13.367647 | +
# Partial Dependence Plot of how x acts on selected expectile
+lgblss.expectile_plot(X_test,
+ expectile="expectile_0.95",
+ feature="x_true",
+ plot_type="Partial_Dependence")
+
# Partial Dependence Plot of how x acts on selected expectile
+lgblss.expectile_plot(X_test,
+ expectile="expectile_0.05",
+ feature="x_true",
+ plot_type="Partial_Dependence")
+
# Global Feature Importance of selected expectile
+lgblss.expectile_plot(X_test,
+ expectile="expectile_0.95",
+ plot_type="Feature_Importance")
+
np.random.seed(123)
+
+###
+# Actual Expectiles
+###
+y_loc = np.array([10])
+y_scale = np.array([1 + 4*((0.3 < test["x_true"].values) & (test["x_true"].values < 0.5)) + 2*(test["x_true"].values > 0.7)])
+tau_lower = np.array([lgblss.dist.tau[0]])
+tau_upper = np.array([lgblss.dist.tau[1]])
+
+# Calculates exact expectiles assuming a Normal distribution
+expectile_lb = expectile_norm(tau=tau_lower,
+ m=y_loc,
+ sd=y_scale).reshape(-1,)
+expectile_ub = expectile_norm(tau=tau_upper,
+ m=y_loc,
+ sd=y_scale).reshape(-1,)
+
+test["expect"] = np.where(test["y"].values < expectile_lb, 0, np.where(test["y"].values < expectile_ub, 1, 2))
+test["alpha"] = np.where(test["y"].values <= expectile_lb, 1, np.where(test["y"].values >= expectile_ub, 1, 0))
+df_expectiles = test[test["alpha"] == 1]
+
+# Lower Bound
+yl = list(set(expectile_lb))
+yl.sort()
+yl = [yl[2],yl[0],yl[2],yl[1],yl[1]]
+sfunl = pd.DataFrame({"x_true":[0, 0.3, 0.5, 0.7, 1],
+ "y":yl})
+
+# Upper Bound
+yu = list(set(expectile_ub))
+yu.sort()
+yu = [yu[0],yu[2],yu[0],yu[1],yu[1]]
+sfunu = pd.DataFrame({"x_true":[0, 0.3, 0.5, 0.7, 1],
+ "y":yu})
+
+
+
+###
+# Forecasted Expectiles
+###
+test["lb"] = pred_expectile.iloc[:,0]
+test["ub"] = pred_expectile.iloc[:,1]
+
+
+
+###
+# Plot
+###
+(ggplot(test,
+ aes("x_true",
+ "y")) +
+ geom_point(alpha = 0.2, color = "black", size = 2) +
+ theme_bw(base_size=15) +
+ theme(legend_position="bottom",
+ plot_title = element_text(hjust = 0.5)) +
+ labs(title = "LightGBMLSS Expectile Regression - Simulated Data Example") +
+ geom_line(aes("x_true",
+ "ub"),
+ size = 1.5,
+ color = "blue",
+ alpha = 0.7) +
+ geom_line(aes("x_true",
+ "lb"),
+ size = 1.5,
+ color = "blue",
+ alpha = 0.7) +
+ geom_point(df_expectiles,
+ aes("x_true",
+ "y"),
+ color = "red",
+ alpha = 0.7,
+ size = 2) +
+ geom_step(sfunl,
+ aes("x_true",
+ "y"),
+ size = 1,
+ linetype = "dashed") +
+ geom_step(sfunu,
+ aes("x_true",
+ "y"),
+ size = 1,
+ linetype = "dashed")
+)
+
<Figure Size: (2000 x 1000)>+
\n", + " | y_sample0 | \n", + "y_sample1 | \n", + "y_sample2 | \n", + "y_sample3 | \n", + "y_sample4 | \n", + "y_sample5 | \n", + "y_sample6 | \n", + "y_sample7 | \n", + "y_sample8 | \n", + "y_sample9 | \n", + "... | \n", + "y_sample990 | \n", + "y_sample991 | \n", + "y_sample992 | \n", + "y_sample993 | \n", + "y_sample994 | \n", + "y_sample995 | \n", + "y_sample996 | \n", + "y_sample997 | \n", + "y_sample998 | \n", + "y_sample999 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "1.803845 | \n", + "2.055054 | \n", + "3.721187 | \n", + "2.103017 | \n", + "2.785087 | \n", + "1.427447 | \n", + "2.607677 | \n", + "1.835255 | \n", + "2.056286 | \n", + "1.429045 | \n", + "... | \n", + "1.519037 | \n", + "1.412902 | \n", + "1.625722 | \n", + "1.990941 | \n", + "1.041016 | \n", + "1.458764 | \n", + "2.493577 | \n", + "2.022398 | \n", + "2.520749 | \n", + "1.871148 | \n", + "
1 | \n", + "0.955117 | \n", + "0.304340 | \n", + "0.505187 | \n", + "0.712550 | \n", + "1.216954 | \n", + "0.933732 | \n", + "0.380238 | \n", + "1.482657 | \n", + "0.736652 | \n", + "0.427856 | \n", + "... | \n", + "1.171600 | \n", + "1.010972 | \n", + "0.735717 | \n", + "1.319169 | \n", + "1.202447 | \n", + "0.967412 | \n", + "0.966288 | \n", + "1.435245 | \n", + "0.821253 | \n", + "0.793779 | \n", + "
2 | \n", + "1.144286 | \n", + "3.009341 | \n", + "1.845451 | \n", + "3.251571 | \n", + "2.020669 | \n", + "1.400807 | \n", + "2.169500 | \n", + "1.314221 | \n", + "1.898889 | \n", + "1.066060 | \n", + "... | \n", + "1.997797 | \n", + "1.747813 | \n", + "2.150669 | \n", + "2.500485 | \n", + "1.338773 | \n", + "1.857834 | \n", + "1.787327 | \n", + "1.801178 | \n", + "2.843899 | \n", + "1.567025 | \n", + "
3 | \n", + "1.808418 | \n", + "3.125320 | \n", + "1.761757 | \n", + "1.979179 | \n", + "2.679116 | \n", + "1.515085 | \n", + "1.274946 | \n", + "1.122176 | \n", + "2.836676 | \n", + "2.317276 | \n", + "... | \n", + "1.435263 | \n", + "1.846332 | \n", + "1.362321 | \n", + "1.321298 | \n", + "1.959441 | \n", + "1.562435 | \n", + "1.587221 | \n", + "1.903269 | \n", + "1.783693 | \n", + "1.171151 | \n", + "
4 | \n", + "4.249149 | \n", + "3.424617 | \n", + "3.813255 | \n", + "3.438753 | \n", + "2.999937 | \n", + "3.809508 | \n", + "3.346083 | \n", + "2.839856 | \n", + "3.459303 | \n", + "3.179483 | \n", + "... | \n", + "3.772136 | \n", + "3.544357 | \n", + "3.080696 | \n", + "6.813697 | \n", + "4.206345 | \n", + "2.519868 | \n", + "3.878748 | \n", + "5.277604 | \n", + "4.116862 | \n", + "3.673533 | \n", + "
5 rows × 1000 columns
\n", + "\n", + " | quant_0.05 | \n", + "quant_0.95 | \n", + "
---|---|---|
0 | \n", + "1.104986 | \n", + "2.876080 | \n", + "
1 | \n", + "0.493789 | \n", + "1.469833 | \n", + "
2 | \n", + "0.998464 | \n", + "2.626445 | \n", + "
3 | \n", + "1.021038 | \n", + "2.662356 | \n", + "
4 | \n", + "2.412530 | \n", + "5.914484 | \n", + "
\n", + " | concentration | \n", + "rate | \n", + "
---|---|---|
0 | \n", + "12.671469 | \n", + "6.625142 | \n", + "
1 | \n", + "9.840599 | \n", + "10.347679 | \n", + "
2 | \n", + "12.215254 | \n", + "7.179342 | \n", + "
3 | \n", + "11.811074 | \n", + "6.747012 | \n", + "
4 | \n", + "13.350488 | \n", + "3.346810 | \n", + "
from lightgbmlss.model import *
+from lightgbmlss.distributions.Gamma import *
+
+from sklearn import datasets
+from sklearn.model_selection import train_test_split
+
housing_data = datasets.fetch_california_housing()
+X, y = housing_data["data"], housing_data["target"]
+feature_names = housing_data["feature_names"]
+
+X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
+
+dtrain = lgb.Dataset(X_train, label=y_train)
+
# Specifies Gamma distribution with exp response function and option to stabilize Gradient/Hessian. Type ?Gamma for an overview.
+lgblss = LightGBMLSS(
+ Gamma(stabilization="L2", # Options are "None", "MAD", "L2".
+ response_fn="softplus", # Function to transform the concentration and rate parameters, e.g., "exp" or "softplus".
+ loss_fn="nll" # Loss function. Options are "nll" (negative log-likelihood) or "crps"(continuous ranked probability score).
+ )
+)
+
Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows:
+- Float/Int sample_type
+ - {"param_name": ["sample_type", low, high, log]}
+ - sample_type: str, Type of sampling, e.g., "float" or "int"
+ - low: int, Lower endpoint of the range of suggested values
+ - high: int, Upper endpoint of the range of suggested values
+ - log: bool, Flag to sample the value from the log domain or not
+ - Example: {"eta": "float", low=1e-5, high=1, log=True]}
+
+- Categorical sample_type
+ - {"param_name": ["sample_type", ["choice1", "choice2", "choice3", "..."]]}
+ - sample_type: str, Type of sampling, either "categorical"
+ - choice1, choice2, choice3, ...: str, Possible choices for the parameter
+ - Example: {"boosting": ["categorical", ["gbdt", "dart"]]}
+
+- For parameters without tunable choice (this is needed if tree_method = "gpu_hist" and gpu_id needs to be specified)
+ - {"param_name": ["none", [value]]},
+ - param_name: str, Name of the parameter
+ - value: int, Value of the parameter
+ - Example: {"gpu_id": ["none", [0]]}
+param_dict = {
+ "eta": ["float", {"low": 1e-5, "high": 1, "log": True}],
+ "max_depth": ["int", {"low": 1, "high": 10, "log": False}],
+ "subsample": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "feature_fraction": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "boosting": ["categorical", ["gbdt"]],
+}
+
+np.random.seed(123)
+opt_param = lgblss.hyper_opt(param_dict,
+ dtrain,
+ num_boost_round=100, # Number of boosting iterations.
+ nfold=5, # Number of cv-folds.
+ early_stopping_rounds=20, # Number of early-stopping rounds
+ max_minutes=20, # Time budget in minutes, i.e., stop study after the given number of minutes.
+ n_trials=30, # The number of trials. If this argument is set to None, there is no limitation on the number of trials.
+ silence=False, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail.
+ seed=123, # Seed used to generate cv-folds.
+ hp_seed=None # Seed for random number generator used in the Bayesian hyperparameter search.
+ )
+
[I 2023-08-11 12:29:16,191] A new study created in memory with name: LightGBMLSS Hyper-Parameter Optimization ++
0%| | 0/30 [00:00<?, ?it/s]+
[I 2023-08-11 12:29:30,946] Trial 0 finished with value: 3655.994319219647 and parameters: {'eta': 0.0055601230544869195, 'max_depth': 7, 'subsample': 0.9477091024240025, 'feature_fraction': 0.9480677544137415, 'boosting': 'gbdt'}. Best is trial 0 with value: 3655.994319219647. +[I 2023-08-11 12:29:43,746] Trial 1 finished with value: 4227.673235529888 and parameters: {'eta': 0.0023020600298896826, 'max_depth': 4, 'subsample': 0.8651585394401682, 'feature_fraction': 0.8334419259540924, 'boosting': 'gbdt'}. Best is trial 0 with value: 3655.994319219647. +[I 2023-08-11 12:29:56,252] Trial 2 finished with value: 4339.068493424474 and parameters: {'eta': 0.0027858552626389625, 'max_depth': 4, 'subsample': 0.7481047090926836, 'feature_fraction': 0.28093366785144963, 'boosting': 'gbdt'}. Best is trial 0 with value: 3655.994319219647. +[I 2023-08-11 12:30:09,051] Trial 3 finished with value: 4654.563387175151 and parameters: {'eta': 0.000444156259151128, 'max_depth': 3, 'subsample': 0.6107418507428386, 'feature_fraction': 0.3863125393663567, 'boosting': 'gbdt'}. Best is trial 0 with value: 3655.994319219647. +[I 2023-08-11 12:30:22,971] Trial 4 finished with value: 4561.714498700547 and parameters: {'eta': 0.0006413188632848346, 'max_depth': 7, 'subsample': 0.8415936831413335, 'feature_fraction': 0.5304303058022481, 'boosting': 'gbdt'}. Best is trial 0 with value: 3655.994319219647. +[I 2023-08-11 12:30:36,725] Trial 5 finished with value: 3940.645249758013 and parameters: {'eta': 0.004514061436631957, 'max_depth': 8, 'subsample': 0.6813546073094912, 'feature_fraction': 0.37103056892915803, 'boosting': 'gbdt'}. Best is trial 0 with value: 3655.994319219647. +[I 2023-08-11 12:30:49,208] Trial 6 finished with value: 2313.9436995982674 and parameters: {'eta': 0.34104448990548714, 'max_depth': 2, 'subsample': 0.49381730590587314, 'feature_fraction': 0.48051971736040777, 'boosting': 'gbdt'}. Best is trial 6 with value: 2313.9436995982674. +[I 2023-08-11 12:31:01,412] Trial 7 finished with value: 4716.048776510342 and parameters: {'eta': 4.121739938983374e-05, 'max_depth': 3, 'subsample': 0.8639944116265685, 'feature_fraction': 0.8978770904112527, 'boosting': 'gbdt'}. Best is trial 6 with value: 2313.9436995982674. +[I 2023-08-11 12:31:15,571] Trial 8 finished with value: 4633.214125114749 and parameters: {'eta': 0.00030771118259821184, 'max_depth': 9, 'subsample': 0.8618195165072102, 'feature_fraction': 0.9999114906902047, 'boosting': 'gbdt'}. Best is trial 6 with value: 2313.9436995982674. +[I 2023-08-11 12:31:29,560] Trial 9 finished with value: 3478.353837243644 and parameters: {'eta': 0.01610742689419875, 'max_depth': 9, 'subsample': 0.33497542766410426, 'feature_fraction': 0.29392943880535444, 'boosting': 'gbdt'}. Best is trial 6 with value: 2313.9436995982674. +[I 2023-08-11 12:31:41,953] Trial 10 finished with value: 2325.067843452219 and parameters: {'eta': 0.8686888033261265, 'max_depth': 1, 'subsample': 0.4392043546139532, 'feature_fraction': 0.6492156437954112, 'boosting': 'gbdt'}. Best is trial 6 with value: 2313.9436995982674. +[I 2023-08-11 12:31:54,009] Trial 11 finished with value: 2380.659260562121 and parameters: {'eta': 0.7699218284463614, 'max_depth': 1, 'subsample': 0.4581350563769593, 'feature_fraction': 0.6781897708396918, 'boosting': 'gbdt'}. Best is trial 6 with value: 2313.9436995982674. +[I 2023-08-11 12:32:06,259] Trial 12 finished with value: 2300.620681894269 and parameters: {'eta': 0.9611466035281578, 'max_depth': 1, 'subsample': 0.4748729196012275, 'feature_fraction': 0.5804945378046049, 'boosting': 'gbdt'}. Best is trial 12 with value: 2300.620681894269. +[I 2023-08-11 12:32:18,560] Trial 13 finished with value: 3140.364558904482 and parameters: {'eta': 0.13151506526519777, 'max_depth': 1, 'subsample': 0.20986359097182283, 'feature_fraction': 0.49641299908154024, 'boosting': 'gbdt'}. Best is trial 12 with value: 2300.620681894269. +[I 2023-08-11 12:32:31,398] Trial 14 finished with value: 2607.970802667295 and parameters: {'eta': 0.13257582555637926, 'max_depth': 3, 'subsample': 0.5234273923433372, 'feature_fraction': 0.560112443439662, 'boosting': 'gbdt'}. Best is trial 12 with value: 2300.620681894269. +[I 2023-08-11 12:32:44,762] Trial 15 finished with value: 2385.2393013330156 and parameters: {'eta': 0.11688247797482056, 'max_depth': 5, 'subsample': 0.5485181191665621, 'feature_fraction': 0.7236071396551794, 'boosting': 'gbdt'}. Best is trial 12 with value: 2300.620681894269. +[I 2023-08-11 12:32:56,986] Trial 16 finished with value: 1972.3296999647914 and parameters: {'eta': 0.9322627775031824, 'max_depth': 2, 'subsample': 0.3884067129956542, 'feature_fraction': 0.46443454447840615, 'boosting': 'gbdt'}. Best is trial 16 with value: 1972.3296999647914. +[I 2023-08-11 12:33:09,216] Trial 17 finished with value: 3338.4070116064154 and parameters: {'eta': 0.050131523125958345, 'max_depth': 2, 'subsample': 0.3614410986311337, 'feature_fraction': 0.20670293691731673, 'boosting': 'gbdt'}. Best is trial 16 with value: 1972.3296999647914. +[I 2023-08-11 12:33:13,236] Trial 18 finished with value: 3389.8614075906107 and parameters: {'eta': 0.986925756263617, 'max_depth': 5, 'subsample': 0.6167322950411971, 'feature_fraction': 0.5972303136885762, 'boosting': 'gbdt'}. Best is trial 16 with value: 1972.3296999647914. +[I 2023-08-11 12:33:16,990] Trial 19 pruned. Trial was pruned at iteration 30. +[I 2023-08-11 12:33:30,329] Trial 20 finished with value: 1808.1962308645848 and parameters: {'eta': 0.3847448879651696, 'max_depth': 6, 'subsample': 0.2765534220110189, 'feature_fraction': 0.4489981481795888, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:33:45,265] Trial 21 finished with value: 1899.1444205576186 and parameters: {'eta': 0.3036015753737849, 'max_depth': 6, 'subsample': 0.23678743004304076, 'feature_fraction': 0.4675647812287519, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:33:58,702] Trial 22 finished with value: 1908.5410750844935 and parameters: {'eta': 0.29460827340472634, 'max_depth': 6, 'subsample': 0.237820065177013, 'feature_fraction': 0.44683152950742716, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:34:12,440] Trial 23 finished with value: 1929.1341625507328 and parameters: {'eta': 0.3229800617391119, 'max_depth': 6, 'subsample': 0.2055577261442562, 'feature_fraction': 0.43369472632611744, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:34:25,588] Trial 24 finished with value: 2699.3212142027087 and parameters: {'eta': 0.07071121976729182, 'max_depth': 6, 'subsample': 0.2792800081576346, 'feature_fraction': 0.4327598617720121, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:34:39,033] Trial 25 finished with value: 1917.4633105999606 and parameters: {'eta': 0.27555064079028785, 'max_depth': 7, 'subsample': 0.28227436090885094, 'feature_fraction': 0.5387936166895363, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:34:41,956] Trial 26 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:34:55,636] Trial 27 finished with value: 2011.3551667558138 and parameters: {'eta': 0.23623752924982647, 'max_depth': 6, 'subsample': 0.21095407561350688, 'feature_fraction': 0.5018294529971035, 'boosting': 'gbdt'}. Best is trial 20 with value: 1808.1962308645848. +[I 2023-08-11 12:34:59,898] Trial 28 pruned. Trial was pruned at iteration 31. +[I 2023-08-11 12:35:02,824] Trial 29 pruned. Trial was pruned at iteration 20. + +Hyper-Parameter Optimization successfully finished. + Number of finished trials: 30 + Best trial: + Value: 1808.1962308645848 + Params: + eta: 0.3847448879651696 + max_depth: 6 + subsample: 0.2765534220110189 + feature_fraction: 0.4489981481795888 + boosting: gbdt + opt_rounds: 100 ++
np.random.seed(123)
+
+opt_params = opt_param.copy()
+n_rounds = opt_params["opt_rounds"]
+del opt_params["opt_rounds"]
+
+# Train Model with optimized hyperparameters
+lgblss.train(opt_params,
+ dtrain,
+ num_boost_round=n_rounds
+ )
+
# Set seed for reproducibility
+torch.manual_seed(123)
+
+# Number of samples to draw from predicted distribution
+n_samples = 1000
+quant_sel = [0.05, 0.95] # Quantiles to calculate from predicted distribution
+
+# Sample from predicted distribution
+pred_samples = lgblss.predict(X_test,
+ pred_type="samples",
+ n_samples=n_samples,
+ seed=123)
+
+# Calculate quantiles from predicted distribution
+pred_quantiles = lgblss.predict(X_test,
+ pred_type="quantiles",
+ n_samples=n_samples,
+ quantiles=quant_sel)
+
+# Returns predicted distributional parameters
+pred_params = lgblss.predict(X_test,
+ pred_type="parameters")
+
pred_samples.head()
+
+ | y_sample0 | +y_sample1 | +y_sample2 | +y_sample3 | +y_sample4 | +y_sample5 | +y_sample6 | +y_sample7 | +y_sample8 | +y_sample9 | +... | +y_sample990 | +y_sample991 | +y_sample992 | +y_sample993 | +y_sample994 | +y_sample995 | +y_sample996 | +y_sample997 | +y_sample998 | +y_sample999 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +1.803845 | +2.055054 | +3.721187 | +2.103017 | +2.785087 | +1.427447 | +2.607677 | +1.835255 | +2.056286 | +1.429045 | +... | +1.519037 | +1.412902 | +1.625722 | +1.990941 | +1.041016 | +1.458764 | +2.493577 | +2.022398 | +2.520749 | +1.871148 | +
1 | +0.955117 | +0.304340 | +0.505187 | +0.712550 | +1.216954 | +0.933732 | +0.380238 | +1.482657 | +0.736652 | +0.427856 | +... | +1.171600 | +1.010972 | +0.735717 | +1.319169 | +1.202447 | +0.967412 | +0.966288 | +1.435245 | +0.821253 | +0.793779 | +
2 | +1.144286 | +3.009341 | +1.845451 | +3.251571 | +2.020669 | +1.400807 | +2.169500 | +1.314221 | +1.898889 | +1.066060 | +... | +1.997797 | +1.747813 | +2.150669 | +2.500485 | +1.338773 | +1.857834 | +1.787327 | +1.801178 | +2.843899 | +1.567025 | +
3 | +1.808418 | +3.125320 | +1.761757 | +1.979179 | +2.679116 | +1.515085 | +1.274946 | +1.122176 | +2.836676 | +2.317276 | +... | +1.435263 | +1.846332 | +1.362321 | +1.321298 | +1.959441 | +1.562435 | +1.587221 | +1.903269 | +1.783693 | +1.171151 | +
4 | +4.249149 | +3.424617 | +3.813255 | +3.438753 | +2.999937 | +3.809508 | +3.346083 | +2.839856 | +3.459303 | +3.179483 | +... | +3.772136 | +3.544357 | +3.080696 | +6.813697 | +4.206345 | +2.519868 | +3.878748 | +5.277604 | +4.116862 | +3.673533 | +
5 rows × 1000 columns
+pred_quantiles.head()
+
+ | quant_0.05 | +quant_0.95 | +
---|---|---|
0 | +1.104986 | +2.876080 | +
1 | +0.493789 | +1.469833 | +
2 | +0.998464 | +2.626445 | +
3 | +1.021038 | +2.662356 | +
4 | +2.412530 | +5.914484 | +
pred_params.head()
+
+ | concentration | +rate | +
---|---|---|
0 | +12.671469 | +6.625142 | +
1 | +9.840599 | +10.347679 | +
2 | +12.215254 | +7.179342 | +
3 | +11.811074 | +6.747012 | +
4 | +13.350488 | +3.346810 | +
# Partial Dependence Plot
+shap_df = pd.DataFrame(X_train, columns=feature_names)
+
+lgblss.plot(shap_df,
+ parameter="concentration",
+ feature=feature_names[0],
+ plot_type="Partial_Dependence")
+
# Feature Importance
+lgblss.plot(shap_df,
+ parameter="concentration",
+ plot_type="Feature_Importance")
+
\n", + " | nll | \n", + "distribution | \n", + "
---|---|---|
rank | \n", + "\n", + " | \n", + " |
1 | \n", + "13576.063477 | \n", + "Mixture(Gaussian, tau=1.0, M=4) | \n", + "
2 | \n", + "23268.498047 | \n", + "Mixture(Gaussian, tau=1.0, M=3) | \n", + "
3 | \n", + "23737.701172 | \n", + "Mixture(Gaussian, tau=1.0, M=2) | \n", + "
\n", + " | y_sample0 | \n", + "y_sample1 | \n", + "y_sample2 | \n", + "y_sample3 | \n", + "y_sample4 | \n", + "y_sample5 | \n", + "y_sample6 | \n", + "y_sample7 | \n", + "y_sample8 | \n", + "y_sample9 | \n", + "... | \n", + "y_sample4118 | \n", + "y_sample4119 | \n", + "y_sample4120 | \n", + "y_sample4121 | \n", + "y_sample4122 | \n", + "y_sample4123 | \n", + "y_sample4124 | \n", + "y_sample4125 | \n", + "y_sample4126 | \n", + "y_sample4127 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "2.183442 | \n", + "1.743304 | \n", + "2.216470 | \n", + "1.648098 | \n", + "1.507569 | \n", + "2.085007 | \n", + "1.849130 | \n", + "1.713528 | \n", + "2.646151 | \n", + "1.948517 | \n", + "... | \n", + "2.056915 | \n", + "1.473761 | \n", + "1.776685 | \n", + "2.372680 | \n", + "1.886026 | \n", + "2.179646 | \n", + "1.874402 | \n", + "1.914331 | \n", + "2.328575 | \n", + "5.000010 | \n", + "
1 | \n", + "0.929630 | \n", + "0.864679 | \n", + "0.786885 | \n", + "0.947967 | \n", + "1.161466 | \n", + "0.800401 | \n", + "0.866021 | \n", + "1.139378 | \n", + "0.996050 | \n", + "2.367984 | \n", + "... | \n", + "0.796424 | \n", + "0.631778 | \n", + "0.832294 | \n", + "1.434881 | \n", + "1.046734 | \n", + "0.739934 | \n", + "0.933194 | \n", + "0.945460 | \n", + "0.846469 | \n", + "1.022823 | \n", + "
2 | \n", + "1.670022 | \n", + "1.420544 | \n", + "1.394724 | \n", + "1.287219 | \n", + "1.562551 | \n", + "1.532210 | \n", + "1.351166 | \n", + "1.282929 | \n", + "1.370430 | \n", + "1.379523 | \n", + "... | \n", + "1.517579 | \n", + "1.609070 | \n", + "1.263225 | \n", + "1.447542 | \n", + "1.422559 | \n", + "1.812555 | \n", + "1.506740 | \n", + "1.657970 | \n", + "1.837240 | \n", + "1.364948 | \n", + "
3 | \n", + "1.648252 | \n", + "5.000010 | \n", + "1.913860 | \n", + "1.524939 | \n", + "1.235044 | \n", + "1.695537 | \n", + "1.822447 | \n", + "5.000010 | \n", + "1.594498 | \n", + "1.504216 | \n", + "... | \n", + "1.976638 | \n", + "1.827072 | \n", + "1.785533 | \n", + "1.455808 | \n", + "1.893041 | \n", + "1.747105 | \n", + "1.818361 | \n", + "1.555387 | \n", + "1.533211 | \n", + "2.001599 | \n", + "
4 | \n", + "3.372061 | \n", + "2.330062 | \n", + "3.898244 | \n", + "2.260443 | \n", + "1.889219 | \n", + "4.780472 | \n", + "3.087396 | \n", + "1.930116 | \n", + "1.833662 | \n", + "2.483598 | \n", + "... | \n", + "5.000010 | \n", + "1.515394 | \n", + "2.842309 | \n", + "4.024762 | \n", + "3.275737 | \n", + "3.986492 | \n", + "2.120926 | \n", + "4.455951 | \n", + "4.522376 | \n", + "4.789488 | \n", + "
5 rows × 4128 columns
\n", + "\n", + " | quant_0.05 | \n", + "quant_0.95 | \n", + "
---|---|---|
0 | \n", + "1.573016 | \n", + "2.779724 | \n", + "
1 | \n", + "0.658904 | \n", + "1.298734 | \n", + "
2 | \n", + "1.122872 | \n", + "1.713133 | \n", + "
3 | \n", + "1.417880 | \n", + "4.537072 | \n", + "
4 | \n", + "1.771191 | \n", + "5.000010 | \n", + "
\n", + " | loc_1 | \n", + "loc_2 | \n", + "loc_3 | \n", + "loc_4 | \n", + "scale_1 | \n", + "scale_2 | \n", + "scale_3 | \n", + "scale_4 | \n", + "mix_prob_1 | \n", + "mix_prob_2 | \n", + "mix_prob_3 | \n", + "mix_prob_4 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "5.00001 | \n", + "1.678897 | \n", + "2.053436 | \n", + "2.501868 | \n", + "0.000001 | \n", + "0.163506 | \n", + "0.227133 | \n", + "0.428317 | \n", + "0.012478 | \n", + "0.140148 | \n", + "0.698507 | \n", + "0.148867 | \n", + "
1 | \n", + "5.00001 | \n", + "0.880190 | \n", + "0.931146 | \n", + "1.836034 | \n", + "0.000001 | \n", + "0.096553 | \n", + "0.227794 | \n", + "0.718976 | \n", + "0.002728 | \n", + "0.606532 | \n", + "0.348535 | \n", + "0.042204 | \n", + "
2 | \n", + "5.00001 | \n", + "1.375407 | \n", + "1.457845 | \n", + "1.915358 | \n", + "0.000001 | \n", + "0.152090 | \n", + "0.198093 | \n", + "0.601602 | \n", + "0.001360 | \n", + "0.766224 | \n", + "0.204061 | \n", + "0.028355 | \n", + "
3 | \n", + "5.00001 | \n", + "1.660305 | \n", + "1.861541 | \n", + "2.109918 | \n", + "0.000001 | \n", + "0.186978 | \n", + "0.192834 | \n", + "0.445636 | \n", + "0.057176 | \n", + "0.266268 | \n", + "0.341051 | \n", + "0.335504 | \n", + "
4 | \n", + "5.00001 | \n", + "2.177890 | \n", + "3.303763 | \n", + "3.737532 | \n", + "0.000001 | \n", + "0.360660 | \n", + "0.799386 | \n", + "0.648436 | \n", + "0.039917 | \n", + "0.320158 | \n", + "0.273619 | \n", + "0.366306 | \n", + "
Mixture densities or mixture distributions offer an extension to the notion of traditional univariate distributions by allowing the observed data to be thought of as arising from multiple underlying processes. In its essence, a mixture distribution is a weighted combination of several component distributions, where each component contributes to the overall mixture distribution, with the weights indicating the importance of each component. For instance, if you imagine the observed data distribution having multiple modes, a mixture of Gaussians could be employed to capture each mode with a separate Gaussian distribution.
+For each component of the mixture, there would be a set of parameters that depend on covariates, and additional mixing coefficients which are also modeled as a function of covariates. This is particularly useful when a single parametric distribution cannot adequately capture the underlying data generating process. A mixture distribution can be represented as follows:
+\begin{equation} +f\bigl(y_{i} | \boldsymbol{\theta}_{i}(x_{i})\bigr) = \sum_{m=1}^{M} w_{i,m}(x_{i}) \cdot f_{m}\bigl(y_{i} | \boldsymbol{\theta}_{i,m}(x_{i})\bigr) +\end{equation}
+where $f(\cdot)$ represents the mixture density for the $i$-th observation, $f_{m}(\cdot)$ is the $m$-th component density, each with its own set of parameters $\boldsymbol{\theta}_{i,m}(\cdot)$, and $w_{i,m}(\cdot)$ represent the weights of the $m$-th component in the mixture, subject to $\sum_{j=1}^{M} w_{i,m} = 1$. The components can either be a combination of different parametric univariate distributions, such as a combination of a Normal and a StudentT, or, as in our implementation, a combination of the same distribution-type with different parameterizations, e.g., Gaussian-Mixture or StudentT-Mixture. The choice of the component distributions depends on the characteristics of the data and the underlying assumptions. Due to their high flexibility, mixture densities can portray a diverse range of shapes, making them adaptable to a plethora of datasets.
+from lightgbmlss.model import *
+from lightgbmlss.distributions.Gaussian import *
+from lightgbmlss.distributions.Mixture import *
+from lightgbmlss.distributions.mixture_distribution_utils import MixtureDistributionClass
+
+from sklearn import datasets
+from sklearn.model_selection import train_test_split
+import matplotlib.pyplot as plt
+import seaborn as sns
+figure_size = (10,5)
+import plotnine
+from plotnine import *
+plotnine.options.figure_size = figure_size
+
housing_data = datasets.fetch_california_housing()
+X, y = housing_data["data"], housing_data["target"]
+feature_names = housing_data["feature_names"]
+
+X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
+
+dtrain = lgb.Dataset(X_train, label=y_train)
+
In the following, we specify a list of candidate distributions. The function dist_select
returns the negative log-likelihood of each distribution for the target variable. The distribution with the lowest negative log-likelihood is selected. The function also plots the density of the target variable and the fitted density, using the best suitable distribution among the specified ones. However, note that choosing the best performing mixture-distribution based solely on training data may lead to overfitting, since mixture-densities can approximate any distribution arbitrarily well. It is therefore crucial to carefully select the specifications to strike a balance between model complexity and generalization ability.
mix_dist_class = MixtureDistributionClass()
+
+candidate_distributions = [
+ Mixture(Gaussian(response_fn="softplus"), M = 2),
+ Mixture(Gaussian(response_fn="softplus"), M = 3),
+ Mixture(Gaussian(response_fn="softplus"), M = 4),
+]
+
+dist_nll = mix_dist_class.dist_select(target=y_train, candidate_distributions=candidate_distributions, max_iter=50, plot=True, figure_size=(8, 5))
+dist_nll
+
Fitting of candidate distributions completed: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3/3 [00:05<00:00, 1.76s/it] ++
+ | nll | +distribution | +
---|---|---|
rank | ++ | + |
1 | +13576.063477 | +Mixture(Gaussian, tau=1.0, M=4) | +
2 | +23268.498047 | +Mixture(Gaussian, tau=1.0, M=3) | +
3 | +23737.701172 | +Mixture(Gaussian, tau=1.0, M=2) | +
# Specifies a mixture of Gaussians. See ?Mixture for an overview.
+lgblss = LightGBMLSS(
+ Mixture(
+ Gaussian(response_fn="softplus", stabilization="L2"),
+ M = 4,
+ tau=1.0,
+ hessian_mode="individual",
+ )
+)
+
Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows:
+- Float/Int sample_type
+ - {"param_name": ["sample_type", low, high, log]}
+ - sample_type: str, Type of sampling, e.g., "float" or "int"
+ - low: int, Lower endpoint of the range of suggested values
+ - high: int, Upper endpoint of the range of suggested values
+ - log: bool, Flag to sample the value from the log domain or not
+ - Example: {"eta": "float", low=1e-5, high=1, log=True]}
+
+- Categorical sample_type
+ - {"param_name": ["sample_type", ["choice1", "choice2", "choice3", "..."]]}
+ - sample_type: str, Type of sampling, either "categorical"
+ - choice1, choice2, choice3, ...: str, Possible choices for the parameter
+ - Example: {"boosting": ["categorical", ["gbdt", "dart"]]}
+
+- For parameters without tunable choice (this is needed if tree_method = "gpu_hist" and gpu_id needs to be specified)
+ - {"param_name": ["none", [value]]},
+ - param_name: str, Name of the parameter
+ - value: int, Value of the parameter
+ - Example: {"gpu_id": ["none", [0]]}
+param_dict = {
+ "eta": ["float", {"low": 1e-5, "high": 1, "log": True}],
+ "max_depth": ["int", {"low": 1, "high": 10, "log": False}],
+ "min_gain_to_split": ["float", {"low": 1e-8, "high": 40, "log": True}],
+ "min_sum_hessian_in_leaf": ["float", {"low": 1e-8, "high": 500, "log": True}],
+ "subsample": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "feature_fraction": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "boosting": ["categorical", ["gbdt"]],
+}
+
+np.random.seed(123)
+opt_param = lgblss.hyper_opt(param_dict,
+ dtrain,
+ num_boost_round=100, # Number of boosting iterations.
+ nfold=5, # Number of cv-folds.
+ early_stopping_rounds=20, # Number of early-stopping rounds
+ max_minutes=60, # Time budget in minutes, i.e., stop study after the given number of minutes.
+ n_trials=20, # The number of trials. If this argument is set to None, there is no limitation on the number of trials.
+ silence=True, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail.
+ seed=123, # Seed used to generate cv-folds.
+ hp_seed=None # Seed for random number generator used in the Bayesian hyperparameter search.
+ )
+
0%| | 0/20 [00:00<?, ?it/s]+
+Hyper-Parameter Optimization successfully finished. + Number of finished trials: 20 + Best trial: + Value: -177.13368962158688 + Params: + eta: 0.013216124285390762 + max_depth: 10 + min_gain_to_split: 2.3995429964658155e-08 + min_sum_hessian_in_leaf: 75.94605936994567 + subsample: 0.20049396237050426 + feature_fraction: 0.7536825284855249 + boosting: gbdt + opt_rounds: 100 ++
np.random.seed(123)
+
+opt_params = opt_param.copy()
+n_rounds = opt_params["opt_rounds"]
+del opt_params["opt_rounds"]
+
+# Train Model with optimized hyperparameters
+lgblss.train(opt_params,
+ dtrain,
+ num_boost_round=n_rounds,
+ )
+
# Set seed for reproducibility
+torch.manual_seed(123)
+
+# Number of samples to draw from predicted distribution
+n_samples = y_test.shape[0]
+quant_sel = [0.05, 0.95] # Quantiles to calculate from predicted distribution
+
+# Sample from predicted distribution
+pred_samples = lgblss.predict(X_test,
+ pred_type="samples",
+ n_samples=n_samples,
+ seed=123)
+
+# Calculate quantiles from predicted distribution
+pred_quantiles = lgblss.predict(X_test,
+ pred_type="quantiles",
+ n_samples=n_samples,
+ quantiles=quant_sel)
+
+# Returns predicted distributional parameters
+pred_params = lgblss.predict(X_test,
+ pred_type="parameters")
+
pred_samples.head()
+
+ | y_sample0 | +y_sample1 | +y_sample2 | +y_sample3 | +y_sample4 | +y_sample5 | +y_sample6 | +y_sample7 | +y_sample8 | +y_sample9 | +... | +y_sample4118 | +y_sample4119 | +y_sample4120 | +y_sample4121 | +y_sample4122 | +y_sample4123 | +y_sample4124 | +y_sample4125 | +y_sample4126 | +y_sample4127 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +2.183442 | +1.743304 | +2.216470 | +1.648098 | +1.507569 | +2.085007 | +1.849130 | +1.713528 | +2.646151 | +1.948517 | +... | +2.056915 | +1.473761 | +1.776685 | +2.372680 | +1.886026 | +2.179646 | +1.874402 | +1.914331 | +2.328575 | +5.000010 | +
1 | +0.929630 | +0.864679 | +0.786885 | +0.947967 | +1.161466 | +0.800401 | +0.866021 | +1.139378 | +0.996050 | +2.367984 | +... | +0.796424 | +0.631778 | +0.832294 | +1.434881 | +1.046734 | +0.739934 | +0.933194 | +0.945460 | +0.846469 | +1.022823 | +
2 | +1.670022 | +1.420544 | +1.394724 | +1.287219 | +1.562551 | +1.532210 | +1.351166 | +1.282929 | +1.370430 | +1.379523 | +... | +1.517579 | +1.609070 | +1.263225 | +1.447542 | +1.422559 | +1.812555 | +1.506740 | +1.657970 | +1.837240 | +1.364948 | +
3 | +1.648252 | +5.000010 | +1.913860 | +1.524939 | +1.235044 | +1.695537 | +1.822447 | +5.000010 | +1.594498 | +1.504216 | +... | +1.976638 | +1.827072 | +1.785533 | +1.455808 | +1.893041 | +1.747105 | +1.818361 | +1.555387 | +1.533211 | +2.001599 | +
4 | +3.372061 | +2.330062 | +3.898244 | +2.260443 | +1.889219 | +4.780472 | +3.087396 | +1.930116 | +1.833662 | +2.483598 | +... | +5.000010 | +1.515394 | +2.842309 | +4.024762 | +3.275737 | +3.986492 | +2.120926 | +4.455951 | +4.522376 | +4.789488 | +
5 rows × 4128 columns
+pred_quantiles.head()
+
+ | quant_0.05 | +quant_0.95 | +
---|---|---|
0 | +1.573016 | +2.779724 | +
1 | +0.658904 | +1.298734 | +
2 | +1.122872 | +1.713133 | +
3 | +1.417880 | +4.537072 | +
4 | +1.771191 | +5.000010 | +
pred_params.head()
+
+ | loc_1 | +loc_2 | +loc_3 | +loc_4 | +scale_1 | +scale_2 | +scale_3 | +scale_4 | +mix_prob_1 | +mix_prob_2 | +mix_prob_3 | +mix_prob_4 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +5.00001 | +1.678897 | +2.053436 | +2.501868 | +0.000001 | +0.163506 | +0.227133 | +0.428317 | +0.012478 | +0.140148 | +0.698507 | +0.148867 | +
1 | +5.00001 | +0.880190 | +0.931146 | +1.836034 | +0.000001 | +0.096553 | +0.227794 | +0.718976 | +0.002728 | +0.606532 | +0.348535 | +0.042204 | +
2 | +5.00001 | +1.375407 | +1.457845 | +1.915358 | +0.000001 | +0.152090 | +0.198093 | +0.601602 | +0.001360 | +0.766224 | +0.204061 | +0.028355 | +
3 | +5.00001 | +1.660305 | +1.861541 | +2.109918 | +0.000001 | +0.186978 | +0.192834 | +0.445636 | +0.057176 | +0.266268 | +0.341051 | +0.335504 | +
4 | +5.00001 | +2.177890 | +3.303763 | +3.737532 | +0.000001 | +0.360660 | +0.799386 | +0.648436 | +0.039917 | +0.320158 | +0.273619 | +0.366306 | +
# Partial Dependence Plot
+shap_df = pd.DataFrame(X_train, columns=feature_names)
+lgblss.plot(shap_df,
+ parameter="mix_prob_1",
+ feature=feature_names[0],
+ plot_type="Partial_Dependence")
+
# Feature Importance
+lgblss.plot(shap_df,
+ parameter="mix_prob_1",
+ plot_type="Feature_Importance")
+
In the following, we plot the actual and a subsample of the predicted denstites.
+n_subset = 10
+pred_df = pred_samples.iloc[:,0:n_subset]
+pred_df.columns=[f"Sample {i+1}" for i in range(n_subset)]
+actual_df = pd.DataFrame(y_test.reshape(-1,), columns = ["Actual"])
+plot_df = pd.concat([pred_df, actual_df], axis=1)
+linestyles = ["--" for _ in range(n_subset)] + ["solid"]
+linewidths = [1 for _ in range(n_subset)] + [2.5]
+
+plt.figure(figsize=figure_size)
+for idx, col in enumerate(plot_df.columns):
+ sns.kdeplot(plot_df[col], linestyle=linestyles[idx], lw=linewidths[idx], label=col)
+plt.legend()
+plt.title("Actual vs. Predicted Densities", fontsize=20)
+plt.gca().set_xlabel("y")
+plt.gca().set_ylabel("f(y)")
+plt.show()
+
We can also plot each Gaussian component distribution.
+# Extract predicted parameters
+mix_params = torch.split(torch.tensor(pred_params.values[0,:]).reshape(1,-1), lgblss.dist.M, dim=1)
+mix_params[1][0][0] = mix_params[1][0][0] + torch.tensor(0.1) # increase the std of the first density for plotting reasons
+
+# Create Mixture-Distribution
+torch.manual_seed(123)
+mix_dist = lgblss.dist.create_mixture_distribution(mix_params)
+gaus_dist = mix_dist._component_distribution
+gaus_samples = pd.DataFrame(
+ gaus_dist.sample((y_test.shape[0],)).reshape(-1, lgblss.dist.M).numpy(),
+ columns = [f"Density {i+1}" for i in range(lgblss.dist.M)]
+)
+
+# Plot
+plt.figure(figsize=figure_size)
+sns.kdeplot(gaus_samples, lw=2.5)
+plt.title("Gaussian-Mixture Component Distributions", fontsize=20)
+plt.show()
+
Since we predict the entire conditional distribution, we can overlay the point predictions with predicted densities, from which we can also derive quantiles of interest.
+y_pred = []
+
+n_examples = 8
+q_sel = [0.05, 0.95]
+y_sel=0
+samples_arr = pred_samples.values.reshape(-1,n_samples)
+
+for i in range(n_examples):
+ y_samples = pd.DataFrame(samples_arr[i,:].reshape(-1,1), columns=["PREDICT_DENSITY"])
+ y_samples["PREDICT_POINT"] = y_samples["PREDICT_DENSITY"].mean()
+ y_samples["PREDICT_Q05"] = y_samples["PREDICT_DENSITY"].quantile(q=q_sel[0])
+ y_samples["PREDICT_Q95"] = y_samples["PREDICT_DENSITY"].quantile(q=q_sel[1])
+ y_samples["ACTUAL"] = y_test[i]
+ y_samples["obs"]= f"Obervation {i+1}"
+ y_pred.append(y_samples)
+
+pred_df = pd.melt(pd.concat(y_pred, axis=0), id_vars="obs")
+pred_df["obs"] = pd.Categorical(pred_df["obs"], categories=[f"Obervation {i+1}" for i in range(n_examples)])
+df_actual, df_pred_dens, df_pred_point, df_q05, df_q95 = [x for _, x in pred_df.groupby("variable")]
+
+plot_pred = (
+ ggplot(pred_df,
+ aes(color="variable")) +
+ stat_density(df_pred_dens,
+ aes(x="value"),
+ size=1.1) +
+ geom_point(df_pred_point,
+ aes(x="value",
+ y=0),
+ size=1.4) +
+ geom_point(df_actual,
+ aes(x="value",
+ y=0),
+ size=1.4) +
+ geom_vline(df_q05,
+ aes(xintercept="value",
+ fill="variable",
+ color="variable"),
+ linetype="dashed",
+ size=1.1) +
+ geom_vline(df_q95,
+ aes(xintercept="value",
+ fill="variable",
+ color="variable"),
+ linetype="dashed",
+ size=1.1) +
+ facet_wrap("obs",
+ scales="free",
+ ncol=4) +
+ labs(title="Predicted vs. Actual \n",
+ x = "") +
+ theme_bw(base_size=15) +
+ theme(plot_title = element_text(hjust = 0.5)) +
+ scale_fill_brewer(type="qual", palette="Dark2") +
+ theme(legend_position="bottom",
+ legend_title = element_blank()
+ )
+)
+
+print(plot_pred)
+
++
\n", + " | y_sample0 | \n", + "y_sample1 | \n", + "y_sample2 | \n", + "y_sample3 | \n", + "y_sample4 | \n", + "y_sample5 | \n", + "y_sample6 | \n", + "y_sample7 | \n", + "y_sample8 | \n", + "y_sample9 | \n", + "... | \n", + "y_sample990 | \n", + "y_sample991 | \n", + "y_sample992 | \n", + "y_sample993 | \n", + "y_sample994 | \n", + "y_sample995 | \n", + "y_sample996 | \n", + "y_sample997 | \n", + "y_sample998 | \n", + "y_sample999 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "10.969691 | \n", + "8.598016 | \n", + "11.316233 | \n", + "11.196012 | \n", + "-0.772420 | \n", + "10.135987 | \n", + "14.128653 | \n", + "7.630763 | \n", + "12.698465 | \n", + "8.239548 | \n", + "... | \n", + "10.933374 | \n", + "11.149927 | \n", + "8.531628 | \n", + "9.486668 | \n", + "10.501742 | \n", + "9.149441 | \n", + "11.645898 | \n", + "7.640362 | \n", + "9.958486 | \n", + "6.289453 | \n", + "
1 | \n", + "9.461757 | \n", + "9.600316 | \n", + "10.147589 | \n", + "12.562353 | \n", + "16.377707 | \n", + "10.912953 | \n", + "9.325396 | \n", + "9.916863 | \n", + "7.682811 | \n", + "17.506680 | \n", + "... | \n", + "10.509146 | \n", + "7.077166 | \n", + "2.610499 | \n", + "8.764830 | \n", + "16.280003 | \n", + "8.840451 | \n", + "8.440387 | \n", + "16.157856 | \n", + "9.217056 | \n", + "8.147191 | \n", + "
2 | \n", + "9.655623 | \n", + "10.095434 | \n", + "10.497488 | \n", + "8.415718 | \n", + "10.877867 | \n", + "8.264465 | \n", + "10.300594 | \n", + "11.423710 | \n", + "10.838738 | \n", + "9.795403 | \n", + "... | \n", + "11.465590 | \n", + "10.625634 | \n", + "8.727895 | \n", + "9.756786 | \n", + "8.575561 | \n", + "9.242844 | \n", + "9.997345 | \n", + "10.150554 | \n", + "10.739549 | \n", + "9.458581 | \n", + "
3 | \n", + "7.315505 | \n", + "15.109694 | \n", + "9.312259 | \n", + "-3.643276 | \n", + "12.262859 | \n", + "8.238321 | \n", + "7.009054 | \n", + "6.200121 | \n", + "3.878844 | \n", + "13.135740 | \n", + "... | \n", + "19.848660 | \n", + "15.314698 | \n", + "8.589100 | \n", + "12.168641 | \n", + "8.016486 | \n", + "13.352606 | \n", + "9.907011 | \n", + "8.088248 | \n", + "12.577316 | \n", + "7.965759 | \n", + "
4 | \n", + "11.067125 | \n", + "10.200111 | \n", + "9.035689 | \n", + "8.689579 | \n", + "10.475836 | \n", + "8.348648 | \n", + "6.953860 | \n", + "7.018825 | \n", + "11.546731 | \n", + "4.925195 | \n", + "... | \n", + "9.588341 | \n", + "12.277424 | \n", + "9.194149 | \n", + "13.164710 | \n", + "11.918127 | \n", + "7.771739 | \n", + "10.544153 | \n", + "9.177776 | \n", + "7.839355 | \n", + "11.506623 | \n", + "
5 rows × 1000 columns
\n", + "\n", + " | quant_0.05 | \n", + "quant_0.95 | \n", + "
---|---|---|
0 | \n", + "5.405031 | \n", + "14.896293 | \n", + "
1 | \n", + "5.210804 | \n", + "15.007315 | \n", + "
2 | \n", + "8.298658 | \n", + "11.832828 | \n", + "
3 | \n", + "2.962531 | \n", + "17.087187 | \n", + "
4 | \n", + "4.807709 | \n", + "14.909888 | \n", + "
\n", + " | loc | \n", + "scale | \n", + "
---|---|---|
0 | \n", + "9.984035 | \n", + "2.921586 | \n", + "
1 | \n", + "9.979074 | \n", + "2.909918 | \n", + "
2 | \n", + "9.979074 | \n", + "1.065636 | \n", + "
3 | \n", + "9.979074 | \n", + "4.529788 | \n", + "
4 | \n", + "9.979074 | \n", + "3.121158 | \n", + "
In this example, we model and predict all parameters of a univariate Normal distribution. Recall that distributional regression models and predicts all parameters $\theta_{ik}, k=1, \ldots, K$ parameters of a distribution $\mathcal{D}$ as a function of covariates:
+\begin{equation} +y_{i} \stackrel{ind}{\sim} \mathcal{D} + \begin{pmatrix} + h_{1}\bigl(\theta_{i1}(x_{i})\bigr) = \eta_{i1} \\ + h_{2}\bigl(\theta_{i2}(x_{i})\bigr) = \eta_{i2} \\ + \vdots \\ + h_{K}\bigl(\theta_{iK}(x_{i})\bigr) = \eta_{iK} +\end{pmatrix} +\quad ,i=1, \ldots, N. +\end{equation}
+where $h_{k}(\cdot)$ transforms each distributional parameter to the corresponding parameter scale. For the univariate Normal case, we can specify the above as $y_{i} \stackrel{ind}{\sim} \mathcal{N}\bigl(\mu_{i}(x_{i}), \sigma_{i}(x_{i})\bigr)$. Since $\mu_{i}(\cdot) \in \mathbb{R}$ and since the standard-deviation cannot be negative, $h_{k}(\cdot)$ is applied to $\sigma_{i}(\cdot)$ only. Typical choices are the exponential or the softplus function.
+First, we import the necessary functions.
+from lightgbmlss.model import *
+from lightgbmlss.distributions.Gaussian import *
+from lightgbmlss.datasets.data_loader import load_simulated_gaussian_data
+from scipy.stats import norm
+
+import plotnine
+from plotnine import *
+plotnine.options.figure_size = (12, 8)
+
The data is simulated as a Gaussian, where $x_{true}$ is the only true feature and all others are noise variables:
+We first load the simulated dataset, filter for the target and covariates and then create the lgb.Dataset
. LightGBMLSS is designed to closely resemble the usage of LightGBM, ensuring ease of adoption and full compatibility.
train, test = load_simulated_gaussian_data()
+
+X_train, y_train = train.filter(regex="x"), train["y"].values
+X_test, y_test = test.filter(regex="x"), test["y"].values
+
+dtrain = lgb.Dataset(X_train, label=y_train)
+
Next, we specify a Gaussian distribution. By modifying the specification in the following, the user can specify alternative distributional assumptions. This includes the option to choose from a wide range of parametric univariate distributions, as well as to model the data using Normalizing Flows. The user also has different function arguments for each distribution:
+stabilization
: specifies the stabilization method for the Gradient and Hessian. Options are None
, MAD
and L2
.response_fn
: specifies $h_{k}(\cdot)$ and transforms the distributional parameter to the correct support. Here, we specify an exponential for $\sigma_{i}(\cdot)$ only.loss_fn
: specifies the loss function used for training. Options are nll
(negative log-likelihood) or crps
(continuous ranked probability score).For additional details, see ?Gaussian
.
lgblss = LightGBMLSS(
+ Gaussian(stabilization="None",
+ response_fn="exp",
+ loss_fn="nll"
+ )
+)
+
Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows:
+- Float/Int sample_type
+ - {"param_name": ["sample_type", low, high, log]}
+ - sample_type: str, Type of sampling, e.g., "float" or "int"
+ - low: int, Lower endpoint of the range of suggested values
+ - high: int, Upper endpoint of the range of suggested values
+ - log: bool, Flag to sample the value from the log domain or not
+ - Example: {"eta": "float", low=1e-5, high=1, log=True]}
+
+- Categorical sample_type
+ - {"param_name": ["sample_type", ["choice1", "choice2", "choice3", "..."]]}
+ - sample_type: str, Type of sampling, either "categorical"
+ - choice1, choice2, choice3, ...: str, Possible choices for the parameter
+ - Example: {"boosting": ["categorical", ["gbdt", "dart"]]}
+
+- For parameters without tunable choice (this is needed if tree_method = "gpu_hist" and gpu_id needs to be specified)
+ - {"param_name": ["none", [value]]},
+ - param_name: str, Name of the parameter
+ - value: int, Value of the parameter
+ - Example: {"gpu_id": ["none", [0]]}
+param_dict = {
+ "eta": ["float", {"low": 1e-5, "high": 1, "log": True}],
+ "max_depth": ["int", {"low": 1, "high": 10, "log": False}],
+ "num_leaves": ["int", {"low": 255, "high": 255, "log": False}], # set to constant for this example
+ "min_data_in_leaf": ["int", {"low": 20, "high": 20, "log": False}], # set to constant for this example
+ "min_gain_to_split": ["float", {"low": 1e-8, "high": 40, "log": False}],
+ "min_sum_hessian_in_leaf": ["float", {"low": 1e-8, "high": 500, "log": True}],
+ "subsample": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "feature_fraction": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "boosting": ["categorical", ["gbdt"]],
+}
+
+np.random.seed(123)
+opt_param = lgblss.hyper_opt(param_dict,
+ dtrain,
+ num_boost_round=100, # Number of boosting iterations.
+ nfold=5, # Number of cv-folds.
+ early_stopping_rounds=20, # Number of early-stopping rounds
+ max_minutes=10, # Time budget in minutes, i.e., stop study after the given number of minutes.
+ n_trials=30 , # The number of trials. If this argument is set to None, there is no limitation on the number of trials.
+ silence=True, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail.
+ seed=123, # Seed used to generate cv-folds.
+ hp_seed=123 # Seed for random number generator used in the Bayesian hyperparameter search.
+ )
+
0%| | 0/30 [00:00<?, ?it/s]+
+Hyper-Parameter Optimization successfully finished. + Number of finished trials: 30 + Best trial: + Value: 2916.681710293905 + Params: + eta: 0.04397442966488241 + max_depth: 3 + num_leaves: 255 + min_data_in_leaf: 20 + min_gain_to_split: 9.411991003859868 + min_sum_hessian_in_leaf: 462.25410582290624 + subsample: 0.9517916433037966 + feature_fraction: 0.5063785131055764 + boosting: gbdt + opt_rounds: 97 ++
We use the optimized hyper-parameters and train the model.
+np.random.seed(123)
+
+opt_params = opt_param.copy()
+n_rounds = opt_params["opt_rounds"]
+del opt_params["opt_rounds"]
+
+# Train Model with optimized hyperparameters
+lgblss.train(opt_params,
+ dtrain,
+ num_boost_round=n_rounds
+ )
+
Similar to a LightGBM model, we now predict from the trained model. Different options are available:
+samples
: draws n_samples
from the predicted distribution.quantiles
: calculates quantiles from the predicted distribution.parameters
: returns predicted distributional parameters.# Set seed for reproducibility
+torch.manual_seed(123)
+
+# Number of samples to draw from predicted distribution
+n_samples = 1000
+quant_sel = [0.05, 0.95] # Quantiles to calculate from predicted distribution
+
+# Sample from predicted distribution
+pred_samples = lgblss.predict(X_test,
+ pred_type="samples",
+ n_samples=n_samples,
+ seed=123)
+
+# Calculate quantiles from predicted distribution
+pred_quantiles = lgblss.predict(X_test,
+ pred_type="quantiles",
+ n_samples=n_samples,
+ quantiles=quant_sel)
+
+# Return predicted distributional parameters
+pred_params = lgblss.predict(X_test,
+ pred_type="parameters")
+
pred_samples.head()
+
+ | y_sample0 | +y_sample1 | +y_sample2 | +y_sample3 | +y_sample4 | +y_sample5 | +y_sample6 | +y_sample7 | +y_sample8 | +y_sample9 | +... | +y_sample990 | +y_sample991 | +y_sample992 | +y_sample993 | +y_sample994 | +y_sample995 | +y_sample996 | +y_sample997 | +y_sample998 | +y_sample999 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +10.969691 | +8.598016 | +11.316233 | +11.196012 | +-0.772420 | +10.135987 | +14.128653 | +7.630763 | +12.698465 | +8.239548 | +... | +10.933374 | +11.149927 | +8.531628 | +9.486668 | +10.501742 | +9.149441 | +11.645898 | +7.640362 | +9.958486 | +6.289453 | +
1 | +9.461757 | +9.600316 | +10.147589 | +12.562353 | +16.377707 | +10.912953 | +9.325396 | +9.916863 | +7.682811 | +17.506680 | +... | +10.509146 | +7.077166 | +2.610499 | +8.764830 | +16.280003 | +8.840451 | +8.440387 | +16.157856 | +9.217056 | +8.147191 | +
2 | +9.655623 | +10.095434 | +10.497488 | +8.415718 | +10.877867 | +8.264465 | +10.300594 | +11.423710 | +10.838738 | +9.795403 | +... | +11.465590 | +10.625634 | +8.727895 | +9.756786 | +8.575561 | +9.242844 | +9.997345 | +10.150554 | +10.739549 | +9.458581 | +
3 | +7.315505 | +15.109694 | +9.312259 | +-3.643276 | +12.262859 | +8.238321 | +7.009054 | +6.200121 | +3.878844 | +13.135740 | +... | +19.848660 | +15.314698 | +8.589100 | +12.168641 | +8.016486 | +13.352606 | +9.907011 | +8.088248 | +12.577316 | +7.965759 | +
4 | +11.067125 | +10.200111 | +9.035689 | +8.689579 | +10.475836 | +8.348648 | +6.953860 | +7.018825 | +11.546731 | +4.925195 | +... | +9.588341 | +12.277424 | +9.194149 | +13.164710 | +11.918127 | +7.771739 | +10.544153 | +9.177776 | +7.839355 | +11.506623 | +
5 rows × 1000 columns
+pred_quantiles.head()
+
+ | quant_0.05 | +quant_0.95 | +
---|---|---|
0 | +5.405031 | +14.896293 | +
1 | +5.210804 | +15.007315 | +
2 | +8.298658 | +11.832828 | +
3 | +2.962531 | +17.087187 | +
4 | +4.807709 | +14.909888 | +
pred_params.head()
+
+ | loc | +scale | +
---|---|---|
0 | +9.984035 | +2.921586 | +
1 | +9.979074 | +2.909918 | +
2 | +9.979074 | +1.065636 | +
3 | +9.979074 | +4.529788 | +
4 | +9.979074 | +3.121158 | +
To get a deeper understanding of the data generating process, LightGBMLSS also provides attribute importance and partial dependence plots using the Shapley-Value approach.
+# Partial Dependence Plot of how x_true acts on variance
+lgblss.plot(X_test,
+ parameter="scale",
+ feature="x_true",
+ plot_type="Partial_Dependence")
+
# Feature Importance of scale parameter
+lgblss.plot(X_test,
+ parameter="scale",
+ plot_type="Feature_Importance")
+
In the following, we plot the predicted quantiles (blue) and compare them to the actual quantiles (dashed-black).
+np.random.seed(123)
+
+###
+# Actual Quantiles
+###
+q1 = norm.ppf(quant_sel[0], loc = 10, scale = 1 + 4*((0.3 < test["x_true"].values) & (test["x_true"].values < 0.5)) + 2*(test["x_true"].values > 0.7))
+q2 = norm.ppf(quant_sel[1], loc = 10, scale = 1 + 4*((0.3 < test["x_true"].values) & (test["x_true"].values < 0.5)) + 2*(test["x_true"].values > 0.7))
+test["quant"] = np.where(test["y"].values < q1, 0, np.where(test["y"].values < q2, 1, 2))
+test["alpha"] = np.where(test["y"].values <= q1, 1, np.where(test["y"].values >= q2, 1, 0))
+df_quantiles = test[test["alpha"] == 1]
+
+# Lower Bound
+yl = list(set(q1))
+yl.sort()
+yl = [yl[2],yl[0],yl[2],yl[1],yl[1]]
+sfunl = pd.DataFrame({"x_true":[0, 0.3, 0.5, 0.7, 1], "y":yl})
+
+# Upper Bound
+yu = list(set(q2))
+yu.sort()
+yu = [yu[0],yu[2],yu[0],yu[1],yu[1]]
+sfunu = pd.DataFrame({"x_true":[0, 0.3, 0.5, 0.7, 1], "y":yu})
+
+###
+# Predicted Quantiles
+###
+test["lb"] = pred_quantiles.iloc[:,0]
+test["ub"] = pred_quantiles.iloc[:,1]
+
+###
+# Plot
+###
+(ggplot(test,
+ aes("x_true",
+ "y")) +
+ geom_point(alpha = 0.2, color = "black", size = 2) +
+ theme_bw(base_size=15) +
+ theme(legend_position="none",
+ plot_title = element_text(hjust = 0.5),
+ plot_subtitle = element_text(hjust = 0.5)) +
+ labs(title = "LightGBMLSS Regression - Simulated Data Example",
+ subtitle = "Comparison of Actual (black) vs. Predicted Quantiles (blue)",
+ x="x") +
+ geom_line(aes("x_true",
+ "ub"),
+ size = 1,
+ color = "blue",
+ alpha = 0.7) +
+ geom_line(aes("x_true",
+ "lb"),
+ size = 1,
+ color = "blue",
+ alpha = 0.7) +
+ geom_point(df_quantiles,
+ aes("x_true",
+ "y"),
+ color = "red",
+ alpha = 0.7,
+ size = 2) +
+ geom_step(sfunl,
+ aes("x_true",
+ "y"),
+ size = 1,
+ linetype = "dashed") +
+ geom_step(sfunu,
+ aes("x_true",
+ "y"),
+ size = 1,
+ linetype = "dashed")
+)
+
<Figure Size: (1200 x 800)>+
In the following figure, we compare the true parameters of the Gaussian with the ones predicted by LightGBMLSS. The below figure shows that the estimated parameters closely match the true ones (recall that the location parameter $\mu=10$ is simulated as being a constant).
+pred_params["x_true"] = X_test["x_true"].values
+dist_params = list(lgblss.dist.param_dict.keys())
+
+# Data with actual values
+plot_df_actual = pd.melt(test[["x_true"] + dist_params],
+ id_vars="x_true",
+ value_vars=dist_params)
+plot_df_actual["type"] = "TRUE"
+
+# Data with predicted values
+plot_df_predt = pd.melt(pred_params[["x_true"] + dist_params],
+ id_vars="x_true",
+ value_vars=dist_params)
+plot_df_predt["type"] = "PREDICT"
+
+plot_df = pd.concat([plot_df_predt, plot_df_actual])
+
+plot_df["variable"] = plot_df.variable.str.upper()
+plot_df["type"] = pd.Categorical(plot_df["type"], categories = ["PREDICT", "TRUE"])
+
+(ggplot(plot_df,
+ aes(x="x_true",
+ y="value",
+ color="type")) +
+ geom_line(size=1.1) +
+ facet_wrap("variable",
+ scales="free") +
+ labs(title="Parameters of univariate Gaussian predicted with LightGBMLSS",
+ x="",
+ y="") +
+ theme_bw(base_size=15) +
+ theme(legend_position="bottom",
+ plot_title = element_text(hjust = 0.5),
+ legend_title = element_blank())
+)
+
<Figure Size: (1200 x 800)>+
Since we predict the entire conditional distribution, we can overlay the point predictions with predicted densities, from which we can also derive quantiles of interest.
+y_pred = []
+
+n_examples = 9
+
+for i in range(n_examples):
+ y_samples = pd.DataFrame(pred_samples.values[i,:].reshape(-1,1), columns=["PREDICT_DENSITY"])
+ y_samples["PREDICT_POINT"] = y_samples["PREDICT_DENSITY"].mean()
+ y_samples["PREDICT_Q05"] = y_samples["PREDICT_DENSITY"].quantile(q=quant_sel[0])
+ y_samples["PREDICT_Q95"] = y_samples["PREDICT_DENSITY"].quantile(q=quant_sel[1])
+ y_samples["ACTUAL"] = y_test[i]
+ y_samples["obs"]= f"Obervation {i+1}"
+ y_pred.append(y_samples)
+
+pred_df = pd.melt(pd.concat(y_pred, axis=0), id_vars="obs")
+pred_df["obs"] = pd.Categorical(pred_df["obs"], categories=[f"Obervation {i+1}" for i in range(n_examples)])
+df_actual, df_pred_dens, df_pred_point, df_q05, df_q95 = [x for _, x in pred_df.groupby("variable")]
+
+plot_pred = (
+ ggplot(pred_df,
+ aes(color="variable")) +
+ stat_density(df_pred_dens,
+ aes(x="value"),
+ size=1.1) +
+ geom_point(df_pred_point,
+ aes(x="value",
+ y=0),
+ size=1.4) +
+ geom_point(df_actual,
+ aes(x="value",
+ y=0),
+ size=1.4) +
+ geom_vline(df_q05,
+ aes(xintercept="value",
+ fill="variable",
+ color="variable"),
+ linetype="dashed",
+ size=1.1) +
+ geom_vline(df_q95,
+ aes(xintercept="value",
+ fill="variable",
+ color="variable"),
+ linetype="dashed",
+ size=1.1) +
+ facet_wrap("obs",
+ scales="free",
+ ncol=3) +
+ labs(title="Predicted vs. Actual \n",
+ x = "") +
+ theme_bw(base_size=15) +
+ scale_fill_brewer(type="qual", palette="Dark2") +
+ theme(legend_position="bottom",
+ plot_title = element_text(hjust = 0.5),
+ legend_title = element_blank()
+ )
+)
+
+print(plot_pred)
+
++
\n", + " | nll | \n", + "distribution | \n", + "
---|---|---|
rank | \n", + "\n", + " | \n", + " |
1 | \n", + "23596.791908 | \n", + "LogNormal | \n", + "
2 | \n", + "23632.597656 | \n", + "Gamma | \n", + "
3 | \n", + "23899.039920 | \n", + "Gumbel | \n", + "
4 | \n", + "24083.658916 | \n", + "Weibull | \n", + "
5 | \n", + "25690.867630 | \n", + "StudentT | \n", + "
6 | \n", + "25796.219456 | \n", + "Gaussian | \n", + "
7 | \n", + "25925.138312 | \n", + "Laplace | \n", + "
8 | \n", + "27559.623077 | \n", + "Cauchy | \n", + "
In this example we will show how to select a distribution for a univariate target variable. We use the California housing dataset and select a distribution for the target variable median_house_value
.
from lightgbmlss.distributions import *
+from lightgbmlss.distributions.distribution_utils import DistributionClass
+from sklearn import datasets
+from sklearn.model_selection import train_test_split
+
housing_data = datasets.fetch_california_housing()
+X, y = housing_data["data"], housing_data["target"]
+feature_names = housing_data["feature_names"]
+
+X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
+
In the following, we specify a list of candidate distributions. The function dist_select
returns the negative log-likelihood of each distribution for the target variable. The distribution with the lowest negative log-likelihood is selected. The function also plots the density of the target variable and the fitted density, using the best suitable distribution among the specified ones.
It is important to note that the list of candidate distributions should be chosen to be suitable for the target variable at hand. For example, if the target variable is a count variable, then the list of candidate distributions should include the Poisson and Negative Binomial. Similarly, if the target variable is on the positive real scale, then the list of continuous candidate distributions should be chosen accordingly.
+lgblss_dist_class = DistributionClass()
+candidate_distributions = [Gaussian, StudentT, Gamma, Cauchy, LogNormal, Weibull, Gumbel, Laplace]
+
+dist_nll = lgblss_dist_class.dist_select(target=y_train, candidate_distributions=candidate_distributions, max_iter=50, plot=True, figure_size=(10, 5))
+dist_nll
+
Fitting of candidate distributions completed: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:12<00:00, 1.58s/it] ++
+ | nll | +distribution | +
---|---|---|
rank | ++ | + |
1 | +23596.791908 | +LogNormal | +
2 | +23632.597656 | +Gamma | +
3 | +23899.039920 | +Gumbel | +
4 | +24083.658916 | +Weibull | +
5 | +25690.867630 | +StudentT | +
6 | +25796.219456 | +Gaussian | +
7 | +25925.138312 | +Laplace | +
8 | +27559.623077 | +Cauchy | +
\n", + " | nll | \n", + "NormFlow | \n", + "
---|---|---|
rank | \n", + "\n", + " | \n", + " |
1 | \n", + "16595.917006 | \n", + "SplineFlow(count_bins: 20, order: linear) | \n", + "
2 | \n", + "16608.693807 | \n", + "SplineFlow(count_bins: 12, order: quadratic) | \n", + "
3 | \n", + "16622.862265 | \n", + "SplineFlow(count_bins: 16, order: quadratic) | \n", + "
4 | \n", + "16640.156074 | \n", + "SplineFlow(count_bins: 6, order: linear) | \n", + "
5 | \n", + "16640.611035 | \n", + "SplineFlow(count_bins: 16, order: linear) | \n", + "
6 | \n", + "16649.404709 | \n", + "SplineFlow(count_bins: 8, order: linear) | \n", + "
7 | \n", + "16651.375456 | \n", + "SplineFlow(count_bins: 8, order: quadratic) | \n", + "
8 | \n", + "16653.378393 | \n", + "SplineFlow(count_bins: 6, order: quadratic) | \n", + "
9 | \n", + "16674.331780 | \n", + "SplineFlow(count_bins: 12, order: linear) | \n", + "
10 | \n", + "16822.629927 | \n", + "SplineFlow(count_bins: 4, order: quadratic) | \n", + "
11 | \n", + "16902.398862 | \n", + "SplineFlow(count_bins: 20, order: quadratic) | \n", + "
12 | \n", + "17538.588405 | \n", + "SplineFlow(count_bins: 4, order: linear) | \n", + "
13 | \n", + "17692.968508 | \n", + "SplineFlow(count_bins: 2, order: linear) | \n", + "
14 | \n", + "17737.569055 | \n", + "SplineFlow(count_bins: 2, order: quadratic) | \n", + "
\n", + " | y_sample0 | \n", + "y_sample1 | \n", + "y_sample2 | \n", + "y_sample3 | \n", + "y_sample4 | \n", + "y_sample5 | \n", + "y_sample6 | \n", + "y_sample7 | \n", + "y_sample8 | \n", + "y_sample9 | \n", + "... | \n", + "y_sample9990 | \n", + "y_sample9991 | \n", + "y_sample9992 | \n", + "y_sample9993 | \n", + "y_sample9994 | \n", + "y_sample9995 | \n", + "y_sample9996 | \n", + "y_sample9997 | \n", + "y_sample9998 | \n", + "y_sample9999 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "12.315617 | \n", + "11.148891 | \n", + "4.624041 | \n", + "11.273465 | \n", + "4.864611 | \n", + "13.651609 | \n", + "11.573423 | \n", + "14.295194 | \n", + "9.939115 | \n", + "8.728693 | \n", + "... | \n", + "13.490867 | \n", + "9.317229 | \n", + "8.216475 | \n", + "11.480260 | \n", + "9.670330 | \n", + "14.858524 | \n", + "5.511271 | \n", + "10.839365 | \n", + "11.708204 | \n", + "6.417930 | \n", + "
1 | \n", + "16.409412 | \n", + "8.340928 | \n", + "10.923915 | \n", + "13.388198 | \n", + "8.266445 | \n", + "10.249456 | \n", + "12.452267 | \n", + "10.588787 | \n", + "12.331830 | \n", + "9.027943 | \n", + "... | \n", + "10.883108 | \n", + "2.895114 | \n", + "11.342189 | \n", + "13.237988 | \n", + "6.858677 | \n", + "15.392724 | \n", + "9.221172 | \n", + "9.709686 | \n", + "4.011642 | \n", + "10.250503 | \n", + "
2 | \n", + "11.589569 | \n", + "9.669224 | \n", + "9.724932 | \n", + "10.960885 | \n", + "13.362031 | \n", + "11.276797 | \n", + "10.801574 | \n", + "9.534609 | \n", + "9.327179 | \n", + "10.494802 | \n", + "... | \n", + "10.138949 | \n", + "11.022624 | \n", + "8.731875 | \n", + "10.401649 | \n", + "9.127348 | \n", + "10.243149 | \n", + "9.944046 | \n", + "8.483890 | \n", + "10.321774 | \n", + "10.243544 | \n", + "
3 | \n", + "10.743192 | \n", + "9.116656 | \n", + "12.323032 | \n", + "5.270192 | \n", + "15.528632 | \n", + "15.910578 | \n", + "8.639240 | \n", + "16.705225 | \n", + "7.853291 | \n", + "9.957293 | \n", + "... | \n", + "1.351381 | \n", + "9.039200 | \n", + "16.516144 | \n", + "8.261804 | \n", + "4.777587 | \n", + "9.089480 | \n", + "12.751478 | \n", + "12.829519 | \n", + "12.620325 | \n", + "9.282865 | \n", + "
4 | \n", + "12.725435 | \n", + "8.629576 | \n", + "7.140886 | \n", + "5.782235 | \n", + "6.844902 | \n", + "10.355301 | \n", + "7.330880 | \n", + "14.259795 | \n", + "10.242219 | \n", + "15.360129 | \n", + "... | \n", + "9.775837 | \n", + "8.244545 | \n", + "9.040227 | \n", + "11.909575 | \n", + "8.996835 | \n", + "8.084206 | \n", + "13.316805 | \n", + "10.463615 | \n", + "8.097854 | \n", + "9.440963 | \n", + "
5 rows × 10000 columns
\n", + "\n", + " | quant_0.05 | \n", + "quant_0.95 | \n", + "
---|---|---|
0 | \n", + "5.220628 | \n", + "14.963007 | \n", + "
1 | \n", + "5.365820 | \n", + "15.105494 | \n", + "
2 | \n", + "8.211895 | \n", + "11.933730 | \n", + "
3 | \n", + "2.047034 | \n", + "17.560992 | \n", + "
4 | \n", + "5.048092 | \n", + "15.145446 | \n", + "
\n", + " | param_1 | \n", + "param_2 | \n", + "param_3 | \n", + "param_4 | \n", + "param_5 | \n", + "param_6 | \n", + "param_7 | \n", + "param_8 | \n", + "param_9 | \n", + "param_10 | \n", + "... | \n", + "param_22 | \n", + "param_23 | \n", + "param_24 | \n", + "param_25 | \n", + "param_26 | \n", + "param_27 | \n", + "param_28 | \n", + "param_29 | \n", + "param_30 | \n", + "param_31 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "-0.418612 | \n", + "-0.302259 | \n", + "-0.532314 | \n", + "-0.775419 | \n", + "0.697199 | \n", + "-0.227767 | \n", + "-3.043896 | \n", + "1.286783 | \n", + "1.113216 | \n", + "0.349978 | \n", + "... | \n", + "1.817143 | \n", + "0.570572 | \n", + "0.945506 | \n", + "0.805746 | \n", + "0.693497 | \n", + "0.639612 | \n", + "1.854735 | \n", + "1.306370 | \n", + "0.909254 | \n", + "0.839281 | \n", + "
1 | \n", + "-0.418612 | \n", + "-0.302259 | \n", + "-0.532314 | \n", + "-0.775419 | \n", + "0.697199 | \n", + "-0.227767 | \n", + "-3.043896 | \n", + "1.286783 | \n", + "1.113216 | \n", + "0.349978 | \n", + "... | \n", + "1.777844 | \n", + "0.570572 | \n", + "0.945506 | \n", + "0.805746 | \n", + "0.693497 | \n", + "0.639612 | \n", + "1.854735 | \n", + "1.306370 | \n", + "0.909254 | \n", + "0.839281 | \n", + "
2 | \n", + "-0.418612 | \n", + "-0.302259 | \n", + "-0.532314 | \n", + "-0.775419 | \n", + "0.697199 | \n", + "-0.227767 | \n", + "-3.043896 | \n", + "1.286783 | \n", + "1.113216 | \n", + "0.349978 | \n", + "... | \n", + "1.495455 | \n", + "0.570572 | \n", + "0.945506 | \n", + "0.805746 | \n", + "0.693497 | \n", + "0.639612 | \n", + "0.868014 | \n", + "2.313089 | \n", + "0.909254 | \n", + "0.839281 | \n", + "
3 | \n", + "-0.418612 | \n", + "-0.302259 | \n", + "-0.532314 | \n", + "-0.775419 | \n", + "0.697199 | \n", + "-0.227767 | \n", + "-3.043896 | \n", + "1.286783 | \n", + "1.113216 | \n", + "0.349978 | \n", + "... | \n", + "1.276525 | \n", + "0.570572 | \n", + "0.945506 | \n", + "0.805746 | \n", + "0.693497 | \n", + "0.639612 | \n", + "2.172172 | \n", + "-1.196684 | \n", + "0.909254 | \n", + "0.839281 | \n", + "
4 | \n", + "-0.418612 | \n", + "-0.302259 | \n", + "-0.532314 | \n", + "-0.775419 | \n", + "0.697199 | \n", + "-0.227767 | \n", + "-3.043896 | \n", + "1.286783 | \n", + "1.113216 | \n", + "0.349978 | \n", + "... | \n", + "1.535858 | \n", + "0.570572 | \n", + "0.945506 | \n", + "0.805746 | \n", + "0.693497 | \n", + "0.639612 | \n", + "1.854735 | \n", + "1.127365 | \n", + "0.909254 | \n", + "0.839281 | \n", + "
5 rows × 31 columns
\n", + "Normalizing flows transform a simple distribution into a complex data distribution through a series of invertible transformations. The key steps involved in the operation of normalizing flows are as follows (from left to right):
+By stacking multiple transformations in a sequence, normalizing flows can model complex and multi-modal distributions while providing the ability to compute the likelihood of the data and perform efficient sampling in both directions (from base to complex and vice versa). However, it is important to note that since LightGBMLSS is based on a one vs. all estimation strategy, where a separate tree is grown for each parameter, estimating many parameters for a large dataset can become computationally expensive. For more details, we refer to our related paper Alexander März and Thomas Kneib (2022): Distributional Gradient Boosting Machines.
+from lightgbmlss.model import *
+from lightgbmlss.distributions.SplineFlow import *
+from lightgbmlss.distributions.flow_utils import NormalizingFlowClass
+from lightgbmlss.datasets.data_loader import load_simulated_gaussian_data
+from scipy.stats import norm
+
+import plotnine
+from plotnine import *
+plotnine.options.figure_size = (20, 10)
+
# The data is simulated as a Gaussian, where x is the only true feature and all others are noise variables
+ # loc = 10
+ # scale = 1 + 4 * ((0.3 < x) & (x < 0.5)) + 2 * (x > 0.7)
+
+train, test = load_simulated_gaussian_data()
+
+X_train, y_train = train.filter(regex="x"), train["y"].values
+X_test, y_test = test.filter(regex="x"), test["y"].values
+
+dtrain = lgb.Dataset(X_train, label=y_train)
+
In the following, we specify a list of candidate normalizing flows. The function flow_select returns the negative log-likelihood of each specification. The normalizing flow with the lowest negative log-likelihood is selected. The function also plots the density of the target variable and the fitted density, using the best suitable normalizing flow among the specified ones. However, note that choosing the best performing flow based solely on training data may lead to overfitting, since normalizing flows have a higher risk of overfitting compared to parametric distributions. When using normalizing flows, it is crucial to carefully select the specifications to strike a balance between model complexity and generalization ability.
+# See ?SplineFlow for an overview.
+bound = np.max([np.abs(y_train.min()), y_train.max()])
+target_support = "real"
+
+candidate_flows = [
+
+ SplineFlow(target_support=target_support, count_bins=2, bound=bound, order="linear"),
+ SplineFlow(target_support=target_support, count_bins=4, bound=bound, order="linear"),
+ SplineFlow(target_support=target_support, count_bins=6, bound=bound, order="linear"),
+ SplineFlow(target_support=target_support, count_bins=8, bound=bound, order="linear"),
+ SplineFlow(target_support=target_support, count_bins=12, bound=bound, order="linear"),
+ SplineFlow(target_support=target_support, count_bins=16, bound=bound, order="linear"),
+ SplineFlow(target_support=target_support, count_bins=20, bound=bound, order="linear"),
+
+ SplineFlow(target_support=target_support, count_bins=2, bound=bound, order="quadratic"),
+ SplineFlow(target_support=target_support, count_bins=4, bound=bound, order="quadratic"),
+ SplineFlow(target_support=target_support, count_bins=6, bound=bound, order="quadratic"),
+ SplineFlow(target_support=target_support, count_bins=8, bound=bound, order="quadratic"),
+ SplineFlow(target_support=target_support, count_bins=12, bound=bound, order="quadratic"),
+ SplineFlow(target_support=target_support, count_bins=16, bound=bound, order="quadratic"),
+ SplineFlow(target_support=target_support, count_bins=20, bound=bound, order="quadratic"),
+
+]
+
+flow_nll = NormalizingFlowClass().flow_select(target=y_train, candidate_flows=candidate_flows, max_iter=50, n_samples=10000, plot=True, figure_size=(12, 5))
+flow_nll
+
Fitting of candidate normalizing flows completed: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 14/14 [01:20<00:00, 5.78s/it] ++
+ | nll | +NormFlow | +
---|---|---|
rank | ++ | + |
1 | +16595.917006 | +SplineFlow(count_bins: 20, order: linear) | +
2 | +16608.693807 | +SplineFlow(count_bins: 12, order: quadratic) | +
3 | +16622.862265 | +SplineFlow(count_bins: 16, order: quadratic) | +
4 | +16640.156074 | +SplineFlow(count_bins: 6, order: linear) | +
5 | +16640.611035 | +SplineFlow(count_bins: 16, order: linear) | +
6 | +16649.404709 | +SplineFlow(count_bins: 8, order: linear) | +
7 | +16651.375456 | +SplineFlow(count_bins: 8, order: quadratic) | +
8 | +16653.378393 | +SplineFlow(count_bins: 6, order: quadratic) | +
9 | +16674.331780 | +SplineFlow(count_bins: 12, order: linear) | +
10 | +16822.629927 | +SplineFlow(count_bins: 4, order: quadratic) | +
11 | +16902.398862 | +SplineFlow(count_bins: 20, order: quadratic) | +
12 | +17538.588405 | +SplineFlow(count_bins: 4, order: linear) | +
13 | +17692.968508 | +SplineFlow(count_bins: 2, order: linear) | +
14 | +17737.569055 | +SplineFlow(count_bins: 2, order: quadratic) | +
Even though SplineFlow(count_bins: 20, order: linear) shows the best fit to the data, we choose a more parameter parsimonious specification (recall that a separate tree is grown for each parameter):
+# Specifies Spline-Flow. See ?SplineFlow for an overview.
+bound = np.max([np.abs(y_train.min()), y_train.max()])
+
+lgblss = LightGBMLSS(
+ SplineFlow(target_support="real", # Specifies the support of the target. Options are "real", "positive", "positive_integer" or "unit_interval"
+ count_bins=8, # The number of segments comprising the spline.
+ bound=bound, # By adjusting the value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on.
+ order="linear", # The order of the spline. Options are "linear" or "quadratic".
+ stabilization="None", # Options are "None", "MAD" or "L2".
+ loss_fn="nll" # Loss function. Options are "nll" (negative log-likelihood) or "crps"(continuous ranked probability score).
+ )
+)
+
Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows:
+- Float/Int sample_type
+ - {"param_name": ["sample_type", low, high, log]}
+ - sample_type: str, Type of sampling, e.g., "float" or "int"
+ - low: int, Lower endpoint of the range of suggested values
+ - high: int, Upper endpoint of the range of suggested values
+ - log: bool, Flag to sample the value from the log domain or not
+ - Example: {"eta": "float", low=1e-5, high=1, log=True]}
+
+- Categorical sample_type
+ - {"param_name": ["sample_type", ["choice1", "choice2", "choice3", "..."]]}
+ - sample_type: str, Type of sampling, either "categorical"
+ - choice1, choice2, choice3, ...: str, Possible choices for the parameter
+ - Example: {"boosting": ["categorical", ["gbdt", "dart"]]}
+
+- For parameters without tunable choice (this is needed if tree_method = "gpu_hist" and gpu_id needs to be specified)
+ - {"param_name": ["none", [value]]},
+ - param_name: str, Name of the parameter
+ - value: int, Value of the parameter
+ - Example: {"gpu_id": ["none", [0]]}
+param_dict = {
+ "eta": ["float", {"low": 1e-5, "high": 1, "log": True}],
+ "max_depth": ["int", {"low": 1, "high": 10, "log": False}],
+ "num_leaves": ["int", {"low": 255, "high": 255, "log": False}], # set to constant for this example
+ "min_data_in_leaf": ["int", {"low": 20, "high": 20, "log": False}], # set to constant for this example
+ "min_gain_to_split": ["float", {"low": 1e-8, "high": 40, "log": False}],
+ "min_sum_hessian_in_leaf": ["float", {"low": 1e-8, "high": 500, "log": True}],
+ "subsample": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "feature_fraction": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "boosting": ["categorical", ["gbdt"]],
+}
+
+np.random.seed(123)
+opt_param = lgblss.hyper_opt(param_dict,
+ dtrain,
+ num_boost_round=100, # Number of boosting iterations.
+ nfold=5, # Number of cv-folds.
+ early_stopping_rounds=20, # Number of early-stopping rounds
+ max_minutes=1000, # Time budget in minutes, i.e., stop study after the given number of minutes.
+ n_trials=50, # The number of trials. If this argument is set to None, there is no limitation on the number of trials.
+ silence=False, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail.
+ seed=123, # Seed used to generate cv-folds.
+ hp_seed=None # Seed for random number generator used in the Bayesian hyperparameter search.
+ )
+
[I 2023-08-11 12:37:41,890] A new study created in memory with name: LightGBMLSS Hyper-Parameter Optimization ++
0%| | 0/50 [00:00<?, ?it/s]+
[I 2023-08-11 12:38:01,301] Trial 0 finished with value: 367804.0625 and parameters: {'eta': 0.010375304627985965, 'max_depth': 7, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 30.21888913980533, 'min_sum_hessian_in_leaf': 5.864939907646467e-06, 'subsample': 0.658481420114019, 'feature_fraction': 0.9114946619928868, 'boosting': 'gbdt'}. Best is trial 0 with value: 367804.0625. +[I 2023-08-11 12:38:15,005] Trial 1 finished with value: 327890.15625 and parameters: {'eta': 5.3460212858136167e-05, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 22.51771738796101, 'min_sum_hessian_in_leaf': 3.782715103966255e-06, 'subsample': 0.8565244625430195, 'feature_fraction': 0.8120123399840897, 'boosting': 'gbdt'}. Best is trial 1 with value: 327890.15625. +[I 2023-08-11 12:38:55,787] Trial 2 finished with value: 3321.39404296875 and parameters: {'eta': 2.778156946755216e-05, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 30.563335570212864, 'min_sum_hessian_in_leaf': 0.018998048940462937, 'subsample': 0.5982826614247189, 'feature_fraction': 0.9178957649663875, 'boosting': 'gbdt'}. Best is trial 2 with value: 3321.39404296875. +[I 2023-08-11 12:39:41,746] Trial 3 finished with value: 3264.73486328125 and parameters: {'eta': 0.00016286470418944287, 'max_depth': 8, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 0.010186033781953225, 'min_sum_hessian_in_leaf': 0.0320584443452124, 'subsample': 0.521567388874646, 'feature_fraction': 0.6273169259837587, 'boosting': 'gbdt'}. Best is trial 3 with value: 3264.73486328125. +[I 2023-08-11 12:39:51,741] Trial 4 finished with value: 376967.6875 and parameters: {'eta': 1.5684870736165636e-05, 'max_depth': 8, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 33.8874711080051, 'min_sum_hessian_in_leaf': 1.8783327364128395e-08, 'subsample': 0.8823849618907202, 'feature_fraction': 0.7045235387891493, 'boosting': 'gbdt'}. Best is trial 3 with value: 3264.73486328125. +[I 2023-08-11 12:40:02,153] Trial 5 finished with value: 398357.78125 and parameters: {'eta': 0.5548705152962901, 'max_depth': 8, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 13.667421918402665, 'min_sum_hessian_in_leaf': 4.6972881363604445e-07, 'subsample': 0.20226750190291407, 'feature_fraction': 0.6190150789735767, 'boosting': 'gbdt'}. Best is trial 3 with value: 3264.73486328125. +[I 2023-08-11 12:40:12,081] Trial 6 finished with value: 400577.875 and parameters: {'eta': 0.1234951759976552, 'max_depth': 9, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 33.13372404468565, 'min_sum_hessian_in_leaf': 1.2261575574099422e-07, 'subsample': 0.940693907349371, 'feature_fraction': 0.9449458870167231, 'boosting': 'gbdt'}. Best is trial 3 with value: 3264.73486328125. +[I 2023-08-11 12:40:52,496] Trial 7 finished with value: 142739.59375 and parameters: {'eta': 0.0003413864481981204, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 22.366955613110136, 'min_sum_hessian_in_leaf': 6.002551849974542e-05, 'subsample': 0.6991813218844587, 'feature_fraction': 0.234857420886585, 'boosting': 'gbdt'}. Best is trial 3 with value: 3264.73486328125. +[I 2023-08-11 12:41:01,002] Trial 8 finished with value: 365344.75 and parameters: {'eta': 0.00020409810478252383, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 36.855824335790395, 'min_sum_hessian_in_leaf': 1.2378209039169021e-08, 'subsample': 0.770484341624629, 'feature_fraction': 0.5551457106408251, 'boosting': 'gbdt'}. Best is trial 3 with value: 3264.73486328125. +[I 2023-08-11 12:41:40,436] Trial 9 finished with value: 3135.341064453125 and parameters: {'eta': 0.015236694462247857, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 19.349498338142997, 'min_sum_hessian_in_leaf': 56.443023006446865, 'subsample': 0.7640980691353356, 'feature_fraction': 0.41488213555402687, 'boosting': 'gbdt'}. Best is trial 9 with value: 3135.341064453125. +[I 2023-08-11 12:41:55,509] Trial 10 pruned. Trial was pruned at iteration 36. +[I 2023-08-11 12:42:12,555] Trial 11 pruned. Trial was pruned at iteration 36. +[I 2023-08-11 12:42:56,725] Trial 12 finished with value: 3256.26220703125 and parameters: {'eta': 0.0018049878745409515, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 3.374230470458916, 'min_sum_hessian_in_leaf': 0.0530979406814048, 'subsample': 0.4960846397538058, 'feature_fraction': 0.40371528463535267, 'boosting': 'gbdt'}. Best is trial 9 with value: 3135.341064453125. +[I 2023-08-11 12:43:10,887] Trial 13 pruned. Trial was pruned at iteration 32. +[I 2023-08-11 12:43:55,548] Trial 14 finished with value: 3212.943359375 and parameters: {'eta': 0.025859495137694966, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 6.685774705475238, 'min_sum_hessian_in_leaf': 222.98371851641804, 'subsample': 0.7995471962331264, 'feature_fraction': 0.47768677368077295, 'boosting': 'gbdt'}. Best is trial 9 with value: 3135.341064453125. +[I 2023-08-11 12:44:03,829] Trial 15 finished with value: 3329.883544921875 and parameters: {'eta': 0.03539939269416217, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 9.188990720570771, 'min_sum_hessian_in_leaf': 488.0785240623253, 'subsample': 0.7894909220530997, 'feature_fraction': 0.5151030280739416, 'boosting': 'gbdt'}. Best is trial 9 with value: 3135.341064453125. +[I 2023-08-11 12:44:43,469] Trial 16 finished with value: 3026.395263671875 and parameters: {'eta': 0.07594812682515821, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 17.792180133102768, 'min_sum_hessian_in_leaf': 22.17210994438013, 'subsample': 0.776822951588817, 'feature_fraction': 0.2890728975559498, 'boosting': 'gbdt'}. Best is trial 16 with value: 3026.395263671875. +[I 2023-08-11 12:45:09,701] Trial 17 finished with value: 3024.16162109375 and parameters: {'eta': 0.14050335876069467, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 17.26511630421208, 'min_sum_hessian_in_leaf': 8.270011184858163, 'subsample': 0.9896356934682706, 'feature_fraction': 0.24430206777352248, 'boosting': 'gbdt'}. Best is trial 17 with value: 3024.16162109375. +[I 2023-08-11 12:45:29,462] Trial 18 finished with value: 3172.02294921875 and parameters: {'eta': 0.7236262880129627, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 26.302808049098353, 'min_sum_hessian_in_leaf': 4.65621731262215, 'subsample': 0.9798594849561478, 'feature_fraction': 0.21013091104429663, 'boosting': 'gbdt'}. Best is trial 17 with value: 3024.16162109375. +[I 2023-08-11 12:46:09,170] Trial 19 finished with value: 3030.934326171875 and parameters: {'eta': 0.11541934708482965, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 17.39372679689958, 'min_sum_hessian_in_leaf': 0.4875471297011469, 'subsample': 0.8735372230489282, 'feature_fraction': 0.29512485474393757, 'boosting': 'gbdt'}. Best is trial 17 with value: 3024.16162109375. +[I 2023-08-11 12:46:49,777] Trial 20 finished with value: 3013.43505859375 and parameters: {'eta': 0.10851774600973758, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 12.778099763813188, 'min_sum_hessian_in_leaf': 19.409084894454672, 'subsample': 0.9906733215538556, 'feature_fraction': 0.31427354411349523, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:47:31,244] Trial 21 finished with value: 3020.79150390625 and parameters: {'eta': 0.10941312206423358, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 12.955452035644882, 'min_sum_hessian_in_leaf': 34.43299195414659, 'subsample': 0.993865946118584, 'feature_fraction': 0.29169024300447044, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:47:54,911] Trial 22 finished with value: 3017.7353515625 and parameters: {'eta': 0.27099448628298356, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 12.164474712430403, 'min_sum_hessian_in_leaf': 41.68415304679111, 'subsample': 0.9980808126058845, 'feature_fraction': 0.3153447935530275, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:48:20,851] Trial 23 finished with value: 3019.21435546875 and parameters: {'eta': 0.30191668351694284, 'max_depth': 7, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 11.917898671607471, 'min_sum_hessian_in_leaf': 44.28300751774489, 'subsample': 0.9273319278552463, 'feature_fraction': 0.3276698697022868, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:48:29,020] Trial 24 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:48:50,180] Trial 25 finished with value: 3018.3837890625 and parameters: {'eta': 0.3692147736260462, 'max_depth': 7, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 10.83827075251106, 'min_sum_hessian_in_leaf': 54.98180387829638, 'subsample': 0.9355321657142412, 'feature_fraction': 0.3542729437699873, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:48:58,960] Trial 26 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:49:07,470] Trial 27 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:49:20,520] Trial 28 pruned. Trial was pruned at iteration 32. +[I 2023-08-11 12:49:36,017] Trial 29 pruned. Trial was pruned at iteration 38. +[I 2023-08-11 12:49:52,087] Trial 30 pruned. Trial was pruned at iteration 39. +[I 2023-08-11 12:50:24,408] Trial 31 finished with value: 3019.494873046875 and parameters: {'eta': 0.2725991488316807, 'max_depth': 7, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 11.759315835515839, 'min_sum_hessian_in_leaf': 26.987460432181546, 'subsample': 0.998147928571545, 'feature_fraction': 0.32559028682493446, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:50:43,988] Trial 32 finished with value: 3015.494140625 and parameters: {'eta': 0.3829501207030448, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 10.006822682462404, 'min_sum_hessian_in_leaf': 9.670757510705382, 'subsample': 0.9424904465703395, 'feature_fraction': 0.3111946445504374, 'boosting': 'gbdt'}. Best is trial 20 with value: 3013.43505859375. +[I 2023-08-11 12:50:52,661] Trial 33 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:51:33,990] Trial 34 finished with value: 3005.75537109375 and parameters: {'eta': 0.06012752850837495, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 5.803902946638742, 'min_sum_hessian_in_leaf': 2.3261118096334563, 'subsample': 0.9482108811109413, 'feature_fraction': 0.41287326458142043, 'boosting': 'gbdt'}. Best is trial 34 with value: 3005.75537109375. +[I 2023-08-11 12:52:15,466] Trial 35 finished with value: 3000.99462890625 and parameters: {'eta': 0.06943267180218958, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 4.133726741234719, 'min_sum_hessian_in_leaf': 1.7385973286087377, 'subsample': 0.8908962112260067, 'feature_fraction': 0.42160506776781076, 'boosting': 'gbdt'}. Best is trial 35 with value: 3000.99462890625. +[I 2023-08-11 12:52:26,240] Trial 36 pruned. Trial was pruned at iteration 24. +[I 2023-08-11 12:52:36,406] Trial 37 pruned. Trial was pruned at iteration 24. +[I 2023-08-11 12:52:45,020] Trial 38 pruned. Trial was pruned at iteration 21. +[I 2023-08-11 12:52:53,486] Trial 39 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:53:02,563] Trial 40 pruned. Trial was pruned at iteration 21. +[I 2023-08-11 12:53:41,543] Trial 41 finished with value: 3005.391845703125 and parameters: {'eta': 0.15498347493908707, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 7.365763456361323, 'min_sum_hessian_in_leaf': 12.723870730914529, 'subsample': 0.9556994060266463, 'feature_fraction': 0.3792850237470474, 'boosting': 'gbdt'}. Best is trial 35 with value: 3000.99462890625. +[I 2023-08-11 12:54:21,491] Trial 42 finished with value: 3007.25048828125 and parameters: {'eta': 0.1548185495847896, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 7.156358312749202, 'min_sum_hessian_in_leaf': 9.06469298926148, 'subsample': 0.9562432564521889, 'feature_fraction': 0.3674839079182071, 'boosting': 'gbdt'}. Best is trial 35 with value: 3000.99462890625. +[I 2023-08-11 12:54:29,988] Trial 43 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:54:38,844] Trial 44 pruned. Trial was pruned at iteration 21. +[I 2023-08-11 12:54:47,767] Trial 45 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:54:56,168] Trial 46 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:55:23,650] Trial 47 finished with value: 3023.676025390625 and parameters: {'eta': 0.16467209050245155, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 3.230462326784356, 'min_sum_hessian_in_leaf': 1.1106443915756805, 'subsample': 0.8624551086972744, 'feature_fraction': 0.45067165580000546, 'boosting': 'gbdt'}. Best is trial 35 with value: 3000.99462890625. +[I 2023-08-11 12:55:32,412] Trial 48 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:56:15,258] Trial 49 finished with value: 3001.418212890625 and parameters: {'eta': 0.08071812906410179, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 4.294318716703948, 'min_sum_hessian_in_leaf': 2.6750545089295956, 'subsample': 0.9632339172694735, 'feature_fraction': 0.5074780056230868, 'boosting': 'gbdt'}. Best is trial 35 with value: 3000.99462890625. + +Hyper-Parameter Optimization successfully finished. + Number of finished trials: 50 + Best trial: + Value: 3000.99462890625 + Params: + eta: 0.06943267180218958 + max_depth: 4 + num_leaves: 255 + min_data_in_leaf: 20 + min_gain_to_split: 4.133726741234719 + min_sum_hessian_in_leaf: 1.7385973286087377 + subsample: 0.8908962112260067 + feature_fraction: 0.42160506776781076 + boosting: gbdt + opt_rounds: 90 ++
np.random.seed(123)
+
+opt_params = opt_param.copy()
+n_rounds = opt_params["opt_rounds"]
+del opt_params["opt_rounds"]
+
+# Train Model with optimized hyperparameters
+lgblss.train(opt_params,
+ dtrain,
+ num_boost_round=n_rounds
+ )
+
# Set seed for reproducibility
+torch.manual_seed(123)
+
+# Number of samples to draw from predicted distribution
+n_samples = 10000
+# Quantiles to calculate from predicted distribution
+quant_sel = [0.05, 0.95]
+
+# Sample from predicted distribution
+pred_samples = lgblss.predict(X_test,
+ pred_type="samples",
+ n_samples=n_samples,
+ seed=123)
+
+# Calculate quantiles from predicted distribution
+pred_quantiles = lgblss.predict(X_test,
+ pred_type="quantiles",
+ n_samples=n_samples,
+ quantiles=quant_sel)
+
+# Returns predicted parameters
+pred_params = lgblss.predict(X_test,
+ pred_type="parameters")
+
pred_samples.head()
+
+ | y_sample0 | +y_sample1 | +y_sample2 | +y_sample3 | +y_sample4 | +y_sample5 | +y_sample6 | +y_sample7 | +y_sample8 | +y_sample9 | +... | +y_sample9990 | +y_sample9991 | +y_sample9992 | +y_sample9993 | +y_sample9994 | +y_sample9995 | +y_sample9996 | +y_sample9997 | +y_sample9998 | +y_sample9999 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +12.315617 | +11.148891 | +4.624041 | +11.273465 | +4.864611 | +13.651609 | +11.573423 | +14.295194 | +9.939115 | +8.728693 | +... | +13.490867 | +9.317229 | +8.216475 | +11.480260 | +9.670330 | +14.858524 | +5.511271 | +10.839365 | +11.708204 | +6.417930 | +
1 | +16.409412 | +8.340928 | +10.923915 | +13.388198 | +8.266445 | +10.249456 | +12.452267 | +10.588787 | +12.331830 | +9.027943 | +... | +10.883108 | +2.895114 | +11.342189 | +13.237988 | +6.858677 | +15.392724 | +9.221172 | +9.709686 | +4.011642 | +10.250503 | +
2 | +11.589569 | +9.669224 | +9.724932 | +10.960885 | +13.362031 | +11.276797 | +10.801574 | +9.534609 | +9.327179 | +10.494802 | +... | +10.138949 | +11.022624 | +8.731875 | +10.401649 | +9.127348 | +10.243149 | +9.944046 | +8.483890 | +10.321774 | +10.243544 | +
3 | +10.743192 | +9.116656 | +12.323032 | +5.270192 | +15.528632 | +15.910578 | +8.639240 | +16.705225 | +7.853291 | +9.957293 | +... | +1.351381 | +9.039200 | +16.516144 | +8.261804 | +4.777587 | +9.089480 | +12.751478 | +12.829519 | +12.620325 | +9.282865 | +
4 | +12.725435 | +8.629576 | +7.140886 | +5.782235 | +6.844902 | +10.355301 | +7.330880 | +14.259795 | +10.242219 | +15.360129 | +... | +9.775837 | +8.244545 | +9.040227 | +11.909575 | +8.996835 | +8.084206 | +13.316805 | +10.463615 | +8.097854 | +9.440963 | +
5 rows × 10000 columns
+pred_quantiles.head()
+
+ | quant_0.05 | +quant_0.95 | +
---|---|---|
0 | +5.220628 | +14.963007 | +
1 | +5.365820 | +15.105494 | +
2 | +8.211895 | +11.933730 | +
3 | +2.047034 | +17.560992 | +
4 | +5.048092 | +15.145446 | +
pred_params.head()
+
+ | param_1 | +param_2 | +param_3 | +param_4 | +param_5 | +param_6 | +param_7 | +param_8 | +param_9 | +param_10 | +... | +param_22 | +param_23 | +param_24 | +param_25 | +param_26 | +param_27 | +param_28 | +param_29 | +param_30 | +param_31 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +-0.418612 | +-0.302259 | +-0.532314 | +-0.775419 | +0.697199 | +-0.227767 | +-3.043896 | +1.286783 | +1.113216 | +0.349978 | +... | +1.817143 | +0.570572 | +0.945506 | +0.805746 | +0.693497 | +0.639612 | +1.854735 | +1.306370 | +0.909254 | +0.839281 | +
1 | +-0.418612 | +-0.302259 | +-0.532314 | +-0.775419 | +0.697199 | +-0.227767 | +-3.043896 | +1.286783 | +1.113216 | +0.349978 | +... | +1.777844 | +0.570572 | +0.945506 | +0.805746 | +0.693497 | +0.639612 | +1.854735 | +1.306370 | +0.909254 | +0.839281 | +
2 | +-0.418612 | +-0.302259 | +-0.532314 | +-0.775419 | +0.697199 | +-0.227767 | +-3.043896 | +1.286783 | +1.113216 | +0.349978 | +... | +1.495455 | +0.570572 | +0.945506 | +0.805746 | +0.693497 | +0.639612 | +0.868014 | +2.313089 | +0.909254 | +0.839281 | +
3 | +-0.418612 | +-0.302259 | +-0.532314 | +-0.775419 | +0.697199 | +-0.227767 | +-3.043896 | +1.286783 | +1.113216 | +0.349978 | +... | +1.276525 | +0.570572 | +0.945506 | +0.805746 | +0.693497 | +0.639612 | +2.172172 | +-1.196684 | +0.909254 | +0.839281 | +
4 | +-0.418612 | +-0.302259 | +-0.532314 | +-0.775419 | +0.697199 | +-0.227767 | +-3.043896 | +1.286783 | +1.113216 | +0.349978 | +... | +1.535858 | +0.570572 | +0.945506 | +0.805746 | +0.693497 | +0.639612 | +1.854735 | +1.127365 | +0.909254 | +0.839281 | +
5 rows × 31 columns
+Note that in contrast to parametric distributions, the parameters of the Spline-Flow do not have a direct interpretation.
+# Partial Dependence Plot of how x acts on param_21
+lgblss.plot(X_test,
+ parameter="param_21",
+ feature="x_true",
+ plot_type="Partial_Dependence")
+
# Feature Importance of param_21
+lgblss.plot(X_test,
+ parameter="param_21",
+ plot_type="Feature_Importance")
+
np.random.seed(123)
+
+###
+# Actual Quantiles
+###
+q1 = norm.ppf(quant_sel[0], loc = 10, scale = 1 + 4*((0.3 < test["x_true"].values) & (test["x_true"].values < 0.5)) + 2*(test["x_true"].values > 0.7))
+q2 = norm.ppf(quant_sel[1], loc = 10, scale = 1 + 4*((0.3 < test["x_true"].values) & (test["x_true"].values < 0.5)) + 2*(test["x_true"].values > 0.7))
+test["quant"] = np.where(test["y"].values < q1, 0, np.where(test["y"].values < q2, 1, 2))
+test["alpha"] = np.where(test["y"].values <= q1, 1, np.where(test["y"].values >= q2, 1, 0))
+df_quantiles = test[test["alpha"] == 1]
+
+# Lower Bound
+yl = list(set(q1))
+yl.sort()
+yl = [yl[2],yl[0],yl[2],yl[1],yl[1]]
+sfunl = pd.DataFrame({"x_true":[0, 0.3, 0.5, 0.7, 1], "y":yl})
+
+# Upper Bound
+yu = list(set(q2))
+yu.sort()
+yu = [yu[0],yu[2],yu[0],yu[1],yu[1]]
+sfunu = pd.DataFrame({"x_true":[0, 0.3, 0.5, 0.7, 1], "y":yu})
+
+###
+# Predicted Quantiles
+###
+test["lb"] = pred_quantiles.iloc[:,0]
+test["ub"] = pred_quantiles.iloc[:,1]
+
+###
+# Plot
+###
+(ggplot(test,
+ aes("x_true",
+ "y")) +
+ geom_point(alpha = 0.2, color = "black", size = 2) +
+ theme_bw(base_size=15) +
+ theme(legend_position="none",
+ plot_title = element_text(hjust = 0.5)) +
+ labs(title = "LightGBMLSS Regression - Simulated Data Example",
+ x="x") +
+ geom_line(aes("x_true",
+ "ub"),
+ size = 1,
+ color = "blue",
+ alpha = 0.7) +
+ geom_line(aes("x_true",
+ "lb"),
+ size = 1,
+ color = "blue",
+ alpha = 0.7) +
+ geom_point(df_quantiles,
+ aes("x_true",
+ "y"),
+ color = "red",
+ alpha = 0.7,
+ size = 2) +
+ geom_step(sfunl,
+ aes("x_true",
+ "y"),
+ size = 1,
+ linetype = "dashed") +
+ geom_step(sfunu,
+ aes("x_true",
+ "y"),
+ size = 1,
+ linetype = "dashed")
+)
+
<Figure Size: (2000 x 1000)>+
In the following figure, we compare the true parameters of the Gaussian with the ones predicted by LightGBMLSS. The below figure shows that the estimated parameters closely match the true ones (recall that the location parameter $\mu=10$ is simulated as being a constant).
+dist_params = ["loc", "scale"]
+
+# Calculate parameters from samples
+sample_params = pd.DataFrame.from_dict(
+ {
+ "loc": pred_samples.mean(axis=1),
+ "scale": pred_samples.std(axis=1),
+ "x_true": X_test["x_true"].values
+ }
+)
+
+# Data with predicted values
+plot_df_predt = pd.melt(sample_params[["x_true"] + dist_params],
+ id_vars="x_true",
+ value_vars=dist_params)
+plot_df_predt["type"] = "PREDICT"
+
+# Data with actual values
+plot_df_actual = pd.melt(test[["x_true"] + dist_params],
+ id_vars="x_true",
+ value_vars=dist_params)
+plot_df_actual["type"] = "TRUE"
+
+# Combine data for plotting
+plot_df = pd.concat([plot_df_predt, plot_df_actual])
+plot_df["variable"] = plot_df.variable.str.upper()
+plot_df["type"] = pd.Categorical(plot_df["type"], categories = ["PREDICT", "TRUE"])
+
+# Plot
+(ggplot(plot_df,
+ aes(x="x_true",
+ y="value",
+ color="type")) +
+ geom_line(size=1.1) +
+ facet_wrap("variable",
+ scales="free") +
+ labs(title="Parameters of univariate Gaussian predicted with LightGBMLSS",
+ x="",
+ y="") +
+ theme_bw(base_size=15) +
+ theme(legend_position="bottom",
+ plot_title = element_text(hjust = 0.5),
+ legend_title = element_blank())
+)
+
<Figure Size: (2000 x 1000)>+
pred_df = pd.melt(pred_samples.iloc[:,0:5])
+actual_df = pd.DataFrame.from_dict({"variable": "ACTUAL", "value": y_test.reshape(-1,)})
+plot_df = pd.concat([pred_df, actual_df])
+
+(
+ ggplot(plot_df,
+ aes(x="value",
+ color="variable",
+ fill="variable")) +
+ geom_density(alpha=0.4) +
+ facet_wrap("variable",
+ ncol=2) +
+ theme_bw(base_size=15) +
+ theme(plot_title = element_text(hjust = 0.5)) +
+ theme(legend_position="none")
+)
+
<Figure Size: (2000 x 1000)>+
Since we predict the entire conditional distribution, we can overlay the point predictions with predicted densities, from which we can also derive quantiles of interest.
+y_pred = []
+
+n_examples = 8
+q_sel = [0.05, 0.95]
+y_sel=0
+samples_arr = pred_samples.values.reshape(-1,n_samples)
+
+for i in range(n_examples):
+ y_samples = pd.DataFrame(samples_arr[i,:].reshape(-1,1), columns=["PREDICT_DENSITY"])
+ y_samples["PREDICT_POINT"] = y_samples["PREDICT_DENSITY"].mean()
+ y_samples["PREDICT_Q05"] = y_samples["PREDICT_DENSITY"].quantile(q=q_sel[0])
+ y_samples["PREDICT_Q95"] = y_samples["PREDICT_DENSITY"].quantile(q=q_sel[1])
+ y_samples["ACTUAL"] = y_test[i]
+ y_samples["obs"]= f"Obervation {i+1}"
+ y_pred.append(y_samples)
+
+pred_df = pd.melt(pd.concat(y_pred, axis=0), id_vars="obs")
+pred_df["obs"] = pd.Categorical(pred_df["obs"], categories=[f"Obervation {i+1}" for i in range(n_examples)])
+df_actual, df_pred_dens, df_pred_point, df_q05, df_q95 = [x for _, x in pred_df.groupby("variable")]
+
+plot_pred = (
+ ggplot(pred_df,
+ aes(color="variable")) +
+ stat_density(df_pred_dens,
+ aes(x="value"),
+ size=1.1) +
+ geom_point(df_pred_point,
+ aes(x="value",
+ y=0),
+ size=1.4) +
+ geom_point(df_actual,
+ aes(x="value",
+ y=0),
+ size=1.4) +
+ geom_vline(df_q05,
+ aes(xintercept="value",
+ fill="variable",
+ color="variable"),
+ linetype="dashed",
+ size=1.1) +
+ geom_vline(df_q95,
+ aes(xintercept="value",
+ fill="variable",
+ color="variable"),
+ linetype="dashed",
+ size=1.1) +
+ facet_wrap("obs",
+ scales="free",
+ ncol=4) +
+ labs(title="Predicted vs. Actual \n",
+ x = "") +
+ theme_bw(base_size=15) +
+ theme(plot_title = element_text(hjust = 0.5)) +
+ scale_fill_brewer(type="qual", palette="Dark2") +
+ theme(legend_position="bottom",
+ legend_title = element_blank()
+ )
+)
+
+print(plot_pred)
+
++
\n", + " | y_sample0 | \n", + "y_sample1 | \n", + "y_sample2 | \n", + "y_sample3 | \n", + "y_sample4 | \n", + "y_sample5 | \n", + "y_sample6 | \n", + "y_sample7 | \n", + "y_sample8 | \n", + "y_sample9 | \n", + "... | \n", + "y_sample990 | \n", + "y_sample991 | \n", + "y_sample992 | \n", + "y_sample993 | \n", + "y_sample994 | \n", + "y_sample995 | \n", + "y_sample996 | \n", + "y_sample997 | \n", + "y_sample998 | \n", + "y_sample999 | \n", + "
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "69.516289 | \n", + "0.000000 | \n", + "0.000000 | \n", + "... | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "
1 | \n", + "60.929550 | \n", + "105.531929 | \n", + "64.138367 | \n", + "110.538750 | \n", + "83.007111 | \n", + "65.914368 | \n", + "114.569489 | \n", + "83.754181 | \n", + "76.929359 | \n", + "106.249832 | \n", + "... | \n", + "126.694046 | \n", + "58.902824 | \n", + "106.914062 | \n", + "109.303070 | \n", + "64.697182 | \n", + "71.515106 | \n", + "96.678009 | \n", + "93.943962 | \n", + "62.027760 | \n", + "0.000000 | \n", + "
2 | \n", + "37.353783 | \n", + "32.012577 | \n", + "31.435833 | \n", + "38.747078 | \n", + "74.590515 | \n", + "53.662891 | \n", + "22.332142 | \n", + "55.751812 | \n", + "19.008478 | \n", + "32.330696 | \n", + "... | \n", + "0.000000 | \n", + "28.788361 | \n", + "22.363855 | \n", + "16.031998 | \n", + "38.852062 | \n", + "25.945065 | \n", + "30.270662 | \n", + "23.981115 | \n", + "37.747807 | \n", + "25.279463 | \n", + "
3 | \n", + "30.415985 | \n", + "51.455875 | \n", + "47.262981 | \n", + "56.843914 | \n", + "75.915627 | \n", + "90.310211 | \n", + "78.174225 | \n", + "73.345291 | \n", + "48.665390 | \n", + "84.060516 | \n", + "... | \n", + "35.612038 | \n", + "56.444321 | \n", + "64.669716 | \n", + "63.445686 | \n", + "94.317162 | \n", + "33.868572 | \n", + "28.650946 | \n", + "30.990072 | \n", + "83.390099 | \n", + "59.522594 | \n", + "
4 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "23.404318 | \n", + "0.000000 | \n", + "46.938168 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "... | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "0.000000 | \n", + "
5 rows × 1000 columns
\n", + "\n", + " | quant_0.05 | \n", + "quant_0.95 | \n", + "
---|---|---|
0 | \n", + "0.0 | \n", + "77.157624 | \n", + "
1 | \n", + "0.0 | \n", + "133.450491 | \n", + "
2 | \n", + "0.0 | \n", + "51.579050 | \n", + "
3 | \n", + "0.0 | \n", + "91.223141 | \n", + "
4 | \n", + "0.0 | \n", + "24.037125 | \n", + "
\n", + " | concentration | \n", + "rate | \n", + "gate | \n", + "
---|---|---|---|
0 | \n", + "13.424025 | \n", + "0.149034 | \n", + "0.943975 | \n", + "
1 | \n", + "13.424025 | \n", + "0.149034 | \n", + "0.054856 | \n", + "
2 | \n", + "9.111379 | \n", + "0.275753 | \n", + "0.054856 | \n", + "
3 | \n", + "9.111379 | \n", + "0.158935 | \n", + "0.054856 | \n", + "
4 | \n", + "9.111379 | \n", + "0.275753 | \n", + "0.943975 | \n", + "
from lightgbmlss.model import *
+from lightgbmlss.distributions.ZAGamma import *
+
+from sklearn.model_selection import train_test_split
+import pandas as pd
+import plotnine
+from plotnine import *
+plotnine.options.figure_size = (18, 9)
+
# The simulation example closely follows https://towardsdatascience.com/zero-inflated-regression-c7dfc656d8af
+np.random.seed(123)
+n_samples = 1000
+
+data = pd.DataFrame({"age": np.random.randint(1, 100, size=n_samples)})
+data["income"] = np.where((data.age > 17) & (data.age < 70), 1500*data.age + 5000 + 10000*np.random.randn(n_samples), 0) / 1000
+
+y = data["income"].values
+X = data.drop(columns="income")
+X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
+
+dtrain = lgb.Dataset(X_train, label=y_train)
+
# Specifies Zero-Adjusted Gamma distribution. See ?ZAGamma for an overview.
+lgblss = LightGBMLSS(
+ ZAGamma(stabilization="None", # Options are "None", "MAD", "L2".
+ response_fn="exp", # Function to transform the concentration and rate parameters, e.g., "exp" or "softplus".
+ loss_fn="nll" # Loss function. Options are "nll" (negative log-likelihood) or "crps"(continuous ranked probability score).)
+ )
+)
+
Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows:
+- Float/Int sample_type
+ - {"param_name": ["sample_type", low, high, log]}
+ - sample_type: str, Type of sampling, e.g., "float" or "int"
+ - low: int, Lower endpoint of the range of suggested values
+ - high: int, Upper endpoint of the range of suggested values
+ - log: bool, Flag to sample the value from the log domain or not
+ - Example: {"eta": "float", low=1e-5, high=1, log=True]}
+
+- Categorical sample_type
+ - {"param_name": ["sample_type", ["choice1", "choice2", "choice3", "..."]]}
+ - sample_type: str, Type of sampling, either "categorical"
+ - choice1, choice2, choice3, ...: str, Possible choices for the parameter
+ - Example: {"boosting": ["categorical", ["gbdt", "dart"]]}
+
+- For parameters without tunable choice (this is needed if tree_method = "gpu_hist" and gpu_id needs to be specified)
+ - {"param_name": ["none", [value]]},
+ - param_name: str, Name of the parameter
+ - value: int, Value of the parameter
+ - Example: {"gpu_id": ["none", [0]]}
+param_dict = {
+ "eta": ["float", {"low": 1e-5, "high": 1, "log": True}],
+ "max_depth": ["int", {"low": 1, "high": 10, "log": False}],
+ "min_gain_to_split": ["float", {"low": 1e-8, "high": 40, "log": False}],
+ "min_sum_hessian_in_leaf": ["float", {"low": 1e-8, "high": 500, "log": True}],
+ "subsample": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "feature_fraction": ["float", {"low": 0.2, "high": 1.0, "log": False}],
+ "boosting": ["categorical", ["gbdt"]],
+}
+
+np.random.seed(123)
+opt_param = lgblss.hyper_opt(param_dict,
+ dtrain,
+ num_boost_round=100, # Number of boosting iterations.
+ nfold=5, # Number of cv-folds.
+ early_stopping_rounds=20, # Number of early-stopping rounds
+ max_minutes=5, # Time budget in minutes, i.e., stop study after the given number of minutes.
+ n_trials=20, # The number of trials. If this argument is set to None, there is no limitation on the number of trials.
+ silence=False, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail.
+ seed=123, # Seed used to generate cv-folds.
+ hp_seed=None # Seed for random number generator used in the Bayesian hyperparameter search.
+ )
+
[I 2023-08-11 12:13:43,049] A new study created in memory with name: LightGBMLSS Hyper-Parameter Optimization ++
0%| | 0/20 [00:00<?, ?it/s]+
[I 2023-08-11 12:13:46,907] Trial 0 finished with value: 493.3361578406364 and parameters: {'eta': 0.0003914367990686954, 'max_depth': 8, 'min_gain_to_split': 33.26852906521015, 'min_sum_hessian_in_leaf': 0.08584812980967063, 'subsample': 0.8021623973944538, 'feature_fraction': 0.8005070720671894, 'boosting': 'gbdt'}. Best is trial 0 with value: 493.3361578406364. +[I 2023-08-11 12:13:47,643] Trial 1 finished with value: 471.35372890681646 and parameters: {'eta': 0.40295767323333237, 'max_depth': 10, 'min_gain_to_split': 24.549932527360678, 'min_sum_hessian_in_leaf': 256.4870626827937, 'subsample': 0.8683684494312434, 'feature_fraction': 0.7849944406887219, 'boosting': 'gbdt'}. Best is trial 1 with value: 471.35372890681646. +[I 2023-08-11 12:13:51,060] Trial 2 finished with value: 503.6348357284388 and parameters: {'eta': 1.222371366077261e-05, 'max_depth': 4, 'min_gain_to_split': 28.061063870566468, 'min_sum_hessian_in_leaf': 0.0007422808244019155, 'subsample': 0.5522281492920988, 'feature_fraction': 0.6521861926605665, 'boosting': 'gbdt'}. Best is trial 1 with value: 471.35372890681646. +[I 2023-08-11 12:13:54,435] Trial 3 finished with value: 503.3457675319369 and parameters: {'eta': 2.274369193692896e-05, 'max_depth': 5, 'min_gain_to_split': 39.238368142734814, 'min_sum_hessian_in_leaf': 0.0006223989318040594, 'subsample': 0.7412515727598581, 'feature_fraction': 0.9452613103135388, 'boosting': 'gbdt'}. Best is trial 1 with value: 471.35372890681646. +[I 2023-08-11 12:13:57,807] Trial 4 finished with value: 502.3184990853341 and parameters: {'eta': 5.598186442465094e-05, 'max_depth': 3, 'min_gain_to_split': 21.23896288198938, 'min_sum_hessian_in_leaf': 0.21992447027885145, 'subsample': 0.45542723654621087, 'feature_fraction': 0.8855719650525622, 'boosting': 'gbdt'}. Best is trial 1 with value: 471.35372890681646. +[I 2023-08-11 12:13:59,599] Trial 5 finished with value: 401.0408565228521 and parameters: {'eta': 0.041701168766377694, 'max_depth': 8, 'min_gain_to_split': 17.301602719722112, 'min_sum_hessian_in_leaf': 32.73881939196468, 'subsample': 0.7377555648382916, 'feature_fraction': 0.9073126421475854, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. +[I 2023-08-11 12:14:02,830] Trial 6 finished with value: 407.386455067243 and parameters: {'eta': 0.007126608872225107, 'max_depth': 5, 'min_gain_to_split': 24.033222285497697, 'min_sum_hessian_in_leaf': 8.907709262528446, 'subsample': 0.4954284084741154, 'feature_fraction': 0.8681250783490104, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. +[I 2023-08-11 12:14:06,129] Trial 7 finished with value: 496.7796683249693 and parameters: {'eta': 0.00026493753680363304, 'max_depth': 3, 'min_gain_to_split': 38.809508809426916, 'min_sum_hessian_in_leaf': 6.812433286336614e-05, 'subsample': 0.21286798118279357, 'feature_fraction': 0.6112152779922334, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. +[I 2023-08-11 12:14:09,346] Trial 8 finished with value: 407.1040293789821 and parameters: {'eta': 0.007732793896149601, 'max_depth': 3, 'min_gain_to_split': 37.84720417299646, 'min_sum_hessian_in_leaf': 6.236162955045407e-08, 'subsample': 0.9353287449899692, 'feature_fraction': 0.3984151482200087, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. ++
C:\Users\maerzale\.virtualenvs\LightGBMLSS--u9b4l4T\lib\site-packages\numpy\core\_methods.py:236: RuntimeWarning: invalid value encountered in subtract ++
[I 2023-08-11 12:14:10,085] Trial 9 finished with value: 435.35091073184594 and parameters: {'eta': 0.9428989545188251, 'max_depth': 10, 'min_gain_to_split': 4.488114462250434, 'min_sum_hessian_in_leaf': 1.7998195761788722, 'subsample': 0.29209480384149383, 'feature_fraction': 0.9592081048531047, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. +[I 2023-08-11 12:14:11,097] Trial 10 finished with value: 469.6566333865485 and parameters: {'eta': 0.20312012140237876, 'max_depth': 7, 'min_gain_to_split': 12.765970540892038, 'min_sum_hessian_in_leaf': 167.938324975031, 'subsample': 0.7047234963516413, 'feature_fraction': 0.26898548607003475, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. +[I 2023-08-11 12:14:14,892] Trial 11 finished with value: 410.651136877749 and parameters: {'eta': 0.016887194634392852, 'max_depth': 1, 'min_gain_to_split': 16.11709690117341, 'min_sum_hessian_in_leaf': 7.572146181609595e-08, 'subsample': 0.9978336630147084, 'feature_fraction': 0.4268355236219649, 'boosting': 'gbdt'}. Best is trial 5 with value: 401.0408565228521. +[I 2023-08-11 12:14:17,623] Trial 12 finished with value: 377.60158084711895 and parameters: {'eta': 0.03699519752376613, 'max_depth': 7, 'min_gain_to_split': 29.510639132062416, 'min_sum_hessian_in_leaf': 1.251547058329255e-08, 'subsample': 0.9633512537434151, 'feature_fraction': 0.482137842472506, 'boosting': 'gbdt'}. Best is trial 12 with value: 377.60158084711895. +[I 2023-08-11 12:14:19,401] Trial 13 finished with value: 377.8206629881021 and parameters: {'eta': 0.0764582042253138, 'max_depth': 8, 'min_gain_to_split': 30.97426056045043, 'min_sum_hessian_in_leaf': 6.7959924810432435e-06, 'subsample': 0.8745012211500811, 'feature_fraction': 0.6773493987586585, 'boosting': 'gbdt'}. Best is trial 12 with value: 377.60158084711895. +[I 2023-08-11 12:14:21,249] Trial 14 finished with value: 377.08800070558675 and parameters: {'eta': 0.0763535359468984, 'max_depth': 7, 'min_gain_to_split': 30.028503666783337, 'min_sum_hessian_in_leaf': 2.4843843388235532e-06, 'subsample': 0.9967113125593, 'feature_fraction': 0.5022537683346732, 'boosting': 'gbdt'}. Best is trial 14 with value: 377.08800070558675. +[I 2023-08-11 12:14:22,835] Trial 15 finished with value: 376.7629699437488 and parameters: {'eta': 0.09620102011855627, 'max_depth': 7, 'min_gain_to_split': 29.437274575766544, 'min_sum_hessian_in_leaf': 1.1839961164729091e-08, 'subsample': 0.9694329011088387, 'feature_fraction': 0.5286979187071998, 'boosting': 'gbdt'}. Best is trial 15 with value: 376.7629699437488. +[I 2023-08-11 12:14:24,205] Trial 16 finished with value: 377.7666131850925 and parameters: {'eta': 0.149654603762201, 'max_depth': 6, 'min_gain_to_split': 33.99184916354226, 'min_sum_hessian_in_leaf': 8.319116968525249e-07, 'subsample': 0.990017156839053, 'feature_fraction': 0.5284298109620141, 'boosting': 'gbdt'}. Best is trial 15 with value: 376.7629699437488. +[I 2023-08-11 12:14:25,170] Trial 17 pruned. Trial was pruned at iteration 20. +[I 2023-08-11 12:14:26,420] Trial 18 finished with value: 364.798082239509 and parameters: {'eta': 0.8932571745812234, 'max_depth': 6, 'min_gain_to_split': 32.84914712406526, 'min_sum_hessian_in_leaf': 1.2602701826082383e-05, 'subsample': 0.6414980864372755, 'feature_fraction': 0.2984688213287875, 'boosting': 'gbdt'}. Best is trial 18 with value: 364.798082239509. +[I 2023-08-11 12:14:27,424] Trial 19 finished with value: 374.1395215044128 and parameters: {'eta': 0.7729049694175503, 'max_depth': 6, 'min_gain_to_split': 36.447746033684936, 'min_sum_hessian_in_leaf': 1.8155247988671537e-05, 'subsample': 0.6136889714080027, 'feature_fraction': 0.24597577103025603, 'boosting': 'gbdt'}. Best is trial 18 with value: 364.798082239509. + +Hyper-Parameter Optimization successfully finished. + Number of finished trials: 20 + Best trial: + Value: 364.798082239509 + Params: + eta: 0.8932571745812234 + max_depth: 6 + min_gain_to_split: 32.84914712406526 + min_sum_hessian_in_leaf: 1.2602701826082383e-05 + subsample: 0.6414980864372755 + feature_fraction: 0.2984688213287875 + boosting: gbdt + opt_rounds: 9 ++
np.random.seed(123)
+
+opt_params = opt_param.copy()
+n_rounds = opt_params["opt_rounds"]
+del opt_params["opt_rounds"]
+
+# Train Model with optimized hyperparameters
+lgblss.train(opt_params,
+ dtrain,
+ num_boost_round=n_rounds
+ )
+
# Set seed for reproducibility
+torch.manual_seed(123)
+
+# Number of samples to draw from predicted distribution
+n_samples = 1000
+# Quantiles to calculate from predicted distribution
+quant_sel = [0.05, 0.95]
+
+# Sample from predicted distribution
+pred_samples = lgblss.predict(X_test,
+ pred_type="samples",
+ n_samples=n_samples,
+ seed=123)
+
+# Calculate quantiles from predicted distribution
+pred_quantiles = lgblss.predict(X_test,
+ pred_type="quantiles",
+ n_samples=n_samples,
+ quantiles=quant_sel)
+
+# Returns predicted distributional parameters
+pred_params = lgblss.predict(X_test,
+ pred_type="parameters")
+
pred_samples.head()
+
+ | y_sample0 | +y_sample1 | +y_sample2 | +y_sample3 | +y_sample4 | +y_sample5 | +y_sample6 | +y_sample7 | +y_sample8 | +y_sample9 | +... | +y_sample990 | +y_sample991 | +y_sample992 | +y_sample993 | +y_sample994 | +y_sample995 | +y_sample996 | +y_sample997 | +y_sample998 | +y_sample999 | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +69.516289 | +0.000000 | +0.000000 | +... | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +
1 | +60.929550 | +105.531929 | +64.138367 | +110.538750 | +83.007111 | +65.914368 | +114.569489 | +83.754181 | +76.929359 | +106.249832 | +... | +126.694046 | +58.902824 | +106.914062 | +109.303070 | +64.697182 | +71.515106 | +96.678009 | +93.943962 | +62.027760 | +0.000000 | +
2 | +37.353783 | +32.012577 | +31.435833 | +38.747078 | +74.590515 | +53.662891 | +22.332142 | +55.751812 | +19.008478 | +32.330696 | +... | +0.000000 | +28.788361 | +22.363855 | +16.031998 | +38.852062 | +25.945065 | +30.270662 | +23.981115 | +37.747807 | +25.279463 | +
3 | +30.415985 | +51.455875 | +47.262981 | +56.843914 | +75.915627 | +90.310211 | +78.174225 | +73.345291 | +48.665390 | +84.060516 | +... | +35.612038 | +56.444321 | +64.669716 | +63.445686 | +94.317162 | +33.868572 | +28.650946 | +30.990072 | +83.390099 | +59.522594 | +
4 | +0.000000 | +0.000000 | +0.000000 | +23.404318 | +0.000000 | +46.938168 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +... | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +0.000000 | +
5 rows × 1000 columns
+pred_quantiles.head()
+
+ | quant_0.05 | +quant_0.95 | +
---|---|---|
0 | +0.0 | +77.157624 | +
1 | +0.0 | +133.450491 | +
2 | +0.0 | +51.579050 | +
3 | +0.0 | +91.223141 | +
4 | +0.0 | +24.037125 | +
pred_params.head()
+
+ | concentration | +rate | +gate | +
---|---|---|---|
0 | +13.424025 | +0.149034 | +0.943975 | +
1 | +13.424025 | +0.149034 | +0.054856 | +
2 | +9.111379 | +0.275753 | +0.054856 | +
3 | +9.111379 | +0.158935 | +0.054856 | +
4 | +9.111379 | +0.275753 | +0.943975 | +
# Partial Dependence Plot of concentration parameter
+lgblss.plot(X_test,
+ parameter="concentration",
+ feature="age",
+ plot_type="Partial_Dependence")
+
# Feature Importance of gate parameter
+lgblss.plot(X_test,
+ parameter="gate",
+ plot_type="Feature_Importance")
+
pred_df = pd.melt(pred_samples.iloc[:,0:5])
+actual_df = pd.DataFrame.from_dict({"variable": "ACTUAL", "value": y_test.reshape(-1,)})
+plot_df = pd.concat([pred_df, actual_df])
+
+(
+ ggplot(plot_df,
+ aes(x="value",
+ color="variable",
+ fill="variable")) +
+ geom_density(alpha=0.4) +
+ facet_wrap("variable",
+ scales="free_y",
+ ncol=2) +
+ theme_bw(base_size=15) +
+ theme(legend_position="none")
+)
+
<Figure Size: (1800 x 900)>+
+
We introduce a comprehensive framework that models and predicts the full conditional distribution of a univariate target as a function of covariates. Choosing from a wide range of continuous, discrete, and mixed discrete-continuous distributions, modelling and predicting the entire conditional distribution greatly enhances the flexibility of LightGBM, as it allows to create probabilistic forecasts from which prediction intervals and quantiles of interest can be derived.
+To install LightGBMLSS, please first run
+pip install git+https://github.com/StatMixedML/LightGBMLSS.git
+
+Then, to install the shap-dependency, run
+pip install git+https://github.com/dsgibbons/shap.git
+
+Since LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to variability regarding the ranges, the estimation of Gradients and Hessians might become unstable so that LightGBMLSS might not converge or might converge very slowly. To mitigate these effects, we have implemented a stabilization of Gradients and Hessians.
+For improved convergence, an alternative approach is to standardize the (continuous) response variable, such as dividing it by 100 (e.g., y/100). This approach proves especially valuable when the response range significantly differs from that of Gradients and Hessians. Nevertheless, it is essential to carefully evaluate and apply both the built-in stabilization and response standardization techniques in consideration of the specific dataset at hand.
+Since LightGBMLSS is based on a one vs. all estimation strategy, where a separate tree is grown for each distributional parameter, it requires training [number of iterations] * [number of distributional parameters]
trees. Hence, the runtime of LightGBMLSS is generally slightly higher for univariate distributions as compared to LightGBM, which requires training [number of iterations]
trees only.
März, A. and Kneib, T.: (2022) Distributional Gradient Boosting Machines.
+März, Alexander (2019): XGBoostLSS - An extension of XGBoost to probabilistic forecasting.
' + escapeHtml(summary) +'
' + noResultsText + '
'); + } +} + +function doSearch () { + var query = document.getElementById('mkdocs-search-query').value; + if (query.length > min_search_length) { + if (!window.Worker) { + displayResults(search(query)); + } else { + searchWorker.postMessage({query: query}); + } + } else { + // Clear results for short queries + displayResults([]); + } +} + +function initSearch () { + var search_input = document.getElementById('mkdocs-search-query'); + if (search_input) { + search_input.addEventListener("keyup", doSearch); + } + var term = getSearchTermFromLocation(); + if (term) { + search_input.value = term; + doSearch(); + } +} + +function onWorkerMessage (e) { + if (e.data.allowSearch) { + initSearch(); + } else if (e.data.results) { + var results = e.data.results; + displayResults(results); + } else if (e.data.config) { + min_search_length = e.data.config.min_search_length-1; + } +} + +if (!window.Worker) { + console.log('Web Worker API not supported'); + // load index in main thread + $.getScript(joinUrl(base_url, "search/worker.js")).done(function () { + console.log('Loaded worker'); + init(); + window.postMessage = function (msg) { + onWorkerMessage({data: msg}); + }; + }).fail(function (jqxhr, settings, exception) { + console.error('Could not load worker.js'); + }); +} else { + // Wrap search in a web worker + var searchWorker = new Worker(joinUrl(base_url, "search/worker.js")); + searchWorker.postMessage({init: true}); + searchWorker.onmessage = onWorkerMessage; +} diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..3455832 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"LightGBMLSS - An extension of LightGBM to probabilistic modelling and prediction We introduce a comprehensive framework that models and predicts the full conditional distribution of a univariate target as a function of covariates. Choosing from a wide range of continuous, discrete, and mixed discrete-continuous distributions, modelling and predicting the entire conditional distribution greatly enhances the flexibility of LightGBM, as it allows to create probabilistic forecasts from which prediction intervals and quantiles of interest can be derived. Features Estimation of all distributional parameters. Normalizing Flows allow modelling of complex and multi-modal distributions. Mixture-Densities can model a diverse range of data characteristics. Zero-Adjusted and Zero-Inflated Distributions for modelling excess of zeros in the data. Automatic derivation of Gradients and Hessian of all distributional parameters using PyTorch . Automated hyper-parameter search, including pruning, is done via Optuna . The output of LightGBMLSS is explained using SHapley Additive exPlanations . LightGBMLSS provides full compatibility with all the features and functionality of LightGBM. LightGBMLSS is available in Python. Installation To install LightGBMLSS, please first run pip install git+https://github.com/StatMixedML/LightGBMLSS.git Then, to install the shap-dependency, run pip install git+https://github.com/dsgibbons/shap.git Some Notes Stabilization Since LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to variability regarding the ranges, the estimation of Gradients and Hessians might become unstable so that LightGBMLSS might not converge or might converge very slowly. To mitigate these effects, we have implemented a stabilization of Gradients and Hessians. For improved convergence, an alternative approach is to standardize the (continuous) response variable, such as dividing it by 100 (e.g., y/100). This approach proves especially valuable when the response range significantly differs from that of Gradients and Hessians. Nevertheless, it is essential to carefully evaluate and apply both the built-in stabilization and response standardization techniques in consideration of the specific dataset at hand. Runtime Since LightGBMLSS is based on a one vs. all estimation strategy , where a separate tree is grown for each distributional parameter, it requires training [number of iterations] * [number of distributional parameters] trees. Hence, the runtime of LightGBMLSS is generally slightly higher for univariate distributions as compared to LightGBM, which requires training [number of iterations] trees only. Reference Paper M\u00e4rz, A. and Kneib, T.: (2022) Distributional Gradient Boosting Machines . M\u00e4rz, Alexander (2019): XGBoostLSS - An extension of XGBoost to probabilistic forecasting .","title":"Home"},{"location":"#lightgbmlss-an-extension-of-lightgbm-to-probabilistic-modelling-and-prediction","text":"We introduce a comprehensive framework that models and predicts the full conditional distribution of a univariate target as a function of covariates. Choosing from a wide range of continuous, discrete, and mixed discrete-continuous distributions, modelling and predicting the entire conditional distribution greatly enhances the flexibility of LightGBM, as it allows to create probabilistic forecasts from which prediction intervals and quantiles of interest can be derived.","title":"LightGBMLSS - An extension of LightGBM to probabilistic modelling and prediction"},{"location":"#features","text":"Estimation of all distributional parameters. Normalizing Flows allow modelling of complex and multi-modal distributions. Mixture-Densities can model a diverse range of data characteristics. Zero-Adjusted and Zero-Inflated Distributions for modelling excess of zeros in the data. Automatic derivation of Gradients and Hessian of all distributional parameters using PyTorch . Automated hyper-parameter search, including pruning, is done via Optuna . The output of LightGBMLSS is explained using SHapley Additive exPlanations . LightGBMLSS provides full compatibility with all the features and functionality of LightGBM. LightGBMLSS is available in Python.","title":"Features"},{"location":"#installation","text":"To install LightGBMLSS, please first run pip install git+https://github.com/StatMixedML/LightGBMLSS.git Then, to install the shap-dependency, run pip install git+https://github.com/dsgibbons/shap.git","title":"Installation"},{"location":"#some-notes","text":"","title":"Some Notes"},{"location":"#stabilization","text":"Since LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to variability regarding the ranges, the estimation of Gradients and Hessians might become unstable so that LightGBMLSS might not converge or might converge very slowly. To mitigate these effects, we have implemented a stabilization of Gradients and Hessians. For improved convergence, an alternative approach is to standardize the (continuous) response variable, such as dividing it by 100 (e.g., y/100). This approach proves especially valuable when the response range significantly differs from that of Gradients and Hessians. Nevertheless, it is essential to carefully evaluate and apply both the built-in stabilization and response standardization techniques in consideration of the specific dataset at hand.","title":"Stabilization"},{"location":"#runtime","text":"Since LightGBMLSS is based on a one vs. all estimation strategy , where a separate tree is grown for each distributional parameter, it requires training [number of iterations] * [number of distributional parameters] trees. Hence, the runtime of LightGBMLSS is generally slightly higher for univariate distributions as compared to LightGBM, which requires training [number of iterations] trees only.","title":"Runtime"},{"location":"#reference-paper","text":"M\u00e4rz, A. and Kneib, T.: (2022) Distributional Gradient Boosting Machines . M\u00e4rz, Alexander (2019): XGBoostLSS - An extension of XGBoost to probabilistic forecasting .","title":"Reference Paper"},{"location":"api/","text":"API references LightGBMLSS - An extension of LightGBM to probabilistic forecasting datasets LightGBMLSS - An extension of LightGBM to probabilistic forecasting data_loader load_simulated_gaussian_data () Returns train/test dataframe of a simulated example. Contains the following columns y int64: response x int64: x-feature X1:X10 int64: random noise features lightgbmlss/datasets/data_loader.py 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def load_simulated_gaussian_data (): \"\"\" Returns train/test dataframe of a simulated example. Contains the following columns: y int64: response x int64: x-feature X1:X10 int64: random noise features \"\"\" train_path = pkg_resources . resource_stream ( __name__ , \"gaussian_train_sim.csv\" ) train_df = pd . read_csv ( train_path ) test_path = pkg_resources . resource_stream ( __name__ , \"gaussian_test_sim.csv\" ) test_df = pd . read_csv ( test_path ) return train_df , test_df load_simulated_studentT_data () Returns train/test dataframe of a simulated example. Contains the following columns y int64: response x int64: x-feature X1:X10 int64: random noise features lightgbmlss/datasets/data_loader.py 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 def load_simulated_studentT_data (): \"\"\" Returns train/test dataframe of a simulated example. Contains the following columns: y int64: response x int64: x-feature X1:X10 int64: random noise features \"\"\" train_path = pkg_resources . resource_stream ( __name__ , \"studentT_train_sim.csv\" ) train_df = pd . read_csv ( train_path ) test_path = pkg_resources . resource_stream ( __name__ , \"studentT_test_sim.csv\" ) test_df = pd . read_csv ( test_path ) return train_df , test_df distributions LightGBMLSS - An extension of LightGBM to probabilistic forecasting Beta Beta Bases: DistributionClass Beta distribution class. Distributional Parameters concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). Source https://pytorch.org/docs/stable/distributions.html#beta Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Beta.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Beta ( DistributionClass ): \"\"\" Beta distribution class. Distributional Parameters ------------------------- concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#beta Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Beta_Torch param_dict = { \"concentration1\" : response_fn , \"concentration0\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Cauchy Cauchy Bases: DistributionClass Cauchy distribution class. Distributional Parameters loc: torch.Tensor Mode or median of the distribution. scale: torch.Tensor Half width at half maximum. Source https://pytorch.org/docs/stable/distributions.html#cauchy Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Cauchy.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Cauchy ( DistributionClass ): \"\"\" Cauchy distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mode or median of the distribution. scale: torch.Tensor Half width at half maximum. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#cauchy Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Cauchy_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Expectile Expectile Bases: DistributionClass Expectile distribution class. Distributional Parameters expectile: List List of specified expectiles. Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". expectiles: List List of expectiles in increasing order. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. Source code in lightgbmlss/distributions/Expectile.py 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class Expectile ( DistributionClass ): \"\"\" Expectile distribution class. Distributional Parameters ------------------------- expectile: List List of specified expectiles. Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". expectiles: List List of expectiles in increasing order. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. \"\"\" def __init__ ( self , stabilization : str = \"None\" , expectiles : List = [ 0.1 , 0.5 , 0.9 ], penalize_crossing : bool = False , ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if not isinstance ( expectiles , list ): raise ValueError ( \"Expectiles must be a list.\" ) if not all ([ 0 < expectile < 1 for expectile in expectiles ]): raise ValueError ( \"Expectiles must be between 0 and 1.\" ) if not isinstance ( penalize_crossing , bool ): raise ValueError ( \"penalize_crossing must be a boolean. Please choose from True or False.\" ) # Set the parameters specific to the distribution distribution = Expectile_Torch torch . distributions . Distribution . set_default_validate_args ( False ) expectiles . sort () param_dict = {} for expectile in expectiles : key = f \"expectile_ { expectile } \" param_dict [ key ] = identity_fn # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = \"nll\" , tau = torch . tensor ( expectiles ), penalize_crossing = penalize_crossing ) Expectile_Torch Bases: Distribution PyTorch implementation of expectiles. Arguments expectiles : List[torch.Tensor] List of expectiles. penalize_crossing : bool Whether to include a penalty term to discourage crossing of expectiles. Source code in lightgbmlss/distributions/Expectile.py 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 class Expectile_Torch ( Distribution ): \"\"\" PyTorch implementation of expectiles. Arguments --------- expectiles : List[torch.Tensor] List of expectiles. penalize_crossing : bool Whether to include a penalty term to discourage crossing of expectiles. \"\"\" def __init__ ( self , expectiles : List [ torch . Tensor ], penalize_crossing : bool = False , ): super ( Expectile_Torch ) . __init__ () self . expectiles = expectiles self . penalize_crossing = penalize_crossing self . __class__ . __name__ = \"Expectile\" def log_prob ( self , value : torch . Tensor , tau : List [ torch . Tensor ]) -> torch . Tensor : \"\"\" Returns the log of the probability density function evaluated at `value`. Arguments --------- value : torch.Tensor Response for which log probability is to be calculated. tau : List[torch.Tensor] List of asymmetry parameters. Returns ------- torch.Tensor Log probability of `value`. \"\"\" value = value . reshape ( - 1 , 1 ) loss = torch . tensor ( 0.0 , dtype = torch . float32 ) penalty = torch . tensor ( 0.0 , dtype = torch . float32 ) # Calculate loss predt_expectiles = [] for expectile , tau_value in zip ( self . expectiles , tau ): weight = torch . where ( value - expectile >= 0 , tau_value , 1 - tau_value ) loss += torch . nansum ( weight * ( value - expectile ) ** 2 ) predt_expectiles . append ( expectile . reshape ( - 1 , 1 )) # Penalty term to discourage crossing of expectiles if self . penalize_crossing : predt_expectiles = torch . cat ( predt_expectiles , dim = 1 ) penalty = torch . mean ( ( ~ torch . all ( torch . diff ( predt_expectiles , dim = 1 ) > 0 , dim = 1 )) . float () ) loss = ( loss * ( 1 + penalty )) / len ( self . expectiles ) return - loss log_prob ( value , tau ) Returns the log of the probability density function evaluated at value . Arguments value : torch.Tensor Response for which log probability is to be calculated. tau : List[torch.Tensor] List of asymmetry parameters. Returns torch.Tensor Log probability of value . lightgbmlss/distributions/Expectile.py 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 def log_prob ( self , value : torch . Tensor , tau : List [ torch . Tensor ]) -> torch . Tensor : \"\"\" Returns the log of the probability density function evaluated at `value`. Arguments --------- value : torch.Tensor Response for which log probability is to be calculated. tau : List[torch.Tensor] List of asymmetry parameters. Returns ------- torch.Tensor Log probability of `value`. \"\"\" value = value . reshape ( - 1 , 1 ) loss = torch . tensor ( 0.0 , dtype = torch . float32 ) penalty = torch . tensor ( 0.0 , dtype = torch . float32 ) # Calculate loss predt_expectiles = [] for expectile , tau_value in zip ( self . expectiles , tau ): weight = torch . where ( value - expectile >= 0 , tau_value , 1 - tau_value ) loss += torch . nansum ( weight * ( value - expectile ) ** 2 ) predt_expectiles . append ( expectile . reshape ( - 1 , 1 )) # Penalty term to discourage crossing of expectiles if self . penalize_crossing : predt_expectiles = torch . cat ( predt_expectiles , dim = 1 ) penalty = torch . mean ( ( ~ torch . all ( torch . diff ( predt_expectiles , dim = 1 ) > 0 , dim = 1 )) . float () ) loss = ( loss * ( 1 + penalty )) / len ( self . expectiles ) return - loss expectile_norm ( tau = 0.5 , m = 0 , sd = 1 ) Calculates expectiles from Normal distribution for given tau values. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns np.ndarray lightgbmlss/distributions/Expectile.py 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 def expectile_norm ( tau : np . ndarray = 0.5 , m : np . ndarray = 0 , sd : np . ndarray = 1 ): \"\"\" Calculates expectiles from Normal distribution for given tau values. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments _________ tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns _______ np.ndarray \"\"\" tau [ tau > 1 or tau < 0 ] = np . nan zz = 0 * tau lower = np . array ( - 10 , dtype = \"float\" ) lower = np . repeat ( lower [ np . newaxis , ... ], len ( tau ), axis = 0 ) upper = np . array ( 10 , dtype = \"float\" ) upper = np . repeat ( upper [ np . newaxis , ... ], len ( tau ), axis = 0 ) diff = 1 index = 0 while ( diff > 1e-10 ) and ( index < 1000 ): root = expectile_pnorm ( zz ) - tau root [ np . isnan ( root )] = 0 lower [ root < 0 ] = zz [ root < 0 ] upper [ root > 0 ] = zz [ root > 0 ] zz = ( upper + lower ) / 2 diff = np . nanmax ( np . abs ( root )) index = index + 1 zz [ np . isnan ( tau )] = np . nan return zz * sd + m expectile_pnorm ( tau = 0.5 , m = 0 , sd = 1 ) Normal Expectile Distribution Function. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns tau : np.ndarray Expectiles from the Normal distribution. lightgbmlss/distributions/Expectile.py 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 def expectile_pnorm ( tau : np . ndarray = 0.5 , m : np . ndarray = 0 , sd : np . ndarray = 1 ): \"\"\" Normal Expectile Distribution Function. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments _________ tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns _______ tau : np.ndarray Expectiles from the Normal distribution. \"\"\" z = ( tau - m ) / sd p = norm . cdf ( z , loc = m , scale = sd ) d = norm . pdf ( z , loc = m , scale = sd ) u = - d - z * p tau = u / ( 2 * u + z ) return tau Gamma Gamma Bases: DistributionClass Gamma distribution class. Distributional Parameters concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) Source https://pytorch.org/docs/stable/distributions.html#gamma Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Gamma.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Gamma ( DistributionClass ): \"\"\" Gamma distribution class. Distributional Parameters -------------------------- concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) Source ------------------------- https://pytorch.org/docs/stable/distributions.html#gamma Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Gamma_Torch param_dict = { \"concentration\" : response_fn , \"rate\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Gaussian Gaussian Bases: DistributionClass Gaussian distribution class. Distributional Parameters loc: torch.Tensor Mean of the distribution (often referred to as mu). scale: torch.Tensor Standard deviation of the distribution (often referred to as sigma). Source https://pytorch.org/docs/stable/distributions.html#normal Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Gaussian.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Gaussian ( DistributionClass ): \"\"\" Gaussian distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mean of the distribution (often referred to as mu). scale: torch.Tensor Standard deviation of the distribution (often referred to as sigma). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#normal Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Gaussian_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Gumbel Gumbel Bases: DistributionClass Gumbel distribution class. Distributional Parameters loc: torch.Tensor Location parameter of the distribution. scale: torch.Tensor Scale parameter of the distribution. Source https://pytorch.org/docs/stable/distributions.html#gumbel Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Gumbel.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Gumbel ( DistributionClass ): \"\"\" Gumbel distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Location parameter of the distribution. scale: torch.Tensor Scale parameter of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#gumbel Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Gumbel_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Laplace Laplace Bases: DistributionClass Laplace distribution class. Distributional Parameters loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution. Source https://pytorch.org/docs/stable/distributions.html#laplace Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Laplace.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Laplace ( DistributionClass ): \"\"\" Laplace distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#laplace Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Laplace_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) LogNormal LogNormal Bases: DistributionClass LogNormal distribution class. Distributional Parameters loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. Source https://pytorch.org/docs/stable/distributions.html#lognormal Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/LogNormal.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class LogNormal ( DistributionClass ): \"\"\" LogNormal distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#lognormal Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = LogNormal_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Mixture Mixture Bases: MixtureDistributionClass Mixture-Density distribution class. Implements a mixture-density distribution for univariate targets, where all components are from different parameterizations of the same distribution-type. A mixture-density distribution is a concept used to model a complex distribution that arises from combining multiple simpler distributions. The Mixture-Density distribution is parameterized by a categorical selecting distribution (over M components) and M-component distributions. For more information on the Mixture-Density distribution, see: Bishop, C. M. (1994). Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK. Distributional Parameters Inherits the distributional parameters from the component distributions. Source https://pytorch.org/docs/stable/distributions.html#mixturesamefamily Parameters component_distribution: torch.distributions.Distribution Distribution class for the components of the mixture distribution. Has to be one of the available univariate distributions of the package. M: int Number of components in the mixture distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, the Hessian corresponds to the second-order derivative with respect to that specific parameter only. The resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can be more accurate. - \"grouped\": Each tensor contains all parameters for a specific parameter-type, e.g., for a Gaussian-Mixture with M=2, loc=[loc_1, loc_2], scale=[scale_1, scale_2], and mix_prob=[mix_prob_1, mix_prob_2]. When computing the Hessian, the derivatives for all parameters in the respective tensor are calculated jointly. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter-type. This is usually less runtime intensive, but can be less accurate. tau: float, non-negative scalar temperature. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. Source code in lightgbmlss/distributions/Mixture.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 class Mixture ( MixtureDistributionClass ): \"\"\" Mixture-Density distribution class. Implements a mixture-density distribution for univariate targets, where all components are from different parameterizations of the same distribution-type. A mixture-density distribution is a concept used to model a complex distribution that arises from combining multiple simpler distributions. The Mixture-Density distribution is parameterized by a categorical selecting distribution (over M components) and M-component distributions. For more information on the Mixture-Density distribution, see: Bishop, C. M. (1994). Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK. Distributional Parameters ------------------------- Inherits the distributional parameters from the component distributions. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#mixturesamefamily Parameters ------------------------- component_distribution: torch.distributions.Distribution Distribution class for the components of the mixture distribution. Has to be one of the available univariate distributions of the package. M: int Number of components in the mixture distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, the Hessian corresponds to the second-order derivative with respect to that specific parameter only. The resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can be more accurate. - \"grouped\": Each tensor contains all parameters for a specific parameter-type, e.g., for a Gaussian-Mixture with M=2, loc=[loc_1, loc_2], scale=[scale_1, scale_2], and mix_prob=[mix_prob_1, mix_prob_2]. When computing the Hessian, the derivatives for all parameters in the respective tensor are calculated jointly. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter-type. This is usually less runtime intensive, but can be less accurate. tau: float, non-negative scalar temperature. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. \"\"\" def __init__ ( self , component_distribution : torch . distributions . Distribution , M : int = 2 , hessian_mode : str = \"individual\" , tau : float = 1.0 ): # Input Checks mixt_dist = get_component_distributions () if str ( component_distribution . __class__ ) . split ( \".\" )[ - 2 ] not in mixt_dist : raise ValueError ( f \"component_distribution must be one of the following: { mixt_dist } .\" ) if not isinstance ( M , int ): raise ValueError ( \"M must be an integer.\" ) if M < 2 : raise ValueError ( \"M must be greater than 1.\" ) if component_distribution . loss_fn != \"nll\" : raise ValueError ( \"Loss for component_distribution must be 'nll'.\" ) if not isinstance ( hessian_mode , str ): raise ValueError ( \"hessian_mode must be a string.\" ) if hessian_mode not in [ \"individual\" , \"grouped\" ]: raise ValueError ( \"hessian_mode must be either 'individual' or 'grouped'.\" ) if not isinstance ( tau , float ): raise ValueError ( \"tau must be a float.\" ) if tau <= 0 : raise ValueError ( \"tau must be greater than 0.\" ) # Set the parameters specific to the distribution param_dict = component_distribution . param_dict preset_gumbel_fn = partial ( gumbel_softmax_fn , tau = tau ) param_dict . update ({ \"mix_prob\" : preset_gumbel_fn }) distribution_arg_names = [ f \" { key } _ { i } \" for key in param_dict for i in range ( 1 , M + 1 )] torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = component_distribution , M = M , temperature = tau , hessian_mode = hessian_mode , univariate = True , discrete = component_distribution . discrete , n_dist_param = len ( distribution_arg_names ), stabilization = component_distribution . stabilization , param_dict = param_dict , distribution_arg_names = distribution_arg_names , loss_fn = component_distribution . loss_fn ) NegativeBinomial NegativeBinomial Bases: DistributionClass NegativeBinomial distribution class. Distributional Parameters total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds for probabilities of success. Source https://pytorch.org/docs/stable/distributions.html#negativebinomial Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/NegativeBinomial.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 class NegativeBinomial ( DistributionClass ): \"\"\" NegativeBinomial distribution class. Distributional Parameters ------------------------- total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds for probabilities of success. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#negativebinomial Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn_total_count : str = \"relu\" , response_fn_probs : str = \"sigmoid\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions for total_count response_functions_total_count = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn_total_count in response_functions_total_count : response_fn_total_count = response_functions_total_count [ response_fn_total_count ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Specify Response Functions for probs response_functions_probs = { \"sigmoid\" : sigmoid_fn } if response_fn_probs in response_functions_probs : response_fn_probs = response_functions_probs [ response_fn_probs ] else : raise ValueError ( \"Invalid response function for probs. Please select 'sigmoid'.\" ) # Set the parameters specific to the distribution distribution = NegativeBinomial_Torch param_dict = { \"total_count\" : response_fn_total_count , \"probs\" : response_fn_probs } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Poisson Poisson Bases: DistributionClass Poisson distribution class. Distributional Parameters rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). Source https://pytorch.org/docs/stable/distributions.html#poisson Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/Poisson.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class Poisson ( DistributionClass ): \"\"\" Poisson distribution class. Distributional Parameters ------------------------- rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#poisson Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"relu\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Set the parameters specific to the distribution distribution = Poisson_Torch param_dict = { \"rate\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) SplineFlow SplineFlow Bases: NormalizingFlowClass Spline Flow class. The spline flow is a normalizing flow based on element-wise rational spline bijections of linear and quadratic order (Durkan et al., 2019; Dolatabadi et al., 2020). Rational splines are functions that are comprised of segments that are the ratio of two polynomials. Rational splines offer an excellent combination of functional flexibility whilst maintaining a numerically stable inverse. For more details, see: - Durkan, C., Bekasov, A., Murray, I. and Papamakarios, G. Neural Spline Flows. NeurIPS 2019. - Dolatabadi, H. M., Erfani, S. and Leckie, C., Invertible Generative Modeling using Linear Rational Splines. AISTATS 2020. Source https://docs.pyro.ai/en/stable/distributions.html#pyro.distributions.transforms.Spline Arguments target_support: str The target support. Options are - \"real\": [-inf, inf] - \"positive\": [0, inf] - \"positive_integer\": [0, 1, 2, 3, ...] - \"unit_interval\": [0, 1] count_bins: int The number of segments comprising the spline. bound: float The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. order: str The order of the spline. Options are \"linear\" or \"quadratic\". stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/SplineFlow.py 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 class SplineFlow ( NormalizingFlowClass ): \"\"\" Spline Flow class. The spline flow is a normalizing flow based on element-wise rational spline bijections of linear and quadratic order (Durkan et al., 2019; Dolatabadi et al., 2020). Rational splines are functions that are comprised of segments that are the ratio of two polynomials. Rational splines offer an excellent combination of functional flexibility whilst maintaining a numerically stable inverse. For more details, see: - Durkan, C., Bekasov, A., Murray, I. and Papamakarios, G. Neural Spline Flows. NeurIPS 2019. - Dolatabadi, H. M., Erfani, S. and Leckie, C., Invertible Generative Modeling using Linear Rational Splines. AISTATS 2020. Source --------- https://docs.pyro.ai/en/stable/distributions.html#pyro.distributions.transforms.Spline Arguments --------- target_support: str The target support. Options are - \"real\": [-inf, inf] - \"positive\": [0, inf] - \"positive_integer\": [0, 1, 2, 3, ...] - \"unit_interval\": [0, 1] count_bins: int The number of segments comprising the spline. bound: float The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. order: str The order of the spline. Options are \"linear\" or \"quadratic\". stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , target_support : str = \"real\" , count_bins : int = 8 , bound : float = 3.0 , order : str = \"linear\" , stabilization : str = \"None\" , loss_fn : str = \"nll\" ): # Specify Target Transform if not isinstance ( target_support , str ): raise ValueError ( \"target_support must be a string.\" ) transforms = { \"real\" : ( identity_transform , False ), \"positive\" : ( SoftplusTransform (), False ), \"positive_integer\" : ( SoftplusTransform (), True ), \"unit_interval\" : ( SigmoidTransform (), False ) } if target_support in transforms : target_transform , discrete = transforms [ target_support ] else : raise ValueError ( \"Invalid target_support. Options are 'real', 'positive', 'positive_integer', or 'unit_interval'.\" ) # Check if count_bins is valid if not isinstance ( count_bins , int ): raise ValueError ( \"count_bins must be an integer.\" ) if count_bins <= 0 : raise ValueError ( \"count_bins must be a positive integer > 0.\" ) # Check if bound is float if not isinstance ( bound , float ): raise ValueError ( \"bound must be a float.\" ) # Number of parameters if not isinstance ( order , str ): raise ValueError ( \"order must be a string.\" ) order_params = { \"quadratic\" : 2 * count_bins + ( count_bins - 1 ), \"linear\" : 3 * count_bins + ( count_bins - 1 ) } if order in order_params : n_params = order_params [ order ] else : raise ValueError ( \"Invalid order specification. Options are 'linear' or 'quadratic'.\" ) # Check if stabilization method is valid. if not isinstance ( stabilization , str ): raise ValueError ( \"stabilization must be a string.\" ) if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Options are 'None', 'MAD' or 'L2'.\" ) # Check if loss function is valid. if not isinstance ( loss_fn , str ): raise ValueError ( \"loss_fn must be a string.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss_fn. Options are 'nll' or 'crps'.\" ) # Specify parameter dictionary param_dict = { f \"param_ { i + 1 } \" : identity_fn for i in range ( n_params )} torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Normalizing Flow Class super () . __init__ ( base_dist = Normal , # Base distribution, currently only Normal is supported. flow_transform = Spline , count_bins = count_bins , bound = bound , order = order , n_dist_param = n_params , param_dict = param_dict , target_transform = target_transform , discrete = discrete , univariate = True , stabilization = stabilization , loss_fn = loss_fn ) StudentT StudentT Bases: DistributionClass Student-T Distribution Class Distributional Parameters df: torch.Tensor Degrees of freedom. loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution. Source https://pytorch.org/docs/stable/distributions.html#studentt Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/StudentT.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 class StudentT ( DistributionClass ): \"\"\" Student-T Distribution Class Distributional Parameters ------------------------- df: torch.Tensor Degrees of freedom. loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#studentt Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : ( exp_fn , exp_fn_df ), \"softplus\" : ( softplus_fn , softplus_fn_df ) } if response_fn in response_functions : response_fn , response_fn_df = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = StudentT_Torch param_dict = { \"df\" : response_fn_df , \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) Weibull Weibull Bases: DistributionClass Weibull distribution class. Distributional Parameters scale: torch.Tensor Scale parameter of distribution (lambda). concentration: torch.Tensor Concentration parameter of distribution (k/shape). Source https://pytorch.org/docs/stable/distributions.html#weibull Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Weibull.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Weibull ( DistributionClass ): \"\"\" Weibull distribution class. Distributional Parameters ------------------------- scale: torch.Tensor Scale parameter of distribution (lambda). concentration: torch.Tensor Concentration parameter of distribution (k/shape). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#weibull Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Weibull_Torch param_dict = { \"scale\" : response_fn , \"concentration\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) ZABeta ZABeta Bases: DistributionClass Zero-Adjusted Beta distribution class. The zero-adjusted Beta distribution is similar to the Beta distribution but allows zeros as y values. Distributional Parameters concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZABeta.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class ZABeta ( DistributionClass ): \"\"\" Zero-Adjusted Beta distribution class. The zero-adjusted Beta distribution is similar to the Beta distribution but allows zeros as y values. Distributional Parameters ------------------------- concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = ZeroAdjustedBeta_Torch param_dict = { \"concentration1\" : response_fn , \"concentration0\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) ZAGamma ZAGamma Bases: DistributionClass Zero-Adjusted Gamma distribution class. The zero-adjusted Gamma distribution is similar to the Gamma distribution but allows zeros as y values. Distributional Parameters concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZAGamma.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class ZAGamma ( DistributionClass ): \"\"\" Zero-Adjusted Gamma distribution class. The zero-adjusted Gamma distribution is similar to the Gamma distribution but allows zeros as y values. Distributional Parameters -------------------------- concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = ZeroAdjustedGamma_Torch param_dict = { \"concentration\" : response_fn , \"rate\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) ZALN ZALN Bases: DistributionClass Zero-Adjusted LogNormal distribution class. The zero-adjusted Log-Normal distribution is similar to the Log-Normal distribution but allows zeros as y values. Distributional Parameters loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZALN.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class ZALN ( DistributionClass ): \"\"\" Zero-Adjusted LogNormal distribution class. The zero-adjusted Log-Normal distribution is similar to the Log-Normal distribution but allows zeros as y values. Distributional Parameters ------------------------- loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = ZeroAdjustedLogNormal_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) ZINB ZINB Bases: DistributionClass Zero-Inflated Negative Binomial distribution class. Distributional Parameters total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZINB.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 class ZINB ( DistributionClass ): \"\"\" Zero-Inflated Negative Binomial distribution class. Distributional Parameters ------------------------- total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn_total_count : str = \"relu\" , response_fn_probs : str = \"sigmoid\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions for total_count response_functions_total_count = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn_total_count in response_functions_total_count : response_fn_total_count = response_functions_total_count [ response_fn_total_count ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Specify Response Functions for probs response_functions_probs = { \"sigmoid\" : sigmoid_fn } if response_fn_probs in response_functions_probs : response_fn_probs = response_functions_probs [ response_fn_probs ] else : raise ValueError ( \"Invalid response function for probs. Please select 'sigmoid'.\" ) # Set the parameters specific to the distribution distribution = ZeroInflatedNegativeBinomial_Torch param_dict = { \"total_count\" : response_fn_total_count , \"probs\" : response_fn_probs , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) ZIPoisson ZIPoisson Bases: DistributionClass Zero-Inflated Poisson distribution class. Distributional Parameters rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 Parameters stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZIPoisson.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class ZIPoisson ( DistributionClass ): \"\"\" Zero-Inflated Poisson distribution class. Distributional Parameters ------------------------- rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"relu\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Set the parameters specific to the distribution distribution = ZeroInflatedPoisson_Torch param_dict = { \"rate\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn ) distribution_utils DistributionClass Generic class that contains general functions for univariate distributions. Arguments distribution: torch.distributions.Distribution PyTorch Distribution class. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. tau: List List of expectiles. Only used for Expectile distributon. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. Only used for Expectile distribution. Source code in lightgbmlss/distributions/distribution_utils.py 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 class DistributionClass : \"\"\" Generic class that contains general functions for univariate distributions. Arguments --------- distribution: torch.distributions.Distribution PyTorch Distribution class. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. tau: List List of expectiles. Only used for Expectile distributon. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. Only used for Expectile distribution. \"\"\" def __init__ ( self , distribution : torch . distributions . Distribution = None , univariate : bool = True , discrete : bool = False , n_dist_param : int = None , stabilization : str = \"None\" , param_dict : Dict [ str , Any ] = None , distribution_arg_names : List = None , loss_fn : str = \"nll\" , tau : Optional [ List [ torch . Tensor ]] = None , penalize_crossing : bool = False , ): self . distribution = distribution self . univariate = univariate self . discrete = discrete self . n_dist_param = n_dist_param self . stabilization = stabilization self . param_dict = param_dict self . distribution_arg_names = distribution_arg_names self . loss_fn = loss_fn self . tau = tau self . penalize_crossing = penalize_crossing def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Transform parameters to response scale params = [ response_fn ( params [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) # Specify Distribution and Loss if self . tau is None : dist = self . distribution ( * params ) loss = - torch . nansum ( dist . log_prob ( target )) else : dist = self . distribution ( params , self . penalize_crossing ) loss = - torch . nansum ( dist . log_prob ( target , self . tau )) return loss def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] for epoch in range ( max_iter ): loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = [ torch . tensor ( predt [:, i ] . reshape ( - 1 , 1 ), requires_grad = requires_grad ) for i in range ( self . n_dist_param ) ] # Predicted Parameters transformed to response scale predt_transformed = [ response_fn ( predt [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Specify Distribution and Loss if self . tau is None : dist_kwargs = dict ( zip ( self . distribution_arg_names , predt_transformed )) dist_fit = self . distribution ( ** dist_kwargs ) if self . loss_fn == \"nll\" : loss = - torch . nansum ( dist_fit . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = dist_fit . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) else : dist_fit = self . distribution ( predt_transformed , self . penalize_crossing ) loss = - torch . nansum ( dist_fit . log_prob ( target , self . tau )) return predt , loss def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) if self . tau is None : pred_params = torch . tensor ( predt_params . values ) dist_kwargs = { arg_name : param for arg_name , param in zip ( self . distribution_arg_names , pred_params . T )} dist_pred = self . distribution ( ** dist_kwargs ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] else : dist_samples = None if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = np . concatenate ( [ response_fun ( predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( self . param_dict . items ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"expectiles\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # # Approximation of Hessian # step_size = 1e-6 # predt_upper = [ # response_fn(predt[i] + step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_upper = dict(zip(self.distribution_arg_names, predt_upper)) # dist_fit_upper = self.distribution(**dist_kwargs_upper) # dist_samples_upper = dist_fit_upper.rsample((30,)).squeeze(-1) # loss_upper = torch.nansum(self.crps_score(self.target, dist_samples_upper)) # # predt_lower = [ # response_fn(predt[i] - step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_lower = dict(zip(self.distribution_arg_names, predt_lower)) # dist_fit_lower = self.distribution(**dist_kwargs_lower) # dist_samples_lower = dist_fit_lower.rsample((30,)).squeeze(-1) # loss_lower = torch.nansum(self.crps_score(self.target, dist_samples_lower)) # # grad_upper = autograd(loss_upper, inputs=predt_upper) # grad_lower = autograd(loss_lower, inputs=predt_lower) # hess = [(grad_upper[i] - grad_lower[i]) / (2 * step_size) for i in range(len(grad))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Parameters ---------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns ------- crps: torch.Tensor CRPS score. References ---------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source ------ https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . __name__ . split ( \".\" )[ 2 ] pbar . set_description ( f \"Fitting { dist_name } distribution\" ) dist_sel = getattr ( candidate_distributions [ i ], dist_name )() try : loss , params = dist_sel . calculate_start_values ( target = target . reshape ( - 1 , 1 ), max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { self . loss_fn : loss . reshape ( - 1 ,), \"distribution\" : str ( dist_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { self . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = self . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ self . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for dist in candidate_distributions : if dist . __name__ . split ( \".\" )[ 2 ] == best_dist [ \"distribution\" ] . values [ 0 ]: best_dist_sel = dist break best_dist_sel = getattr ( best_dist_sel , best_dist [ \"distribution\" ] . values [ 0 ])() params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( - 1 , best_dist_sel . n_dist_param ) # Transform parameters to the response scale and draw samples fitted_params = np . concatenate ( [ response_fun ( params [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . param_dict . keys ()) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df calculate_start_values ( target , max_iter = 50 ) Function that calculates the starting values for each distributional parameter. Arguments target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. lightgbmlss/distributions/distribution_utils.py 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] for epoch in range ( max_iter ): loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values compute_gradients_and_hessians ( loss , predt , weights ) Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. lightgbmlss/distributions/distribution_utils.py 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # # Approximation of Hessian # step_size = 1e-6 # predt_upper = [ # response_fn(predt[i] + step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_upper = dict(zip(self.distribution_arg_names, predt_upper)) # dist_fit_upper = self.distribution(**dist_kwargs_upper) # dist_samples_upper = dist_fit_upper.rsample((30,)).squeeze(-1) # loss_upper = torch.nansum(self.crps_score(self.target, dist_samples_upper)) # # predt_lower = [ # response_fn(predt[i] - step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_lower = dict(zip(self.distribution_arg_names, predt_lower)) # dist_fit_lower = self.distribution(**dist_kwargs_lower) # dist_samples_lower = dist_fit_lower.rsample((30,)).squeeze(-1) # loss_lower = torch.nansum(self.crps_score(self.target, dist_samples_lower)) # # grad_upper = autograd(loss_upper, inputs=predt_upper) # grad_lower = autograd(loss_lower, inputs=predt_lower) # hess = [(grad_upper[i] - grad_lower[i]) / (2 * step_size) for i in range(len(grad))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess crps_score ( y , yhat_dist ) Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Parameters y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns crps: torch.Tensor CRPS score. References Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 lightgbmlss/distributions/distribution_utils.py 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Parameters ---------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns ------- crps: torch.Tensor CRPS score. References ---------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source ------ https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps dist_select ( target , candidate_distributions , max_iter = 100 , plot = False , figure_size = ( 10 , 5 )) Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. lightgbmlss/distributions/distribution_utils.py 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . __name__ . split ( \".\" )[ 2 ] pbar . set_description ( f \"Fitting { dist_name } distribution\" ) dist_sel = getattr ( candidate_distributions [ i ], dist_name )() try : loss , params = dist_sel . calculate_start_values ( target = target . reshape ( - 1 , 1 ), max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { self . loss_fn : loss . reshape ( - 1 ,), \"distribution\" : str ( dist_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { self . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = self . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ self . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for dist in candidate_distributions : if dist . __name__ . split ( \".\" )[ 2 ] == best_dist [ \"distribution\" ] . values [ 0 ]: best_dist_sel = dist break best_dist_sel = getattr ( best_dist_sel , best_dist [ \"distribution\" ] . values [ 0 ])() params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( - 1 , best_dist_sel . n_dist_param ) # Transform parameters to the response scale and draw samples fitted_params = np . concatenate ( [ response_fun ( params [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . param_dict . keys ()) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df draw_samples ( predt_params , n_samples = 1000 , seed = 123 ) Function that draws n_samples from a predicted distribution. Arguments predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. lightgbmlss/distributions/distribution_utils.py 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) if self . tau is None : pred_params = torch . tensor ( predt_params . values ) dist_kwargs = { arg_name : param for arg_name , param in zip ( self . distribution_arg_names , pred_params . T )} dist_pred = self . distribution ( ** dist_kwargs ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] else : dist_samples = None if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples get_params_loss ( predt , target , start_values , requires_grad = False ) Function that returns the predicted parameters and the loss. Arguments predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. lightgbmlss/distributions/distribution_utils.py 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = [ torch . tensor ( predt [:, i ] . reshape ( - 1 , 1 ), requires_grad = requires_grad ) for i in range ( self . n_dist_param ) ] # Predicted Parameters transformed to response scale predt_transformed = [ response_fn ( predt [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Specify Distribution and Loss if self . tau is None : dist_kwargs = dict ( zip ( self . distribution_arg_names , predt_transformed )) dist_fit = self . distribution ( ** dist_kwargs ) if self . loss_fn == \"nll\" : loss = - torch . nansum ( dist_fit . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = dist_fit . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) else : dist_fit = self . distribution ( predt_transformed , self . penalize_crossing ) loss = - torch . nansum ( dist_fit . log_prob ( target , self . tau )) return predt , loss loss_fn_start_values ( params , target ) Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns loss: torch.Tensor Loss value. lightgbmlss/distributions/distribution_utils.py 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Transform parameters to response scale params = [ response_fn ( params [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) # Specify Distribution and Loss if self . tau is None : dist = self . distribution ( * params ) loss = - torch . nansum ( dist . log_prob ( target )) else : dist = self . distribution ( params , self . penalize_crossing ) loss = - torch . nansum ( dist . log_prob ( target , self . tau )) return loss metric_fn ( predt , data ) Function that evaluates the predictions using the negative log-likelihood. Arguments predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. lightgbmlss/distributions/distribution_utils.py 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better objective_fn ( predt , data ) Function to estimate gradients and hessians of distributional parameters. Arguments predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns grad: np.ndarray Gradient. hess: np.ndarray Hessian. lightgbmlss/distributions/distribution_utils.py 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess predict_dist ( booster , data , start_values , pred_type = 'parameters' , n_samples = 1000 , quantiles = [ 0.1 , 0.5 , 0.9 ], seed = 123 ) Function that predicts from the trained model. Arguments booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns pred : pd.DataFrame Predictions. lightgbmlss/distributions/distribution_utils.py 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = np . concatenate ( [ response_fun ( predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( self . param_dict . items ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"expectiles\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df stabilize_derivative ( input_der , type = 'MAD' ) Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns stab_der : torch.Tensor Stabilized Gradient or Hessian. lightgbmlss/distributions/distribution_utils.py 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der flow_utils NormalizingFlowClass Generic class that contains general functions for normalizing flows. Arguments base_dist: torch.distributions.Distribution PyTorch Distribution class. Currently only Normal is supported. flow_transform: Transform Specify the normalizing flow transform. count_bins: Optional[int] The number of segments comprising the spline. Only used if flow_transform is Spline. bound: Optional[float] The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. Only used if flow_transform is Spline. order: Optional[str] The order of the spline. Options are \"linear\" or \"quadratic\". Only used if flow_transform is Spline. n_dist_param: int Number of parameters. param_dict: Dict[str, Any] Dictionary that maps parameters to their response scale. target_transform: Transform Specify the target transform. discrete: bool Whether the target is discrete or not. univariate: bool Whether the distribution is univariate or multivariate. stabilization: str Stabilization method. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/flow_utils.py 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 class NormalizingFlowClass : \"\"\" Generic class that contains general functions for normalizing flows. Arguments --------- base_dist: torch.distributions.Distribution PyTorch Distribution class. Currently only Normal is supported. flow_transform: Transform Specify the normalizing flow transform. count_bins: Optional[int] The number of segments comprising the spline. Only used if flow_transform is Spline. bound: Optional[float] The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. Only used if flow_transform is Spline. order: Optional[str] The order of the spline. Options are \"linear\" or \"quadratic\". Only used if flow_transform is Spline. n_dist_param: int Number of parameters. param_dict: Dict[str, Any] Dictionary that maps parameters to their response scale. target_transform: Transform Specify the target transform. discrete: bool Whether the target is discrete or not. univariate: bool Whether the distribution is univariate or multivariate. stabilization: str Stabilization method. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , base_dist : torch . distributions . Distribution = None , flow_transform : Transform = None , count_bins : Optional [ int ] = 8 , bound : Optional [ float ] = 3.0 , order : Optional [ str ] = \"quadratic\" , n_dist_param : int = None , param_dict : Dict [ str , Any ] = None , target_transform : Transform = None , discrete : bool = False , univariate : bool = True , stabilization : str = \"None\" , loss_fn : str = \"nll\" , ): self . base_dist = base_dist self . flow_transform = flow_transform self . count_bins = count_bins self . bound = bound self . order = order self . n_dist_param = n_dist_param self . param_dict = param_dict self . target_transform = target_transform self . discrete = discrete self . univariate = univariate self . stabilization = stabilization self . loss_fn = loss_fn def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of normalizing flow parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the specified loss function. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. loss: float Loss value. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values ) return self . loss_fn , loss . detach (), is_higher_better def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates starting values for each parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Create Normalizing Flow flow_dist = self . create_spline_flow ( input_dim = 1 ) # Specify optimizer optimizer = LBFGS ( flow_dist . transforms [ 0 ] . parameters (), lr = 0.3 , max_iter = np . min ([ int ( max_iter / 4 ), 50 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 5 ) # Define closure def closure (): optimizer . zero_grad () loss = - torch . nansum ( flow_dist . log_prob ( target )) loss . backward () flow_dist . clear_cache () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 # Tolerance level for loss change patience = 5 # Patience level for loss change best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = list ( flow_dist . transforms [ 0 ] . parameters ()) start_values = torch . cat ([ param . view ( - 1 ) for param in start_values ]) . detach () . numpy () # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each parameter. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Reshape Target target = target . view ( - 1 ) # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = torch . tensor ( predt , dtype = torch . float32 ) # Specify Normalizing Flow flow_dist = self . create_spline_flow ( target . shape [ 0 ]) # Replace parameters with estimated ones params , flow_dist = self . replace_parameters ( predt , flow_dist ) # Calculate loss if self . loss_fn == \"nll\" : loss = - torch . nansum ( flow_dist . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = flow_dist . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) return params , loss def create_spline_flow ( self , input_dim : int = None , ) -> Transform : \"\"\" Function that constructs a Normalizing Flow. Arguments --------- input_dim: int Input dimension. Returns ------- spline_flow: Transform Normalizing Flow. \"\"\" # Create flow distribution (currently only Normal) loc , scale = torch . zeros ( input_dim ), torch . ones ( input_dim ) flow_dist = self . base_dist ( loc , scale ) # Create Spline Transform torch . manual_seed ( 123 ) spline_transform = self . flow_transform ( input_dim , count_bins = self . count_bins , bound = self . bound , order = self . order ) # Create Normalizing Flow spline_flow = TransformedDistribution ( flow_dist , [ spline_transform , self . target_transform ]) return spline_flow def replace_parameters ( self , params : torch . Tensor , flow_dist : Transform , ) -> Tuple [ List , Transform ]: \"\"\" Replace parameters with estimated ones. Arguments --------- params: torch.Tensor Estimated parameters. flow_dist: Transform Normalizing Flow. Returns ------- params_list: List List of estimated parameters. flow_dist: Transform Normalizing Flow with estimated parameters. \"\"\" # Split parameters into list if self . order == \"quadratic\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 ], dim = 1 ) elif self . order == \"linear\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 , self . count_bins ], dim = 1 ) # Replace parameters for param , new_value in zip ( flow_dist . transforms [ 0 ] . parameters (), params_list ): param . data = new_value # Get parameters (including require_grad=True) params_list = list ( flow_dist . transforms [ 0 ] . parameters ()) return params_list , flow_dist def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) # Specify Normalizing Flow pred_params = torch . tensor ( predt_params . values ) flow_dist_pred = self . create_spline_flow ( pred_params . shape [ 0 ]) # Replace parameters with estimated ones _ , flow_dist_pred = self . replace_parameters ( pred_params , flow_dist_pred ) # Draw samples flow_samples = pd . DataFrame ( flow_dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) flow_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( flow_samples . shape [ 1 ])] if self . discrete : flow_samples = flow_samples . astype ( int ) return flow_samples def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. start_values : np.ndarray Starting values for each distributional parameter. data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" # Predict raw scores predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. Hence, it needs to be # added manually. dist_params_predt = pd . DataFrame ( np . concatenate ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param )], axis = 1 ) ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source --------- https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Arguments --------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns --------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Arguments --------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns --------- crps: torch.Tensor CRPS score. References --------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source --------- https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps def flow_select ( self , target : np . ndarray , candidate_flows : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable normalizing flow specification among the candidate_flow for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_flows: List List of candidate normalizing flow specifications. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted normalizing flow. \"\"\" flow_list = [] total_iterations = len ( candidate_flows ) with tqdm ( total = total_iterations , desc = \"Fitting candidate normalizing flows\" ) as pbar : for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec pbar . set_description ( f \"Fitting { flow_name } \" ) flow_sel = flow try : loss , params = flow_sel . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { flow_sel . loss_fn : loss . reshape ( - 1 , ), \"NormFlow\" : str ( flow_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { flow_sel } NormFlow: { str ( e ) } \" ) fit_df = pd . DataFrame ( { flow_sel . loss_fn : np . nan , \"NormFlow\" : str ( flow_sel ), \"params\" : [ np . nan ] * flow_sel . n_dist_param } ) flow_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate normalizing flows completed\" ) fit_df = pd . concat ( flow_list ) . sort_values ( by = flow_sel . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ flow_sel . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select normalizing flow with the lowest loss best_flow = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec if flow_name == best_flow [ \"NormFlow\" ] . values [ 0 ]: best_flow_sel = flow break # Draw samples from distribution flow_params = torch . tensor ( best_flow [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) flow_dist_sel = best_flow_sel . create_spline_flow ( input_dim = 1 ) _ , flow_dist_sel = best_flow_sel . replace_parameters ( flow_params , flow_dist_sel ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) flow_samples = pd . DataFrame ( flow_dist_sel . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( flow_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_flow [ 'NormFlow' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df calculate_start_values ( target , max_iter = 50 ) Function that calculates starting values for each parameter. Arguments target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns loss: float Loss value. start_values: np.ndarray Starting values for each parameter. lightgbmlss/distributions/flow_utils.py 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates starting values for each parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Create Normalizing Flow flow_dist = self . create_spline_flow ( input_dim = 1 ) # Specify optimizer optimizer = LBFGS ( flow_dist . transforms [ 0 ] . parameters (), lr = 0.3 , max_iter = np . min ([ int ( max_iter / 4 ), 50 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 5 ) # Define closure def closure (): optimizer . zero_grad () loss = - torch . nansum ( flow_dist . log_prob ( target )) loss . backward () flow_dist . clear_cache () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 # Tolerance level for loss change patience = 5 # Patience level for loss change best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = list ( flow_dist . transforms [ 0 ] . parameters ()) start_values = torch . cat ([ param . view ( - 1 ) for param in start_values ]) . detach () . numpy () # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values compute_gradients_and_hessians ( loss , predt , weights ) Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. lightgbmlss/distributions/flow_utils.py 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess create_spline_flow ( input_dim = None ) Function that constructs a Normalizing Flow. Arguments input_dim: int Input dimension. Returns spline_flow: Transform Normalizing Flow. lightgbmlss/distributions/flow_utils.py 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 def create_spline_flow ( self , input_dim : int = None , ) -> Transform : \"\"\" Function that constructs a Normalizing Flow. Arguments --------- input_dim: int Input dimension. Returns ------- spline_flow: Transform Normalizing Flow. \"\"\" # Create flow distribution (currently only Normal) loc , scale = torch . zeros ( input_dim ), torch . ones ( input_dim ) flow_dist = self . base_dist ( loc , scale ) # Create Spline Transform torch . manual_seed ( 123 ) spline_transform = self . flow_transform ( input_dim , count_bins = self . count_bins , bound = self . bound , order = self . order ) # Create Normalizing Flow spline_flow = TransformedDistribution ( flow_dist , [ spline_transform , self . target_transform ]) return spline_flow crps_score ( y , yhat_dist ) Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Arguments y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns crps: torch.Tensor CRPS score. References Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 lightgbmlss/distributions/flow_utils.py 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Arguments --------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns --------- crps: torch.Tensor CRPS score. References --------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source --------- https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps draw_samples ( predt_params , n_samples = 1000 , seed = 123 ) Function that draws n_samples from a predicted distribution. Arguments predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. lightgbmlss/distributions/flow_utils.py 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) # Specify Normalizing Flow pred_params = torch . tensor ( predt_params . values ) flow_dist_pred = self . create_spline_flow ( pred_params . shape [ 0 ]) # Replace parameters with estimated ones _ , flow_dist_pred = self . replace_parameters ( pred_params , flow_dist_pred ) # Draw samples flow_samples = pd . DataFrame ( flow_dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) flow_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( flow_samples . shape [ 1 ])] if self . discrete : flow_samples = flow_samples . astype ( int ) return flow_samples flow_select ( target , candidate_flows , max_iter = 100 , plot = False , figure_size = ( 10 , 5 )) Function that selects the most suitable normalizing flow specification among the candidate_flow for the target variable, based on the NegLogLikelihood (lower is better). Parameters target: np.ndarray Response variable. candidate_flows: List List of candidate normalizing flow specifications. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns fit_df: pd.DataFrame Dataframe with the loss values of the fitted normalizing flow. lightgbmlss/distributions/flow_utils.py 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 def flow_select ( self , target : np . ndarray , candidate_flows : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable normalizing flow specification among the candidate_flow for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_flows: List List of candidate normalizing flow specifications. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted normalizing flow. \"\"\" flow_list = [] total_iterations = len ( candidate_flows ) with tqdm ( total = total_iterations , desc = \"Fitting candidate normalizing flows\" ) as pbar : for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec pbar . set_description ( f \"Fitting { flow_name } \" ) flow_sel = flow try : loss , params = flow_sel . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { flow_sel . loss_fn : loss . reshape ( - 1 , ), \"NormFlow\" : str ( flow_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { flow_sel } NormFlow: { str ( e ) } \" ) fit_df = pd . DataFrame ( { flow_sel . loss_fn : np . nan , \"NormFlow\" : str ( flow_sel ), \"params\" : [ np . nan ] * flow_sel . n_dist_param } ) flow_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate normalizing flows completed\" ) fit_df = pd . concat ( flow_list ) . sort_values ( by = flow_sel . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ flow_sel . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select normalizing flow with the lowest loss best_flow = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec if flow_name == best_flow [ \"NormFlow\" ] . values [ 0 ]: best_flow_sel = flow break # Draw samples from distribution flow_params = torch . tensor ( best_flow [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) flow_dist_sel = best_flow_sel . create_spline_flow ( input_dim = 1 ) _ , flow_dist_sel = best_flow_sel . replace_parameters ( flow_params , flow_dist_sel ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) flow_samples = pd . DataFrame ( flow_dist_sel . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( flow_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_flow [ 'NormFlow' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df get_params_loss ( predt , target , start_values , requires_grad = False ) Function that returns the predicted parameters and the loss. Arguments predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each parameter. Returns predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. lightgbmlss/distributions/flow_utils.py 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each parameter. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Reshape Target target = target . view ( - 1 ) # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = torch . tensor ( predt , dtype = torch . float32 ) # Specify Normalizing Flow flow_dist = self . create_spline_flow ( target . shape [ 0 ]) # Replace parameters with estimated ones params , flow_dist = self . replace_parameters ( predt , flow_dist ) # Calculate loss if self . loss_fn == \"nll\" : loss = - torch . nansum ( flow_dist . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = flow_dist . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) return params , loss metric_fn ( predt , data ) Function that evaluates the predictions using the specified loss function. Arguments predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns name: str Name of the evaluation metric. loss: float Loss value. lightgbmlss/distributions/flow_utils.py 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the specified loss function. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. loss: float Loss value. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values ) return self . loss_fn , loss . detach (), is_higher_better objective_fn ( predt , data ) Function to estimate gradients and hessians of normalizing flow parameters. Arguments predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns grad: np.ndarray Gradient. hess: np.ndarray Hessian. lightgbmlss/distributions/flow_utils.py 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of normalizing flow parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess predict_dist ( booster , data , start_values , pred_type = 'parameters' , n_samples = 1000 , quantiles = [ 0.1 , 0.5 , 0.9 ], seed = 123 ) Function that predicts from the trained model. Arguments booster : lgb.Booster Trained model. start_values : np.ndarray Starting values for each distributional parameter. data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns pred : pd.DataFrame Predictions. lightgbmlss/distributions/flow_utils.py 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. start_values : np.ndarray Starting values for each distributional parameter. data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" # Predict raw scores predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. Hence, it needs to be # added manually. dist_params_predt = pd . DataFrame ( np . concatenate ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param )], axis = 1 ) ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df replace_parameters ( params , flow_dist ) Replace parameters with estimated ones. Arguments params: torch.Tensor Estimated parameters. flow_dist: Transform Normalizing Flow. Returns params_list: List List of estimated parameters. flow_dist: Transform Normalizing Flow with estimated parameters. lightgbmlss/distributions/flow_utils.py 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 def replace_parameters ( self , params : torch . Tensor , flow_dist : Transform , ) -> Tuple [ List , Transform ]: \"\"\" Replace parameters with estimated ones. Arguments --------- params: torch.Tensor Estimated parameters. flow_dist: Transform Normalizing Flow. Returns ------- params_list: List List of estimated parameters. flow_dist: Transform Normalizing Flow with estimated parameters. \"\"\" # Split parameters into list if self . order == \"quadratic\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 ], dim = 1 ) elif self . order == \"linear\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 , self . count_bins ], dim = 1 ) # Replace parameters for param , new_value in zip ( flow_dist . transforms [ 0 ] . parameters (), params_list ): param . data = new_value # Get parameters (including require_grad=True) params_list = list ( flow_dist . transforms [ 0 ] . parameters ()) return params_list , flow_dist stabilize_derivative ( input_der , type = 'MAD' ) Function that stabilizes Gradients and Hessians. Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Arguments input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns stab_der : torch.Tensor Stabilized Gradient or Hessian. lightgbmlss/distributions/flow_utils.py 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source --------- https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Arguments --------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns --------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der mixture_distribution_utils MixtureDistributionClass Generic class that contains general functions for mixed-density distributions. Arguments distribution: torch.distributions.Distribution PyTorch Distribution class. M: int Number of components in the mixture distribution. temperature: float Temperature for the Gumbel-Softmax distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, when the Hessian is calculated for each gradient element, this corresponds to the second derivative with respect to that specific tensor element only. This means the resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can also be more accurate. - \"grouped\": Each parameter is a tensor containing all values for a specific parameter type, e.g., loc, scale, or mixture probabilities for a Gaussian Mixture. When computing the Hessian for each gradient element, the Hessian matrix for all the values in the respective tensor are calculated together. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter type tensor. This is usually less runtime intensive, but can be less accurate. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/mixture_distribution_utils.py 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 class MixtureDistributionClass : \"\"\" Generic class that contains general functions for mixed-density distributions. Arguments --------- distribution: torch.distributions.Distribution PyTorch Distribution class. M: int Number of components in the mixture distribution. temperature: float Temperature for the Gumbel-Softmax distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, when the Hessian is calculated for each gradient element, this corresponds to the second derivative with respect to that specific tensor element only. This means the resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can also be more accurate. - \"grouped\": Each parameter is a tensor containing all values for a specific parameter type, e.g., loc, scale, or mixture probabilities for a Gaussian Mixture. When computing the Hessian for each gradient element, the Hessian matrix for all the values in the respective tensor are calculated together. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter type tensor. This is usually less runtime intensive, but can be less accurate. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , distribution : torch . distributions . Distribution = None , M : int = 2 , temperature : float = 1.0 , hessian_mode : str = \"individual\" , univariate : bool = True , discrete : bool = False , n_dist_param : int = None , stabilization : str = \"None\" , param_dict : Dict [ str , Any ] = None , distribution_arg_names : List = None , loss_fn : str = \"nll\" , ): self . distribution = distribution self . M = M self . temperature = temperature self . hessian_mode = hessian_mode self . univariate = univariate self . discrete = discrete self . n_dist_param = n_dist_param self . stabilization = stabilization self . param_dict = param_dict self . distribution_arg_names = distribution_arg_names self . loss_fn = loss_fn def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = np . ones_like ( target , dtype = \"float32\" ) else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better def create_mixture_distribution ( self , params : List [ torch . Tensor ], ) -> torch . distributions . Distribution : \"\"\" Function that creates a mixture distribution. Arguments --------- params: torch.Tensor Distributional parameters. Returns ------- dist: torch.distributions.Distribution Mixture distribution. \"\"\" # Create Mixture Distribution mixture_cat = Categorical ( probs = params [ - 1 ]) mixture_comp = self . distribution . distribution ( * params [: - 1 ]) mixture_dist = MixtureSameFamily ( mixture_cat , mixture_comp ) return mixture_dist def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) . reshape ( 1 , - 1 ) params = torch . split ( params , self . M , dim = 1 ) # Transform parameters to response scale params = [ response_fn ( params [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] # Specify Distribution and Loss dist = self . create_mixture_distribution ( params ) loss = - torch . nansum ( dist . log_prob ( target )) return loss def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target , dtype = torch . float32 ) . flatten () # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 patience = 5 best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) if self . hessian_mode == \"grouped\" : # Convert to torch.Tensor: splits the parameters into tensors for each parameter-type predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), self . M , dim = 1 ) # Transform parameters to response scale predt_transformed = [ response_fn ( predt [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] else : # Convert to torch.Tensor: splits the parameters into tensors for each parameter individually predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), 1 , dim = 1 ) # Transform parameters to response scale keys = list ( self . param_dict . keys ()) max_index = len ( self . param_dict ) * self . M index_ranges = [] for i in range ( 0 , max_index , self . M ): if i + self . M >= max_index : index_ranges . append (( i , None )) break index_ranges . append (( i , i + self . M )) predt_transformed = [ self . param_dict [ key ]( torch . cat ( predt [ start : end ], dim = 1 )) for key , ( start , end ) in zip ( keys , index_ranges ) ] # Specify Distribution and Loss dist_fit = self . create_mixture_distribution ( predt_transformed ) loss = - torch . nansum ( dist_fit . log_prob ( target )) return predt , loss def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) pred_params = torch . tensor ( predt_params . values ) . reshape ( - 1 , self . n_dist_param ) pred_params = torch . split ( pred_params , self . M , dim = 1 ) dist_pred = self . create_mixture_distribution ( pred_params ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = torch . split ( torch . cat ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param ) ], axis = 1 ), self . M , dim = 1 ) dist_params_predt = np . concatenate ( [ response_fn ( dist_params_predt [ i ]) . numpy () for i , response_fn in enumerate ( self . param_dict . values ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . distribution_arg_names # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) return stab_der def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 8 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . distribution . __class__ . __name__ n_mix = candidate_distributions [ i ] . M tau = candidate_distributions [ i ] . temperature dist_name = f \"Mixture( { dist_name } , tau= { tau } , M= { n_mix } )\" pbar . set_description ( f \"Fitting { dist_name } distribution\" ) try : loss , params = candidate_distributions [ i ] . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { candidate_distributions [ i ] . loss_fn : loss . reshape ( - 1 , ), \"distribution\" : str ( dist_name ), \"params\" : [ params ], \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { candidate_distributions [ i ] . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param , \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = candidate_distributions [ i ] . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ candidate_distributions [ i ] . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == fit_df [ \"rank\" ] . min ()] . reset_index ( drop = True ) . iloc [[ 0 ]] best_dist_pos = int ( best_dist [ \"dist_pos\" ] . values [ 0 ]) best_dist_sel = candidate_distributions [ best_dist_pos ] params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) params = torch . split ( params , best_dist_sel . M , dim = 1 ) fitted_params = np . concatenate ( [ response_fun ( params [ i ]) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . distribution_arg_names ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 ,), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 ,), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" , \"dist_pos\" , \"M\" ], inplace = True ) return fit_df calculate_start_values ( target , max_iter = 50 ) Function that calculates the starting values for each distributional parameter. Arguments target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. lightgbmlss/distributions/mixture_distribution_utils.py 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target , dtype = torch . float32 ) . flatten () # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 patience = 5 best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values compute_gradients_and_hessians ( loss , predt , weights ) Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. lightgbmlss/distributions/mixture_distribution_utils.py 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess create_mixture_distribution ( params ) Function that creates a mixture distribution. Arguments params: torch.Tensor Distributional parameters. Returns dist: torch.distributions.Distribution Mixture distribution. lightgbmlss/distributions/mixture_distribution_utils.py 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 def create_mixture_distribution ( self , params : List [ torch . Tensor ], ) -> torch . distributions . Distribution : \"\"\" Function that creates a mixture distribution. Arguments --------- params: torch.Tensor Distributional parameters. Returns ------- dist: torch.distributions.Distribution Mixture distribution. \"\"\" # Create Mixture Distribution mixture_cat = Categorical ( probs = params [ - 1 ]) mixture_comp = self . distribution . distribution ( * params [: - 1 ]) mixture_dist = MixtureSameFamily ( mixture_cat , mixture_comp ) return mixture_dist dist_select ( target , candidate_distributions , max_iter = 100 , plot = False , figure_size = ( 8 , 5 )) Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. lightgbmlss/distributions/mixture_distribution_utils.py 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 8 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . distribution . __class__ . __name__ n_mix = candidate_distributions [ i ] . M tau = candidate_distributions [ i ] . temperature dist_name = f \"Mixture( { dist_name } , tau= { tau } , M= { n_mix } )\" pbar . set_description ( f \"Fitting { dist_name } distribution\" ) try : loss , params = candidate_distributions [ i ] . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { candidate_distributions [ i ] . loss_fn : loss . reshape ( - 1 , ), \"distribution\" : str ( dist_name ), \"params\" : [ params ], \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { candidate_distributions [ i ] . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param , \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = candidate_distributions [ i ] . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ candidate_distributions [ i ] . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == fit_df [ \"rank\" ] . min ()] . reset_index ( drop = True ) . iloc [[ 0 ]] best_dist_pos = int ( best_dist [ \"dist_pos\" ] . values [ 0 ]) best_dist_sel = candidate_distributions [ best_dist_pos ] params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) params = torch . split ( params , best_dist_sel . M , dim = 1 ) fitted_params = np . concatenate ( [ response_fun ( params [ i ]) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . distribution_arg_names ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 ,), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 ,), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" , \"dist_pos\" , \"M\" ], inplace = True ) return fit_df draw_samples ( predt_params , n_samples = 1000 , seed = 123 ) Function that draws n_samples from a predicted distribution. Arguments predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. lightgbmlss/distributions/mixture_distribution_utils.py 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) pred_params = torch . tensor ( predt_params . values ) . reshape ( - 1 , self . n_dist_param ) pred_params = torch . split ( pred_params , self . M , dim = 1 ) dist_pred = self . create_mixture_distribution ( pred_params ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples get_params_loss ( predt , target , start_values , requires_grad = False ) Function that returns the predicted parameters and the loss. Arguments predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. lightgbmlss/distributions/mixture_distribution_utils.py 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) if self . hessian_mode == \"grouped\" : # Convert to torch.Tensor: splits the parameters into tensors for each parameter-type predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), self . M , dim = 1 ) # Transform parameters to response scale predt_transformed = [ response_fn ( predt [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] else : # Convert to torch.Tensor: splits the parameters into tensors for each parameter individually predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), 1 , dim = 1 ) # Transform parameters to response scale keys = list ( self . param_dict . keys ()) max_index = len ( self . param_dict ) * self . M index_ranges = [] for i in range ( 0 , max_index , self . M ): if i + self . M >= max_index : index_ranges . append (( i , None )) break index_ranges . append (( i , i + self . M )) predt_transformed = [ self . param_dict [ key ]( torch . cat ( predt [ start : end ], dim = 1 )) for key , ( start , end ) in zip ( keys , index_ranges ) ] # Specify Distribution and Loss dist_fit = self . create_mixture_distribution ( predt_transformed ) loss = - torch . nansum ( dist_fit . log_prob ( target )) return predt , loss loss_fn_start_values ( params , target ) Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns loss: torch.Tensor Loss value. lightgbmlss/distributions/mixture_distribution_utils.py 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) . reshape ( 1 , - 1 ) params = torch . split ( params , self . M , dim = 1 ) # Transform parameters to response scale params = [ response_fn ( params [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] # Specify Distribution and Loss dist = self . create_mixture_distribution ( params ) loss = - torch . nansum ( dist . log_prob ( target )) return loss metric_fn ( predt , data ) Function that evaluates the predictions using the negative log-likelihood. Arguments predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. lightgbmlss/distributions/mixture_distribution_utils.py 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better objective_fn ( predt , data ) Function to estimate gradients and hessians of distributional parameters. Arguments predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns grad: np.ndarray Gradient. hess: np.ndarray Hessian. lightgbmlss/distributions/mixture_distribution_utils.py 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = np . ones_like ( target , dtype = \"float32\" ) else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess predict_dist ( booster , data , start_values , pred_type = 'parameters' , n_samples = 1000 , quantiles = [ 0.1 , 0.5 , 0.9 ], seed = 123 ) Function that predicts from the trained model. Arguments booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns pred : pd.DataFrame Predictions. lightgbmlss/distributions/mixture_distribution_utils.py 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = torch . split ( torch . cat ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param ) ], axis = 1 ), self . M , dim = 1 ) dist_params_predt = np . concatenate ( [ response_fn ( dist_params_predt [ i ]) . numpy () for i , response_fn in enumerate ( self . param_dict . values ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . distribution_arg_names # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df stabilize_derivative ( input_der , type = 'MAD' ) Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns stab_der : torch.Tensor Stabilized Gradient or Hessian. lightgbmlss/distributions/mixture_distribution_utils.py 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) return stab_der get_component_distributions () Function that returns component distributions for creating a mixing distribution. Arguments None Returns distns: List List of all available distributions. lightgbmlss/distributions/mixture_distribution_utils.py 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 def get_component_distributions (): \"\"\" Function that returns component distributions for creating a mixing distribution. Arguments --------- None Returns ------- distns: List List of all available distributions. \"\"\" # Get all distribution names mixture_distns = [ dist for dist in dir ( distributions ) if dist [ 0 ] . isupper ()] # Remove specific distributions distns_remove = [ \"Expectile\" , \"Mixture\" , \"SplineFlow\" ] mixture_distns = [ item for item in mixture_distns if item not in distns_remove ] return mixture_distns zero_inflated ZeroAdjustedBeta Bases: ZeroInflatedDistribution A Zero-Adjusted Beta distribution. Parameter concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Source code in lightgbmlss/distributions/zero_inflated.py 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 class ZeroAdjustedBeta ( ZeroInflatedDistribution ): \"\"\" A Zero-Adjusted Beta distribution. Parameter --------- concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py \"\"\" arg_constraints = { \"concentration1\" : constraints . positive , \"concentration0\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . unit_interval def __init__ ( self , concentration1 , concentration0 , gate = None , validate_args = None ): base_dist = Beta ( concentration1 = concentration1 , concentration0 = concentration0 , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def concentration1 ( self ): return self . base_dist . concentration1 @property def concentration0 ( self ): return self . base_dist . concentration0 ZeroAdjustedGamma Bases: ZeroInflatedDistribution A Zero-Adjusted Gamma distribution. Parameter concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Source code in lightgbmlss/distributions/zero_inflated.py 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 class ZeroAdjustedGamma ( ZeroInflatedDistribution ): \"\"\" A Zero-Adjusted Gamma distribution. Parameter --------- concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py \"\"\" arg_constraints = { \"concentration\" : constraints . positive , \"rate\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative def __init__ ( self , concentration , rate , gate = None , validate_args = None ): base_dist = Gamma ( concentration = concentration , rate = rate , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def concentration ( self ): return self . base_dist . concentration @property def rate ( self ): return self . base_dist . rate ZeroAdjustedLogNormal Bases: ZeroInflatedDistribution A Zero-Adjusted Log-Normal distribution. Parameter loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Source code in lightgbmlss/distributions/zero_inflated.py 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 class ZeroAdjustedLogNormal ( ZeroInflatedDistribution ): \"\"\" A Zero-Adjusted Log-Normal distribution. Parameter --------- loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py \"\"\" arg_constraints = { \"loc\" : constraints . real , \"scale\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative def __init__ ( self , loc , scale , gate = None , validate_args = None ): base_dist = LogNormal ( loc = loc , scale = scale , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def loc ( self ): return self . base_dist . loc @property def scale ( self ): return self . base_dist . scale ZeroInflatedDistribution Bases: TorchDistribution Generic Zero Inflated distribution. This can be used directly or can be used as a base class as e.g. for :class: ZeroInflatedPoisson and :class: ZeroInflatedNegativeBinomial . Parameters gate : torch.Tensor Probability of extra zeros given via a Bernoulli distribution. base_dist : torch.distributions.Distribution The base distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L18 Source code in lightgbmlss/distributions/zero_inflated.py 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 class ZeroInflatedDistribution ( TorchDistribution ): \"\"\" Generic Zero Inflated distribution. This can be used directly or can be used as a base class as e.g. for :class:`ZeroInflatedPoisson` and :class:`ZeroInflatedNegativeBinomial`. Parameters ---------- gate : torch.Tensor Probability of extra zeros given via a Bernoulli distribution. base_dist : torch.distributions.Distribution The base distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L18 \"\"\" arg_constraints = { \"gate\" : constraints . unit_interval , \"gate_logits\" : constraints . real , } def __init__ ( self , base_dist , * , gate = None , gate_logits = None , validate_args = None ): if ( gate is None ) == ( gate_logits is None ): raise ValueError ( \"Either `gate` or `gate_logits` must be specified, but not both.\" ) if gate is not None : batch_shape = broadcast_shape ( gate . shape , base_dist . batch_shape ) self . gate = gate . expand ( batch_shape ) else : batch_shape = broadcast_shape ( gate_logits . shape , base_dist . batch_shape ) self . gate_logits = gate_logits . expand ( batch_shape ) if base_dist . event_shape : raise ValueError ( \"ZeroInflatedDistribution expected empty \" \"base_dist.event_shape but got {} \" . format ( base_dist . event_shape ) ) self . base_dist = base_dist . expand ( batch_shape ) event_shape = torch . Size () super () . __init__ ( batch_shape , event_shape , validate_args ) @constraints . dependent_property def support ( self ): return self . base_dist . support @lazy_property def gate ( self ): return logits_to_probs ( self . gate_logits ) @lazy_property def gate_logits ( self ): return probs_to_logits ( self . gate ) def log_prob ( self , value ): if self . _validate_args : self . _validate_sample ( value ) zero_idx = ( value == 0 ) support = self . support epsilon = abs ( torch . finfo ( value . dtype ) . eps ) if hasattr ( support , \"lower_bound\" ): if is_identically_zero ( getattr ( support , \"lower_bound\" , None )): value = value . clamp_min ( epsilon ) if hasattr ( support , \"upper_bound\" ): if is_identically_one ( getattr ( support , \"upper_bound\" , None )) & ( value . max () == 1.0 ): value = value . clamp_max ( 1 - epsilon ) if \"gate\" in self . __dict__ : gate , value = broadcast_all ( self . gate , value ) log_prob = ( - gate ) . log1p () + self . base_dist . log_prob ( value ) log_prob = torch . where ( zero_idx , ( gate + log_prob . exp ()) . log (), log_prob ) else : gate_logits , value = broadcast_all ( self . gate_logits , value ) log_prob_minus_log_gate = - gate_logits + self . base_dist . log_prob ( value ) log_gate = - softplus ( - gate_logits ) log_prob = log_prob_minus_log_gate + log_gate zero_log_prob = softplus ( log_prob_minus_log_gate ) + log_gate log_prob = torch . where ( zero_idx , zero_log_prob , log_prob ) return log_prob def sample ( self , sample_shape = torch . Size ()): shape = self . _extended_shape ( sample_shape ) with torch . no_grad (): mask = torch . bernoulli ( self . gate . expand ( shape )) . bool () samples = self . base_dist . expand ( shape ) . sample () samples = torch . where ( mask , samples . new_zeros (()), samples ) return samples @lazy_property def mean ( self ): return ( 1 - self . gate ) * self . base_dist . mean @lazy_property def variance ( self ): return ( 1 - self . gate ) * ( self . base_dist . mean ** 2 + self . base_dist . variance ) - self . mean ** 2 def expand ( self , batch_shape , _instance = None ): new = self . _get_checked_instance ( type ( self ), _instance ) batch_shape = torch . Size ( batch_shape ) gate = self . gate . expand ( batch_shape ) if \"gate\" in self . __dict__ else None gate_logits = ( self . gate_logits . expand ( batch_shape ) if \"gate_logits\" in self . __dict__ else None ) base_dist = self . base_dist . expand ( batch_shape ) ZeroInflatedDistribution . __init__ ( new , base_dist , gate = gate , gate_logits = gate_logits , validate_args = False ) new . _validate_args = self . _validate_args return new ZeroInflatedNegativeBinomial Bases: ZeroInflatedDistribution A Zero Inflated Negative Binomial distribution. Parameter total_count: torch.Tensor Non-negative number of negative Bernoulli trial. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds of success (log(p/(1-p))). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Source code in lightgbmlss/distributions/zero_inflated.py 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 class ZeroInflatedNegativeBinomial ( ZeroInflatedDistribution ): \"\"\" A Zero Inflated Negative Binomial distribution. Parameter --------- total_count: torch.Tensor Non-negative number of negative Bernoulli trial. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds of success (log(p/(1-p))). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------ - https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 \"\"\" arg_constraints = { \"total_count\" : constraints . greater_than_eq ( 0 ), \"probs\" : constraints . half_open_interval ( 0.0 , 1.0 ), \"logits\" : constraints . real , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative_integer def __init__ ( self , total_count , probs = None , gate = None , validate_args = None ): base_dist = NegativeBinomial ( total_count = total_count , probs = probs , logits = None , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def total_count ( self ): return self . base_dist . total_count @property def probs ( self ): return self . base_dist . probs @property def logits ( self ): return self . base_dist . logits ZeroInflatedPoisson Bases: ZeroInflatedDistribution A Zero-Inflated Poisson distribution. Parameter rate: torch.Tensor The rate of the Poisson distribution. gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 Source code in lightgbmlss/distributions/zero_inflated.py 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 class ZeroInflatedPoisson ( ZeroInflatedDistribution ): \"\"\" A Zero-Inflated Poisson distribution. Parameter --------- rate: torch.Tensor The rate of the Poisson distribution. gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 \"\"\" arg_constraints = { \"rate\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative_integer def __init__ ( self , rate , gate = None , validate_args = None ): base_dist = Poisson ( rate = rate , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def rate ( self ): return self . base_dist . rate model LightGBMLSS LightGBMLSS model class Parameters dist : Distribution DistributionClass object. start_values : np.ndarray Starting values for each distributional parameter. Source code in lightgbmlss/model.py 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 class LightGBMLSS : \"\"\" LightGBMLSS model class Parameters ---------- dist : Distribution DistributionClass object. start_values : np.ndarray Starting values for each distributional parameter. \"\"\" def __init__ ( self , dist ): self . dist = dist # Distribution object self . start_values = None # Starting values for distributional parameters def set_params ( self , params : Dict [ str , Any ]) -> Dict [ str , Any ]: \"\"\" Set parameters for distributional model. Arguments --------- params : Dict[str, Any] Parameters for model. Returns ------- params : Dict[str, Any] Updated Parameters for model. \"\"\" params_adj = { \"num_class\" : self . dist . n_dist_param , \"metric\" : \"None\" , \"objective\" : \"None\" , \"random_seed\" : 123 , \"verbose\" : - 1 } params . update ( params_adj ) return params def set_init_score ( self , dmatrix : Dataset ) -> None : \"\"\" Set init_score for distributions. Arguments --------- dmatrix : Dataset Dataset to set base margin for. Returns ------- None \"\"\" if self . start_values is None : _ , self . start_values = self . dist . calculate_start_values ( dmatrix . get_label ()) init_score = ( np . ones ( shape = ( dmatrix . get_label () . shape [ 0 ], 1 ))) * self . start_values dmatrix . set_init_score ( init_score . ravel ( order = \"F\" )) def train ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , valid_sets : Optional [ List [ Dataset ]] = None , valid_names : Optional [ List [ str ]] = None , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , keep_training_booster : bool = False , callbacks : Optional [ List [ Callable ]] = None ) -> Booster : \"\"\"Function to perform the training of a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. valid_sets : list of Dataset, or None, optional (default=None) List of data to be evaluated on during training. valid_names : list of str, or None, optional (default=None) Names of ``valid_sets``. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. keep_training_booster : bool, optional (default=False) Whether the returned Booster will be used to keep training. If False, the returned value will be converted into _InnerPredictor before returning. This means you won't be able to use ``eval``, ``eval_train`` or ``eval_valid`` methods of the returned Booster. When your model is very large and cause the memory error, you can try to set this param to ``True`` to avoid the model conversion performed during the internal call of ``model_to_string``. You can still use _InnerPredictor as ``init_model`` for future continue training. callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. Returns ------- booster : Booster The trained Booster model. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) if valid_sets is not None : valid_sets = self . set_valid_margin ( valid_sets , self . start_values ) self . booster = lgb . train ( params , train_set , num_boost_round = num_boost_round , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , valid_sets = valid_sets , valid_names = valid_names , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , keep_training_booster = keep_training_booster , callbacks = callbacks ) def cv ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , folds : Optional [ Union [ Iterable [ Tuple [ np . ndarray , np . ndarray ]], _LGBMBaseCrossValidator ]] = None , nfold : int = 5 , stratified : bool = True , shuffle : bool = True , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , fpreproc : Optional [ _LGBM_PreprocFunction ] = None , seed : int = 123 , callbacks : Optional [ List [ Callable ]] = None , eval_train_metric : bool = False , return_cvbooster : bool = False ) -> Dict [ str , Union [ List [ float ], CVBooster ]]: \"\"\"Function to cross-validate a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None) If generator or iterator, it should yield the train and test indices for each fold. If object, it should be one of the scikit-learn splitter classes (https://scikit-learn.org/stable/modules/classes.html#splitter-classes) and have ``split`` method. This argument has highest priority over other data split arguments. nfold : int, optional (default=5) Number of folds in CV. stratified : bool, optional (default=True) Whether to perform stratified sampling. shuffle : bool, optional (default=True) Whether to shuffle before splitting data. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. fpreproc : callable or None, optional (default=None) Preprocessing function that takes (dtrain, dtest, params) and returns transformed versions of those. seed : int, optional (default=0) Seed used to generate the folds (passed to numpy.random.seed). callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. eval_train_metric : bool, optional (default=False) Whether to display the train metric in progress. The score of the metric is calculated again after each training step, so there is some impact on performance. return_cvbooster : bool, optional (default=False) Whether to return Booster models trained on each fold through ``CVBooster``. Returns ------- eval_hist : dict Evaluation history. The dictionary has the following format: {'metric1-mean': [values], 'metric1-stdv': [values], 'metric2-mean': [values], 'metric2-stdv': [values], ...}. If ``return_cvbooster=True``, also returns trained boosters wrapped in a ``CVBooster`` object via ``cvbooster`` key. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) self . bstLSS_cv = lgb . cv ( params , train_set , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , num_boost_round = num_boost_round , folds = folds , nfold = nfold , stratified = False , shuffle = False , metrics = None , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , fpreproc = fpreproc , seed = seed , callbacks = callbacks , eval_train_metric = eval_train_metric , return_cvbooster = return_cvbooster ) return self . bstLSS_cv def hyper_opt ( self , hp_dict : Dict , train_set : lgb . Dataset , num_boost_round = 500 , nfold = 10 , early_stopping_rounds = 20 , max_minutes = 10 , n_trials = None , study_name = None , silence = False , seed = None , hp_seed = None ): \"\"\" Function to tune hyperparameters using optuna. Arguments ---------- hp_dict: dict Dictionary of hyperparameters to tune. train_set: lgb.Dataset Training data. num_boost_round: int Number of boosting iterations. nfold: int Number of folds in CV. early_stopping_rounds: int Activates early stopping. Cross-Validation metric (average of validation metric computed over CV folds) needs to improve at least once in every **early_stopping_rounds** round(s) to continue training. The last entry in the evaluation history will represent the best iteration. If there's more than one metric in the **eval_metric** parameter given in **params**, the last metric will be used for early stopping. max_minutes: int Time budget in minutes, i.e., stop study after the given number of minutes. n_trials: int The number of trials. If this argument is set to None, there is no limitation on the number of trials. study_name: str Name of the hyperparameter study. silence: bool Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed: int Seed used to generate the folds (passed to numpy.random.seed). hp_seed: int Seed for random number generator used in the Bayesian hyper-parameter search. Returns ------- opt_params : dict Optimal hyper-parameters. \"\"\" def objective ( trial ): hyper_params = {} for param_name , param_value in hp_dict . items (): param_type = param_value [ 0 ] if param_type == \"categorical\" or param_type == \"none\" : hyper_params . update ({ param_name : trial . suggest_categorical ( param_name , param_value [ 1 ])}) elif param_type == \"float\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_float ( param_name , low = param_low , high = param_high , log = param_log ) }) elif param_type == \"int\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_int ( param_name , low = param_low , high = param_high , log = param_log ) }) # Add booster if not included in dictionary if \"boosting\" not in hyper_params . keys (): hyper_params . update ({ \"boosting\" : trial . suggest_categorical ( \"boosting\" , [ \"gbdt\" ])}) # Add pruning and early stopping pruning_callback = LightGBMPruningCallback ( trial , self . dist . loss_fn ) early_stopping_callback = lgb . early_stopping ( stopping_rounds = early_stopping_rounds , verbose = False ) lgblss_param_tuning = self . cv ( hyper_params , train_set , num_boost_round = num_boost_round , nfold = nfold , callbacks = [ pruning_callback , early_stopping_callback ], seed = seed , ) # Extract the optimal number of boosting rounds opt_rounds = np . argmin ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) + 1 trial . set_user_attr ( \"opt_round\" , int ( opt_rounds )) # Extract the best score best_score = np . min ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) return best_score if study_name is None : study_name = \"LightGBMLSS Hyper-Parameter Optimization\" if silence : optuna . logging . set_verbosity ( optuna . logging . WARNING ) if hp_seed is not None : sampler = TPESampler ( seed = hp_seed ) else : sampler = TPESampler () pruner = optuna . pruners . MedianPruner ( n_startup_trials = 10 , n_warmup_steps = 20 ) study = optuna . create_study ( sampler = sampler , pruner = pruner , direction = \"minimize\" , study_name = study_name ) study . optimize ( objective , n_trials = n_trials , timeout = 60 * max_minutes , show_progress_bar = True ) print ( \" \\n Hyper-Parameter Optimization successfully finished.\" ) print ( \" Number of finished trials: \" , len ( study . trials )) print ( \" Best trial:\" ) opt_param = study . best_trial # Add optimal stopping round opt_param . params [ \"opt_rounds\" ] = study . trials_dataframe ()[ \"user_attrs_opt_round\" ][ study . trials_dataframe ()[ \"value\" ] . idxmin ()] opt_param . params [ \"opt_rounds\" ] = int ( opt_param . params [ \"opt_rounds\" ]) print ( \" Value: {} \" . format ( opt_param . value )) print ( \" Params: \" ) for key , value in opt_param . params . items (): print ( \" {} : {} \" . format ( key , value )) return opt_param . params def predict ( self , data : pd . DataFrame , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ): \"\"\" Function that predicts from the trained model. Arguments --------- data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- predt_df : pd.DataFrame Predictions. \"\"\" # Predict predt_df = self . dist . predict_dist ( booster = self . booster , data = data , start_values = self . start_values , pred_type = pred_type , n_samples = n_samples , quantiles = quantiles , seed = seed ) return predt_df def plot ( self , X : pd . DataFrame , feature : str = \"x\" , parameter : str = \"loc\" , max_display : int = 15 , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS SHap plotting function. Arguments: --------- X: pd.DataFrame Train/Test Data feature: str Specifies which feature is to be plotted. parameter: str Specifies which distributional parameter is to be plotted. max_display: int Specifies the maximum number of features to be displayed. plot_type: str Specifies the type of plot: \"Partial_Dependence\" plots the partial dependence of the parameter on the feature. \"Feature_Importance\" plots the feature importance of the parameter. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) param_pos = self . dist . distribution_arg_names . index ( parameter ) if plot_type == \"Partial_Dependence\" : if self . dist . n_dist_param == 1 : shap . plots . scatter ( shap_values [:, feature ], color = shap_values [:, feature ]) else : shap . plots . scatter ( shap_values [:, feature ][:, param_pos ], color = shap_values [:, feature ][:, param_pos ]) elif plot_type == \"Feature_Importance\" : if self . dist . n_dist_param == 1 : shap . plots . bar ( shap_values , max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ]) else : shap . plots . bar ( shap_values [:, :, param_pos ], max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ] ) def expectile_plot ( self , X : pd . DataFrame , feature : str = \"x\" , expectile : str = \"0.05\" , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS function for plotting expectile SHapley values. X: pd.DataFrame Train/Test Data feature: str Specifies which feature to use for plotting Partial_Dependence plot. expectile: str Specifies which expectile to plot. plot_type: str Specifies which SHapley-plot to visualize. Currently, \"Partial_Dependence\" and \"Feature_Importance\" are supported. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) expect_pos = list ( self . dist . param_dict . keys ()) . index ( expectile ) if plot_type == \"Partial_Dependence\" : shap . plots . scatter ( shap_values [:, feature ][:, expect_pos ], color = shap_values [:, feature ][:, expect_pos ]) elif plot_type == \"Feature_Importance\" : shap . plots . bar ( shap_values [:, :, expect_pos ], max_display = 15 if X . shape [ 1 ] > 15 else X . shape [ 1 ]) def set_valid_margin ( self , valid_sets : list , start_values : np . ndarray ) -> list : \"\"\" Function that sets the base margin for the validation set. Arguments --------- valid_sets : list List of tuples containing the train and evaluation set. valid_names: list List of tuples containing the name of train and evaluation set. start_values : np.ndarray Array containing the start values for the distributional parameters. Returns ------- valid_sets : list List of tuples containing the train and evaluation set. \"\"\" valid_sets1 = valid_sets [ 0 ] init_score_val1 = ( np . ones ( shape = ( valid_sets1 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets1 . set_init_score ( init_score_val1 . ravel ( order = \"F\" )) valid_sets2 = valid_sets [ 1 ] init_score_val2 = ( np . ones ( shape = ( valid_sets2 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets2 . set_init_score ( init_score_val2 . ravel ( order = \"F\" )) valid_sets = [ valid_sets1 , valid_sets2 ] return valid_sets def save_model ( self , model_path : str ) -> None : \"\"\" Save the model to a file. Parameters ---------- model_path : str The path to save the model. Returns ------- None \"\"\" with open ( model_path , \"wb\" ) as f : pickle . dump ( self , f ) @staticmethod def load_model ( model_path : str ): \"\"\" Load the model from a file. Parameters ---------- model_path : str The path to the saved model. Returns ------- The loaded model. \"\"\" with open ( model_path , \"rb\" ) as f : return pickle . load ( f ) cv ( params , train_set , num_boost_round = 100 , folds = None , nfold = 5 , stratified = True , shuffle = True , init_model = None , feature_name = 'auto' , categorical_feature = 'auto' , fpreproc = None , seed = 123 , callbacks = None , eval_train_metric = False , return_cvbooster = False ) Function to cross-validate a LightGBMLSS model with given parameters. Parameters params : dict Parameters for training. Values passed through params take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None) If generator or iterator, it should yield the train and test indices for each fold. If object, it should be one of the scikit-learn splitter classes (https://scikit-learn.org/stable/modules/classes.html#splitter-classes) and have split method. This argument has highest priority over other data split arguments. nfold : int, optional (default=5) Number of folds in CV. stratified : bool, optional (default=True) Whether to perform stratified sampling. shuffle : bool, optional (default=True) Whether to shuffle before splitting data. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify feature_name as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. fpreproc : callable or None, optional (default=None) Preprocessing function that takes (dtrain, dtest, params) and returns transformed versions of those. seed : int, optional (default=0) Seed used to generate the folds (passed to numpy.random.seed). callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. eval_train_metric : bool, optional (default=False) Whether to display the train metric in progress. The score of the metric is calculated again after each training step, so there is some impact on performance. return_cvbooster : bool, optional (default=False) Whether to return Booster models trained on each fold through CVBooster . Returns eval_hist : dict Evaluation history. The dictionary has the following format: {'metric1-mean': [values], 'metric1-stdv': [values], 'metric2-mean': [values], 'metric2-stdv': [values], ...}. If return_cvbooster=True , also returns trained boosters wrapped in a CVBooster object via cvbooster key. lightgbmlss/model.py 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 def cv ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , folds : Optional [ Union [ Iterable [ Tuple [ np . ndarray , np . ndarray ]], _LGBMBaseCrossValidator ]] = None , nfold : int = 5 , stratified : bool = True , shuffle : bool = True , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , fpreproc : Optional [ _LGBM_PreprocFunction ] = None , seed : int = 123 , callbacks : Optional [ List [ Callable ]] = None , eval_train_metric : bool = False , return_cvbooster : bool = False ) -> Dict [ str , Union [ List [ float ], CVBooster ]]: \"\"\"Function to cross-validate a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None) If generator or iterator, it should yield the train and test indices for each fold. If object, it should be one of the scikit-learn splitter classes (https://scikit-learn.org/stable/modules/classes.html#splitter-classes) and have ``split`` method. This argument has highest priority over other data split arguments. nfold : int, optional (default=5) Number of folds in CV. stratified : bool, optional (default=True) Whether to perform stratified sampling. shuffle : bool, optional (default=True) Whether to shuffle before splitting data. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. fpreproc : callable or None, optional (default=None) Preprocessing function that takes (dtrain, dtest, params) and returns transformed versions of those. seed : int, optional (default=0) Seed used to generate the folds (passed to numpy.random.seed). callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. eval_train_metric : bool, optional (default=False) Whether to display the train metric in progress. The score of the metric is calculated again after each training step, so there is some impact on performance. return_cvbooster : bool, optional (default=False) Whether to return Booster models trained on each fold through ``CVBooster``. Returns ------- eval_hist : dict Evaluation history. The dictionary has the following format: {'metric1-mean': [values], 'metric1-stdv': [values], 'metric2-mean': [values], 'metric2-stdv': [values], ...}. If ``return_cvbooster=True``, also returns trained boosters wrapped in a ``CVBooster`` object via ``cvbooster`` key. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) self . bstLSS_cv = lgb . cv ( params , train_set , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , num_boost_round = num_boost_round , folds = folds , nfold = nfold , stratified = False , shuffle = False , metrics = None , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , fpreproc = fpreproc , seed = seed , callbacks = callbacks , eval_train_metric = eval_train_metric , return_cvbooster = return_cvbooster ) return self . bstLSS_cv expectile_plot ( X , feature = 'x' , expectile = '0.05' , plot_type = 'Partial_Dependence' ) LightGBMLSS function for plotting expectile SHapley values. pd.DataFrame Train/Test Data feature: str Specifies which feature to use for plotting Partial_Dependence plot. expectile: str Specifies which expectile to plot. plot_type: str Specifies which SHapley-plot to visualize. Currently, \"Partial_Dependence\" and \"Feature_Importance\" are supported. lightgbmlss/model.py 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 def expectile_plot ( self , X : pd . DataFrame , feature : str = \"x\" , expectile : str = \"0.05\" , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS function for plotting expectile SHapley values. X: pd.DataFrame Train/Test Data feature: str Specifies which feature to use for plotting Partial_Dependence plot. expectile: str Specifies which expectile to plot. plot_type: str Specifies which SHapley-plot to visualize. Currently, \"Partial_Dependence\" and \"Feature_Importance\" are supported. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) expect_pos = list ( self . dist . param_dict . keys ()) . index ( expectile ) if plot_type == \"Partial_Dependence\" : shap . plots . scatter ( shap_values [:, feature ][:, expect_pos ], color = shap_values [:, feature ][:, expect_pos ]) elif plot_type == \"Feature_Importance\" : shap . plots . bar ( shap_values [:, :, expect_pos ], max_display = 15 if X . shape [ 1 ] > 15 else X . shape [ 1 ]) hyper_opt ( hp_dict , train_set , num_boost_round = 500 , nfold = 10 , early_stopping_rounds = 20 , max_minutes = 10 , n_trials = None , study_name = None , silence = False , seed = None , hp_seed = None ) Function to tune hyperparameters using optuna. Arguments hp_dict: dict Dictionary of hyperparameters to tune. train_set: lgb.Dataset Training data. num_boost_round: int Number of boosting iterations. nfold: int Number of folds in CV. early_stopping_rounds: int Activates early stopping. Cross-Validation metric (average of validation metric computed over CV folds) needs to improve at least once in every early_stopping_rounds round(s) to continue training. The last entry in the evaluation history will represent the best iteration. If there's more than one metric in the eval_metric parameter given in params , the last metric will be used for early stopping. max_minutes: int Time budget in minutes, i.e., stop study after the given number of minutes. n_trials: int The number of trials. If this argument is set to None, there is no limitation on the number of trials. study_name: str Name of the hyperparameter study. silence: bool Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed: int Seed used to generate the folds (passed to numpy.random.seed). hp_seed: int Seed for random number generator used in the Bayesian hyper-parameter search. Returns opt_params : dict Optimal hyper-parameters. lightgbmlss/model.py 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 def hyper_opt ( self , hp_dict : Dict , train_set : lgb . Dataset , num_boost_round = 500 , nfold = 10 , early_stopping_rounds = 20 , max_minutes = 10 , n_trials = None , study_name = None , silence = False , seed = None , hp_seed = None ): \"\"\" Function to tune hyperparameters using optuna. Arguments ---------- hp_dict: dict Dictionary of hyperparameters to tune. train_set: lgb.Dataset Training data. num_boost_round: int Number of boosting iterations. nfold: int Number of folds in CV. early_stopping_rounds: int Activates early stopping. Cross-Validation metric (average of validation metric computed over CV folds) needs to improve at least once in every **early_stopping_rounds** round(s) to continue training. The last entry in the evaluation history will represent the best iteration. If there's more than one metric in the **eval_metric** parameter given in **params**, the last metric will be used for early stopping. max_minutes: int Time budget in minutes, i.e., stop study after the given number of minutes. n_trials: int The number of trials. If this argument is set to None, there is no limitation on the number of trials. study_name: str Name of the hyperparameter study. silence: bool Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed: int Seed used to generate the folds (passed to numpy.random.seed). hp_seed: int Seed for random number generator used in the Bayesian hyper-parameter search. Returns ------- opt_params : dict Optimal hyper-parameters. \"\"\" def objective ( trial ): hyper_params = {} for param_name , param_value in hp_dict . items (): param_type = param_value [ 0 ] if param_type == \"categorical\" or param_type == \"none\" : hyper_params . update ({ param_name : trial . suggest_categorical ( param_name , param_value [ 1 ])}) elif param_type == \"float\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_float ( param_name , low = param_low , high = param_high , log = param_log ) }) elif param_type == \"int\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_int ( param_name , low = param_low , high = param_high , log = param_log ) }) # Add booster if not included in dictionary if \"boosting\" not in hyper_params . keys (): hyper_params . update ({ \"boosting\" : trial . suggest_categorical ( \"boosting\" , [ \"gbdt\" ])}) # Add pruning and early stopping pruning_callback = LightGBMPruningCallback ( trial , self . dist . loss_fn ) early_stopping_callback = lgb . early_stopping ( stopping_rounds = early_stopping_rounds , verbose = False ) lgblss_param_tuning = self . cv ( hyper_params , train_set , num_boost_round = num_boost_round , nfold = nfold , callbacks = [ pruning_callback , early_stopping_callback ], seed = seed , ) # Extract the optimal number of boosting rounds opt_rounds = np . argmin ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) + 1 trial . set_user_attr ( \"opt_round\" , int ( opt_rounds )) # Extract the best score best_score = np . min ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) return best_score if study_name is None : study_name = \"LightGBMLSS Hyper-Parameter Optimization\" if silence : optuna . logging . set_verbosity ( optuna . logging . WARNING ) if hp_seed is not None : sampler = TPESampler ( seed = hp_seed ) else : sampler = TPESampler () pruner = optuna . pruners . MedianPruner ( n_startup_trials = 10 , n_warmup_steps = 20 ) study = optuna . create_study ( sampler = sampler , pruner = pruner , direction = \"minimize\" , study_name = study_name ) study . optimize ( objective , n_trials = n_trials , timeout = 60 * max_minutes , show_progress_bar = True ) print ( \" \\n Hyper-Parameter Optimization successfully finished.\" ) print ( \" Number of finished trials: \" , len ( study . trials )) print ( \" Best trial:\" ) opt_param = study . best_trial # Add optimal stopping round opt_param . params [ \"opt_rounds\" ] = study . trials_dataframe ()[ \"user_attrs_opt_round\" ][ study . trials_dataframe ()[ \"value\" ] . idxmin ()] opt_param . params [ \"opt_rounds\" ] = int ( opt_param . params [ \"opt_rounds\" ]) print ( \" Value: {} \" . format ( opt_param . value )) print ( \" Params: \" ) for key , value in opt_param . params . items (): print ( \" {} : {} \" . format ( key , value )) return opt_param . params load_model ( model_path ) staticmethod Load the model from a file. Parameters model_path : str The path to the saved model. Returns The loaded model. lightgbmlss/model.py 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 @staticmethod def load_model ( model_path : str ): \"\"\" Load the model from a file. Parameters ---------- model_path : str The path to the saved model. Returns ------- The loaded model. \"\"\" with open ( model_path , \"rb\" ) as f : return pickle . load ( f ) plot ( X , feature = 'x' , parameter = 'loc' , max_display = 15 , plot_type = 'Partial_Dependence' ) LightGBMLSS SHap plotting function. Arguments: X: pd.DataFrame Train/Test Data feature: str Specifies which feature is to be plotted. parameter: str Specifies which distributional parameter is to be plotted. max_display: int Specifies the maximum number of features to be displayed. plot_type: str Specifies the type of plot: \"Partial_Dependence\" plots the partial dependence of the parameter on the feature. \"Feature_Importance\" plots the feature importance of the parameter. lightgbmlss/model.py 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 def plot ( self , X : pd . DataFrame , feature : str = \"x\" , parameter : str = \"loc\" , max_display : int = 15 , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS SHap plotting function. Arguments: --------- X: pd.DataFrame Train/Test Data feature: str Specifies which feature is to be plotted. parameter: str Specifies which distributional parameter is to be plotted. max_display: int Specifies the maximum number of features to be displayed. plot_type: str Specifies the type of plot: \"Partial_Dependence\" plots the partial dependence of the parameter on the feature. \"Feature_Importance\" plots the feature importance of the parameter. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) param_pos = self . dist . distribution_arg_names . index ( parameter ) if plot_type == \"Partial_Dependence\" : if self . dist . n_dist_param == 1 : shap . plots . scatter ( shap_values [:, feature ], color = shap_values [:, feature ]) else : shap . plots . scatter ( shap_values [:, feature ][:, param_pos ], color = shap_values [:, feature ][:, param_pos ]) elif plot_type == \"Feature_Importance\" : if self . dist . n_dist_param == 1 : shap . plots . bar ( shap_values , max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ]) else : shap . plots . bar ( shap_values [:, :, param_pos ], max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ] ) predict ( data , pred_type = 'parameters' , n_samples = 1000 , quantiles = [ 0.1 , 0.5 , 0.9 ], seed = 123 ) Function that predicts from the trained model. Arguments data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns predt_df : pd.DataFrame Predictions. lightgbmlss/model.py 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 def predict ( self , data : pd . DataFrame , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ): \"\"\" Function that predicts from the trained model. Arguments --------- data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- predt_df : pd.DataFrame Predictions. \"\"\" # Predict predt_df = self . dist . predict_dist ( booster = self . booster , data = data , start_values = self . start_values , pred_type = pred_type , n_samples = n_samples , quantiles = quantiles , seed = seed ) return predt_df save_model ( model_path ) Save the model to a file. Parameters model_path : str The path to save the model. Returns None lightgbmlss/model.py 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 def save_model ( self , model_path : str ) -> None : \"\"\" Save the model to a file. Parameters ---------- model_path : str The path to save the model. Returns ------- None \"\"\" with open ( model_path , \"wb\" ) as f : pickle . dump ( self , f ) set_init_score ( dmatrix ) Set init_score for distributions. Arguments dmatrix : Dataset Dataset to set base margin for. Returns None lightgbmlss/model.py 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 def set_init_score ( self , dmatrix : Dataset ) -> None : \"\"\" Set init_score for distributions. Arguments --------- dmatrix : Dataset Dataset to set base margin for. Returns ------- None \"\"\" if self . start_values is None : _ , self . start_values = self . dist . calculate_start_values ( dmatrix . get_label ()) init_score = ( np . ones ( shape = ( dmatrix . get_label () . shape [ 0 ], 1 ))) * self . start_values dmatrix . set_init_score ( init_score . ravel ( order = \"F\" )) set_params ( params ) Set parameters for distributional model. Arguments params : Dict[str, Any] Parameters for model. Returns params : Dict[str, Any] Updated Parameters for model. lightgbmlss/model.py 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 def set_params ( self , params : Dict [ str , Any ]) -> Dict [ str , Any ]: \"\"\" Set parameters for distributional model. Arguments --------- params : Dict[str, Any] Parameters for model. Returns ------- params : Dict[str, Any] Updated Parameters for model. \"\"\" params_adj = { \"num_class\" : self . dist . n_dist_param , \"metric\" : \"None\" , \"objective\" : \"None\" , \"random_seed\" : 123 , \"verbose\" : - 1 } params . update ( params_adj ) return params set_valid_margin ( valid_sets , start_values ) Function that sets the base margin for the validation set. Arguments valid_sets : list List of tuples containing the train and evaluation set. valid_names: list List of tuples containing the name of train and evaluation set. start_values : np.ndarray Array containing the start values for the distributional parameters. Returns valid_sets : list List of tuples containing the train and evaluation set. lightgbmlss/model.py 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 def set_valid_margin ( self , valid_sets : list , start_values : np . ndarray ) -> list : \"\"\" Function that sets the base margin for the validation set. Arguments --------- valid_sets : list List of tuples containing the train and evaluation set. valid_names: list List of tuples containing the name of train and evaluation set. start_values : np.ndarray Array containing the start values for the distributional parameters. Returns ------- valid_sets : list List of tuples containing the train and evaluation set. \"\"\" valid_sets1 = valid_sets [ 0 ] init_score_val1 = ( np . ones ( shape = ( valid_sets1 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets1 . set_init_score ( init_score_val1 . ravel ( order = \"F\" )) valid_sets2 = valid_sets [ 1 ] init_score_val2 = ( np . ones ( shape = ( valid_sets2 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets2 . set_init_score ( init_score_val2 . ravel ( order = \"F\" )) valid_sets = [ valid_sets1 , valid_sets2 ] return valid_sets train ( params , train_set , num_boost_round = 100 , valid_sets = None , valid_names = None , init_model = None , feature_name = 'auto' , categorical_feature = 'auto' , keep_training_booster = False , callbacks = None ) Function to perform the training of a LightGBMLSS model with given parameters. Parameters params : dict Parameters for training. Values passed through params take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. valid_sets : list of Dataset, or None, optional (default=None) List of data to be evaluated on during training. valid_names : list of str, or None, optional (default=None) Names of valid_sets . init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify feature_name as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. keep_training_booster : bool, optional (default=False) Whether the returned Booster will be used to keep training. If False, the returned value will be converted into _InnerPredictor before returning. This means you won't be able to use eval , eval_train or eval_valid methods of the returned Booster. When your model is very large and cause the memory error, you can try to set this param to True to avoid the model conversion performed during the internal call of model_to_string . You can still use _InnerPredictor as init_model for future continue training. callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. Returns booster : Booster The trained Booster model. lightgbmlss/model.py 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 def train ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , valid_sets : Optional [ List [ Dataset ]] = None , valid_names : Optional [ List [ str ]] = None , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , keep_training_booster : bool = False , callbacks : Optional [ List [ Callable ]] = None ) -> Booster : \"\"\"Function to perform the training of a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. valid_sets : list of Dataset, or None, optional (default=None) List of data to be evaluated on during training. valid_names : list of str, or None, optional (default=None) Names of ``valid_sets``. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. keep_training_booster : bool, optional (default=False) Whether the returned Booster will be used to keep training. If False, the returned value will be converted into _InnerPredictor before returning. This means you won't be able to use ``eval``, ``eval_train`` or ``eval_valid`` methods of the returned Booster. When your model is very large and cause the memory error, you can try to set this param to ``True`` to avoid the model conversion performed during the internal call of ``model_to_string``. You can still use _InnerPredictor as ``init_model`` for future continue training. callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. Returns ------- booster : Booster The trained Booster model. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) if valid_sets is not None : valid_sets = self . set_valid_margin ( valid_sets , self . start_values ) self . booster = lgb . train ( params , train_set , num_boost_round = num_boost_round , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , valid_sets = valid_sets , valid_names = valid_names , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , keep_training_booster = keep_training_booster , callbacks = callbacks ) utils exp_fn ( predt ) Exponential function used to ensure predt is strictly positive. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 def exp_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Exponential function used to ensure predt is strictly positive. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . exp ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt exp_fn_df ( predt ) Exponential function used for Student-T distribution. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 def exp_fn_df ( predt : torch . tensor ) -> torch . tensor : \"\"\" Exponential function used for Student-T distribution. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . exp ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt + torch . tensor ( 2.0 , dtype = predt . dtype ) gumbel_softmax_fn ( predt , tau = 1.0 ) Gumbel-softmax function used to ensure predt is adding to one. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. Arguments predt: torch.tensor Predicted values. tau: float, non-negative scalar temperature. Temperature parameter for the Gumbel-softmax distribution. As tau -> 0, the output becomes more discrete, and as tau -> inf, the output becomes more uniform. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 def gumbel_softmax_fn ( predt : torch . tensor , tau : float = 1.0 ) -> torch . tensor : \"\"\" Gumbel-softmax function used to ensure predt is adding to one. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. Arguments --------- predt: torch.tensor Predicted values. tau: float, non-negative scalar temperature. Temperature parameter for the Gumbel-softmax distribution. As tau -> 0, the output becomes more discrete, and as tau -> inf, the output becomes more uniform. Returns ------- predt: torch.tensor Predicted values. \"\"\" torch . manual_seed ( 123 ) predt = gumbel_softmax ( nan_to_num ( predt ), tau = tau , dim = 1 ) + torch . tensor ( 0 , dtype = predt . dtype ) return predt identity_fn ( predt ) Identity mapping of predt. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 def identity_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Identity mapping of predt. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = nan_to_num ( predt ) + torch . tensor ( 0 , dtype = predt . dtype ) return predt nan_to_num ( predt ) Replace nan, inf and -inf with the mean of predt. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def nan_to_num ( predt : torch . tensor ) -> torch . tensor : \"\"\" Replace nan, inf and -inf with the mean of predt. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . nan_to_num ( predt , nan = float ( torch . nanmean ( predt )), posinf = float ( torch . nanmean ( predt )), neginf = float ( torch . nanmean ( predt )) ) return predt relu_fn ( predt ) Function used to ensure predt are scaled to max(0, predt). Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 def relu_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Function used to ensure predt are scaled to max(0, predt). Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . relu ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt sigmoid_fn ( predt ) Function used to ensure predt are scaled to (0,1). Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 def sigmoid_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Function used to ensure predt are scaled to (0,1). Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . sigmoid ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) predt = torch . clamp ( predt , 1e-03 , 1 - 1e-03 ) return predt softmax_fn ( predt ) Softmax function used to ensure predt is adding to one. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 def softmax_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Softmax function used to ensure predt is adding to one. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = softmax ( nan_to_num ( predt ), dim = 1 ) + torch . tensor ( 0 , dtype = predt . dtype ) return predt softplus_fn ( predt ) Softplus function used to ensure predt is strictly positive. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 def softplus_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Softplus function used to ensure predt is strictly positive. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = softplus ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt softplus_fn_df ( predt ) Softplus function used for Student-T distribution. Arguments predt: torch.tensor Predicted values. Returns predt: torch.tensor Predicted values. lightgbmlss/utils.py 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 def softplus_fn_df ( predt : torch . tensor ) -> torch . tensor : \"\"\" Softplus function used for Student-T distribution. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = softplus ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt + torch . tensor ( 2.0 , dtype = predt . dtype )","title":"API references"},{"location":"api/#api-references","text":"LightGBMLSS - An extension of LightGBM to probabilistic forecasting","title":"API references"},{"location":"api/#lightgbmlss.datasets","text":"LightGBMLSS - An extension of LightGBM to probabilistic forecasting","title":"datasets"},{"location":"api/#lightgbmlss.datasets.data_loader","text":"","title":"data_loader"},{"location":"api/#lightgbmlss.datasets.data_loader.load_simulated_gaussian_data","text":"Returns train/test dataframe of a simulated example. Contains the following columns y int64: response x int64: x-feature X1:X10 int64: random noise features lightgbmlss/datasets/data_loader.py 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def load_simulated_gaussian_data (): \"\"\" Returns train/test dataframe of a simulated example. Contains the following columns: y int64: response x int64: x-feature X1:X10 int64: random noise features \"\"\" train_path = pkg_resources . resource_stream ( __name__ , \"gaussian_train_sim.csv\" ) train_df = pd . read_csv ( train_path ) test_path = pkg_resources . resource_stream ( __name__ , \"gaussian_test_sim.csv\" ) test_df = pd . read_csv ( test_path ) return train_df , test_df","title":"load_simulated_gaussian_data()"},{"location":"api/#lightgbmlss.datasets.data_loader.load_simulated_studentT_data","text":"Returns train/test dataframe of a simulated example. Contains the following columns y int64: response x int64: x-feature X1:X10 int64: random noise features lightgbmlss/datasets/data_loader.py 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 def load_simulated_studentT_data (): \"\"\" Returns train/test dataframe of a simulated example. Contains the following columns: y int64: response x int64: x-feature X1:X10 int64: random noise features \"\"\" train_path = pkg_resources . resource_stream ( __name__ , \"studentT_train_sim.csv\" ) train_df = pd . read_csv ( train_path ) test_path = pkg_resources . resource_stream ( __name__ , \"studentT_test_sim.csv\" ) test_df = pd . read_csv ( test_path ) return train_df , test_df","title":"load_simulated_studentT_data()"},{"location":"api/#lightgbmlss.distributions","text":"LightGBMLSS - An extension of LightGBM to probabilistic forecasting","title":"distributions"},{"location":"api/#lightgbmlss.distributions.Beta","text":"","title":"Beta"},{"location":"api/#lightgbmlss.distributions.Beta.Beta","text":"Bases: DistributionClass Beta distribution class.","title":"Beta"},{"location":"api/#lightgbmlss.distributions.Beta.Beta--distributional-parameters","text":"concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta).","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Beta.Beta--source","text":"https://pytorch.org/docs/stable/distributions.html#beta","title":"Source"},{"location":"api/#lightgbmlss.distributions.Beta.Beta--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Beta.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Beta ( DistributionClass ): \"\"\" Beta distribution class. Distributional Parameters ------------------------- concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#beta Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Beta_Torch param_dict = { \"concentration1\" : response_fn , \"concentration0\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Cauchy","text":"","title":"Cauchy"},{"location":"api/#lightgbmlss.distributions.Cauchy.Cauchy","text":"Bases: DistributionClass Cauchy distribution class.","title":"Cauchy"},{"location":"api/#lightgbmlss.distributions.Cauchy.Cauchy--distributional-parameters","text":"loc: torch.Tensor Mode or median of the distribution. scale: torch.Tensor Half width at half maximum.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Cauchy.Cauchy--source","text":"https://pytorch.org/docs/stable/distributions.html#cauchy","title":"Source"},{"location":"api/#lightgbmlss.distributions.Cauchy.Cauchy--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Cauchy.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Cauchy ( DistributionClass ): \"\"\" Cauchy distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mode or median of the distribution. scale: torch.Tensor Half width at half maximum. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#cauchy Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Cauchy_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Expectile","text":"","title":"Expectile"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile","text":"Bases: DistributionClass Expectile distribution class.","title":"Expectile"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile--distributional-parameters","text":"expectile: List List of specified expectiles.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". expectiles: List List of expectiles in increasing order. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. Source code in lightgbmlss/distributions/Expectile.py 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class Expectile ( DistributionClass ): \"\"\" Expectile distribution class. Distributional Parameters ------------------------- expectile: List List of specified expectiles. Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". expectiles: List List of expectiles in increasing order. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. \"\"\" def __init__ ( self , stabilization : str = \"None\" , expectiles : List = [ 0.1 , 0.5 , 0.9 ], penalize_crossing : bool = False , ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if not isinstance ( expectiles , list ): raise ValueError ( \"Expectiles must be a list.\" ) if not all ([ 0 < expectile < 1 for expectile in expectiles ]): raise ValueError ( \"Expectiles must be between 0 and 1.\" ) if not isinstance ( penalize_crossing , bool ): raise ValueError ( \"penalize_crossing must be a boolean. Please choose from True or False.\" ) # Set the parameters specific to the distribution distribution = Expectile_Torch torch . distributions . Distribution . set_default_validate_args ( False ) expectiles . sort () param_dict = {} for expectile in expectiles : key = f \"expectile_ { expectile } \" param_dict [ key ] = identity_fn # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = \"nll\" , tau = torch . tensor ( expectiles ), penalize_crossing = penalize_crossing )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile_Torch","text":"Bases: Distribution PyTorch implementation of expectiles.","title":"Expectile_Torch"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile_Torch--arguments","text":"expectiles : List[torch.Tensor] List of expectiles. penalize_crossing : bool Whether to include a penalty term to discourage crossing of expectiles. Source code in lightgbmlss/distributions/Expectile.py 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 class Expectile_Torch ( Distribution ): \"\"\" PyTorch implementation of expectiles. Arguments --------- expectiles : List[torch.Tensor] List of expectiles. penalize_crossing : bool Whether to include a penalty term to discourage crossing of expectiles. \"\"\" def __init__ ( self , expectiles : List [ torch . Tensor ], penalize_crossing : bool = False , ): super ( Expectile_Torch ) . __init__ () self . expectiles = expectiles self . penalize_crossing = penalize_crossing self . __class__ . __name__ = \"Expectile\" def log_prob ( self , value : torch . Tensor , tau : List [ torch . Tensor ]) -> torch . Tensor : \"\"\" Returns the log of the probability density function evaluated at `value`. Arguments --------- value : torch.Tensor Response for which log probability is to be calculated. tau : List[torch.Tensor] List of asymmetry parameters. Returns ------- torch.Tensor Log probability of `value`. \"\"\" value = value . reshape ( - 1 , 1 ) loss = torch . tensor ( 0.0 , dtype = torch . float32 ) penalty = torch . tensor ( 0.0 , dtype = torch . float32 ) # Calculate loss predt_expectiles = [] for expectile , tau_value in zip ( self . expectiles , tau ): weight = torch . where ( value - expectile >= 0 , tau_value , 1 - tau_value ) loss += torch . nansum ( weight * ( value - expectile ) ** 2 ) predt_expectiles . append ( expectile . reshape ( - 1 , 1 )) # Penalty term to discourage crossing of expectiles if self . penalize_crossing : predt_expectiles = torch . cat ( predt_expectiles , dim = 1 ) penalty = torch . mean ( ( ~ torch . all ( torch . diff ( predt_expectiles , dim = 1 ) > 0 , dim = 1 )) . float () ) loss = ( loss * ( 1 + penalty )) / len ( self . expectiles ) return - loss","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile_Torch.log_prob","text":"Returns the log of the probability density function evaluated at value .","title":"log_prob()"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile_Torch.log_prob--arguments","text":"value : torch.Tensor Response for which log probability is to be calculated. tau : List[torch.Tensor] List of asymmetry parameters.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.Expectile.Expectile_Torch.log_prob--returns","text":"torch.Tensor Log probability of value . lightgbmlss/distributions/Expectile.py 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 def log_prob ( self , value : torch . Tensor , tau : List [ torch . Tensor ]) -> torch . Tensor : \"\"\" Returns the log of the probability density function evaluated at `value`. Arguments --------- value : torch.Tensor Response for which log probability is to be calculated. tau : List[torch.Tensor] List of asymmetry parameters. Returns ------- torch.Tensor Log probability of `value`. \"\"\" value = value . reshape ( - 1 , 1 ) loss = torch . tensor ( 0.0 , dtype = torch . float32 ) penalty = torch . tensor ( 0.0 , dtype = torch . float32 ) # Calculate loss predt_expectiles = [] for expectile , tau_value in zip ( self . expectiles , tau ): weight = torch . where ( value - expectile >= 0 , tau_value , 1 - tau_value ) loss += torch . nansum ( weight * ( value - expectile ) ** 2 ) predt_expectiles . append ( expectile . reshape ( - 1 , 1 )) # Penalty term to discourage crossing of expectiles if self . penalize_crossing : predt_expectiles = torch . cat ( predt_expectiles , dim = 1 ) penalty = torch . mean ( ( ~ torch . all ( torch . diff ( predt_expectiles , dim = 1 ) > 0 , dim = 1 )) . float () ) loss = ( loss * ( 1 + penalty )) / len ( self . expectiles ) return - loss","title":"Returns"},{"location":"api/#lightgbmlss.distributions.Expectile.expectile_norm","text":"Calculates expectiles from Normal distribution for given tau values. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns np.ndarray lightgbmlss/distributions/Expectile.py 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 def expectile_norm ( tau : np . ndarray = 0.5 , m : np . ndarray = 0 , sd : np . ndarray = 1 ): \"\"\" Calculates expectiles from Normal distribution for given tau values. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments _________ tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns _______ np.ndarray \"\"\" tau [ tau > 1 or tau < 0 ] = np . nan zz = 0 * tau lower = np . array ( - 10 , dtype = \"float\" ) lower = np . repeat ( lower [ np . newaxis , ... ], len ( tau ), axis = 0 ) upper = np . array ( 10 , dtype = \"float\" ) upper = np . repeat ( upper [ np . newaxis , ... ], len ( tau ), axis = 0 ) diff = 1 index = 0 while ( diff > 1e-10 ) and ( index < 1000 ): root = expectile_pnorm ( zz ) - tau root [ np . isnan ( root )] = 0 lower [ root < 0 ] = zz [ root < 0 ] upper [ root > 0 ] = zz [ root > 0 ] zz = ( upper + lower ) / 2 diff = np . nanmax ( np . abs ( root )) index = index + 1 zz [ np . isnan ( tau )] = np . nan return zz * sd + m","title":"expectile_norm()"},{"location":"api/#lightgbmlss.distributions.Expectile.expectile_pnorm","text":"Normal Expectile Distribution Function. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns tau : np.ndarray Expectiles from the Normal distribution. lightgbmlss/distributions/Expectile.py 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 def expectile_pnorm ( tau : np . ndarray = 0.5 , m : np . ndarray = 0 , sd : np . ndarray = 1 ): \"\"\" Normal Expectile Distribution Function. For more details and distributions see https://rdrr.io/cran/expectreg/man/enorm.html Arguments _________ tau : np.ndarray Vector of expectiles from the respective distribution. m : np.ndarray Mean of the Normal distribution. sd : np.ndarray Standard deviation of the Normal distribution. Returns _______ tau : np.ndarray Expectiles from the Normal distribution. \"\"\" z = ( tau - m ) / sd p = norm . cdf ( z , loc = m , scale = sd ) d = norm . pdf ( z , loc = m , scale = sd ) u = - d - z * p tau = u / ( 2 * u + z ) return tau","title":"expectile_pnorm()"},{"location":"api/#lightgbmlss.distributions.Gamma","text":"","title":"Gamma"},{"location":"api/#lightgbmlss.distributions.Gamma.Gamma","text":"Bases: DistributionClass Gamma distribution class.","title":"Gamma"},{"location":"api/#lightgbmlss.distributions.Gamma.Gamma--distributional-parameters","text":"concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta)","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Gamma.Gamma--source","text":"https://pytorch.org/docs/stable/distributions.html#gamma","title":"Source"},{"location":"api/#lightgbmlss.distributions.Gamma.Gamma--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Gamma.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Gamma ( DistributionClass ): \"\"\" Gamma distribution class. Distributional Parameters -------------------------- concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) Source ------------------------- https://pytorch.org/docs/stable/distributions.html#gamma Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Gamma_Torch param_dict = { \"concentration\" : response_fn , \"rate\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Gaussian","text":"","title":"Gaussian"},{"location":"api/#lightgbmlss.distributions.Gaussian.Gaussian","text":"Bases: DistributionClass Gaussian distribution class.","title":"Gaussian"},{"location":"api/#lightgbmlss.distributions.Gaussian.Gaussian--distributional-parameters","text":"loc: torch.Tensor Mean of the distribution (often referred to as mu). scale: torch.Tensor Standard deviation of the distribution (often referred to as sigma).","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Gaussian.Gaussian--source","text":"https://pytorch.org/docs/stable/distributions.html#normal","title":"Source"},{"location":"api/#lightgbmlss.distributions.Gaussian.Gaussian--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Gaussian.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Gaussian ( DistributionClass ): \"\"\" Gaussian distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mean of the distribution (often referred to as mu). scale: torch.Tensor Standard deviation of the distribution (often referred to as sigma). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#normal Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Gaussian_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Gumbel","text":"","title":"Gumbel"},{"location":"api/#lightgbmlss.distributions.Gumbel.Gumbel","text":"Bases: DistributionClass Gumbel distribution class.","title":"Gumbel"},{"location":"api/#lightgbmlss.distributions.Gumbel.Gumbel--distributional-parameters","text":"loc: torch.Tensor Location parameter of the distribution. scale: torch.Tensor Scale parameter of the distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Gumbel.Gumbel--source","text":"https://pytorch.org/docs/stable/distributions.html#gumbel","title":"Source"},{"location":"api/#lightgbmlss.distributions.Gumbel.Gumbel--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Gumbel.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Gumbel ( DistributionClass ): \"\"\" Gumbel distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Location parameter of the distribution. scale: torch.Tensor Scale parameter of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#gumbel Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Gumbel_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Laplace","text":"","title":"Laplace"},{"location":"api/#lightgbmlss.distributions.Laplace.Laplace","text":"Bases: DistributionClass Laplace distribution class.","title":"Laplace"},{"location":"api/#lightgbmlss.distributions.Laplace.Laplace--distributional-parameters","text":"loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Laplace.Laplace--source","text":"https://pytorch.org/docs/stable/distributions.html#laplace","title":"Source"},{"location":"api/#lightgbmlss.distributions.Laplace.Laplace--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Laplace.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Laplace ( DistributionClass ): \"\"\" Laplace distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#laplace Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Laplace_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.LogNormal","text":"","title":"LogNormal"},{"location":"api/#lightgbmlss.distributions.LogNormal.LogNormal","text":"Bases: DistributionClass LogNormal distribution class.","title":"LogNormal"},{"location":"api/#lightgbmlss.distributions.LogNormal.LogNormal--distributional-parameters","text":"loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.LogNormal.LogNormal--source","text":"https://pytorch.org/docs/stable/distributions.html#lognormal","title":"Source"},{"location":"api/#lightgbmlss.distributions.LogNormal.LogNormal--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/LogNormal.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class LogNormal ( DistributionClass ): \"\"\" LogNormal distribution class. Distributional Parameters ------------------------- loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#lognormal Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = LogNormal_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Mixture","text":"","title":"Mixture"},{"location":"api/#lightgbmlss.distributions.Mixture.Mixture","text":"Bases: MixtureDistributionClass Mixture-Density distribution class. Implements a mixture-density distribution for univariate targets, where all components are from different parameterizations of the same distribution-type. A mixture-density distribution is a concept used to model a complex distribution that arises from combining multiple simpler distributions. The Mixture-Density distribution is parameterized by a categorical selecting distribution (over M components) and M-component distributions. For more information on the Mixture-Density distribution, see: Bishop, C. M. (1994). Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK.","title":"Mixture"},{"location":"api/#lightgbmlss.distributions.Mixture.Mixture--distributional-parameters","text":"Inherits the distributional parameters from the component distributions.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Mixture.Mixture--source","text":"https://pytorch.org/docs/stable/distributions.html#mixturesamefamily","title":"Source"},{"location":"api/#lightgbmlss.distributions.Mixture.Mixture--parameters","text":"component_distribution: torch.distributions.Distribution Distribution class for the components of the mixture distribution. Has to be one of the available univariate distributions of the package. M: int Number of components in the mixture distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, the Hessian corresponds to the second-order derivative with respect to that specific parameter only. The resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can be more accurate. - \"grouped\": Each tensor contains all parameters for a specific parameter-type, e.g., for a Gaussian-Mixture with M=2, loc=[loc_1, loc_2], scale=[scale_1, scale_2], and mix_prob=[mix_prob_1, mix_prob_2]. When computing the Hessian, the derivatives for all parameters in the respective tensor are calculated jointly. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter-type. This is usually less runtime intensive, but can be less accurate. tau: float, non-negative scalar temperature. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. Source code in lightgbmlss/distributions/Mixture.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 class Mixture ( MixtureDistributionClass ): \"\"\" Mixture-Density distribution class. Implements a mixture-density distribution for univariate targets, where all components are from different parameterizations of the same distribution-type. A mixture-density distribution is a concept used to model a complex distribution that arises from combining multiple simpler distributions. The Mixture-Density distribution is parameterized by a categorical selecting distribution (over M components) and M-component distributions. For more information on the Mixture-Density distribution, see: Bishop, C. M. (1994). Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK. Distributional Parameters ------------------------- Inherits the distributional parameters from the component distributions. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#mixturesamefamily Parameters ------------------------- component_distribution: torch.distributions.Distribution Distribution class for the components of the mixture distribution. Has to be one of the available univariate distributions of the package. M: int Number of components in the mixture distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, the Hessian corresponds to the second-order derivative with respect to that specific parameter only. The resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can be more accurate. - \"grouped\": Each tensor contains all parameters for a specific parameter-type, e.g., for a Gaussian-Mixture with M=2, loc=[loc_1, loc_2], scale=[scale_1, scale_2], and mix_prob=[mix_prob_1, mix_prob_2]. When computing the Hessian, the derivatives for all parameters in the respective tensor are calculated jointly. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter-type. This is usually less runtime intensive, but can be less accurate. tau: float, non-negative scalar temperature. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. \"\"\" def __init__ ( self , component_distribution : torch . distributions . Distribution , M : int = 2 , hessian_mode : str = \"individual\" , tau : float = 1.0 ): # Input Checks mixt_dist = get_component_distributions () if str ( component_distribution . __class__ ) . split ( \".\" )[ - 2 ] not in mixt_dist : raise ValueError ( f \"component_distribution must be one of the following: { mixt_dist } .\" ) if not isinstance ( M , int ): raise ValueError ( \"M must be an integer.\" ) if M < 2 : raise ValueError ( \"M must be greater than 1.\" ) if component_distribution . loss_fn != \"nll\" : raise ValueError ( \"Loss for component_distribution must be 'nll'.\" ) if not isinstance ( hessian_mode , str ): raise ValueError ( \"hessian_mode must be a string.\" ) if hessian_mode not in [ \"individual\" , \"grouped\" ]: raise ValueError ( \"hessian_mode must be either 'individual' or 'grouped'.\" ) if not isinstance ( tau , float ): raise ValueError ( \"tau must be a float.\" ) if tau <= 0 : raise ValueError ( \"tau must be greater than 0.\" ) # Set the parameters specific to the distribution param_dict = component_distribution . param_dict preset_gumbel_fn = partial ( gumbel_softmax_fn , tau = tau ) param_dict . update ({ \"mix_prob\" : preset_gumbel_fn }) distribution_arg_names = [ f \" { key } _ { i } \" for key in param_dict for i in range ( 1 , M + 1 )] torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = component_distribution , M = M , temperature = tau , hessian_mode = hessian_mode , univariate = True , discrete = component_distribution . discrete , n_dist_param = len ( distribution_arg_names ), stabilization = component_distribution . stabilization , param_dict = param_dict , distribution_arg_names = distribution_arg_names , loss_fn = component_distribution . loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.NegativeBinomial","text":"","title":"NegativeBinomial"},{"location":"api/#lightgbmlss.distributions.NegativeBinomial.NegativeBinomial","text":"Bases: DistributionClass NegativeBinomial distribution class.","title":"NegativeBinomial"},{"location":"api/#lightgbmlss.distributions.NegativeBinomial.NegativeBinomial--distributional-parameters","text":"total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds for probabilities of success.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.NegativeBinomial.NegativeBinomial--source","text":"https://pytorch.org/docs/stable/distributions.html#negativebinomial","title":"Source"},{"location":"api/#lightgbmlss.distributions.NegativeBinomial.NegativeBinomial--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/NegativeBinomial.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 class NegativeBinomial ( DistributionClass ): \"\"\" NegativeBinomial distribution class. Distributional Parameters ------------------------- total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds for probabilities of success. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#negativebinomial Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn_total_count : str = \"relu\" , response_fn_probs : str = \"sigmoid\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions for total_count response_functions_total_count = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn_total_count in response_functions_total_count : response_fn_total_count = response_functions_total_count [ response_fn_total_count ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Specify Response Functions for probs response_functions_probs = { \"sigmoid\" : sigmoid_fn } if response_fn_probs in response_functions_probs : response_fn_probs = response_functions_probs [ response_fn_probs ] else : raise ValueError ( \"Invalid response function for probs. Please select 'sigmoid'.\" ) # Set the parameters specific to the distribution distribution = NegativeBinomial_Torch param_dict = { \"total_count\" : response_fn_total_count , \"probs\" : response_fn_probs } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Poisson","text":"","title":"Poisson"},{"location":"api/#lightgbmlss.distributions.Poisson.Poisson","text":"Bases: DistributionClass Poisson distribution class.","title":"Poisson"},{"location":"api/#lightgbmlss.distributions.Poisson.Poisson--distributional-parameters","text":"rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda).","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Poisson.Poisson--source","text":"https://pytorch.org/docs/stable/distributions.html#poisson","title":"Source"},{"location":"api/#lightgbmlss.distributions.Poisson.Poisson--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/Poisson.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class Poisson ( DistributionClass ): \"\"\" Poisson distribution class. Distributional Parameters ------------------------- rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#poisson Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"relu\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Set the parameters specific to the distribution distribution = Poisson_Torch param_dict = { \"rate\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.SplineFlow","text":"","title":"SplineFlow"},{"location":"api/#lightgbmlss.distributions.SplineFlow.SplineFlow","text":"Bases: NormalizingFlowClass Spline Flow class. The spline flow is a normalizing flow based on element-wise rational spline bijections of linear and quadratic order (Durkan et al., 2019; Dolatabadi et al., 2020). Rational splines are functions that are comprised of segments that are the ratio of two polynomials. Rational splines offer an excellent combination of functional flexibility whilst maintaining a numerically stable inverse. For more details, see: - Durkan, C., Bekasov, A., Murray, I. and Papamakarios, G. Neural Spline Flows. NeurIPS 2019. - Dolatabadi, H. M., Erfani, S. and Leckie, C., Invertible Generative Modeling using Linear Rational Splines. AISTATS 2020.","title":"SplineFlow"},{"location":"api/#lightgbmlss.distributions.SplineFlow.SplineFlow--source","text":"https://docs.pyro.ai/en/stable/distributions.html#pyro.distributions.transforms.Spline","title":"Source"},{"location":"api/#lightgbmlss.distributions.SplineFlow.SplineFlow--arguments","text":"target_support: str The target support. Options are - \"real\": [-inf, inf] - \"positive\": [0, inf] - \"positive_integer\": [0, 1, 2, 3, ...] - \"unit_interval\": [0, 1] count_bins: int The number of segments comprising the spline. bound: float The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. order: str The order of the spline. Options are \"linear\" or \"quadratic\". stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/SplineFlow.py 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 class SplineFlow ( NormalizingFlowClass ): \"\"\" Spline Flow class. The spline flow is a normalizing flow based on element-wise rational spline bijections of linear and quadratic order (Durkan et al., 2019; Dolatabadi et al., 2020). Rational splines are functions that are comprised of segments that are the ratio of two polynomials. Rational splines offer an excellent combination of functional flexibility whilst maintaining a numerically stable inverse. For more details, see: - Durkan, C., Bekasov, A., Murray, I. and Papamakarios, G. Neural Spline Flows. NeurIPS 2019. - Dolatabadi, H. M., Erfani, S. and Leckie, C., Invertible Generative Modeling using Linear Rational Splines. AISTATS 2020. Source --------- https://docs.pyro.ai/en/stable/distributions.html#pyro.distributions.transforms.Spline Arguments --------- target_support: str The target support. Options are - \"real\": [-inf, inf] - \"positive\": [0, inf] - \"positive_integer\": [0, 1, 2, 3, ...] - \"unit_interval\": [0, 1] count_bins: int The number of segments comprising the spline. bound: float The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. order: str The order of the spline. Options are \"linear\" or \"quadratic\". stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , target_support : str = \"real\" , count_bins : int = 8 , bound : float = 3.0 , order : str = \"linear\" , stabilization : str = \"None\" , loss_fn : str = \"nll\" ): # Specify Target Transform if not isinstance ( target_support , str ): raise ValueError ( \"target_support must be a string.\" ) transforms = { \"real\" : ( identity_transform , False ), \"positive\" : ( SoftplusTransform (), False ), \"positive_integer\" : ( SoftplusTransform (), True ), \"unit_interval\" : ( SigmoidTransform (), False ) } if target_support in transforms : target_transform , discrete = transforms [ target_support ] else : raise ValueError ( \"Invalid target_support. Options are 'real', 'positive', 'positive_integer', or 'unit_interval'.\" ) # Check if count_bins is valid if not isinstance ( count_bins , int ): raise ValueError ( \"count_bins must be an integer.\" ) if count_bins <= 0 : raise ValueError ( \"count_bins must be a positive integer > 0.\" ) # Check if bound is float if not isinstance ( bound , float ): raise ValueError ( \"bound must be a float.\" ) # Number of parameters if not isinstance ( order , str ): raise ValueError ( \"order must be a string.\" ) order_params = { \"quadratic\" : 2 * count_bins + ( count_bins - 1 ), \"linear\" : 3 * count_bins + ( count_bins - 1 ) } if order in order_params : n_params = order_params [ order ] else : raise ValueError ( \"Invalid order specification. Options are 'linear' or 'quadratic'.\" ) # Check if stabilization method is valid. if not isinstance ( stabilization , str ): raise ValueError ( \"stabilization must be a string.\" ) if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Options are 'None', 'MAD' or 'L2'.\" ) # Check if loss function is valid. if not isinstance ( loss_fn , str ): raise ValueError ( \"loss_fn must be a string.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss_fn. Options are 'nll' or 'crps'.\" ) # Specify parameter dictionary param_dict = { f \"param_ { i + 1 } \" : identity_fn for i in range ( n_params )} torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Normalizing Flow Class super () . __init__ ( base_dist = Normal , # Base distribution, currently only Normal is supported. flow_transform = Spline , count_bins = count_bins , bound = bound , order = order , n_dist_param = n_params , param_dict = param_dict , target_transform = target_transform , discrete = discrete , univariate = True , stabilization = stabilization , loss_fn = loss_fn )","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.StudentT","text":"","title":"StudentT"},{"location":"api/#lightgbmlss.distributions.StudentT.StudentT","text":"Bases: DistributionClass Student-T Distribution Class","title":"StudentT"},{"location":"api/#lightgbmlss.distributions.StudentT.StudentT--distributional-parameters","text":"df: torch.Tensor Degrees of freedom. loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.StudentT.StudentT--source","text":"https://pytorch.org/docs/stable/distributions.html#studentt","title":"Source"},{"location":"api/#lightgbmlss.distributions.StudentT.StudentT--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/StudentT.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 class StudentT ( DistributionClass ): \"\"\" Student-T Distribution Class Distributional Parameters ------------------------- df: torch.Tensor Degrees of freedom. loc: torch.Tensor Mean of the distribution. scale: torch.Tensor Scale of the distribution. Source ------------------------- https://pytorch.org/docs/stable/distributions.html#studentt Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : ( exp_fn , exp_fn_df ), \"softplus\" : ( softplus_fn , softplus_fn_df ) } if response_fn in response_functions : response_fn , response_fn_df = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = StudentT_Torch param_dict = { \"df\" : response_fn_df , \"loc\" : identity_fn , \"scale\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.Weibull","text":"","title":"Weibull"},{"location":"api/#lightgbmlss.distributions.Weibull.Weibull","text":"Bases: DistributionClass Weibull distribution class.","title":"Weibull"},{"location":"api/#lightgbmlss.distributions.Weibull.Weibull--distributional-parameters","text":"scale: torch.Tensor Scale parameter of distribution (lambda). concentration: torch.Tensor Concentration parameter of distribution (k/shape).","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.Weibull.Weibull--source","text":"https://pytorch.org/docs/stable/distributions.html#weibull","title":"Source"},{"location":"api/#lightgbmlss.distributions.Weibull.Weibull--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/Weibull.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Weibull ( DistributionClass ): \"\"\" Weibull distribution class. Distributional Parameters ------------------------- scale: torch.Tensor Scale parameter of distribution (lambda). concentration: torch.Tensor Concentration parameter of distribution (k/shape). Source ------------------------- https://pytorch.org/docs/stable/distributions.html#weibull Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" , \"crps\" ]: raise ValueError ( \"Invalid loss function. Please choose from 'nll' or 'crps'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = Weibull_Torch param_dict = { \"scale\" : response_fn , \"concentration\" : response_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.ZABeta","text":"","title":"ZABeta"},{"location":"api/#lightgbmlss.distributions.ZABeta.ZABeta","text":"Bases: DistributionClass Zero-Adjusted Beta distribution class. The zero-adjusted Beta distribution is similar to the Beta distribution but allows zeros as y values.","title":"ZABeta"},{"location":"api/#lightgbmlss.distributions.ZABeta.ZABeta--distributional-parameters","text":"concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.ZABeta.ZABeta--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py","title":"Source"},{"location":"api/#lightgbmlss.distributions.ZABeta.ZABeta--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZABeta.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class ZABeta ( DistributionClass ): \"\"\" Zero-Adjusted Beta distribution class. The zero-adjusted Beta distribution is similar to the Beta distribution but allows zeros as y values. Distributional Parameters ------------------------- concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = ZeroAdjustedBeta_Torch param_dict = { \"concentration1\" : response_fn , \"concentration0\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.ZAGamma","text":"","title":"ZAGamma"},{"location":"api/#lightgbmlss.distributions.ZAGamma.ZAGamma","text":"Bases: DistributionClass Zero-Adjusted Gamma distribution class. The zero-adjusted Gamma distribution is similar to the Gamma distribution but allows zeros as y values.","title":"ZAGamma"},{"location":"api/#lightgbmlss.distributions.ZAGamma.ZAGamma--distributional-parameters","text":"concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.ZAGamma.ZAGamma--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150","title":"Source"},{"location":"api/#lightgbmlss.distributions.ZAGamma.ZAGamma--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZAGamma.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class ZAGamma ( DistributionClass ): \"\"\" Zero-Adjusted Gamma distribution class. The zero-adjusted Gamma distribution is similar to the Gamma distribution but allows zeros as y values. Distributional Parameters -------------------------- concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = ZeroAdjustedGamma_Torch param_dict = { \"concentration\" : response_fn , \"rate\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.ZALN","text":"","title":"ZALN"},{"location":"api/#lightgbmlss.distributions.ZALN.ZALN","text":"Bases: DistributionClass Zero-Adjusted LogNormal distribution class. The zero-adjusted Log-Normal distribution is similar to the Log-Normal distribution but allows zeros as y values.","title":"ZALN"},{"location":"api/#lightgbmlss.distributions.ZALN.ZALN--distributional-parameters","text":"loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.ZALN.ZALN--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150","title":"Source"},{"location":"api/#lightgbmlss.distributions.ZALN.ZALN--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZALN.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class ZALN ( DistributionClass ): \"\"\" Zero-Adjusted LogNormal distribution class. The zero-adjusted Log-Normal distribution is similar to the Log-Normal distribution but allows zeros as y values. Distributional Parameters ------------------------- loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential) or \"softplus\" (softplus). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"exp\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function. Please choose from 'exp' or 'softplus'.\" ) # Set the parameters specific to the distribution distribution = ZeroAdjustedLogNormal_Torch param_dict = { \"loc\" : identity_fn , \"scale\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = False , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.ZINB","text":"","title":"ZINB"},{"location":"api/#lightgbmlss.distributions.ZINB.ZINB","text":"Bases: DistributionClass Zero-Inflated Negative Binomial distribution class.","title":"ZINB"},{"location":"api/#lightgbmlss.distributions.ZINB.ZINB--distributional-parameters","text":"total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.ZINB.ZINB--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150","title":"Source"},{"location":"api/#lightgbmlss.distributions.ZINB.ZINB--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZINB.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 class ZINB ( DistributionClass ): \"\"\" Zero-Inflated Negative Binomial distribution class. Distributional Parameters ------------------------- total_count: torch.Tensor Non-negative number of negative Bernoulli trials to stop. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn_total_count: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). response_fn_probs: str Response function for transforming the distributional parameters to the correct support. Options are \"sigmoid\" (sigmoid). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn_total_count : str = \"relu\" , response_fn_probs : str = \"sigmoid\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions for total_count response_functions_total_count = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn_total_count in response_functions_total_count : response_fn_total_count = response_functions_total_count [ response_fn_total_count ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Specify Response Functions for probs response_functions_probs = { \"sigmoid\" : sigmoid_fn } if response_fn_probs in response_functions_probs : response_fn_probs = response_functions_probs [ response_fn_probs ] else : raise ValueError ( \"Invalid response function for probs. Please select 'sigmoid'.\" ) # Set the parameters specific to the distribution distribution = ZeroInflatedNegativeBinomial_Torch param_dict = { \"total_count\" : response_fn_total_count , \"probs\" : response_fn_probs , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.ZIPoisson","text":"","title":"ZIPoisson"},{"location":"api/#lightgbmlss.distributions.ZIPoisson.ZIPoisson","text":"Bases: DistributionClass Zero-Inflated Poisson distribution class.","title":"ZIPoisson"},{"location":"api/#lightgbmlss.distributions.ZIPoisson.ZIPoisson--distributional-parameters","text":"rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution.","title":"Distributional Parameters"},{"location":"api/#lightgbmlss.distributions.ZIPoisson.ZIPoisson--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121","title":"Source"},{"location":"api/#lightgbmlss.distributions.ZIPoisson.ZIPoisson--parameters","text":"stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/ZIPoisson.py 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class ZIPoisson ( DistributionClass ): \"\"\" Zero-Inflated Poisson distribution class. Distributional Parameters ------------------------- rate: torch.Tensor Rate parameter of the distribution (often referred to as lambda). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------------------------- https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 Parameters ------------------------- stabilization: str Stabilization method for the Gradient and Hessian. Options are \"None\", \"MAD\", \"L2\". response_fn: str Response function for transforming the distributional parameters to the correct support. Options are \"exp\" (exponential), \"softplus\" (softplus) or \"relu\" (rectified linear unit). loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , stabilization : str = \"None\" , response_fn : str = \"relu\" , loss_fn : str = \"nll\" ): # Input Checks if stabilization not in [ \"None\" , \"MAD\" , \"L2\" ]: raise ValueError ( \"Invalid stabilization method. Please choose from 'None', 'MAD' or 'L2'.\" ) if loss_fn not in [ \"nll\" ]: raise ValueError ( \"Invalid loss function. Please select 'nll'.\" ) # Specify Response Functions response_functions = { \"exp\" : exp_fn , \"softplus\" : softplus_fn , \"relu\" : relu_fn } if response_fn in response_functions : response_fn = response_functions [ response_fn ] else : raise ValueError ( \"Invalid response function for total_count. Please choose from 'exp', 'softplus' or 'relu'.\" ) # Set the parameters specific to the distribution distribution = ZeroInflatedPoisson_Torch param_dict = { \"rate\" : response_fn , \"gate\" : sigmoid_fn } torch . distributions . Distribution . set_default_validate_args ( False ) # Specify Distribution Class super () . __init__ ( distribution = distribution , univariate = True , discrete = True , n_dist_param = len ( param_dict ), stabilization = stabilization , param_dict = param_dict , distribution_arg_names = list ( param_dict . keys ()), loss_fn = loss_fn )","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.distribution_utils","text":"","title":"distribution_utils"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass","text":"Generic class that contains general functions for univariate distributions.","title":"DistributionClass"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass--arguments","text":"distribution: torch.distributions.Distribution PyTorch Distribution class. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. tau: List List of expectiles. Only used for Expectile distributon. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. Only used for Expectile distribution. Source code in lightgbmlss/distributions/distribution_utils.py 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 class DistributionClass : \"\"\" Generic class that contains general functions for univariate distributions. Arguments --------- distribution: torch.distributions.Distribution PyTorch Distribution class. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. tau: List List of expectiles. Only used for Expectile distributon. penalize_crossing: bool Whether to include a penalty term to discourage crossing of expectiles. Only used for Expectile distribution. \"\"\" def __init__ ( self , distribution : torch . distributions . Distribution = None , univariate : bool = True , discrete : bool = False , n_dist_param : int = None , stabilization : str = \"None\" , param_dict : Dict [ str , Any ] = None , distribution_arg_names : List = None , loss_fn : str = \"nll\" , tau : Optional [ List [ torch . Tensor ]] = None , penalize_crossing : bool = False , ): self . distribution = distribution self . univariate = univariate self . discrete = discrete self . n_dist_param = n_dist_param self . stabilization = stabilization self . param_dict = param_dict self . distribution_arg_names = distribution_arg_names self . loss_fn = loss_fn self . tau = tau self . penalize_crossing = penalize_crossing def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Transform parameters to response scale params = [ response_fn ( params [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) # Specify Distribution and Loss if self . tau is None : dist = self . distribution ( * params ) loss = - torch . nansum ( dist . log_prob ( target )) else : dist = self . distribution ( params , self . penalize_crossing ) loss = - torch . nansum ( dist . log_prob ( target , self . tau )) return loss def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] for epoch in range ( max_iter ): loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = [ torch . tensor ( predt [:, i ] . reshape ( - 1 , 1 ), requires_grad = requires_grad ) for i in range ( self . n_dist_param ) ] # Predicted Parameters transformed to response scale predt_transformed = [ response_fn ( predt [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Specify Distribution and Loss if self . tau is None : dist_kwargs = dict ( zip ( self . distribution_arg_names , predt_transformed )) dist_fit = self . distribution ( ** dist_kwargs ) if self . loss_fn == \"nll\" : loss = - torch . nansum ( dist_fit . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = dist_fit . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) else : dist_fit = self . distribution ( predt_transformed , self . penalize_crossing ) loss = - torch . nansum ( dist_fit . log_prob ( target , self . tau )) return predt , loss def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) if self . tau is None : pred_params = torch . tensor ( predt_params . values ) dist_kwargs = { arg_name : param for arg_name , param in zip ( self . distribution_arg_names , pred_params . T )} dist_pred = self . distribution ( ** dist_kwargs ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] else : dist_samples = None if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = np . concatenate ( [ response_fun ( predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( self . param_dict . items ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"expectiles\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # # Approximation of Hessian # step_size = 1e-6 # predt_upper = [ # response_fn(predt[i] + step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_upper = dict(zip(self.distribution_arg_names, predt_upper)) # dist_fit_upper = self.distribution(**dist_kwargs_upper) # dist_samples_upper = dist_fit_upper.rsample((30,)).squeeze(-1) # loss_upper = torch.nansum(self.crps_score(self.target, dist_samples_upper)) # # predt_lower = [ # response_fn(predt[i] - step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_lower = dict(zip(self.distribution_arg_names, predt_lower)) # dist_fit_lower = self.distribution(**dist_kwargs_lower) # dist_samples_lower = dist_fit_lower.rsample((30,)).squeeze(-1) # loss_lower = torch.nansum(self.crps_score(self.target, dist_samples_lower)) # # grad_upper = autograd(loss_upper, inputs=predt_upper) # grad_lower = autograd(loss_lower, inputs=predt_lower) # hess = [(grad_upper[i] - grad_lower[i]) / (2 * step_size) for i in range(len(grad))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Parameters ---------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns ------- crps: torch.Tensor CRPS score. References ---------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source ------ https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . __name__ . split ( \".\" )[ 2 ] pbar . set_description ( f \"Fitting { dist_name } distribution\" ) dist_sel = getattr ( candidate_distributions [ i ], dist_name )() try : loss , params = dist_sel . calculate_start_values ( target = target . reshape ( - 1 , 1 ), max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { self . loss_fn : loss . reshape ( - 1 ,), \"distribution\" : str ( dist_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { self . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = self . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ self . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for dist in candidate_distributions : if dist . __name__ . split ( \".\" )[ 2 ] == best_dist [ \"distribution\" ] . values [ 0 ]: best_dist_sel = dist break best_dist_sel = getattr ( best_dist_sel , best_dist [ \"distribution\" ] . values [ 0 ])() params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( - 1 , best_dist_sel . n_dist_param ) # Transform parameters to the response scale and draw samples fitted_params = np . concatenate ( [ response_fun ( params [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . param_dict . keys ()) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.calculate_start_values","text":"Function that calculates the starting values for each distributional parameter.","title":"calculate_start_values()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.calculate_start_values--arguments","text":"target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.calculate_start_values--returns","text":"loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. lightgbmlss/distributions/distribution_utils.py 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] for epoch in range ( max_iter ): loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.compute_gradients_and_hessians","text":"Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1).","title":"compute_gradients_and_hessians()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.compute_gradients_and_hessians--arguments","text":"loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights.","title":"Arguments:"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.compute_gradients_and_hessians--returns","text":"grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. lightgbmlss/distributions/distribution_utils.py 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # # Approximation of Hessian # step_size = 1e-6 # predt_upper = [ # response_fn(predt[i] + step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_upper = dict(zip(self.distribution_arg_names, predt_upper)) # dist_fit_upper = self.distribution(**dist_kwargs_upper) # dist_samples_upper = dist_fit_upper.rsample((30,)).squeeze(-1) # loss_upper = torch.nansum(self.crps_score(self.target, dist_samples_upper)) # # predt_lower = [ # response_fn(predt[i] - step_size).reshape(-1, 1) for i, response_fn in # enumerate(self.param_dict.values()) # ] # dist_kwargs_lower = dict(zip(self.distribution_arg_names, predt_lower)) # dist_fit_lower = self.distribution(**dist_kwargs_lower) # dist_samples_lower = dist_fit_lower.rsample((30,)).squeeze(-1) # loss_lower = torch.nansum(self.crps_score(self.target, dist_samples_lower)) # # grad_upper = autograd(loss_upper, inputs=predt_upper) # grad_lower = autograd(loss_lower, inputs=predt_lower) # hess = [(grad_upper[i] - grad_lower[i]) / (2 * step_size) for i in range(len(grad))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess","title":"Returns:"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.crps_score","text":"Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples.","title":"crps_score()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.crps_score--parameters","text":"y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations).","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.crps_score--returns","text":"crps: torch.Tensor CRPS score.","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.crps_score--references","text":"Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378.","title":"References"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.crps_score--source","text":"https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 lightgbmlss/distributions/distribution_utils.py 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Parameters ---------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns ------- crps: torch.Tensor CRPS score. References ---------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source ------ https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps","title":"Source"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.dist_select","text":"Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better).","title":"dist_select()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.dist_select--parameters","text":"target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot.","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.dist_select--returns","text":"fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. lightgbmlss/distributions/distribution_utils.py 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . __name__ . split ( \".\" )[ 2 ] pbar . set_description ( f \"Fitting { dist_name } distribution\" ) dist_sel = getattr ( candidate_distributions [ i ], dist_name )() try : loss , params = dist_sel . calculate_start_values ( target = target . reshape ( - 1 , 1 ), max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { self . loss_fn : loss . reshape ( - 1 ,), \"distribution\" : str ( dist_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { self . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = self . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ self . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for dist in candidate_distributions : if dist . __name__ . split ( \".\" )[ 2 ] == best_dist [ \"distribution\" ] . values [ 0 ]: best_dist_sel = dist break best_dist_sel = getattr ( best_dist_sel , best_dist [ \"distribution\" ] . values [ 0 ])() params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( - 1 , best_dist_sel . n_dist_param ) # Transform parameters to the response scale and draw samples fitted_params = np . concatenate ( [ response_fun ( params [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . param_dict . keys ()) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.draw_samples","text":"Function that draws n_samples from a predicted distribution.","title":"draw_samples()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.draw_samples--arguments","text":"predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.draw_samples--returns","text":"pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. lightgbmlss/distributions/distribution_utils.py 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) if self . tau is None : pred_params = torch . tensor ( predt_params . values ) dist_kwargs = { arg_name : param for arg_name , param in zip ( self . distribution_arg_names , pred_params . T )} dist_pred = self . distribution ( ** dist_kwargs ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] else : dist_samples = None if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.get_params_loss","text":"Function that returns the predicted parameters and the loss.","title":"get_params_loss()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.get_params_loss--arguments","text":"predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.get_params_loss--returns","text":"predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. lightgbmlss/distributions/distribution_utils.py 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = [ torch . tensor ( predt [:, i ] . reshape ( - 1 , 1 ), requires_grad = requires_grad ) for i in range ( self . n_dist_param ) ] # Predicted Parameters transformed to response scale predt_transformed = [ response_fn ( predt [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Specify Distribution and Loss if self . tau is None : dist_kwargs = dict ( zip ( self . distribution_arg_names , predt_transformed )) dist_fit = self . distribution ( ** dist_kwargs ) if self . loss_fn == \"nll\" : loss = - torch . nansum ( dist_fit . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = dist_fit . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) else : dist_fit = self . distribution ( predt_transformed , self . penalize_crossing ) loss = - torch . nansum ( dist_fit . log_prob ( target , self . tau )) return predt , loss","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.loss_fn_start_values","text":"Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values.","title":"loss_fn_start_values()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.loss_fn_start_values--parameter","text":"params: torch.Tensor Distributional parameters. target: torch.Tensor Target values.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.loss_fn_start_values--returns","text":"loss: torch.Tensor Loss value. lightgbmlss/distributions/distribution_utils.py 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Transform parameters to response scale params = [ response_fn ( params [ i ] . reshape ( - 1 , 1 )) for i , response_fn in enumerate ( self . param_dict . values ()) ] # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) # Specify Distribution and Loss if self . tau is None : dist = self . distribution ( * params ) loss = - torch . nansum ( dist . log_prob ( target )) else : dist = self . distribution ( params , self . penalize_crossing ) loss = - torch . nansum ( dist . log_prob ( target , self . tau )) return loss","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.metric_fn","text":"Function that evaluates the predictions using the negative log-likelihood.","title":"metric_fn()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.metric_fn--arguments","text":"predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.metric_fn--returns","text":"name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. lightgbmlss/distributions/distribution_utils.py 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.objective_fn","text":"Function to estimate gradients and hessians of distributional parameters.","title":"objective_fn()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.objective_fn--arguments","text":"predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.objective_fn--returns","text":"grad: np.ndarray Gradient. hess: np.ndarray Hessian. lightgbmlss/distributions/distribution_utils.py 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.predict_dist","text":"Function that predicts from the trained model.","title":"predict_dist()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.predict_dist--arguments","text":"booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.predict_dist--returns","text":"pred : pd.DataFrame Predictions. lightgbmlss/distributions/distribution_utils.py 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = np . concatenate ( [ response_fun ( predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 )) . numpy () for i , ( dist_param , response_fun ) in enumerate ( self . param_dict . items ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"expectiles\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df","title":"Returns"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.stabilize_derivative","text":"Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173","title":"stabilize_derivative()"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.stabilize_derivative--parameters","text":"input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\".","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.distribution_utils.DistributionClass.stabilize_derivative--returns","text":"stab_der : torch.Tensor Stabilized Gradient or Hessian. lightgbmlss/distributions/distribution_utils.py 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils","text":"","title":"flow_utils"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass","text":"Generic class that contains general functions for normalizing flows.","title":"NormalizingFlowClass"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass--arguments","text":"base_dist: torch.distributions.Distribution PyTorch Distribution class. Currently only Normal is supported. flow_transform: Transform Specify the normalizing flow transform. count_bins: Optional[int] The number of segments comprising the spline. Only used if flow_transform is Spline. bound: Optional[float] The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. Only used if flow_transform is Spline. order: Optional[str] The order of the spline. Options are \"linear\" or \"quadratic\". Only used if flow_transform is Spline. n_dist_param: int Number of parameters. param_dict: Dict[str, Any] Dictionary that maps parameters to their response scale. target_transform: Transform Specify the target transform. discrete: bool Whether the target is discrete or not. univariate: bool Whether the distribution is univariate or multivariate. stabilization: str Stabilization method. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. Source code in lightgbmlss/distributions/flow_utils.py 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 class NormalizingFlowClass : \"\"\" Generic class that contains general functions for normalizing flows. Arguments --------- base_dist: torch.distributions.Distribution PyTorch Distribution class. Currently only Normal is supported. flow_transform: Transform Specify the normalizing flow transform. count_bins: Optional[int] The number of segments comprising the spline. Only used if flow_transform is Spline. bound: Optional[float] The quantity \"K\" determining the bounding box, [-K,K] x [-K,K] of the spline. By adjusting the \"K\" value, you can control the size of the bounding box and consequently control the range of inputs that the spline transform operates on. Larger values of \"K\" will result in a wider valid range for the spline transformation, while smaller values will restrict the valid range to a smaller region. Should be chosen based on the range of the data. Only used if flow_transform is Spline. order: Optional[str] The order of the spline. Options are \"linear\" or \"quadratic\". Only used if flow_transform is Spline. n_dist_param: int Number of parameters. param_dict: Dict[str, Any] Dictionary that maps parameters to their response scale. target_transform: Transform Specify the target transform. discrete: bool Whether the target is discrete or not. univariate: bool Whether the distribution is univariate or multivariate. stabilization: str Stabilization method. Options are \"None\", \"MAD\" or \"L2\". loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood) or \"crps\" (continuous ranked probability score). Note that if \"crps\" is used, the Hessian is set to 1, as the current CRPS version is not twice differentiable. Hence, using the CRPS disregards any variation in the curvature of the loss function. \"\"\" def __init__ ( self , base_dist : torch . distributions . Distribution = None , flow_transform : Transform = None , count_bins : Optional [ int ] = 8 , bound : Optional [ float ] = 3.0 , order : Optional [ str ] = \"quadratic\" , n_dist_param : int = None , param_dict : Dict [ str , Any ] = None , target_transform : Transform = None , discrete : bool = False , univariate : bool = True , stabilization : str = \"None\" , loss_fn : str = \"nll\" , ): self . base_dist = base_dist self . flow_transform = flow_transform self . count_bins = count_bins self . bound = bound self . order = order self . n_dist_param = n_dist_param self . param_dict = param_dict self . target_transform = target_transform self . discrete = discrete self . univariate = univariate self . stabilization = stabilization self . loss_fn = loss_fn def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of normalizing flow parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the specified loss function. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. loss: float Loss value. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values ) return self . loss_fn , loss . detach (), is_higher_better def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates starting values for each parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Create Normalizing Flow flow_dist = self . create_spline_flow ( input_dim = 1 ) # Specify optimizer optimizer = LBFGS ( flow_dist . transforms [ 0 ] . parameters (), lr = 0.3 , max_iter = np . min ([ int ( max_iter / 4 ), 50 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 5 ) # Define closure def closure (): optimizer . zero_grad () loss = - torch . nansum ( flow_dist . log_prob ( target )) loss . backward () flow_dist . clear_cache () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 # Tolerance level for loss change patience = 5 # Patience level for loss change best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = list ( flow_dist . transforms [ 0 ] . parameters ()) start_values = torch . cat ([ param . view ( - 1 ) for param in start_values ]) . detach () . numpy () # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each parameter. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Reshape Target target = target . view ( - 1 ) # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = torch . tensor ( predt , dtype = torch . float32 ) # Specify Normalizing Flow flow_dist = self . create_spline_flow ( target . shape [ 0 ]) # Replace parameters with estimated ones params , flow_dist = self . replace_parameters ( predt , flow_dist ) # Calculate loss if self . loss_fn == \"nll\" : loss = - torch . nansum ( flow_dist . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = flow_dist . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) return params , loss def create_spline_flow ( self , input_dim : int = None , ) -> Transform : \"\"\" Function that constructs a Normalizing Flow. Arguments --------- input_dim: int Input dimension. Returns ------- spline_flow: Transform Normalizing Flow. \"\"\" # Create flow distribution (currently only Normal) loc , scale = torch . zeros ( input_dim ), torch . ones ( input_dim ) flow_dist = self . base_dist ( loc , scale ) # Create Spline Transform torch . manual_seed ( 123 ) spline_transform = self . flow_transform ( input_dim , count_bins = self . count_bins , bound = self . bound , order = self . order ) # Create Normalizing Flow spline_flow = TransformedDistribution ( flow_dist , [ spline_transform , self . target_transform ]) return spline_flow def replace_parameters ( self , params : torch . Tensor , flow_dist : Transform , ) -> Tuple [ List , Transform ]: \"\"\" Replace parameters with estimated ones. Arguments --------- params: torch.Tensor Estimated parameters. flow_dist: Transform Normalizing Flow. Returns ------- params_list: List List of estimated parameters. flow_dist: Transform Normalizing Flow with estimated parameters. \"\"\" # Split parameters into list if self . order == \"quadratic\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 ], dim = 1 ) elif self . order == \"linear\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 , self . count_bins ], dim = 1 ) # Replace parameters for param , new_value in zip ( flow_dist . transforms [ 0 ] . parameters (), params_list ): param . data = new_value # Get parameters (including require_grad=True) params_list = list ( flow_dist . transforms [ 0 ] . parameters ()) return params_list , flow_dist def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) # Specify Normalizing Flow pred_params = torch . tensor ( predt_params . values ) flow_dist_pred = self . create_spline_flow ( pred_params . shape [ 0 ]) # Replace parameters with estimated ones _ , flow_dist_pred = self . replace_parameters ( pred_params , flow_dist_pred ) # Draw samples flow_samples = pd . DataFrame ( flow_dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) flow_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( flow_samples . shape [ 1 ])] if self . discrete : flow_samples = flow_samples . astype ( int ) return flow_samples def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. start_values : np.ndarray Starting values for each distributional parameter. data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" # Predict raw scores predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. Hence, it needs to be # added manually. dist_params_predt = pd . DataFrame ( np . concatenate ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param )], axis = 1 ) ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source --------- https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Arguments --------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns --------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Arguments --------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns --------- crps: torch.Tensor CRPS score. References --------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source --------- https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps def flow_select ( self , target : np . ndarray , candidate_flows : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable normalizing flow specification among the candidate_flow for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_flows: List List of candidate normalizing flow specifications. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted normalizing flow. \"\"\" flow_list = [] total_iterations = len ( candidate_flows ) with tqdm ( total = total_iterations , desc = \"Fitting candidate normalizing flows\" ) as pbar : for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec pbar . set_description ( f \"Fitting { flow_name } \" ) flow_sel = flow try : loss , params = flow_sel . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { flow_sel . loss_fn : loss . reshape ( - 1 , ), \"NormFlow\" : str ( flow_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { flow_sel } NormFlow: { str ( e ) } \" ) fit_df = pd . DataFrame ( { flow_sel . loss_fn : np . nan , \"NormFlow\" : str ( flow_sel ), \"params\" : [ np . nan ] * flow_sel . n_dist_param } ) flow_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate normalizing flows completed\" ) fit_df = pd . concat ( flow_list ) . sort_values ( by = flow_sel . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ flow_sel . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select normalizing flow with the lowest loss best_flow = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec if flow_name == best_flow [ \"NormFlow\" ] . values [ 0 ]: best_flow_sel = flow break # Draw samples from distribution flow_params = torch . tensor ( best_flow [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) flow_dist_sel = best_flow_sel . create_spline_flow ( input_dim = 1 ) _ , flow_dist_sel = best_flow_sel . replace_parameters ( flow_params , flow_dist_sel ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) flow_samples = pd . DataFrame ( flow_dist_sel . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( flow_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_flow [ 'NormFlow' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.calculate_start_values","text":"Function that calculates starting values for each parameter.","title":"calculate_start_values()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.calculate_start_values--arguments","text":"target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.calculate_start_values--returns","text":"loss: float Loss value. start_values: np.ndarray Starting values for each parameter. lightgbmlss/distributions/flow_utils.py 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates starting values for each parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target ) . reshape ( - 1 , 1 ) # Create Normalizing Flow flow_dist = self . create_spline_flow ( input_dim = 1 ) # Specify optimizer optimizer = LBFGS ( flow_dist . transforms [ 0 ] . parameters (), lr = 0.3 , max_iter = np . min ([ int ( max_iter / 4 ), 50 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 5 ) # Define closure def closure (): optimizer . zero_grad () loss = - torch . nansum ( flow_dist . log_prob ( target )) loss . backward () flow_dist . clear_cache () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 # Tolerance level for loss change patience = 5 # Patience level for loss change best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = list ( flow_dist . transforms [ 0 ] . parameters ()) start_values = torch . cat ([ param . view ( - 1 ) for param in start_values ]) . detach () . numpy () # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.compute_gradients_and_hessians","text":"Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1).","title":"compute_gradients_and_hessians()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.compute_gradients_and_hessians--arguments","text":"loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights.","title":"Arguments:"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.compute_gradients_and_hessians--returns","text":"grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. lightgbmlss/distributions/flow_utils.py 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" if self . loss_fn == \"nll\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] elif self . loss_fn == \"crps\" : # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ torch . ones_like ( grad [ i ]) for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess","title":"Returns:"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.create_spline_flow","text":"Function that constructs a Normalizing Flow.","title":"create_spline_flow()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.create_spline_flow--arguments","text":"input_dim: int Input dimension.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.create_spline_flow--returns","text":"spline_flow: Transform Normalizing Flow. lightgbmlss/distributions/flow_utils.py 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 def create_spline_flow ( self , input_dim : int = None , ) -> Transform : \"\"\" Function that constructs a Normalizing Flow. Arguments --------- input_dim: int Input dimension. Returns ------- spline_flow: Transform Normalizing Flow. \"\"\" # Create flow distribution (currently only Normal) loc , scale = torch . zeros ( input_dim ), torch . ones ( input_dim ) flow_dist = self . base_dist ( loc , scale ) # Create Spline Transform torch . manual_seed ( 123 ) spline_transform = self . flow_transform ( input_dim , count_bins = self . count_bins , bound = self . bound , order = self . order ) # Create Normalizing Flow spline_flow = TransformedDistribution ( flow_dist , [ spline_transform , self . target_transform ]) return spline_flow","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.crps_score","text":"Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples.","title":"crps_score()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.crps_score--arguments","text":"y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations).","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.crps_score--returns","text":"crps: torch.Tensor CRPS score.","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.crps_score--references","text":"Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378.","title":"References"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.crps_score--source","text":"https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 lightgbmlss/distributions/flow_utils.py 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 def crps_score ( self , y : torch . tensor , yhat_dist : torch . tensor ) -> torch . tensor : \"\"\" Function that calculates the Continuous Ranked Probability Score (CRPS) for a given set of predicted samples. Arguments --------- y: torch.Tensor Response variable of shape (n_observations,1). yhat_dist: torch.Tensor Predicted samples of shape (n_samples, n_observations). Returns --------- crps: torch.Tensor CRPS score. References --------- Gneiting, Tilmann & Raftery, Adrian. (2007). Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association. 102. 359-378. Source --------- https://github.com/elephaint/pgbm/blob/main/pgbm/torch/pgbm_dist.py#L549 \"\"\" # Get the number of observations n_samples = yhat_dist . shape [ 0 ] # Sort the forecasts in ascending order yhat_dist_sorted , _ = torch . sort ( yhat_dist , 0 ) # Create temporary tensors y_cdf = torch . zeros_like ( y ) yhat_cdf = torch . zeros_like ( y ) yhat_prev = torch . zeros_like ( y ) crps = torch . zeros_like ( y ) # Loop over the predicted samples generated per observation for yhat in yhat_dist_sorted : yhat = yhat . reshape ( - 1 , 1 ) flag = ( y_cdf == 0 ) * ( y < yhat ) crps += flag * (( y - yhat_prev ) * yhat_cdf ** 2 ) crps += flag * (( yhat - y ) * ( yhat_cdf - 1 ) ** 2 ) crps += ( ~ flag ) * (( yhat - yhat_prev ) * ( yhat_cdf - y_cdf ) ** 2 ) y_cdf += flag yhat_cdf += 1 / n_samples yhat_prev = yhat # In case y_cdf == 0 after the loop flag = ( y_cdf == 0 ) crps += flag * ( y - yhat ) return crps","title":"Source"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.draw_samples","text":"Function that draws n_samples from a predicted distribution.","title":"draw_samples()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.draw_samples--arguments","text":"predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.draw_samples--returns","text":"pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. lightgbmlss/distributions/flow_utils.py 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) # Specify Normalizing Flow pred_params = torch . tensor ( predt_params . values ) flow_dist_pred = self . create_spline_flow ( pred_params . shape [ 0 ]) # Replace parameters with estimated ones _ , flow_dist_pred = self . replace_parameters ( pred_params , flow_dist_pred ) # Draw samples flow_samples = pd . DataFrame ( flow_dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) flow_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( flow_samples . shape [ 1 ])] if self . discrete : flow_samples = flow_samples . astype ( int ) return flow_samples","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.flow_select","text":"Function that selects the most suitable normalizing flow specification among the candidate_flow for the target variable, based on the NegLogLikelihood (lower is better).","title":"flow_select()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.flow_select--parameters","text":"target: np.ndarray Response variable. candidate_flows: List List of candidate normalizing flow specifications. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot.","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.flow_select--returns","text":"fit_df: pd.DataFrame Dataframe with the loss values of the fitted normalizing flow. lightgbmlss/distributions/flow_utils.py 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 def flow_select ( self , target : np . ndarray , candidate_flows : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 10 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable normalizing flow specification among the candidate_flow for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_flows: List List of candidate normalizing flow specifications. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted normalizing flow. \"\"\" flow_list = [] total_iterations = len ( candidate_flows ) with tqdm ( total = total_iterations , desc = \"Fitting candidate normalizing flows\" ) as pbar : for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec pbar . set_description ( f \"Fitting { flow_name } \" ) flow_sel = flow try : loss , params = flow_sel . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { flow_sel . loss_fn : loss . reshape ( - 1 , ), \"NormFlow\" : str ( flow_name ), \"params\" : [ params ] } ) except Exception as e : warnings . warn ( f \"Error fitting { flow_sel } NormFlow: { str ( e ) } \" ) fit_df = pd . DataFrame ( { flow_sel . loss_fn : np . nan , \"NormFlow\" : str ( flow_sel ), \"params\" : [ np . nan ] * flow_sel . n_dist_param } ) flow_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate normalizing flows completed\" ) fit_df = pd . concat ( flow_list ) . sort_values ( by = flow_sel . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ flow_sel . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select normalizing flow with the lowest loss best_flow = fit_df [ fit_df [ \"rank\" ] == 1 ] . reset_index ( drop = True ) for flow in candidate_flows : flow_name = str ( flow . __class__ ) . split ( \".\" )[ - 1 ] . split ( \"'>\" )[ 0 ] flow_spec = f \"(count_bins: { flow . count_bins } , order: { flow . order } )\" flow_name = flow_name + flow_spec if flow_name == best_flow [ \"NormFlow\" ] . values [ 0 ]: best_flow_sel = flow break # Draw samples from distribution flow_params = torch . tensor ( best_flow [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) flow_dist_sel = best_flow_sel . create_spline_flow ( input_dim = 1 ) _ , flow_dist_sel = best_flow_sel . replace_parameters ( flow_params , flow_dist_sel ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) flow_samples = pd . DataFrame ( flow_dist_sel . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 , ), label = \"Actual\" ) sns . kdeplot ( flow_samples . reshape ( - 1 , ), label = f \"Best-Fit: { best_flow [ 'NormFlow' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" ], inplace = True ) return fit_df","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.get_params_loss","text":"Function that returns the predicted parameters and the loss.","title":"get_params_loss()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.get_params_loss--arguments","text":"predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each parameter.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.get_params_loss--returns","text":"predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. lightgbmlss/distributions/flow_utils.py 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each parameter. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Reshape Target target = target . view ( - 1 ) # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) # Convert to torch.tensor predt = torch . tensor ( predt , dtype = torch . float32 ) # Specify Normalizing Flow flow_dist = self . create_spline_flow ( target . shape [ 0 ]) # Replace parameters with estimated ones params , flow_dist = self . replace_parameters ( predt , flow_dist ) # Calculate loss if self . loss_fn == \"nll\" : loss = - torch . nansum ( flow_dist . log_prob ( target )) elif self . loss_fn == \"crps\" : torch . manual_seed ( 123 ) dist_samples = flow_dist . rsample (( 30 ,)) . squeeze ( - 1 ) loss = torch . nansum ( self . crps_score ( target , dist_samples )) else : raise ValueError ( \"Invalid loss function. Please select 'nll' or 'crps'.\" ) return params , loss","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.metric_fn","text":"Function that evaluates the predictions using the specified loss function.","title":"metric_fn()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.metric_fn--arguments","text":"predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.metric_fn--returns","text":"name: str Name of the evaluation metric. loss: float Loss value. lightgbmlss/distributions/flow_utils.py 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the specified loss function. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. loss: float Loss value. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target , start_values ) return self . loss_fn , loss . detach (), is_higher_better","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.objective_fn","text":"Function to estimate gradients and hessians of normalizing flow parameters.","title":"objective_fn()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.objective_fn--arguments","text":"predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.objective_fn--returns","text":"grad: np.ndarray Gradient. hess: np.ndarray Hessian. lightgbmlss/distributions/flow_utils.py 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of normalizing flow parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 )) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = torch . ones_like ( target , dtype = target . dtype ) . numpy () else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target , start_values ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.predict_dist","text":"Function that predicts from the trained model.","title":"predict_dist()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.predict_dist--arguments","text":"booster : lgb.Booster Trained model. start_values : np.ndarray Starting values for each distributional parameter. data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.predict_dist--returns","text":"pred : pd.DataFrame Predictions. lightgbmlss/distributions/flow_utils.py 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. start_values : np.ndarray Starting values for each distributional parameter. data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" # Predict raw scores predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. Hence, it needs to be # added manually. dist_params_predt = pd . DataFrame ( np . concatenate ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param )], axis = 1 ) ) dist_params_predt . columns = self . param_dict . keys () # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.replace_parameters","text":"Replace parameters with estimated ones.","title":"replace_parameters()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.replace_parameters--arguments","text":"params: torch.Tensor Estimated parameters. flow_dist: Transform Normalizing Flow.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.replace_parameters--returns","text":"params_list: List List of estimated parameters. flow_dist: Transform Normalizing Flow with estimated parameters. lightgbmlss/distributions/flow_utils.py 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 def replace_parameters ( self , params : torch . Tensor , flow_dist : Transform , ) -> Tuple [ List , Transform ]: \"\"\" Replace parameters with estimated ones. Arguments --------- params: torch.Tensor Estimated parameters. flow_dist: Transform Normalizing Flow. Returns ------- params_list: List List of estimated parameters. flow_dist: Transform Normalizing Flow with estimated parameters. \"\"\" # Split parameters into list if self . order == \"quadratic\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 ], dim = 1 ) elif self . order == \"linear\" : params_list = torch . split ( params , [ self . count_bins , self . count_bins , self . count_bins - 1 , self . count_bins ], dim = 1 ) # Replace parameters for param , new_value in zip ( flow_dist . transforms [ 0 ] . parameters (), params_list ): param . data = new_value # Get parameters (including require_grad=True) params_list = list ( flow_dist . transforms [ 0 ] . parameters ()) return params_list , flow_dist","title":"Returns"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.stabilize_derivative","text":"Function that stabilizes Gradients and Hessians. Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered.","title":"stabilize_derivative()"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.stabilize_derivative--source","text":"https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173","title":"Source"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.stabilize_derivative--arguments","text":"input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\".","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.flow_utils.NormalizingFlowClass.stabilize_derivative--returns","text":"stab_der : torch.Tensor Stabilized Gradient or Hessian. lightgbmlss/distributions/flow_utils.py 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. Since parameters are estimated by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source --------- https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Arguments --------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns --------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der ))) return stab_der","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils","text":"","title":"mixture_distribution_utils"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass","text":"Generic class that contains general functions for mixed-density distributions.","title":"MixtureDistributionClass"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass--arguments","text":"distribution: torch.distributions.Distribution PyTorch Distribution class. M: int Number of components in the mixture distribution. temperature: float Temperature for the Gumbel-Softmax distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, when the Hessian is calculated for each gradient element, this corresponds to the second derivative with respect to that specific tensor element only. This means the resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can also be more accurate. - \"grouped\": Each parameter is a tensor containing all values for a specific parameter type, e.g., loc, scale, or mixture probabilities for a Gaussian Mixture. When computing the Hessian for each gradient element, the Hessian matrix for all the values in the respective tensor are calculated together. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter type tensor. This is usually less runtime intensive, but can be less accurate. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). Source code in lightgbmlss/distributions/mixture_distribution_utils.py 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 class MixtureDistributionClass : \"\"\" Generic class that contains general functions for mixed-density distributions. Arguments --------- distribution: torch.distributions.Distribution PyTorch Distribution class. M: int Number of components in the mixture distribution. temperature: float Temperature for the Gumbel-Softmax distribution. hessian_mode: str Mode for computing the Hessian. Must be one of the following: - \"individual\": Each parameter is treated as a separate tensor. As a result, when the Hessian is calculated for each gradient element, this corresponds to the second derivative with respect to that specific tensor element only. This means the resulting Hessians capture the curvature of the loss w.r.t. each individual parameter. This is usually more runtime intensive, but can also be more accurate. - \"grouped\": Each parameter is a tensor containing all values for a specific parameter type, e.g., loc, scale, or mixture probabilities for a Gaussian Mixture. When computing the Hessian for each gradient element, the Hessian matrix for all the values in the respective tensor are calculated together. The resulting Hessians capture the curvature of the loss w.r.t. the entire parameter type tensor. This is usually less runtime intensive, but can be less accurate. univariate: bool Whether the distribution is univariate or multivariate. discrete: bool Whether the support of the distribution is discrete or continuous. n_dist_param: int Number of distributional parameters. stabilization: str Stabilization method. param_dict: Dict[str, Any] Dictionary that maps distributional parameters to their response scale. distribution_arg_names: List List of distributional parameter names. loss_fn: str Loss function. Options are \"nll\" (negative log-likelihood). \"\"\" def __init__ ( self , distribution : torch . distributions . Distribution = None , M : int = 2 , temperature : float = 1.0 , hessian_mode : str = \"individual\" , univariate : bool = True , discrete : bool = False , n_dist_param : int = None , stabilization : str = \"None\" , param_dict : Dict [ str , Any ] = None , distribution_arg_names : List = None , loss_fn : str = \"nll\" , ): self . distribution = distribution self . M = M self . temperature = temperature self . hessian_mode = hessian_mode self . univariate = univariate self . discrete = discrete self . n_dist_param = n_dist_param self . stabilization = stabilization self . param_dict = param_dict self . distribution_arg_names = distribution_arg_names self . loss_fn = loss_fn def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = np . ones_like ( target , dtype = \"float32\" ) else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better def create_mixture_distribution ( self , params : List [ torch . Tensor ], ) -> torch . distributions . Distribution : \"\"\" Function that creates a mixture distribution. Arguments --------- params: torch.Tensor Distributional parameters. Returns ------- dist: torch.distributions.Distribution Mixture distribution. \"\"\" # Create Mixture Distribution mixture_cat = Categorical ( probs = params [ - 1 ]) mixture_comp = self . distribution . distribution ( * params [: - 1 ]) mixture_dist = MixtureSameFamily ( mixture_cat , mixture_comp ) return mixture_dist def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) . reshape ( 1 , - 1 ) params = torch . split ( params , self . M , dim = 1 ) # Transform parameters to response scale params = [ response_fn ( params [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] # Specify Distribution and Loss dist = self . create_mixture_distribution ( params ) loss = - torch . nansum ( dist . log_prob ( target )) return loss def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target , dtype = torch . float32 ) . flatten () # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 patience = 5 best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) if self . hessian_mode == \"grouped\" : # Convert to torch.Tensor: splits the parameters into tensors for each parameter-type predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), self . M , dim = 1 ) # Transform parameters to response scale predt_transformed = [ response_fn ( predt [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] else : # Convert to torch.Tensor: splits the parameters into tensors for each parameter individually predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), 1 , dim = 1 ) # Transform parameters to response scale keys = list ( self . param_dict . keys ()) max_index = len ( self . param_dict ) * self . M index_ranges = [] for i in range ( 0 , max_index , self . M ): if i + self . M >= max_index : index_ranges . append (( i , None )) break index_ranges . append (( i , i + self . M )) predt_transformed = [ self . param_dict [ key ]( torch . cat ( predt [ start : end ], dim = 1 )) for key , ( start , end ) in zip ( keys , index_ranges ) ] # Specify Distribution and Loss dist_fit = self . create_mixture_distribution ( predt_transformed ) loss = - torch . nansum ( dist_fit . log_prob ( target )) return predt , loss def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) pred_params = torch . tensor ( predt_params . values ) . reshape ( - 1 , self . n_dist_param ) pred_params = torch . split ( pred_params , self . M , dim = 1 ) dist_pred = self . create_mixture_distribution ( pred_params ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = torch . split ( torch . cat ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param ) ], axis = 1 ), self . M , dim = 1 ) dist_params_predt = np . concatenate ( [ response_fn ( dist_params_predt [ i ]) . numpy () for i , response_fn in enumerate ( self . param_dict . values ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . distribution_arg_names # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) return stab_der def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 8 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . distribution . __class__ . __name__ n_mix = candidate_distributions [ i ] . M tau = candidate_distributions [ i ] . temperature dist_name = f \"Mixture( { dist_name } , tau= { tau } , M= { n_mix } )\" pbar . set_description ( f \"Fitting { dist_name } distribution\" ) try : loss , params = candidate_distributions [ i ] . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { candidate_distributions [ i ] . loss_fn : loss . reshape ( - 1 , ), \"distribution\" : str ( dist_name ), \"params\" : [ params ], \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { candidate_distributions [ i ] . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param , \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = candidate_distributions [ i ] . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ candidate_distributions [ i ] . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == fit_df [ \"rank\" ] . min ()] . reset_index ( drop = True ) . iloc [[ 0 ]] best_dist_pos = int ( best_dist [ \"dist_pos\" ] . values [ 0 ]) best_dist_sel = candidate_distributions [ best_dist_pos ] params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) params = torch . split ( params , best_dist_sel . M , dim = 1 ) fitted_params = np . concatenate ( [ response_fun ( params [ i ]) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . distribution_arg_names ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 ,), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 ,), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" , \"dist_pos\" , \"M\" ], inplace = True ) return fit_df","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.calculate_start_values","text":"Function that calculates the starting values for each distributional parameter.","title":"calculate_start_values()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.calculate_start_values--arguments","text":"target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.calculate_start_values--returns","text":"loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. lightgbmlss/distributions/mixture_distribution_utils.py 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 def calculate_start_values ( self , target : np . ndarray , max_iter : int = 50 ) -> Tuple [ float , np . ndarray ]: \"\"\" Function that calculates the starting values for each distributional parameter. Arguments --------- target: np.ndarray Data from which starting values are calculated. max_iter: int Maximum number of iterations. Returns ------- loss: float Loss value. start_values: np.ndarray Starting values for each distributional parameter. \"\"\" # Convert target to torch.tensor target = torch . tensor ( target , dtype = torch . float32 ) . flatten () # Initialize parameters params = [ torch . tensor ( 0.5 , requires_grad = True ) for _ in range ( self . n_dist_param )] # Specify optimizer optimizer = LBFGS ( params , lr = 0.1 , max_iter = np . min ([ int ( max_iter / 4 ), 20 ]), line_search_fn = \"strong_wolfe\" ) # Define learning rate scheduler lr_scheduler = ReduceLROnPlateau ( optimizer , mode = \"min\" , factor = 0.5 , patience = 10 ) # Define closure def closure (): optimizer . zero_grad () loss = self . loss_fn_start_values ( params , target ) loss . backward () return loss # Optimize parameters loss_vals = [] tolerance = 1e-5 patience = 5 best_loss = float ( \"inf\" ) epochs_without_change = 0 for epoch in range ( max_iter ): optimizer . zero_grad () loss = optimizer . step ( closure ) lr_scheduler . step ( loss ) loss_vals . append ( loss . item ()) # Stopping criterion (no improvement in loss) if loss . item () < best_loss - tolerance : best_loss = loss . item () epochs_without_change = 0 else : epochs_without_change += 1 if epochs_without_change >= patience : break # Get final loss loss = np . array ( loss_vals [ - 1 ]) # Get start values start_values = np . array ([ params [ i ] . detach () for i in range ( self . n_dist_param )]) # Replace any remaining NaNs or infinity values with 0.5 start_values = np . nan_to_num ( start_values , nan = 0.5 , posinf = 0.5 , neginf = 0.5 ) return loss , start_values","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.compute_gradients_and_hessians","text":"Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1).","title":"compute_gradients_and_hessians()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.compute_gradients_and_hessians--arguments","text":"loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights.","title":"Arguments:"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.compute_gradients_and_hessians--returns","text":"grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. lightgbmlss/distributions/mixture_distribution_utils.py 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 def compute_gradients_and_hessians ( self , loss : torch . tensor , predt : torch . tensor , weights : np . ndarray ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Calculates gradients and hessians. Output gradients and hessians have shape (n_samples*n_outputs, 1). Arguments: --------- loss: torch.Tensor Loss. predt: torch.Tensor List of predicted parameters. weights: np.ndarray Weights. Returns: ------- grad: torch.Tensor Gradients. hess: torch.Tensor Hessians. \"\"\" # Gradient and Hessian grad = autograd ( loss , inputs = predt , create_graph = True ) hess = [ autograd ( grad [ i ] . nansum (), inputs = predt [ i ], retain_graph = True )[ 0 ] for i in range ( len ( grad ))] # Stabilization of Derivatives if self . stabilization != \"None\" : grad = [ self . stabilize_derivative ( grad [ i ], type = self . stabilization ) for i in range ( len ( grad ))] hess = [ self . stabilize_derivative ( hess [ i ], type = self . stabilization ) for i in range ( len ( hess ))] # Reshape grad = torch . cat ( grad , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () hess = torch . cat ( hess , axis = 1 ) . detach () . squeeze ( - 1 ) . numpy () # Weighting grad *= weights hess *= weights # Reshape grad = grad . ravel ( order = \"F\" ) hess = hess . ravel ( order = \"F\" ) return grad , hess","title":"Returns:"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.create_mixture_distribution","text":"Function that creates a mixture distribution.","title":"create_mixture_distribution()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.create_mixture_distribution--arguments","text":"params: torch.Tensor Distributional parameters.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.create_mixture_distribution--returns","text":"dist: torch.distributions.Distribution Mixture distribution. lightgbmlss/distributions/mixture_distribution_utils.py 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 def create_mixture_distribution ( self , params : List [ torch . Tensor ], ) -> torch . distributions . Distribution : \"\"\" Function that creates a mixture distribution. Arguments --------- params: torch.Tensor Distributional parameters. Returns ------- dist: torch.distributions.Distribution Mixture distribution. \"\"\" # Create Mixture Distribution mixture_cat = Categorical ( probs = params [ - 1 ]) mixture_comp = self . distribution . distribution ( * params [: - 1 ]) mixture_dist = MixtureSameFamily ( mixture_cat , mixture_comp ) return mixture_dist","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.dist_select","text":"Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better).","title":"dist_select()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.dist_select--parameters","text":"target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot.","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.dist_select--returns","text":"fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. lightgbmlss/distributions/mixture_distribution_utils.py 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 def dist_select ( self , target : np . ndarray , candidate_distributions : List , max_iter : int = 100 , plot : bool = False , figure_size : tuple = ( 8 , 5 ), ) -> pd . DataFrame : \"\"\" Function that selects the most suitable distribution among the candidate_distributions for the target variable, based on the NegLogLikelihood (lower is better). Parameters ---------- target: np.ndarray Response variable. candidate_distributions: List List of candidate distributions. max_iter: int Maximum number of iterations for the optimization. plot: bool If True, a density plot of the actual and fitted distribution is created. figure_size: tuple Figure size of the density plot. Returns ------- fit_df: pd.DataFrame Dataframe with the loss values of the fitted candidate distributions. \"\"\" dist_list = [] total_iterations = len ( candidate_distributions ) with tqdm ( total = total_iterations , desc = \"Fitting candidate distributions\" ) as pbar : for i in range ( len ( candidate_distributions )): dist_name = candidate_distributions [ i ] . distribution . __class__ . __name__ n_mix = candidate_distributions [ i ] . M tau = candidate_distributions [ i ] . temperature dist_name = f \"Mixture( { dist_name } , tau= { tau } , M= { n_mix } )\" pbar . set_description ( f \"Fitting { dist_name } distribution\" ) try : loss , params = candidate_distributions [ i ] . calculate_start_values ( target = target , max_iter = max_iter ) fit_df = pd . DataFrame . from_dict ( { candidate_distributions [ i ] . loss_fn : loss . reshape ( - 1 , ), \"distribution\" : str ( dist_name ), \"params\" : [ params ], \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) except Exception as e : warnings . warn ( f \"Error fitting { dist_name } distribution: { str ( e ) } \" ) fit_df = pd . DataFrame ( { candidate_distributions [ i ] . loss_fn : np . nan , \"distribution\" : str ( dist_name ), \"params\" : [ np . nan ] * self . n_dist_param , \"dist_pos\" : i , \"M\" : candidate_distributions [ i ] . M } ) dist_list . append ( fit_df ) pbar . update ( 1 ) pbar . set_description ( f \"Fitting of candidate distributions completed\" ) fit_df = pd . concat ( dist_list ) . sort_values ( by = candidate_distributions [ i ] . loss_fn , ascending = True ) fit_df [ \"rank\" ] = fit_df [ candidate_distributions [ i ] . loss_fn ] . rank () . astype ( int ) fit_df . set_index ( fit_df [ \"rank\" ], inplace = True ) if plot : # Select best distribution best_dist = fit_df [ fit_df [ \"rank\" ] == fit_df [ \"rank\" ] . min ()] . reset_index ( drop = True ) . iloc [[ 0 ]] best_dist_pos = int ( best_dist [ \"dist_pos\" ] . values [ 0 ]) best_dist_sel = candidate_distributions [ best_dist_pos ] params = torch . tensor ( best_dist [ \"params\" ][ 0 ]) . reshape ( 1 , - 1 ) params = torch . split ( params , best_dist_sel . M , dim = 1 ) fitted_params = np . concatenate ( [ response_fun ( params [ i ]) . numpy () for i , ( dist_param , response_fun ) in enumerate ( best_dist_sel . param_dict . items ()) ], axis = 1 , ) fitted_params = pd . DataFrame ( fitted_params , columns = best_dist_sel . distribution_arg_names ) n_samples = np . max ([ 10000 , target . shape [ 0 ]]) n_samples = np . where ( n_samples > 500000 , 100000 , n_samples ) dist_samples = best_dist_sel . draw_samples ( fitted_params , n_samples = n_samples , seed = 123 ) . values # Plot actual and fitted distribution plt . figure ( figsize = figure_size ) sns . kdeplot ( target . reshape ( - 1 ,), label = \"Actual\" ) sns . kdeplot ( dist_samples . reshape ( - 1 ,), label = f \"Best-Fit: { best_dist [ 'distribution' ] . values [ 0 ] } \" ) plt . legend () plt . title ( \"Actual vs. Best-Fit Density\" , fontweight = \"bold\" , fontsize = 16 ) plt . show () fit_df . drop ( columns = [ \"rank\" , \"params\" , \"dist_pos\" , \"M\" ], inplace = True ) return fit_df","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.draw_samples","text":"Function that draws n_samples from a predicted distribution.","title":"draw_samples()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.draw_samples--arguments","text":"predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.draw_samples--returns","text":"pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. lightgbmlss/distributions/mixture_distribution_utils.py 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 def draw_samples ( self , predt_params : pd . DataFrame , n_samples : int = 1000 , seed : int = 123 ) -> pd . DataFrame : \"\"\" Function that draws n_samples from a predicted distribution. Arguments --------- predt_params: pd.DataFrame pd.DataFrame with predicted distributional parameters. n_samples: int Number of sample to draw from predicted response distribution. seed: int Manual seed. Returns ------- pred_dist: pd.DataFrame DataFrame with n_samples drawn from predicted response distribution. \"\"\" torch . manual_seed ( seed ) pred_params = torch . tensor ( predt_params . values ) . reshape ( - 1 , self . n_dist_param ) pred_params = torch . split ( pred_params , self . M , dim = 1 ) dist_pred = self . create_mixture_distribution ( pred_params ) dist_samples = dist_pred . sample (( n_samples ,)) . squeeze () . detach () . numpy () . T dist_samples = pd . DataFrame ( dist_samples ) dist_samples . columns = [ str ( \"y_sample\" ) + str ( i ) for i in range ( dist_samples . shape [ 1 ])] if self . discrete : dist_samples = dist_samples . astype ( int ) return dist_samples","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.get_params_loss","text":"Function that returns the predicted parameters and the loss.","title":"get_params_loss()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.get_params_loss--arguments","text":"predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.get_params_loss--returns","text":"predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. lightgbmlss/distributions/mixture_distribution_utils.py 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 def get_params_loss ( self , predt : np . ndarray , target : torch . Tensor , start_values : List [ float ], requires_grad : bool = False , ) -> Tuple [ List [ torch . Tensor ], np . ndarray ]: \"\"\" Function that returns the predicted parameters and the loss. Arguments --------- predt: np.ndarray Predicted values. target: torch.Tensor Target values. start_values: List Starting values for each distributional parameter. requires_grad: bool Whether to add to the computational graph or not. Returns ------- predt: torch.Tensor Predicted parameters. loss: torch.Tensor Loss value. \"\"\" # Predicted Parameters predt = predt . reshape ( - 1 , self . n_dist_param , order = \"F\" ) # Replace NaNs and infinity values with unconditional start values nan_inf_mask = np . isnan ( predt ) | np . isinf ( predt ) predt [ nan_inf_mask ] = np . take ( start_values , np . where ( nan_inf_mask )[ 1 ]) if self . hessian_mode == \"grouped\" : # Convert to torch.Tensor: splits the parameters into tensors for each parameter-type predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), self . M , dim = 1 ) # Transform parameters to response scale predt_transformed = [ response_fn ( predt [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] else : # Convert to torch.Tensor: splits the parameters into tensors for each parameter individually predt = torch . split ( torch . tensor ( predt , requires_grad = requires_grad ), 1 , dim = 1 ) # Transform parameters to response scale keys = list ( self . param_dict . keys ()) max_index = len ( self . param_dict ) * self . M index_ranges = [] for i in range ( 0 , max_index , self . M ): if i + self . M >= max_index : index_ranges . append (( i , None )) break index_ranges . append (( i , i + self . M )) predt_transformed = [ self . param_dict [ key ]( torch . cat ( predt [ start : end ], dim = 1 )) for key , ( start , end ) in zip ( keys , index_ranges ) ] # Specify Distribution and Loss dist_fit = self . create_mixture_distribution ( predt_transformed ) loss = - torch . nansum ( dist_fit . log_prob ( target )) return predt , loss","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.loss_fn_start_values","text":"Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values.","title":"loss_fn_start_values()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.loss_fn_start_values--parameter","text":"params: torch.Tensor Distributional parameters. target: torch.Tensor Target values.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.loss_fn_start_values--returns","text":"loss: torch.Tensor Loss value. lightgbmlss/distributions/mixture_distribution_utils.py 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 def loss_fn_start_values ( self , params : torch . Tensor , target : torch . Tensor ) -> torch . Tensor : \"\"\" Function that calculates the loss for a given set of distributional parameters. Only used for calculating the loss for the start values. Parameter --------- params: torch.Tensor Distributional parameters. target: torch.Tensor Target values. Returns ------- loss: torch.Tensor Loss value. \"\"\" # Replace NaNs and infinity values with 0.5 nan_inf_idx = torch . isnan ( torch . stack ( params )) | torch . isinf ( torch . stack ( params )) params = torch . where ( nan_inf_idx , torch . tensor ( 0.5 ), torch . stack ( params )) . reshape ( 1 , - 1 ) params = torch . split ( params , self . M , dim = 1 ) # Transform parameters to response scale params = [ response_fn ( params [ i ]) for i , response_fn in enumerate ( self . param_dict . values ())] # Specify Distribution and Loss dist = self . create_mixture_distribution ( params ) loss = - torch . nansum ( dist . log_prob ( target )) return loss","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.metric_fn","text":"Function that evaluates the predictions using the negative log-likelihood.","title":"metric_fn()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.metric_fn--arguments","text":"predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.metric_fn--returns","text":"name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. lightgbmlss/distributions/mixture_distribution_utils.py 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 def metric_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ str , float , bool ]: \"\"\" Function that evaluates the predictions using the negative log-likelihood. Arguments --------- predt: np.ndarray Predicted values. data: lgb.Dataset Data used for training. Returns ------- name: str Name of the evaluation metric. nll: float Negative log-likelihood. is_higher_better: bool Whether a higher value of the metric is better or not. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate loss is_higher_better = False _ , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = False ) return self . loss_fn , loss , is_higher_better","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.objective_fn","text":"Function to estimate gradients and hessians of distributional parameters.","title":"objective_fn()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.objective_fn--arguments","text":"predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.objective_fn--returns","text":"grad: np.ndarray Gradient. hess: np.ndarray Hessian. lightgbmlss/distributions/mixture_distribution_utils.py 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 def objective_fn ( self , predt : np . ndarray , data : lgb . Dataset ) -> Tuple [ np . ndarray , np . ndarray ]: \"\"\" Function to estimate gradients and hessians of distributional parameters. Arguments --------- predt: np.ndarray Predicted values. data: lgb.DMatrix Data used for training. Returns ------- grad: np.ndarray Gradient. hess: np.ndarray Hessian. \"\"\" # Target target = torch . tensor ( data . get_label () . reshape ( - 1 , 1 ), dtype = torch . float32 ) # Weights if data . weight is None : # Use 1 as weight if no weights are specified weights = np . ones_like ( target , dtype = \"float32\" ) else : weights = data . get_weight () . reshape ( - 1 , 1 ) # Start values (needed to replace NaNs in predt) start_values = data . get_init_score () . reshape ( - 1 , self . n_dist_param )[ 0 , :] . tolist () # Calculate gradients and hessians predt , loss = self . get_params_loss ( predt , target . flatten (), start_values , requires_grad = True ) grad , hess = self . compute_gradients_and_hessians ( loss , predt , weights ) return grad , hess","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.predict_dist","text":"Function that predicts from the trained model.","title":"predict_dist()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.predict_dist--arguments","text":"booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution.","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.predict_dist--returns","text":"pred : pd.DataFrame Predictions. lightgbmlss/distributions/mixture_distribution_utils.py 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 def predict_dist ( self , booster : lgb . Booster , data : pd . DataFrame , start_values : np . ndarray , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ) -> pd . DataFrame : \"\"\" Function that predicts from the trained model. Arguments --------- booster : lgb.Booster Trained model. data : pd.DataFrame Data to predict from. start_values : np.ndarray. Starting values for each distributional parameter. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- pred : pd.DataFrame Predictions. \"\"\" predt = torch . tensor ( booster . predict ( data , raw_score = True ), dtype = torch . float32 ) . reshape ( - 1 , self . n_dist_param ) # Set init_score as starting point for each distributional parameter. init_score_pred = torch . tensor ( np . ones ( shape = ( data . shape [ 0 ], 1 )) * start_values , dtype = torch . float32 ) # The predictions don't include the init_score specified in creating the train data. # Hence, it needs to be added manually with the corresponding transform for each distributional parameter. dist_params_predt = torch . split ( torch . cat ( [ predt [:, i ] . reshape ( - 1 , 1 ) + init_score_pred [:, i ] . reshape ( - 1 , 1 ) for i in range ( self . n_dist_param ) ], axis = 1 ), self . M , dim = 1 ) dist_params_predt = np . concatenate ( [ response_fn ( dist_params_predt [ i ]) . numpy () for i , response_fn in enumerate ( self . param_dict . values ()) ], axis = 1 , ) dist_params_predt = pd . DataFrame ( dist_params_predt ) dist_params_predt . columns = self . distribution_arg_names # Draw samples from predicted response distribution pred_samples_df = self . draw_samples ( predt_params = dist_params_predt , n_samples = n_samples , seed = seed ) if pred_type == \"parameters\" : return dist_params_predt elif pred_type == \"samples\" : return pred_samples_df elif pred_type == \"quantiles\" : # Calculate quantiles from predicted response distribution pred_quant_df = pred_samples_df . quantile ( quantiles , axis = 1 ) . T pred_quant_df . columns = [ str ( \"quant_\" ) + str ( quantiles [ i ]) for i in range ( len ( quantiles ))] if self . discrete : pred_quant_df = pred_quant_df . astype ( int ) return pred_quant_df","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.stabilize_derivative","text":"Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173","title":"stabilize_derivative()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.stabilize_derivative--parameters","text":"input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\".","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.MixtureDistributionClass.stabilize_derivative--returns","text":"stab_der : torch.Tensor Stabilized Gradient or Hessian. lightgbmlss/distributions/mixture_distribution_utils.py 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 def stabilize_derivative ( self , input_der : torch . Tensor , type : str = \"MAD\" ) -> torch . Tensor : \"\"\" Function that stabilizes Gradients and Hessians. As LightGBMLSS updates the parameter estimates by optimizing Gradients and Hessians, it is important that these are comparable in magnitude for all distributional parameters. Due to imbalances regarding the ranges, the estimation might become unstable so that it does not converge (or converge very slowly) to the optimal solution. Another way to improve convergence might be to standardize the response variable. This is especially useful if the range of the response differs strongly from the range of the Gradients and Hessians. Both, the stabilization and the standardization of the response are not always advised but need to be carefully considered. Source: https://github.com/boost-R/gamboostLSS/blob/7792951d2984f289ed7e530befa42a2a4cb04d1d/R/helpers.R#L173 Parameters ---------- input_der : torch.Tensor Input derivative, either Gradient or Hessian. type: str Stabilization method. Can be either \"None\", \"MAD\" or \"L2\". Returns ------- stab_der : torch.Tensor Stabilized Gradient or Hessian. \"\"\" if type == \"MAD\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . nanmedian ( torch . abs ( input_der - torch . nanmedian ( input_der ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) stab_der = input_der / div if type == \"L2\" : input_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) div = torch . sqrt ( torch . nanmean ( input_der . pow ( 2 ))) div = torch . where ( div < torch . tensor ( 1e-04 ), torch . tensor ( 1e-04 ), div ) div = torch . where ( div > torch . tensor ( 10000.0 ), torch . tensor ( 10000.0 ), div ) stab_der = input_der / div if type == \"None\" : stab_der = torch . nan_to_num ( input_der , nan = float ( torch . nanmean ( input_der )), posinf = float ( torch . nanmean ( input_der )), neginf = float ( torch . nanmean ( input_der )) ) return stab_der","title":"Returns"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.get_component_distributions","text":"Function that returns component distributions for creating a mixing distribution.","title":"get_component_distributions()"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.get_component_distributions--arguments","text":"None","title":"Arguments"},{"location":"api/#lightgbmlss.distributions.mixture_distribution_utils.get_component_distributions--returns","text":"distns: List List of all available distributions. lightgbmlss/distributions/mixture_distribution_utils.py 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 def get_component_distributions (): \"\"\" Function that returns component distributions for creating a mixing distribution. Arguments --------- None Returns ------- distns: List List of all available distributions. \"\"\" # Get all distribution names mixture_distns = [ dist for dist in dir ( distributions ) if dist [ 0 ] . isupper ()] # Remove specific distributions distns_remove = [ \"Expectile\" , \"Mixture\" , \"SplineFlow\" ] mixture_distns = [ item for item in mixture_distns if item not in distns_remove ] return mixture_distns","title":"Returns"},{"location":"api/#lightgbmlss.distributions.zero_inflated","text":"","title":"zero_inflated"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedBeta","text":"Bases: ZeroInflatedDistribution A Zero-Adjusted Beta distribution.","title":"ZeroAdjustedBeta"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedBeta--parameter","text":"concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedBeta--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Source code in lightgbmlss/distributions/zero_inflated.py 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 class ZeroAdjustedBeta ( ZeroInflatedDistribution ): \"\"\" A Zero-Adjusted Beta distribution. Parameter --------- concentration1: torch.Tensor 1st concentration parameter of the distribution (often referred to as alpha). concentration0: torch.Tensor 2nd concentration parameter of the distribution (often referred to as beta). gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py \"\"\" arg_constraints = { \"concentration1\" : constraints . positive , \"concentration0\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . unit_interval def __init__ ( self , concentration1 , concentration0 , gate = None , validate_args = None ): base_dist = Beta ( concentration1 = concentration1 , concentration0 = concentration0 , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def concentration1 ( self ): return self . base_dist . concentration1 @property def concentration0 ( self ): return self . base_dist . concentration0","title":"Source"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedGamma","text":"Bases: ZeroInflatedDistribution A Zero-Adjusted Gamma distribution.","title":"ZeroAdjustedGamma"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedGamma--parameter","text":"concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedGamma--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Source code in lightgbmlss/distributions/zero_inflated.py 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 class ZeroAdjustedGamma ( ZeroInflatedDistribution ): \"\"\" A Zero-Adjusted Gamma distribution. Parameter --------- concentration: torch.Tensor shape parameter of the distribution (often referred to as alpha) rate: torch.Tensor rate = 1 / scale of the distribution (often referred to as beta) gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py \"\"\" arg_constraints = { \"concentration\" : constraints . positive , \"rate\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative def __init__ ( self , concentration , rate , gate = None , validate_args = None ): base_dist = Gamma ( concentration = concentration , rate = rate , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def concentration ( self ): return self . base_dist . concentration @property def rate ( self ): return self . base_dist . rate","title":"Source"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedLogNormal","text":"Bases: ZeroInflatedDistribution A Zero-Adjusted Log-Normal distribution.","title":"ZeroAdjustedLogNormal"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedLogNormal--parameter","text":"loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroAdjustedLogNormal--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py Source code in lightgbmlss/distributions/zero_inflated.py 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 class ZeroAdjustedLogNormal ( ZeroInflatedDistribution ): \"\"\" A Zero-Adjusted Log-Normal distribution. Parameter --------- loc: torch.Tensor Mean of log of distribution. scale: torch.Tensor Standard deviation of log of the distribution. gate: torch.Tensor Probability of zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py \"\"\" arg_constraints = { \"loc\" : constraints . real , \"scale\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative def __init__ ( self , loc , scale , gate = None , validate_args = None ): base_dist = LogNormal ( loc = loc , scale = scale , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def loc ( self ): return self . base_dist . loc @property def scale ( self ): return self . base_dist . scale","title":"Source"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedDistribution","text":"Bases: TorchDistribution Generic Zero Inflated distribution. This can be used directly or can be used as a base class as e.g. for :class: ZeroInflatedPoisson and :class: ZeroInflatedNegativeBinomial .","title":"ZeroInflatedDistribution"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedDistribution--parameters","text":"gate : torch.Tensor Probability of extra zeros given via a Bernoulli distribution. base_dist : torch.distributions.Distribution The base distribution.","title":"Parameters"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedDistribution--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L18 Source code in lightgbmlss/distributions/zero_inflated.py 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 class ZeroInflatedDistribution ( TorchDistribution ): \"\"\" Generic Zero Inflated distribution. This can be used directly or can be used as a base class as e.g. for :class:`ZeroInflatedPoisson` and :class:`ZeroInflatedNegativeBinomial`. Parameters ---------- gate : torch.Tensor Probability of extra zeros given via a Bernoulli distribution. base_dist : torch.distributions.Distribution The base distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L18 \"\"\" arg_constraints = { \"gate\" : constraints . unit_interval , \"gate_logits\" : constraints . real , } def __init__ ( self , base_dist , * , gate = None , gate_logits = None , validate_args = None ): if ( gate is None ) == ( gate_logits is None ): raise ValueError ( \"Either `gate` or `gate_logits` must be specified, but not both.\" ) if gate is not None : batch_shape = broadcast_shape ( gate . shape , base_dist . batch_shape ) self . gate = gate . expand ( batch_shape ) else : batch_shape = broadcast_shape ( gate_logits . shape , base_dist . batch_shape ) self . gate_logits = gate_logits . expand ( batch_shape ) if base_dist . event_shape : raise ValueError ( \"ZeroInflatedDistribution expected empty \" \"base_dist.event_shape but got {} \" . format ( base_dist . event_shape ) ) self . base_dist = base_dist . expand ( batch_shape ) event_shape = torch . Size () super () . __init__ ( batch_shape , event_shape , validate_args ) @constraints . dependent_property def support ( self ): return self . base_dist . support @lazy_property def gate ( self ): return logits_to_probs ( self . gate_logits ) @lazy_property def gate_logits ( self ): return probs_to_logits ( self . gate ) def log_prob ( self , value ): if self . _validate_args : self . _validate_sample ( value ) zero_idx = ( value == 0 ) support = self . support epsilon = abs ( torch . finfo ( value . dtype ) . eps ) if hasattr ( support , \"lower_bound\" ): if is_identically_zero ( getattr ( support , \"lower_bound\" , None )): value = value . clamp_min ( epsilon ) if hasattr ( support , \"upper_bound\" ): if is_identically_one ( getattr ( support , \"upper_bound\" , None )) & ( value . max () == 1.0 ): value = value . clamp_max ( 1 - epsilon ) if \"gate\" in self . __dict__ : gate , value = broadcast_all ( self . gate , value ) log_prob = ( - gate ) . log1p () + self . base_dist . log_prob ( value ) log_prob = torch . where ( zero_idx , ( gate + log_prob . exp ()) . log (), log_prob ) else : gate_logits , value = broadcast_all ( self . gate_logits , value ) log_prob_minus_log_gate = - gate_logits + self . base_dist . log_prob ( value ) log_gate = - softplus ( - gate_logits ) log_prob = log_prob_minus_log_gate + log_gate zero_log_prob = softplus ( log_prob_minus_log_gate ) + log_gate log_prob = torch . where ( zero_idx , zero_log_prob , log_prob ) return log_prob def sample ( self , sample_shape = torch . Size ()): shape = self . _extended_shape ( sample_shape ) with torch . no_grad (): mask = torch . bernoulli ( self . gate . expand ( shape )) . bool () samples = self . base_dist . expand ( shape ) . sample () samples = torch . where ( mask , samples . new_zeros (()), samples ) return samples @lazy_property def mean ( self ): return ( 1 - self . gate ) * self . base_dist . mean @lazy_property def variance ( self ): return ( 1 - self . gate ) * ( self . base_dist . mean ** 2 + self . base_dist . variance ) - self . mean ** 2 def expand ( self , batch_shape , _instance = None ): new = self . _get_checked_instance ( type ( self ), _instance ) batch_shape = torch . Size ( batch_shape ) gate = self . gate . expand ( batch_shape ) if \"gate\" in self . __dict__ else None gate_logits = ( self . gate_logits . expand ( batch_shape ) if \"gate_logits\" in self . __dict__ else None ) base_dist = self . base_dist . expand ( batch_shape ) ZeroInflatedDistribution . __init__ ( new , base_dist , gate = gate , gate_logits = gate_logits , validate_args = False ) new . _validate_args = self . _validate_args return new","title":"Source"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedNegativeBinomial","text":"Bases: ZeroInflatedDistribution A Zero Inflated Negative Binomial distribution.","title":"ZeroInflatedNegativeBinomial"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedNegativeBinomial--parameter","text":"total_count: torch.Tensor Non-negative number of negative Bernoulli trial. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds of success (log(p/(1-p))). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedNegativeBinomial--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 Source code in lightgbmlss/distributions/zero_inflated.py 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 class ZeroInflatedNegativeBinomial ( ZeroInflatedDistribution ): \"\"\" A Zero Inflated Negative Binomial distribution. Parameter --------- total_count: torch.Tensor Non-negative number of negative Bernoulli trial. probs: torch.Tensor Event probabilities of success in the half open interval [0, 1). logits: torch.Tensor Event log-odds of success (log(p/(1-p))). gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------ - https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L150 \"\"\" arg_constraints = { \"total_count\" : constraints . greater_than_eq ( 0 ), \"probs\" : constraints . half_open_interval ( 0.0 , 1.0 ), \"logits\" : constraints . real , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative_integer def __init__ ( self , total_count , probs = None , gate = None , validate_args = None ): base_dist = NegativeBinomial ( total_count = total_count , probs = probs , logits = None , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def total_count ( self ): return self . base_dist . total_count @property def probs ( self ): return self . base_dist . probs @property def logits ( self ): return self . base_dist . logits","title":"Source"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedPoisson","text":"Bases: ZeroInflatedDistribution A Zero-Inflated Poisson distribution.","title":"ZeroInflatedPoisson"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedPoisson--parameter","text":"rate: torch.Tensor The rate of the Poisson distribution. gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution.","title":"Parameter"},{"location":"api/#lightgbmlss.distributions.zero_inflated.ZeroInflatedPoisson--source","text":"https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 Source code in lightgbmlss/distributions/zero_inflated.py 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 class ZeroInflatedPoisson ( ZeroInflatedDistribution ): \"\"\" A Zero-Inflated Poisson distribution. Parameter --------- rate: torch.Tensor The rate of the Poisson distribution. gate: torch.Tensor Probability of extra zeros given via a Bernoulli distribution. Source ------ https://github.com/pyro-ppl/pyro/blob/dev/pyro/distributions/zero_inflated.py#L121 \"\"\" arg_constraints = { \"rate\" : constraints . positive , \"gate\" : constraints . unit_interval , } support = constraints . nonnegative_integer def __init__ ( self , rate , gate = None , validate_args = None ): base_dist = Poisson ( rate = rate , validate_args = False ) base_dist . _validate_args = validate_args super () . __init__ ( base_dist , gate = gate , validate_args = validate_args ) @property def rate ( self ): return self . base_dist . rate","title":"Source"},{"location":"api/#lightgbmlss.model","text":"","title":"model"},{"location":"api/#lightgbmlss.model.LightGBMLSS","text":"LightGBMLSS model class","title":"LightGBMLSS"},{"location":"api/#lightgbmlss.model.LightGBMLSS--parameters","text":"dist : Distribution DistributionClass object. start_values : np.ndarray Starting values for each distributional parameter. Source code in lightgbmlss/model.py 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 class LightGBMLSS : \"\"\" LightGBMLSS model class Parameters ---------- dist : Distribution DistributionClass object. start_values : np.ndarray Starting values for each distributional parameter. \"\"\" def __init__ ( self , dist ): self . dist = dist # Distribution object self . start_values = None # Starting values for distributional parameters def set_params ( self , params : Dict [ str , Any ]) -> Dict [ str , Any ]: \"\"\" Set parameters for distributional model. Arguments --------- params : Dict[str, Any] Parameters for model. Returns ------- params : Dict[str, Any] Updated Parameters for model. \"\"\" params_adj = { \"num_class\" : self . dist . n_dist_param , \"metric\" : \"None\" , \"objective\" : \"None\" , \"random_seed\" : 123 , \"verbose\" : - 1 } params . update ( params_adj ) return params def set_init_score ( self , dmatrix : Dataset ) -> None : \"\"\" Set init_score for distributions. Arguments --------- dmatrix : Dataset Dataset to set base margin for. Returns ------- None \"\"\" if self . start_values is None : _ , self . start_values = self . dist . calculate_start_values ( dmatrix . get_label ()) init_score = ( np . ones ( shape = ( dmatrix . get_label () . shape [ 0 ], 1 ))) * self . start_values dmatrix . set_init_score ( init_score . ravel ( order = \"F\" )) def train ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , valid_sets : Optional [ List [ Dataset ]] = None , valid_names : Optional [ List [ str ]] = None , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , keep_training_booster : bool = False , callbacks : Optional [ List [ Callable ]] = None ) -> Booster : \"\"\"Function to perform the training of a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. valid_sets : list of Dataset, or None, optional (default=None) List of data to be evaluated on during training. valid_names : list of str, or None, optional (default=None) Names of ``valid_sets``. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. keep_training_booster : bool, optional (default=False) Whether the returned Booster will be used to keep training. If False, the returned value will be converted into _InnerPredictor before returning. This means you won't be able to use ``eval``, ``eval_train`` or ``eval_valid`` methods of the returned Booster. When your model is very large and cause the memory error, you can try to set this param to ``True`` to avoid the model conversion performed during the internal call of ``model_to_string``. You can still use _InnerPredictor as ``init_model`` for future continue training. callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. Returns ------- booster : Booster The trained Booster model. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) if valid_sets is not None : valid_sets = self . set_valid_margin ( valid_sets , self . start_values ) self . booster = lgb . train ( params , train_set , num_boost_round = num_boost_round , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , valid_sets = valid_sets , valid_names = valid_names , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , keep_training_booster = keep_training_booster , callbacks = callbacks ) def cv ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , folds : Optional [ Union [ Iterable [ Tuple [ np . ndarray , np . ndarray ]], _LGBMBaseCrossValidator ]] = None , nfold : int = 5 , stratified : bool = True , shuffle : bool = True , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , fpreproc : Optional [ _LGBM_PreprocFunction ] = None , seed : int = 123 , callbacks : Optional [ List [ Callable ]] = None , eval_train_metric : bool = False , return_cvbooster : bool = False ) -> Dict [ str , Union [ List [ float ], CVBooster ]]: \"\"\"Function to cross-validate a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None) If generator or iterator, it should yield the train and test indices for each fold. If object, it should be one of the scikit-learn splitter classes (https://scikit-learn.org/stable/modules/classes.html#splitter-classes) and have ``split`` method. This argument has highest priority over other data split arguments. nfold : int, optional (default=5) Number of folds in CV. stratified : bool, optional (default=True) Whether to perform stratified sampling. shuffle : bool, optional (default=True) Whether to shuffle before splitting data. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. fpreproc : callable or None, optional (default=None) Preprocessing function that takes (dtrain, dtest, params) and returns transformed versions of those. seed : int, optional (default=0) Seed used to generate the folds (passed to numpy.random.seed). callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. eval_train_metric : bool, optional (default=False) Whether to display the train metric in progress. The score of the metric is calculated again after each training step, so there is some impact on performance. return_cvbooster : bool, optional (default=False) Whether to return Booster models trained on each fold through ``CVBooster``. Returns ------- eval_hist : dict Evaluation history. The dictionary has the following format: {'metric1-mean': [values], 'metric1-stdv': [values], 'metric2-mean': [values], 'metric2-stdv': [values], ...}. If ``return_cvbooster=True``, also returns trained boosters wrapped in a ``CVBooster`` object via ``cvbooster`` key. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) self . bstLSS_cv = lgb . cv ( params , train_set , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , num_boost_round = num_boost_round , folds = folds , nfold = nfold , stratified = False , shuffle = False , metrics = None , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , fpreproc = fpreproc , seed = seed , callbacks = callbacks , eval_train_metric = eval_train_metric , return_cvbooster = return_cvbooster ) return self . bstLSS_cv def hyper_opt ( self , hp_dict : Dict , train_set : lgb . Dataset , num_boost_round = 500 , nfold = 10 , early_stopping_rounds = 20 , max_minutes = 10 , n_trials = None , study_name = None , silence = False , seed = None , hp_seed = None ): \"\"\" Function to tune hyperparameters using optuna. Arguments ---------- hp_dict: dict Dictionary of hyperparameters to tune. train_set: lgb.Dataset Training data. num_boost_round: int Number of boosting iterations. nfold: int Number of folds in CV. early_stopping_rounds: int Activates early stopping. Cross-Validation metric (average of validation metric computed over CV folds) needs to improve at least once in every **early_stopping_rounds** round(s) to continue training. The last entry in the evaluation history will represent the best iteration. If there's more than one metric in the **eval_metric** parameter given in **params**, the last metric will be used for early stopping. max_minutes: int Time budget in minutes, i.e., stop study after the given number of minutes. n_trials: int The number of trials. If this argument is set to None, there is no limitation on the number of trials. study_name: str Name of the hyperparameter study. silence: bool Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed: int Seed used to generate the folds (passed to numpy.random.seed). hp_seed: int Seed for random number generator used in the Bayesian hyper-parameter search. Returns ------- opt_params : dict Optimal hyper-parameters. \"\"\" def objective ( trial ): hyper_params = {} for param_name , param_value in hp_dict . items (): param_type = param_value [ 0 ] if param_type == \"categorical\" or param_type == \"none\" : hyper_params . update ({ param_name : trial . suggest_categorical ( param_name , param_value [ 1 ])}) elif param_type == \"float\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_float ( param_name , low = param_low , high = param_high , log = param_log ) }) elif param_type == \"int\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_int ( param_name , low = param_low , high = param_high , log = param_log ) }) # Add booster if not included in dictionary if \"boosting\" not in hyper_params . keys (): hyper_params . update ({ \"boosting\" : trial . suggest_categorical ( \"boosting\" , [ \"gbdt\" ])}) # Add pruning and early stopping pruning_callback = LightGBMPruningCallback ( trial , self . dist . loss_fn ) early_stopping_callback = lgb . early_stopping ( stopping_rounds = early_stopping_rounds , verbose = False ) lgblss_param_tuning = self . cv ( hyper_params , train_set , num_boost_round = num_boost_round , nfold = nfold , callbacks = [ pruning_callback , early_stopping_callback ], seed = seed , ) # Extract the optimal number of boosting rounds opt_rounds = np . argmin ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) + 1 trial . set_user_attr ( \"opt_round\" , int ( opt_rounds )) # Extract the best score best_score = np . min ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) return best_score if study_name is None : study_name = \"LightGBMLSS Hyper-Parameter Optimization\" if silence : optuna . logging . set_verbosity ( optuna . logging . WARNING ) if hp_seed is not None : sampler = TPESampler ( seed = hp_seed ) else : sampler = TPESampler () pruner = optuna . pruners . MedianPruner ( n_startup_trials = 10 , n_warmup_steps = 20 ) study = optuna . create_study ( sampler = sampler , pruner = pruner , direction = \"minimize\" , study_name = study_name ) study . optimize ( objective , n_trials = n_trials , timeout = 60 * max_minutes , show_progress_bar = True ) print ( \" \\n Hyper-Parameter Optimization successfully finished.\" ) print ( \" Number of finished trials: \" , len ( study . trials )) print ( \" Best trial:\" ) opt_param = study . best_trial # Add optimal stopping round opt_param . params [ \"opt_rounds\" ] = study . trials_dataframe ()[ \"user_attrs_opt_round\" ][ study . trials_dataframe ()[ \"value\" ] . idxmin ()] opt_param . params [ \"opt_rounds\" ] = int ( opt_param . params [ \"opt_rounds\" ]) print ( \" Value: {} \" . format ( opt_param . value )) print ( \" Params: \" ) for key , value in opt_param . params . items (): print ( \" {} : {} \" . format ( key , value )) return opt_param . params def predict ( self , data : pd . DataFrame , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ): \"\"\" Function that predicts from the trained model. Arguments --------- data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- predt_df : pd.DataFrame Predictions. \"\"\" # Predict predt_df = self . dist . predict_dist ( booster = self . booster , data = data , start_values = self . start_values , pred_type = pred_type , n_samples = n_samples , quantiles = quantiles , seed = seed ) return predt_df def plot ( self , X : pd . DataFrame , feature : str = \"x\" , parameter : str = \"loc\" , max_display : int = 15 , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS SHap plotting function. Arguments: --------- X: pd.DataFrame Train/Test Data feature: str Specifies which feature is to be plotted. parameter: str Specifies which distributional parameter is to be plotted. max_display: int Specifies the maximum number of features to be displayed. plot_type: str Specifies the type of plot: \"Partial_Dependence\" plots the partial dependence of the parameter on the feature. \"Feature_Importance\" plots the feature importance of the parameter. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) param_pos = self . dist . distribution_arg_names . index ( parameter ) if plot_type == \"Partial_Dependence\" : if self . dist . n_dist_param == 1 : shap . plots . scatter ( shap_values [:, feature ], color = shap_values [:, feature ]) else : shap . plots . scatter ( shap_values [:, feature ][:, param_pos ], color = shap_values [:, feature ][:, param_pos ]) elif plot_type == \"Feature_Importance\" : if self . dist . n_dist_param == 1 : shap . plots . bar ( shap_values , max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ]) else : shap . plots . bar ( shap_values [:, :, param_pos ], max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ] ) def expectile_plot ( self , X : pd . DataFrame , feature : str = \"x\" , expectile : str = \"0.05\" , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS function for plotting expectile SHapley values. X: pd.DataFrame Train/Test Data feature: str Specifies which feature to use for plotting Partial_Dependence plot. expectile: str Specifies which expectile to plot. plot_type: str Specifies which SHapley-plot to visualize. Currently, \"Partial_Dependence\" and \"Feature_Importance\" are supported. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) expect_pos = list ( self . dist . param_dict . keys ()) . index ( expectile ) if plot_type == \"Partial_Dependence\" : shap . plots . scatter ( shap_values [:, feature ][:, expect_pos ], color = shap_values [:, feature ][:, expect_pos ]) elif plot_type == \"Feature_Importance\" : shap . plots . bar ( shap_values [:, :, expect_pos ], max_display = 15 if X . shape [ 1 ] > 15 else X . shape [ 1 ]) def set_valid_margin ( self , valid_sets : list , start_values : np . ndarray ) -> list : \"\"\" Function that sets the base margin for the validation set. Arguments --------- valid_sets : list List of tuples containing the train and evaluation set. valid_names: list List of tuples containing the name of train and evaluation set. start_values : np.ndarray Array containing the start values for the distributional parameters. Returns ------- valid_sets : list List of tuples containing the train and evaluation set. \"\"\" valid_sets1 = valid_sets [ 0 ] init_score_val1 = ( np . ones ( shape = ( valid_sets1 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets1 . set_init_score ( init_score_val1 . ravel ( order = \"F\" )) valid_sets2 = valid_sets [ 1 ] init_score_val2 = ( np . ones ( shape = ( valid_sets2 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets2 . set_init_score ( init_score_val2 . ravel ( order = \"F\" )) valid_sets = [ valid_sets1 , valid_sets2 ] return valid_sets def save_model ( self , model_path : str ) -> None : \"\"\" Save the model to a file. Parameters ---------- model_path : str The path to save the model. Returns ------- None \"\"\" with open ( model_path , \"wb\" ) as f : pickle . dump ( self , f ) @staticmethod def load_model ( model_path : str ): \"\"\" Load the model from a file. Parameters ---------- model_path : str The path to the saved model. Returns ------- The loaded model. \"\"\" with open ( model_path , \"rb\" ) as f : return pickle . load ( f )","title":"Parameters"},{"location":"api/#lightgbmlss.model.LightGBMLSS.cv","text":"Function to cross-validate a LightGBMLSS model with given parameters.","title":"cv()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.cv--parameters","text":"params : dict Parameters for training. Values passed through params take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None) If generator or iterator, it should yield the train and test indices for each fold. If object, it should be one of the scikit-learn splitter classes (https://scikit-learn.org/stable/modules/classes.html#splitter-classes) and have split method. This argument has highest priority over other data split arguments. nfold : int, optional (default=5) Number of folds in CV. stratified : bool, optional (default=True) Whether to perform stratified sampling. shuffle : bool, optional (default=True) Whether to shuffle before splitting data. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify feature_name as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. fpreproc : callable or None, optional (default=None) Preprocessing function that takes (dtrain, dtest, params) and returns transformed versions of those. seed : int, optional (default=0) Seed used to generate the folds (passed to numpy.random.seed). callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. eval_train_metric : bool, optional (default=False) Whether to display the train metric in progress. The score of the metric is calculated again after each training step, so there is some impact on performance. return_cvbooster : bool, optional (default=False) Whether to return Booster models trained on each fold through CVBooster .","title":"Parameters"},{"location":"api/#lightgbmlss.model.LightGBMLSS.cv--returns","text":"eval_hist : dict Evaluation history. The dictionary has the following format: {'metric1-mean': [values], 'metric1-stdv': [values], 'metric2-mean': [values], 'metric2-stdv': [values], ...}. If return_cvbooster=True , also returns trained boosters wrapped in a CVBooster object via cvbooster key. lightgbmlss/model.py 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 def cv ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , folds : Optional [ Union [ Iterable [ Tuple [ np . ndarray , np . ndarray ]], _LGBMBaseCrossValidator ]] = None , nfold : int = 5 , stratified : bool = True , shuffle : bool = True , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , fpreproc : Optional [ _LGBM_PreprocFunction ] = None , seed : int = 123 , callbacks : Optional [ List [ Callable ]] = None , eval_train_metric : bool = False , return_cvbooster : bool = False ) -> Dict [ str , Union [ List [ float ], CVBooster ]]: \"\"\"Function to cross-validate a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. folds : generator or iterator of (train_idx, test_idx) tuples, scikit-learn splitter object or None, optional (default=None) If generator or iterator, it should yield the train and test indices for each fold. If object, it should be one of the scikit-learn splitter classes (https://scikit-learn.org/stable/modules/classes.html#splitter-classes) and have ``split`` method. This argument has highest priority over other data split arguments. nfold : int, optional (default=5) Number of folds in CV. stratified : bool, optional (default=True) Whether to perform stratified sampling. shuffle : bool, optional (default=True) Whether to shuffle before splitting data. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. fpreproc : callable or None, optional (default=None) Preprocessing function that takes (dtrain, dtest, params) and returns transformed versions of those. seed : int, optional (default=0) Seed used to generate the folds (passed to numpy.random.seed). callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. eval_train_metric : bool, optional (default=False) Whether to display the train metric in progress. The score of the metric is calculated again after each training step, so there is some impact on performance. return_cvbooster : bool, optional (default=False) Whether to return Booster models trained on each fold through ``CVBooster``. Returns ------- eval_hist : dict Evaluation history. The dictionary has the following format: {'metric1-mean': [values], 'metric1-stdv': [values], 'metric2-mean': [values], 'metric2-stdv': [values], ...}. If ``return_cvbooster=True``, also returns trained boosters wrapped in a ``CVBooster`` object via ``cvbooster`` key. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) self . bstLSS_cv = lgb . cv ( params , train_set , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , num_boost_round = num_boost_round , folds = folds , nfold = nfold , stratified = False , shuffle = False , metrics = None , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , fpreproc = fpreproc , seed = seed , callbacks = callbacks , eval_train_metric = eval_train_metric , return_cvbooster = return_cvbooster ) return self . bstLSS_cv","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.expectile_plot","text":"LightGBMLSS function for plotting expectile SHapley values. pd.DataFrame Train/Test Data feature: str Specifies which feature to use for plotting Partial_Dependence plot. expectile: str Specifies which expectile to plot. plot_type: str Specifies which SHapley-plot to visualize. Currently, \"Partial_Dependence\" and \"Feature_Importance\" are supported. lightgbmlss/model.py 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 def expectile_plot ( self , X : pd . DataFrame , feature : str = \"x\" , expectile : str = \"0.05\" , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS function for plotting expectile SHapley values. X: pd.DataFrame Train/Test Data feature: str Specifies which feature to use for plotting Partial_Dependence plot. expectile: str Specifies which expectile to plot. plot_type: str Specifies which SHapley-plot to visualize. Currently, \"Partial_Dependence\" and \"Feature_Importance\" are supported. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) expect_pos = list ( self . dist . param_dict . keys ()) . index ( expectile ) if plot_type == \"Partial_Dependence\" : shap . plots . scatter ( shap_values [:, feature ][:, expect_pos ], color = shap_values [:, feature ][:, expect_pos ]) elif plot_type == \"Feature_Importance\" : shap . plots . bar ( shap_values [:, :, expect_pos ], max_display = 15 if X . shape [ 1 ] > 15 else X . shape [ 1 ])","title":"expectile_plot()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.hyper_opt","text":"Function to tune hyperparameters using optuna.","title":"hyper_opt()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.hyper_opt--arguments","text":"hp_dict: dict Dictionary of hyperparameters to tune. train_set: lgb.Dataset Training data. num_boost_round: int Number of boosting iterations. nfold: int Number of folds in CV. early_stopping_rounds: int Activates early stopping. Cross-Validation metric (average of validation metric computed over CV folds) needs to improve at least once in every early_stopping_rounds round(s) to continue training. The last entry in the evaluation history will represent the best iteration. If there's more than one metric in the eval_metric parameter given in params , the last metric will be used for early stopping. max_minutes: int Time budget in minutes, i.e., stop study after the given number of minutes. n_trials: int The number of trials. If this argument is set to None, there is no limitation on the number of trials. study_name: str Name of the hyperparameter study. silence: bool Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed: int Seed used to generate the folds (passed to numpy.random.seed). hp_seed: int Seed for random number generator used in the Bayesian hyper-parameter search.","title":"Arguments"},{"location":"api/#lightgbmlss.model.LightGBMLSS.hyper_opt--returns","text":"opt_params : dict Optimal hyper-parameters. lightgbmlss/model.py 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 def hyper_opt ( self , hp_dict : Dict , train_set : lgb . Dataset , num_boost_round = 500 , nfold = 10 , early_stopping_rounds = 20 , max_minutes = 10 , n_trials = None , study_name = None , silence = False , seed = None , hp_seed = None ): \"\"\" Function to tune hyperparameters using optuna. Arguments ---------- hp_dict: dict Dictionary of hyperparameters to tune. train_set: lgb.Dataset Training data. num_boost_round: int Number of boosting iterations. nfold: int Number of folds in CV. early_stopping_rounds: int Activates early stopping. Cross-Validation metric (average of validation metric computed over CV folds) needs to improve at least once in every **early_stopping_rounds** round(s) to continue training. The last entry in the evaluation history will represent the best iteration. If there's more than one metric in the **eval_metric** parameter given in **params**, the last metric will be used for early stopping. max_minutes: int Time budget in minutes, i.e., stop study after the given number of minutes. n_trials: int The number of trials. If this argument is set to None, there is no limitation on the number of trials. study_name: str Name of the hyperparameter study. silence: bool Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed: int Seed used to generate the folds (passed to numpy.random.seed). hp_seed: int Seed for random number generator used in the Bayesian hyper-parameter search. Returns ------- opt_params : dict Optimal hyper-parameters. \"\"\" def objective ( trial ): hyper_params = {} for param_name , param_value in hp_dict . items (): param_type = param_value [ 0 ] if param_type == \"categorical\" or param_type == \"none\" : hyper_params . update ({ param_name : trial . suggest_categorical ( param_name , param_value [ 1 ])}) elif param_type == \"float\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_float ( param_name , low = param_low , high = param_high , log = param_log ) }) elif param_type == \"int\" : param_constraints = param_value [ 1 ] param_low = param_constraints [ \"low\" ] param_high = param_constraints [ \"high\" ] param_log = param_constraints [ \"log\" ] hyper_params . update ( { param_name : trial . suggest_int ( param_name , low = param_low , high = param_high , log = param_log ) }) # Add booster if not included in dictionary if \"boosting\" not in hyper_params . keys (): hyper_params . update ({ \"boosting\" : trial . suggest_categorical ( \"boosting\" , [ \"gbdt\" ])}) # Add pruning and early stopping pruning_callback = LightGBMPruningCallback ( trial , self . dist . loss_fn ) early_stopping_callback = lgb . early_stopping ( stopping_rounds = early_stopping_rounds , verbose = False ) lgblss_param_tuning = self . cv ( hyper_params , train_set , num_boost_round = num_boost_round , nfold = nfold , callbacks = [ pruning_callback , early_stopping_callback ], seed = seed , ) # Extract the optimal number of boosting rounds opt_rounds = np . argmin ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) + 1 trial . set_user_attr ( \"opt_round\" , int ( opt_rounds )) # Extract the best score best_score = np . min ( np . array ( lgblss_param_tuning [ f \" { self . dist . loss_fn } -mean\" ])) return best_score if study_name is None : study_name = \"LightGBMLSS Hyper-Parameter Optimization\" if silence : optuna . logging . set_verbosity ( optuna . logging . WARNING ) if hp_seed is not None : sampler = TPESampler ( seed = hp_seed ) else : sampler = TPESampler () pruner = optuna . pruners . MedianPruner ( n_startup_trials = 10 , n_warmup_steps = 20 ) study = optuna . create_study ( sampler = sampler , pruner = pruner , direction = \"minimize\" , study_name = study_name ) study . optimize ( objective , n_trials = n_trials , timeout = 60 * max_minutes , show_progress_bar = True ) print ( \" \\n Hyper-Parameter Optimization successfully finished.\" ) print ( \" Number of finished trials: \" , len ( study . trials )) print ( \" Best trial:\" ) opt_param = study . best_trial # Add optimal stopping round opt_param . params [ \"opt_rounds\" ] = study . trials_dataframe ()[ \"user_attrs_opt_round\" ][ study . trials_dataframe ()[ \"value\" ] . idxmin ()] opt_param . params [ \"opt_rounds\" ] = int ( opt_param . params [ \"opt_rounds\" ]) print ( \" Value: {} \" . format ( opt_param . value )) print ( \" Params: \" ) for key , value in opt_param . params . items (): print ( \" {} : {} \" . format ( key , value )) return opt_param . params","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.load_model","text":"Load the model from a file.","title":"load_model()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.load_model--parameters","text":"model_path : str The path to the saved model.","title":"Parameters"},{"location":"api/#lightgbmlss.model.LightGBMLSS.load_model--returns","text":"The loaded model. lightgbmlss/model.py 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 @staticmethod def load_model ( model_path : str ): \"\"\" Load the model from a file. Parameters ---------- model_path : str The path to the saved model. Returns ------- The loaded model. \"\"\" with open ( model_path , \"rb\" ) as f : return pickle . load ( f )","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.plot","text":"LightGBMLSS SHap plotting function.","title":"plot()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.plot--arguments","text":"X: pd.DataFrame Train/Test Data feature: str Specifies which feature is to be plotted. parameter: str Specifies which distributional parameter is to be plotted. max_display: int Specifies the maximum number of features to be displayed. plot_type: str Specifies the type of plot: \"Partial_Dependence\" plots the partial dependence of the parameter on the feature. \"Feature_Importance\" plots the feature importance of the parameter. lightgbmlss/model.py 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 def plot ( self , X : pd . DataFrame , feature : str = \"x\" , parameter : str = \"loc\" , max_display : int = 15 , plot_type : str = \"Partial_Dependence\" ): \"\"\" LightGBMLSS SHap plotting function. Arguments: --------- X: pd.DataFrame Train/Test Data feature: str Specifies which feature is to be plotted. parameter: str Specifies which distributional parameter is to be plotted. max_display: int Specifies the maximum number of features to be displayed. plot_type: str Specifies the type of plot: \"Partial_Dependence\" plots the partial dependence of the parameter on the feature. \"Feature_Importance\" plots the feature importance of the parameter. \"\"\" shap . initjs () explainer = shap . TreeExplainer ( self . booster ) shap_values = explainer ( X ) param_pos = self . dist . distribution_arg_names . index ( parameter ) if plot_type == \"Partial_Dependence\" : if self . dist . n_dist_param == 1 : shap . plots . scatter ( shap_values [:, feature ], color = shap_values [:, feature ]) else : shap . plots . scatter ( shap_values [:, feature ][:, param_pos ], color = shap_values [:, feature ][:, param_pos ]) elif plot_type == \"Feature_Importance\" : if self . dist . n_dist_param == 1 : shap . plots . bar ( shap_values , max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ]) else : shap . plots . bar ( shap_values [:, :, param_pos ], max_display = max_display if X . shape [ 1 ] > max_display else X . shape [ 1 ] )","title":"Arguments:"},{"location":"api/#lightgbmlss.model.LightGBMLSS.predict","text":"Function that predicts from the trained model.","title":"predict()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.predict--arguments","text":"data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution.","title":"Arguments"},{"location":"api/#lightgbmlss.model.LightGBMLSS.predict--returns","text":"predt_df : pd.DataFrame Predictions. lightgbmlss/model.py 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 def predict ( self , data : pd . DataFrame , pred_type : str = \"parameters\" , n_samples : int = 1000 , quantiles : list = [ 0.1 , 0.5 , 0.9 ], seed : str = 123 ): \"\"\" Function that predicts from the trained model. Arguments --------- data : pd.DataFrame Data to predict from. pred_type : str Type of prediction: - \"samples\" draws n_samples from the predicted distribution. - \"quantiles\" calculates the quantiles from the predicted distribution. - \"parameters\" returns the predicted distributional parameters. - \"expectiles\" returns the predicted expectiles. n_samples : int Number of samples to draw from the predicted distribution. quantiles : List[float] List of quantiles to calculate from the predicted distribution. seed : int Seed for random number generator used to draw samples from the predicted distribution. Returns ------- predt_df : pd.DataFrame Predictions. \"\"\" # Predict predt_df = self . dist . predict_dist ( booster = self . booster , data = data , start_values = self . start_values , pred_type = pred_type , n_samples = n_samples , quantiles = quantiles , seed = seed ) return predt_df","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.save_model","text":"Save the model to a file.","title":"save_model()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.save_model--parameters","text":"model_path : str The path to save the model.","title":"Parameters"},{"location":"api/#lightgbmlss.model.LightGBMLSS.save_model--returns","text":"None lightgbmlss/model.py 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 def save_model ( self , model_path : str ) -> None : \"\"\" Save the model to a file. Parameters ---------- model_path : str The path to save the model. Returns ------- None \"\"\" with open ( model_path , \"wb\" ) as f : pickle . dump ( self , f )","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_init_score","text":"Set init_score for distributions.","title":"set_init_score()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_init_score--arguments","text":"dmatrix : Dataset Dataset to set base margin for.","title":"Arguments"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_init_score--returns","text":"None lightgbmlss/model.py 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 def set_init_score ( self , dmatrix : Dataset ) -> None : \"\"\" Set init_score for distributions. Arguments --------- dmatrix : Dataset Dataset to set base margin for. Returns ------- None \"\"\" if self . start_values is None : _ , self . start_values = self . dist . calculate_start_values ( dmatrix . get_label ()) init_score = ( np . ones ( shape = ( dmatrix . get_label () . shape [ 0 ], 1 ))) * self . start_values dmatrix . set_init_score ( init_score . ravel ( order = \"F\" ))","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_params","text":"Set parameters for distributional model.","title":"set_params()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_params--arguments","text":"params : Dict[str, Any] Parameters for model.","title":"Arguments"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_params--returns","text":"params : Dict[str, Any] Updated Parameters for model. lightgbmlss/model.py 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 def set_params ( self , params : Dict [ str , Any ]) -> Dict [ str , Any ]: \"\"\" Set parameters for distributional model. Arguments --------- params : Dict[str, Any] Parameters for model. Returns ------- params : Dict[str, Any] Updated Parameters for model. \"\"\" params_adj = { \"num_class\" : self . dist . n_dist_param , \"metric\" : \"None\" , \"objective\" : \"None\" , \"random_seed\" : 123 , \"verbose\" : - 1 } params . update ( params_adj ) return params","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_valid_margin","text":"Function that sets the base margin for the validation set.","title":"set_valid_margin()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_valid_margin--arguments","text":"valid_sets : list List of tuples containing the train and evaluation set. valid_names: list List of tuples containing the name of train and evaluation set. start_values : np.ndarray Array containing the start values for the distributional parameters.","title":"Arguments"},{"location":"api/#lightgbmlss.model.LightGBMLSS.set_valid_margin--returns","text":"valid_sets : list List of tuples containing the train and evaluation set. lightgbmlss/model.py 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 def set_valid_margin ( self , valid_sets : list , start_values : np . ndarray ) -> list : \"\"\" Function that sets the base margin for the validation set. Arguments --------- valid_sets : list List of tuples containing the train and evaluation set. valid_names: list List of tuples containing the name of train and evaluation set. start_values : np.ndarray Array containing the start values for the distributional parameters. Returns ------- valid_sets : list List of tuples containing the train and evaluation set. \"\"\" valid_sets1 = valid_sets [ 0 ] init_score_val1 = ( np . ones ( shape = ( valid_sets1 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets1 . set_init_score ( init_score_val1 . ravel ( order = \"F\" )) valid_sets2 = valid_sets [ 1 ] init_score_val2 = ( np . ones ( shape = ( valid_sets2 . get_label () . shape [ 0 ], 1 ))) * start_values valid_sets2 . set_init_score ( init_score_val2 . ravel ( order = \"F\" )) valid_sets = [ valid_sets1 , valid_sets2 ] return valid_sets","title":"Returns"},{"location":"api/#lightgbmlss.model.LightGBMLSS.train","text":"Function to perform the training of a LightGBMLSS model with given parameters.","title":"train()"},{"location":"api/#lightgbmlss.model.LightGBMLSS.train--parameters","text":"params : dict Parameters for training. Values passed through params take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. valid_sets : list of Dataset, or None, optional (default=None) List of data to be evaluated on during training. valid_names : list of str, or None, optional (default=None) Names of valid_sets . init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify feature_name as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. keep_training_booster : bool, optional (default=False) Whether the returned Booster will be used to keep training. If False, the returned value will be converted into _InnerPredictor before returning. This means you won't be able to use eval , eval_train or eval_valid methods of the returned Booster. When your model is very large and cause the memory error, you can try to set this param to True to avoid the model conversion performed during the internal call of model_to_string . You can still use _InnerPredictor as init_model for future continue training. callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information.","title":"Parameters"},{"location":"api/#lightgbmlss.model.LightGBMLSS.train--returns","text":"booster : Booster The trained Booster model. lightgbmlss/model.py 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 def train ( self , params : Dict [ str , Any ], train_set : Dataset , num_boost_round : int = 100 , valid_sets : Optional [ List [ Dataset ]] = None , valid_names : Optional [ List [ str ]] = None , init_model : Optional [ Union [ str , Path , Booster ]] = None , feature_name : _LGBM_FeatureNameConfiguration = 'auto' , categorical_feature : _LGBM_CategoricalFeatureConfiguration = 'auto' , keep_training_booster : bool = False , callbacks : Optional [ List [ Callable ]] = None ) -> Booster : \"\"\"Function to perform the training of a LightGBMLSS model with given parameters. Parameters ---------- params : dict Parameters for training. Values passed through ``params`` take precedence over those supplied via arguments. train_set : Dataset Data to be trained on. num_boost_round : int, optional (default=100) Number of boosting iterations. valid_sets : list of Dataset, or None, optional (default=None) List of data to be evaluated on during training. valid_names : list of str, or None, optional (default=None) Names of ``valid_sets``. init_model : str, pathlib.Path, Booster or None, optional (default=None) Filename of LightGBM model or Booster instance used for continue training. feature_name : list of str, or 'auto', optional (default=\"auto\") Feature names. If 'auto' and data is pandas DataFrame, data columns names are used. categorical_feature : list of str or int, or 'auto', optional (default=\"auto\") Categorical features. If list of int, interpreted as indices. If list of str, interpreted as feature names (need to specify ``feature_name`` as well). If 'auto' and data is pandas DataFrame, pandas unordered categorical columns are used. All values in categorical features will be cast to int32 and thus should be less than int32 max value (2147483647). Large values could be memory consuming. Consider using consecutive integers starting from zero. All negative values in categorical features will be treated as missing values. The output cannot be monotonically constrained with respect to a categorical feature. Floating point numbers in categorical features will be rounded towards 0. keep_training_booster : bool, optional (default=False) Whether the returned Booster will be used to keep training. If False, the returned value will be converted into _InnerPredictor before returning. This means you won't be able to use ``eval``, ``eval_train`` or ``eval_valid`` methods of the returned Booster. When your model is very large and cause the memory error, you can try to set this param to ``True`` to avoid the model conversion performed during the internal call of ``model_to_string``. You can still use _InnerPredictor as ``init_model`` for future continue training. callbacks : list of callable, or None, optional (default=None) List of callback functions that are applied at each iteration. See Callbacks in Python API for more information. Returns ------- booster : Booster The trained Booster model. \"\"\" self . set_params ( params ) self . set_init_score ( train_set ) if valid_sets is not None : valid_sets = self . set_valid_margin ( valid_sets , self . start_values ) self . booster = lgb . train ( params , train_set , num_boost_round = num_boost_round , fobj = self . dist . objective_fn , feval = self . dist . metric_fn , valid_sets = valid_sets , valid_names = valid_names , init_model = init_model , feature_name = feature_name , categorical_feature = categorical_feature , keep_training_booster = keep_training_booster , callbacks = callbacks )","title":"Returns"},{"location":"api/#lightgbmlss.utils","text":"","title":"utils"},{"location":"api/#lightgbmlss.utils.exp_fn","text":"Exponential function used to ensure predt is strictly positive.","title":"exp_fn()"},{"location":"api/#lightgbmlss.utils.exp_fn--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.exp_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 def exp_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Exponential function used to ensure predt is strictly positive. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . exp ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.exp_fn_df","text":"Exponential function used for Student-T distribution.","title":"exp_fn_df()"},{"location":"api/#lightgbmlss.utils.exp_fn_df--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.exp_fn_df--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 def exp_fn_df ( predt : torch . tensor ) -> torch . tensor : \"\"\" Exponential function used for Student-T distribution. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . exp ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt + torch . tensor ( 2.0 , dtype = predt . dtype )","title":"Returns"},{"location":"api/#lightgbmlss.utils.gumbel_softmax_fn","text":"Gumbel-softmax function used to ensure predt is adding to one. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017.","title":"gumbel_softmax_fn()"},{"location":"api/#lightgbmlss.utils.gumbel_softmax_fn--arguments","text":"predt: torch.tensor Predicted values. tau: float, non-negative scalar temperature. Temperature parameter for the Gumbel-softmax distribution. As tau -> 0, the output becomes more discrete, and as tau -> inf, the output becomes more uniform.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.gumbel_softmax_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 def gumbel_softmax_fn ( predt : torch . tensor , tau : float = 1.0 ) -> torch . tensor : \"\"\" Gumbel-softmax function used to ensure predt is adding to one. The Gumbel-softmax distribution is a continuous distribution over the simplex, which can be thought of as a \"soft\" version of a categorical distribution. It\u2019s a way to draw samples from a categorical distribution in a differentiable way. The motivation behind using the Gumbel-Softmax is to make the discrete sampling process of categorical variables differentiable, which is useful in gradient-based optimization problems. To sample from a Gumbel-Softmax distribution, one would use the Gumbel-max trick: add a Gumbel noise to logits and apply the softmax. Formally, given a vector z, the Gumbel-softmax function s(z,tau)_i for a component i at temperature tau is defined as: s(z,tau)_i = frac{e^{(z_i + g_i) / tau}}{sum_{j=1}^M e^{(z_j + g_j) / tau}} where g_i is a sample from the Gumbel(0, 1) distribution. The parameter tau (temperature) controls the sharpness of the output distribution. As tau approaches 0, the mixing probabilities become more discrete, and as tau approaches infty, the mixing probabilities become more uniform. For more information we refer to Jang, E., Gu, Shixiang and Poole, B. \"Categorical Reparameterization with Gumbel-Softmax\", ICLR, 2017. Arguments --------- predt: torch.tensor Predicted values. tau: float, non-negative scalar temperature. Temperature parameter for the Gumbel-softmax distribution. As tau -> 0, the output becomes more discrete, and as tau -> inf, the output becomes more uniform. Returns ------- predt: torch.tensor Predicted values. \"\"\" torch . manual_seed ( 123 ) predt = gumbel_softmax ( nan_to_num ( predt ), tau = tau , dim = 1 ) + torch . tensor ( 0 , dtype = predt . dtype ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.identity_fn","text":"Identity mapping of predt.","title":"identity_fn()"},{"location":"api/#lightgbmlss.utils.identity_fn--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.identity_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 def identity_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Identity mapping of predt. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = nan_to_num ( predt ) + torch . tensor ( 0 , dtype = predt . dtype ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.nan_to_num","text":"Replace nan, inf and -inf with the mean of predt.","title":"nan_to_num()"},{"location":"api/#lightgbmlss.utils.nan_to_num--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.nan_to_num--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def nan_to_num ( predt : torch . tensor ) -> torch . tensor : \"\"\" Replace nan, inf and -inf with the mean of predt. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . nan_to_num ( predt , nan = float ( torch . nanmean ( predt )), posinf = float ( torch . nanmean ( predt )), neginf = float ( torch . nanmean ( predt )) ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.relu_fn","text":"Function used to ensure predt are scaled to max(0, predt).","title":"relu_fn()"},{"location":"api/#lightgbmlss.utils.relu_fn--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.relu_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 def relu_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Function used to ensure predt are scaled to max(0, predt). Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . relu ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.sigmoid_fn","text":"Function used to ensure predt are scaled to (0,1).","title":"sigmoid_fn()"},{"location":"api/#lightgbmlss.utils.sigmoid_fn--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.sigmoid_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 def sigmoid_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Function used to ensure predt are scaled to (0,1). Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = torch . sigmoid ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) predt = torch . clamp ( predt , 1e-03 , 1 - 1e-03 ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.softmax_fn","text":"Softmax function used to ensure predt is adding to one.","title":"softmax_fn()"},{"location":"api/#lightgbmlss.utils.softmax_fn--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.softmax_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 def softmax_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Softmax function used to ensure predt is adding to one. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = softmax ( nan_to_num ( predt ), dim = 1 ) + torch . tensor ( 0 , dtype = predt . dtype ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.softplus_fn","text":"Softplus function used to ensure predt is strictly positive.","title":"softplus_fn()"},{"location":"api/#lightgbmlss.utils.softplus_fn--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.softplus_fn--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 def softplus_fn ( predt : torch . tensor ) -> torch . tensor : \"\"\" Softplus function used to ensure predt is strictly positive. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = softplus ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt","title":"Returns"},{"location":"api/#lightgbmlss.utils.softplus_fn_df","text":"Softplus function used for Student-T distribution.","title":"softplus_fn_df()"},{"location":"api/#lightgbmlss.utils.softplus_fn_df--arguments","text":"predt: torch.tensor Predicted values.","title":"Arguments"},{"location":"api/#lightgbmlss.utils.softplus_fn_df--returns","text":"predt: torch.tensor Predicted values. lightgbmlss/utils.py 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 def softplus_fn_df ( predt : torch . tensor ) -> torch . tensor : \"\"\" Softplus function used for Student-T distribution. Arguments --------- predt: torch.tensor Predicted values. Returns ------- predt: torch.tensor Predicted values. \"\"\" predt = softplus ( nan_to_num ( predt )) + torch . tensor ( 1e-06 , dtype = predt . dtype ) return predt + torch . tensor ( 2.0 , dtype = predt . dtype )","title":"Returns"},{"location":"dgbm/","text":"Introduction The development of modelling approaches that approximate and describe the data generating processes underlying the observed data in as much detail as possible is a guiding principle in both statistics and machine learning. We therefore strongly agree with the statement of Hothorn et al. (2014) that ''the ultimate goal of any regression analysis is to obtain information about the entire conditional distribution \\(F_{Y}(y|\\mathbf{x})\\) of a response given a set of explanatory variables'' . ''Practitioners expect forecasting to reduce future uncertainty by providing accurate predictions like those in hard sciences. However, this is a great misconception. A major purpose of forecasting is not to reduce uncertainty but reveal its full extent and implications by estimating it as precisely as possible. [...] The challenge for the forecasting field is how to persuade practitioners of the reality that all forecasts are uncertain and that this uncertainty cannot be ignored, as doing so could lead to catastrophic consequences.'' Makridakis (2022b) It has not been too long, though, that most regression models focused on estimating the conditional mean \\(\\mathbb{E}(Y|\\mathbf{X} = \\mathbf{x})\\) only, implicitly treating higher moments of the conditional distribution \\(F_{Y}(y|\\mathbf{x})\\) as fixed nuisance parameters. As such, models that minimize an \\(\\ell_{2}\\) -type loss for the conditional mean are not able to fully exploit the information contained in the data, since this is equivalent to assuming a Normal distribution with constant variance. In real world situations, however, the data generating process is usually less well behaved, exhibiting characteristics such as heteroskedasticity, varying degrees of skewness and kurtosis or intermittent and sporadic behaviour. In recent years, however, there has been a clear shift in both academic and corporate research toward modelling the entire conditional distribution. This change in attention is most evident in the recent M5 forecasting competition (Makridakis et al., 2022a,b), which differed from previous ones in that it consisted of two parallel competitions: in addition to providing accurate point forecasts, participants were also asked to forecast nine different quantiles to approximate the distribution of future sales. Distributional Gradient Boosting Machines This section introduces the general idea of distributional modelling. For a more thorough introduction, we refer the interested reader to Rigby and Stasinopoulos (2005); Klein et al. (2015); Stasinopoulos et al. (2017). GAMLSS Probabilistic forecasts are predictions in the form of a probability distribution, rather than a single point estimate only. In this context, the introduction of Generalized Additive Models for Location Scale and Shape (GAMLSS) by Rigby and Stasinopoulos (2005) has stimulated a lot of research and culminated in a new research branch that focuses on modelling the entire conditional distribution in dependence of covariates. Univariate Targets In its original formulation, GAMLSS assume a univariate response \\(y\\) to follow a distribution \\(\\mathcal{D}\\bigl(\\boldsymbol{\\theta}(x)\\bigr)\\) that depends on up to four parameters, i.e., \\(y_{i} \\stackrel{ind}{\\sim} \\mathcal{D}\\bigl(\\boldsymbol{\\theta}_{i}(x_{i})\\bigr)\\) with \\(\\boldsymbol{\\theta}_{i}(x_{i}) = \\bigl(\\mu_{i}(x_{i}), \\sigma^{2}_{i}(x_{i}), \\nu_{i}(x_{i}), \\tau_{i}(x_{i})\\bigr), i=1,\\ldots, N\\) , where \\(\\mu_{i}(\\cdot)\\) and \\(\\sigma^{2}_{i}(\\cdot)\\) are often location and scale parameters, respectively, while \\(\\nu_{i}(\\cdot)\\) and \\(\\tau_{i}(\\cdot)\\) correspond to shape parameters such as skewness and kurtosis. Hence, the framework allows to model not only the mean (or location) but all parameters as functions of explanatory variables. It is important to note that distributional modelling implies that observations are independent, but not necessarily identical realizations \\(y \\stackrel{ind}{\\sim} \\mathcal{D}\\big(\\boldsymbol{\\theta}(x)\\big)\\) , since all distributional parameters \\(\\boldsymbol{\\theta}(x)\\) are related to and allowed to change with covariates. In contrast to Generalized Linear (GLM) and Generalized Additive Models (GAM), the assumption of the response distribution belonging to an exponential family is relaxed in GAMLSS and replaced by a more general class of distributions, including highly skewed and/or kurtotic continuous, discrete and mixed discrete, as well as zero-inflated distributions. While the original formulation of GAMLSS in Rigby and Stasinopoulos (2005) suggests that any distribution can be described by location, scale and shape parameters, it is not necessarily true that the observed data distribution can actually be characterized by all of these parameters. Hence, we follow Klein et al. (2015b) and use the term distributional modelling and GAMLSS interchangeably. From a frequentist point of view, distributional modelling can be formulated as follows \\[\\begin{equation} y_{i} \\stackrel{ind}{\\sim} \\mathcal{D} \\begin{pmatrix} h_{1}\\bigl(\\theta_{i1}(x_{i})\\bigr) = \\eta_{i1} \\\\ h_{2}\\bigl(\\theta_{i2}(x_{i})\\bigr) = \\eta_{i2} \\\\ \\vdots \\\\ h_{K}\\bigl(\\theta_{iK}(x_{i})\\bigr) = \\eta_{iK} \\end{pmatrix} \\end{equation}\\] for \\(i = 1, \\ldots, N\\) , where \\(\\mathcal{D}\\) denotes a parametric distribution for the response \\(\\textbf{y} = (y_{1}, \\ldots, y_{N})^{\\prime}\\) that depends on \\(K\\) distributional parameters \\(\\theta_{k}\\) , \\(k = 1, \\ldots, K\\) , and with \\(h_{k}(\\cdot)\\) denoting a known function relating distributional parameters to predictors \\(\\eta_{k}\\) . In its most generic form, the predictor \\(\\eta_{k}\\) is given by \\[\\begin{equation} \\eta_{k} = f_{k}(\\mathbf{x}), \\qquad k = 1, \\ldots, K \\end{equation}\\] Within the original distributional regression framework, the functions \\(f_{k}(\\cdot)\\) usually represent a combination of linear and GAM-type predictors, which allows to estimate linear effects or categorical variables, as well as highly non-linear and spatial effects using a Spline-based basis function approach. The predictor specification \\(\\eta_{k}\\) is generic enough to use tree-based models as well, which allows us to extend LightGBM to a probabilistic framework. Mixture Distributions Mixture densities, or mixture distributions, offer an extension to the notion of traditional univariate distributions by allowing the observed data to be thought of as arising from multiple underlying processes. In its essence, a mixture distribution is a weighted combination of several component distributions, where each component contributes to the overall mixture distribution, with the weights indicating the importance of each component. For instance, if you imagine the observed data distribution having multiple modes, a mixture of Gaussians could be employed to capture each mode with a separate Gaussian distribution. For each component of the mixture, there would be a set of parameters that depend on covariates, and additional mixing coefficients which are also modeled as a function of covariates. This is particularly useful when a single parametric distribution cannot adequately capture the underlying data generating process. A mixture distribution can be represented as follows: \\[\\begin{equation} f\\bigl(y_{i} | \\boldsymbol{\\theta}_{i}(x_{i})\\bigr) = \\sum_{m=1}^{M} w_{i,m}(x_{i}) \\cdot f_{m}\\bigl(y_{i} | \\boldsymbol{\\theta}_{i,m}(x_{i})\\bigr) \\end{equation}\\] where \\(f(\\cdot)\\) represents the mixture density for the \\(i\\) -th observation, \\(f_{m}(\\cdot)\\) is the \\(m\\) -th component density, each with its own set of parameters \\(\\boldsymbol{\\theta}_{i,m}(\\cdot)\\) , and \\(w_{i,m}(\\cdot)\\) represent the weights of the \\(m\\) -th component in the mixture, subject to \\(\\sum_{j=1}^{M} w_{i,m} = 1\\) . The components can either be a combination of different parametric univariate distributions, such as a combination of a Normal and a StudentT, or, as in our implementation, a combination of the same distribution-type with different parameterizations, e.g., Gaussian-Mixture or StudentT-Mixture. The choice of the component distributions depends on the characteristics of the data and the underlying assumptions. Due to their high flexibility, mixture densities can portray a diverse range of shapes, making them adaptable to a plethora of datasets. By incorporating mixture densities within our framework, users can gain a more comprehensive understanding of the conditional distribution of the response variable, providing a more accurate representation of the data generating process. Hence, mixture densities greatly expand the expressive power of distributional modeling frameworks like GAMLSS, allowing one to capture a wider array of data distributions with enhanced flexibility by combining multiple members of this set. Normalizing Flows Although the GAMLSS framework offers considerable flexibility, parametric distributions may prove not flexible enough to provide a reasonable approximation for certain dataset, e.g., for multi-modal distributions. For such cases, it is preferable to relax the assumption of a parametric distribution and approximate the data non-parametrically. While there are several alternatives for learning conditional distributions, we propose to use Normalizing Flows for their ability to fit complex distributions with only a few parameters. The principle that underlies Normalizing Flows is to turn a simple base distribution, e.g., \\(F_{Z}(\\mathbf{z}) = N(0,1)\\) , into a more complex and realistic distribution of the target variable \\(F_{Y}(\\mathbf{y})\\) by applying several bijective transformations \\(h_{j}\\) , \\(j = 1, \\ldots, J\\) to the variable of the base distribution \\[\\begin{equation} \\mathbf{y} = h_{J} \\circ h_{J-1} \\circ \\cdots \\circ h_{1}(\\mathbf{z}) \\end{equation}\\] Based on the complete transformation function \\(h=h_{J}\\circ\\ldots\\circ h_{1}\\) , the density of \\(\\mathbf{y}\\) is then given by the change of variables theorem \\[\\begin{equation} f_{Y}(\\mathbf{y}) = f_{Z}\\big(h(\\mathbf{y})\\big) \\cdot \\Bigg|\\frac{\\partial h(\\mathbf{y})}{\\partial \\mathbf{y}}\\Bigg| \\end{equation}\\] where scaling with the Jacobian determinant \\(|h^{\\prime}(\\mathbf{y})| = |\\partial h(\\mathbf{y}) / \\partial \\mathbf{y}|\\) ensures \\(f_{Y}(\\mathbf{y})\\) to be a proper density integrating to one. The composition of these transformations is invertible, allowing one to sample from the complex distribution by transforming samples from the base distribution. Image source: https://tikz.net/janosh/normalizing-flow.png Our Normalizing Flow approach is based on element-wise rational splines of linear or quadratic order as introduced by Durkan (2019) and Dolatabadi (2020) and implemented in Pyro, since they offer a combination of functional flexibility and numerical stability. Despite this specific choice, our framework is generic enough to accommodate the use of other parametrizable Normalizing Flows. Gradient Boosting Machines for Location, Scale and Shape We draw inspiration from GAMLSS and label our model as LightGBM for Location, Scale and Shape (LightGBMLSS). Despite its nominal reference to GAMLSS, our framework is designed in such a way to accommodate the modeling of a wide range of parametrizable distributions that go beyond location, scale and shape. LightGBMLSS requires the specification of a suitable distribution from which Gradients and Hessians are derived. These represent the partial first and second order derivatives of the log-likelihood with respect to the parameter of interest. GBMLSS are based on multi-parameter optimization, where a separate tree is grown for each parameter. Estimation of Gradients and Hessians, as well as the evaluation of the loss function is done simultaneously for all parameters. Gradients and Hessians are derived using PyTorch's automatic differentiation capabilities. The flexibility offered by automatic differentiation allows users to easily implement novel or customized parametric distributions for which Gradients and Hessians are difficult to derive analytically. It also facilitates the usage of Normalizing Flows, or to add additional constraints to the loss function. To improve the convergence and stability of GBMLSS estimation, unconditional Maximum Likelihood estimates of the parameters are used as offset values. To enable a deeper understanding of the data generating process, GBMLSS also provide attribute importance and partial dependence plots using the Shapley-Value approach. References C. M. Bishop. Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK, 1994. Hadi Mohaghegh Dolatabadi, Sarah Erfani, and Christopher Leckie. Invertible Generative Modeling using Linear Rational Splines. In The 23rd International Conference on Artificial Intelligence and Statistics (AISTATS), 4236\u20134246, 2020. Conor Durkan, Artur Bekasov, Iain Murray, and George Papamakarios. Neural Spline Flows. In Proceedings of the 33rd International Conference on Neural Information Processing Systems. 2019. Nadja Klein, Thomas Kneib, and Stefan Lang. Bayesian Generalized Additive Models for Location, Scale, and Shape for Zero-Inflated and Overdispersed Count Data. Journal of the American Statistical Association, 110(509):405\u2013419, 2015. Alexander M\u00e4rz, and Thomas Kneib. Distributional Gradient Boosting Machines, 2022b. Alexander M\u00e4rz. XGBoostLSS - An extension of XGBoost to probabilistic forecasting, 2019. Spyros Makridakis, Evangelos Spiliotis, and Vassilios Assimakopoulos. The M5 competition: Background, organization, and implementation. International Journal of Forecasting, 38(4):1325\u20131336, 2022a. Spyros Makridakis, Evangelos Spiliotis, Vassilios Assimakopoulos, Zhi Chen, Anil Gaba, Ilia Tsetlin, and Robert L. Winkler. The M5 uncertainty competition: Results, findings and conclusions. International Journal of Forecasting, 38(4):1365\u20131385, 2022b. R. A. Rigby and D. M. Stasinopoulos. Generalized additive models for location, scale and shape. Journal of the Royal Statistical Society: Series C (Applied Statistics), 54(3):507\u2013554, 2005. Mikis D. Stasinopoulos, Robert A. Rigby, Gillian Z. Heller, Vlasios Voudouris, and Fernanda de Bastiani. Flexible Regression and Smoothing: Using GAMLSS in R. Chapman & Hall / CRC The R Series. CRC Press, London, 2017.","title":"Distributional Modelling"},{"location":"dgbm/#introduction","text":"The development of modelling approaches that approximate and describe the data generating processes underlying the observed data in as much detail as possible is a guiding principle in both statistics and machine learning. We therefore strongly agree with the statement of Hothorn et al. (2014) that ''the ultimate goal of any regression analysis is to obtain information about the entire conditional distribution \\(F_{Y}(y|\\mathbf{x})\\) of a response given a set of explanatory variables'' . ''Practitioners expect forecasting to reduce future uncertainty by providing accurate predictions like those in hard sciences. However, this is a great misconception. A major purpose of forecasting is not to reduce uncertainty but reveal its full extent and implications by estimating it as precisely as possible. [...] The challenge for the forecasting field is how to persuade practitioners of the reality that all forecasts are uncertain and that this uncertainty cannot be ignored, as doing so could lead to catastrophic consequences.'' Makridakis (2022b) It has not been too long, though, that most regression models focused on estimating the conditional mean \\(\\mathbb{E}(Y|\\mathbf{X} = \\mathbf{x})\\) only, implicitly treating higher moments of the conditional distribution \\(F_{Y}(y|\\mathbf{x})\\) as fixed nuisance parameters. As such, models that minimize an \\(\\ell_{2}\\) -type loss for the conditional mean are not able to fully exploit the information contained in the data, since this is equivalent to assuming a Normal distribution with constant variance. In real world situations, however, the data generating process is usually less well behaved, exhibiting characteristics such as heteroskedasticity, varying degrees of skewness and kurtosis or intermittent and sporadic behaviour. In recent years, however, there has been a clear shift in both academic and corporate research toward modelling the entire conditional distribution. This change in attention is most evident in the recent M5 forecasting competition (Makridakis et al., 2022a,b), which differed from previous ones in that it consisted of two parallel competitions: in addition to providing accurate point forecasts, participants were also asked to forecast nine different quantiles to approximate the distribution of future sales.","title":"Introduction"},{"location":"dgbm/#distributional-gradient-boosting-machines","text":"This section introduces the general idea of distributional modelling. For a more thorough introduction, we refer the interested reader to Rigby and Stasinopoulos (2005); Klein et al. (2015); Stasinopoulos et al. (2017).","title":"Distributional Gradient Boosting Machines"},{"location":"dgbm/#gamlss","text":"Probabilistic forecasts are predictions in the form of a probability distribution, rather than a single point estimate only. In this context, the introduction of Generalized Additive Models for Location Scale and Shape (GAMLSS) by Rigby and Stasinopoulos (2005) has stimulated a lot of research and culminated in a new research branch that focuses on modelling the entire conditional distribution in dependence of covariates.","title":"GAMLSS"},{"location":"dgbm/#univariate-targets","text":"In its original formulation, GAMLSS assume a univariate response \\(y\\) to follow a distribution \\(\\mathcal{D}\\bigl(\\boldsymbol{\\theta}(x)\\bigr)\\) that depends on up to four parameters, i.e., \\(y_{i} \\stackrel{ind}{\\sim} \\mathcal{D}\\bigl(\\boldsymbol{\\theta}_{i}(x_{i})\\bigr)\\) with \\(\\boldsymbol{\\theta}_{i}(x_{i}) = \\bigl(\\mu_{i}(x_{i}), \\sigma^{2}_{i}(x_{i}), \\nu_{i}(x_{i}), \\tau_{i}(x_{i})\\bigr), i=1,\\ldots, N\\) , where \\(\\mu_{i}(\\cdot)\\) and \\(\\sigma^{2}_{i}(\\cdot)\\) are often location and scale parameters, respectively, while \\(\\nu_{i}(\\cdot)\\) and \\(\\tau_{i}(\\cdot)\\) correspond to shape parameters such as skewness and kurtosis. Hence, the framework allows to model not only the mean (or location) but all parameters as functions of explanatory variables. It is important to note that distributional modelling implies that observations are independent, but not necessarily identical realizations \\(y \\stackrel{ind}{\\sim} \\mathcal{D}\\big(\\boldsymbol{\\theta}(x)\\big)\\) , since all distributional parameters \\(\\boldsymbol{\\theta}(x)\\) are related to and allowed to change with covariates. In contrast to Generalized Linear (GLM) and Generalized Additive Models (GAM), the assumption of the response distribution belonging to an exponential family is relaxed in GAMLSS and replaced by a more general class of distributions, including highly skewed and/or kurtotic continuous, discrete and mixed discrete, as well as zero-inflated distributions. While the original formulation of GAMLSS in Rigby and Stasinopoulos (2005) suggests that any distribution can be described by location, scale and shape parameters, it is not necessarily true that the observed data distribution can actually be characterized by all of these parameters. Hence, we follow Klein et al. (2015b) and use the term distributional modelling and GAMLSS interchangeably. From a frequentist point of view, distributional modelling can be formulated as follows \\[\\begin{equation} y_{i} \\stackrel{ind}{\\sim} \\mathcal{D} \\begin{pmatrix} h_{1}\\bigl(\\theta_{i1}(x_{i})\\bigr) = \\eta_{i1} \\\\ h_{2}\\bigl(\\theta_{i2}(x_{i})\\bigr) = \\eta_{i2} \\\\ \\vdots \\\\ h_{K}\\bigl(\\theta_{iK}(x_{i})\\bigr) = \\eta_{iK} \\end{pmatrix} \\end{equation}\\] for \\(i = 1, \\ldots, N\\) , where \\(\\mathcal{D}\\) denotes a parametric distribution for the response \\(\\textbf{y} = (y_{1}, \\ldots, y_{N})^{\\prime}\\) that depends on \\(K\\) distributional parameters \\(\\theta_{k}\\) , \\(k = 1, \\ldots, K\\) , and with \\(h_{k}(\\cdot)\\) denoting a known function relating distributional parameters to predictors \\(\\eta_{k}\\) . In its most generic form, the predictor \\(\\eta_{k}\\) is given by \\[\\begin{equation} \\eta_{k} = f_{k}(\\mathbf{x}), \\qquad k = 1, \\ldots, K \\end{equation}\\] Within the original distributional regression framework, the functions \\(f_{k}(\\cdot)\\) usually represent a combination of linear and GAM-type predictors, which allows to estimate linear effects or categorical variables, as well as highly non-linear and spatial effects using a Spline-based basis function approach. The predictor specification \\(\\eta_{k}\\) is generic enough to use tree-based models as well, which allows us to extend LightGBM to a probabilistic framework.","title":"Univariate Targets"},{"location":"dgbm/#mixture-distributions","text":"Mixture densities, or mixture distributions, offer an extension to the notion of traditional univariate distributions by allowing the observed data to be thought of as arising from multiple underlying processes. In its essence, a mixture distribution is a weighted combination of several component distributions, where each component contributes to the overall mixture distribution, with the weights indicating the importance of each component. For instance, if you imagine the observed data distribution having multiple modes, a mixture of Gaussians could be employed to capture each mode with a separate Gaussian distribution. For each component of the mixture, there would be a set of parameters that depend on covariates, and additional mixing coefficients which are also modeled as a function of covariates. This is particularly useful when a single parametric distribution cannot adequately capture the underlying data generating process. A mixture distribution can be represented as follows: \\[\\begin{equation} f\\bigl(y_{i} | \\boldsymbol{\\theta}_{i}(x_{i})\\bigr) = \\sum_{m=1}^{M} w_{i,m}(x_{i}) \\cdot f_{m}\\bigl(y_{i} | \\boldsymbol{\\theta}_{i,m}(x_{i})\\bigr) \\end{equation}\\] where \\(f(\\cdot)\\) represents the mixture density for the \\(i\\) -th observation, \\(f_{m}(\\cdot)\\) is the \\(m\\) -th component density, each with its own set of parameters \\(\\boldsymbol{\\theta}_{i,m}(\\cdot)\\) , and \\(w_{i,m}(\\cdot)\\) represent the weights of the \\(m\\) -th component in the mixture, subject to \\(\\sum_{j=1}^{M} w_{i,m} = 1\\) . The components can either be a combination of different parametric univariate distributions, such as a combination of a Normal and a StudentT, or, as in our implementation, a combination of the same distribution-type with different parameterizations, e.g., Gaussian-Mixture or StudentT-Mixture. The choice of the component distributions depends on the characteristics of the data and the underlying assumptions. Due to their high flexibility, mixture densities can portray a diverse range of shapes, making them adaptable to a plethora of datasets. By incorporating mixture densities within our framework, users can gain a more comprehensive understanding of the conditional distribution of the response variable, providing a more accurate representation of the data generating process. Hence, mixture densities greatly expand the expressive power of distributional modeling frameworks like GAMLSS, allowing one to capture a wider array of data distributions with enhanced flexibility by combining multiple members of this set.","title":"Mixture Distributions"},{"location":"dgbm/#normalizing-flows","text":"Although the GAMLSS framework offers considerable flexibility, parametric distributions may prove not flexible enough to provide a reasonable approximation for certain dataset, e.g., for multi-modal distributions. For such cases, it is preferable to relax the assumption of a parametric distribution and approximate the data non-parametrically. While there are several alternatives for learning conditional distributions, we propose to use Normalizing Flows for their ability to fit complex distributions with only a few parameters. The principle that underlies Normalizing Flows is to turn a simple base distribution, e.g., \\(F_{Z}(\\mathbf{z}) = N(0,1)\\) , into a more complex and realistic distribution of the target variable \\(F_{Y}(\\mathbf{y})\\) by applying several bijective transformations \\(h_{j}\\) , \\(j = 1, \\ldots, J\\) to the variable of the base distribution \\[\\begin{equation} \\mathbf{y} = h_{J} \\circ h_{J-1} \\circ \\cdots \\circ h_{1}(\\mathbf{z}) \\end{equation}\\] Based on the complete transformation function \\(h=h_{J}\\circ\\ldots\\circ h_{1}\\) , the density of \\(\\mathbf{y}\\) is then given by the change of variables theorem \\[\\begin{equation} f_{Y}(\\mathbf{y}) = f_{Z}\\big(h(\\mathbf{y})\\big) \\cdot \\Bigg|\\frac{\\partial h(\\mathbf{y})}{\\partial \\mathbf{y}}\\Bigg| \\end{equation}\\] where scaling with the Jacobian determinant \\(|h^{\\prime}(\\mathbf{y})| = |\\partial h(\\mathbf{y}) / \\partial \\mathbf{y}|\\) ensures \\(f_{Y}(\\mathbf{y})\\) to be a proper density integrating to one. The composition of these transformations is invertible, allowing one to sample from the complex distribution by transforming samples from the base distribution.","title":"Normalizing Flows"},{"location":"dgbm/#gradient-boosting-machines-for-location-scale-and-shape","text":"We draw inspiration from GAMLSS and label our model as LightGBM for Location, Scale and Shape (LightGBMLSS). Despite its nominal reference to GAMLSS, our framework is designed in such a way to accommodate the modeling of a wide range of parametrizable distributions that go beyond location, scale and shape. LightGBMLSS requires the specification of a suitable distribution from which Gradients and Hessians are derived. These represent the partial first and second order derivatives of the log-likelihood with respect to the parameter of interest. GBMLSS are based on multi-parameter optimization, where a separate tree is grown for each parameter. Estimation of Gradients and Hessians, as well as the evaluation of the loss function is done simultaneously for all parameters. Gradients and Hessians are derived using PyTorch's automatic differentiation capabilities. The flexibility offered by automatic differentiation allows users to easily implement novel or customized parametric distributions for which Gradients and Hessians are difficult to derive analytically. It also facilitates the usage of Normalizing Flows, or to add additional constraints to the loss function. To improve the convergence and stability of GBMLSS estimation, unconditional Maximum Likelihood estimates of the parameters are used as offset values. To enable a deeper understanding of the data generating process, GBMLSS also provide attribute importance and partial dependence plots using the Shapley-Value approach.","title":"Gradient Boosting Machines for Location, Scale and Shape"},{"location":"dgbm/#references","text":"C. M. Bishop. Mixture density networks. Technical Report NCRG/4288, Aston University, Birmingham, UK, 1994. Hadi Mohaghegh Dolatabadi, Sarah Erfani, and Christopher Leckie. Invertible Generative Modeling using Linear Rational Splines. In The 23rd International Conference on Artificial Intelligence and Statistics (AISTATS), 4236\u20134246, 2020. Conor Durkan, Artur Bekasov, Iain Murray, and George Papamakarios. Neural Spline Flows. In Proceedings of the 33rd International Conference on Neural Information Processing Systems. 2019. Nadja Klein, Thomas Kneib, and Stefan Lang. Bayesian Generalized Additive Models for Location, Scale, and Shape for Zero-Inflated and Overdispersed Count Data. Journal of the American Statistical Association, 110(509):405\u2013419, 2015. Alexander M\u00e4rz, and Thomas Kneib. Distributional Gradient Boosting Machines, 2022b. Alexander M\u00e4rz. XGBoostLSS - An extension of XGBoost to probabilistic forecasting, 2019. Spyros Makridakis, Evangelos Spiliotis, and Vassilios Assimakopoulos. The M5 competition: Background, organization, and implementation. International Journal of Forecasting, 38(4):1325\u20131336, 2022a. Spyros Makridakis, Evangelos Spiliotis, Vassilios Assimakopoulos, Zhi Chen, Anil Gaba, Ilia Tsetlin, and Robert L. Winkler. The M5 uncertainty competition: Results, findings and conclusions. International Journal of Forecasting, 38(4):1365\u20131385, 2022b. R. A. Rigby and D. M. Stasinopoulos. Generalized additive models for location, scale and shape. Journal of the Royal Statistical Society: Series C (Applied Statistics), 54(3):507\u2013554, 2005. Mikis D. Stasinopoulos, Robert A. Rigby, Gillian Z. Heller, Vlasios Voudouris, and Fernanda de Bastiani. Flexible Regression and Smoothing: Using GAMLSS in R. Chapman & Hall / CRC The R Series. CRC Press, London, 2017.","title":"References"},{"location":"distributions/","text":"Available Distributions LightGBMLSS is built upon PyTorch and Pyro, enabling users to harness a diverse set of distributional families and to leverage automatic differentiation capabilities. This greatly expands the options for probabilistic modeling and uncertainty estimation and allows users to tackle complex regression tasks. LightGBMLSS currently supports the following univariate distributions. Distribution Usage Type Support Number of Parameters Beta Beta() Continuous (Univariate) \\(y \\in (0, 1)\\) 2 Cauchy Cauchy() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 Expectile Expectile() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) Number of expectiles Gamma Gamma() Continuous (Univariate) \\(y \\in (0, \\infty)\\) 2 Gaussian Gaussian() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 Gumbel Gumbel() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 Laplace Laplace() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 LogNormal LogNormal() Continuous (Univariate) \\(y \\in (0,\\infty)\\) 2 Mixture Mixture(CompDist(), M) Continuous & Discrete Count (Univariate) \\(y \\in (-\\infty,\\infty)\\) \\(y \\in [0, \\infty)\\) \\(y \\in [0, 1]\\) \\(y \\in (0, 1, 2, 3, \\ldots)\\) CompDist + M Negative Binomial NegativeBinomial() Discrete Count (Univariate) \\(y \\in (0, 1, 2, 3, \\ldots)\\) 2 Poisson Poisson() Discrete Count (Univariate) \\(y \\in (0, 1, 2, 3, \\ldots)\\) 1 Spline Flow SplineFlow() Continuous & Discrete Count (Univariate) \\(y \\in (-\\infty,\\infty)\\) \\(y \\in [0, \\infty)\\) \\(y \\in [0, 1]\\) \\(y \\in (0, 1, 2, 3, \\ldots)\\) 2xcount_bins + (count_bins-1) (order=quadratic) 3xcount_bins + (count_bins-1) (order=linear) Student-T StudentT() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 3 Weibull Weibull() Continuous (Univariate) \\(y \\in [0, \\infty)\\) 2 Zero-Adjusted Beta ZABeta() Discrete-Continuous (Univariate) \\(y \\in [0, 1)\\) 3 Zero-Adjusted Gamma ZAGamma() Discrete-Continuous (Univariate) \\(y \\in [0, \\infty)\\) 3 Zero-Adjusted LogNormal ZALN() Discrete-Continuous (Univariate) \\(y \\in [0, \\infty)\\) 3 Zero-Inflated Negative Binomial ZINB() Discrete-Count (Univariate) \\(y \\in [0, 1, 2, 3, \\ldots)\\) 3 Zero-Inflated Poisson ZIPoisson() Discrete-Count (Univariate) \\(y \\in [0, 1, 2, 3, \\ldots)\\) 2","title":"Available Distributions"},{"location":"distributions/#available-distributions","text":"LightGBMLSS is built upon PyTorch and Pyro, enabling users to harness a diverse set of distributional families and to leverage automatic differentiation capabilities. This greatly expands the options for probabilistic modeling and uncertainty estimation and allows users to tackle complex regression tasks. LightGBMLSS currently supports the following univariate distributions. Distribution Usage Type Support Number of Parameters Beta Beta() Continuous (Univariate) \\(y \\in (0, 1)\\) 2 Cauchy Cauchy() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 Expectile Expectile() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) Number of expectiles Gamma Gamma() Continuous (Univariate) \\(y \\in (0, \\infty)\\) 2 Gaussian Gaussian() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 Gumbel Gumbel() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 Laplace Laplace() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 2 LogNormal LogNormal() Continuous (Univariate) \\(y \\in (0,\\infty)\\) 2 Mixture Mixture(CompDist(), M) Continuous & Discrete Count (Univariate) \\(y \\in (-\\infty,\\infty)\\) \\(y \\in [0, \\infty)\\) \\(y \\in [0, 1]\\) \\(y \\in (0, 1, 2, 3, \\ldots)\\) CompDist + M Negative Binomial NegativeBinomial() Discrete Count (Univariate) \\(y \\in (0, 1, 2, 3, \\ldots)\\) 2 Poisson Poisson() Discrete Count (Univariate) \\(y \\in (0, 1, 2, 3, \\ldots)\\) 1 Spline Flow SplineFlow() Continuous & Discrete Count (Univariate) \\(y \\in (-\\infty,\\infty)\\) \\(y \\in [0, \\infty)\\) \\(y \\in [0, 1]\\) \\(y \\in (0, 1, 2, 3, \\ldots)\\) 2xcount_bins + (count_bins-1) (order=quadratic) 3xcount_bins + (count_bins-1) (order=linear) Student-T StudentT() Continuous (Univariate) \\(y \\in (-\\infty,\\infty)\\) 3 Weibull Weibull() Continuous (Univariate) \\(y \\in [0, \\infty)\\) 2 Zero-Adjusted Beta ZABeta() Discrete-Continuous (Univariate) \\(y \\in [0, 1)\\) 3 Zero-Adjusted Gamma ZAGamma() Discrete-Continuous (Univariate) \\(y \\in [0, \\infty)\\) 3 Zero-Adjusted LogNormal ZALN() Discrete-Continuous (Univariate) \\(y \\in [0, \\infty)\\) 3 Zero-Inflated Negative Binomial ZINB() Discrete-Count (Univariate) \\(y \\in [0, 1, 2, 3, \\ldots)\\) 3 Zero-Inflated Poisson ZIPoisson() Discrete-Count (Univariate) \\(y \\in [0, 1, 2, 3, \\ldots)\\) 2","title":"Available Distributions"},{"location":"examples/Expectile_Regression/","text":"(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.ClipboardCopyElement = factory()); }(this, function () { 'use strict'; function createNode(text) { const node = document.createElement('pre'); node.style.width = '1px'; node.style.height = '1px'; node.style.position = 'fixed'; node.style.top = '5px'; node.textContent = text; return node; } function copyNode(node) { if ('clipboard' in navigator) { // eslint-disable-next-line flowtype/no-flow-fix-me-comments // $FlowFixMe Clipboard is not defined in Flow yet. return navigator.clipboard.writeText(node.textContent); } const selection = getSelection(); if (selection == null) { return Promise.reject(new Error()); } selection.removeAllRanges(); const range = document.createRange(); range.selectNodeContents(node); selection.addRange(range); document.execCommand('copy'); selection.removeAllRanges(); return Promise.resolve(); } function copyText(text) { if ('clipboard' in navigator) { // eslint-disable-next-line flowtype/no-flow-fix-me-comments // $FlowFixMe Clipboard is not defined in Flow yet. return navigator.clipboard.writeText(text); } const body = document.body; if (!body) { return Promise.reject(new Error()); } const node = createNode(text); body.appendChild(node); copyNode(node); body.removeChild(node); return Promise.resolve(); } function copy(button) { const id = button.getAttribute('for'); const text = button.getAttribute('value'); function trigger() { button.dispatchEvent(new CustomEvent('clipboard-copy', { bubbles: true })); } if (text) { copyText(text).then(trigger); } else if (id) { const root = 'getRootNode' in Element.prototype ? button.getRootNode() : button.ownerDocument; if (!(root instanceof Document || 'ShadowRoot' in window && root instanceof ShadowRoot)) return; const node = root.getElementById(id); if (node) copyTarget(node).then(trigger); } } function copyTarget(content) { if (content instanceof HTMLInputElement || content instanceof HTMLTextAreaElement) { return copyText(content.value); } else if (content instanceof HTMLAnchorElement && content.hasAttribute('href')) { return copyText(content.href); } else { return copyNode(content); } } function clicked(event) { const button = event.currentTarget; if (button instanceof HTMLElement) { copy(button); } } function keydown(event) { if (event.key === ' ' || event.key === 'Enter') { const button = event.currentTarget; if (button instanceof HTMLElement) { event.preventDefault(); copy(button); } } } function focused(event) { event.currentTarget.addEventListener('keydown', keydown); } function blurred(event) { event.currentTarget.removeEventListener('keydown', keydown); } class ClipboardCopyElement extends HTMLElement { constructor() { super(); this.addEventListener('click', clicked); this.addEventListener('focus', focused); this.addEventListener('blur', blurred); } connectedCallback() { if (!this.hasAttribute('tabindex')) { this.setAttribute('tabindex', '0'); } if (!this.hasAttribute('role')) { this.setAttribute('role', 'button'); } } get value() { return this.getAttribute('value') || ''; } set value(text) { this.setAttribute('value', text); } } if (!window.customElements.get('clipboard-copy')) { window.ClipboardCopyElement = ClipboardCopyElement; window.customElements.define('clipboard-copy', ClipboardCopyElement); } return ClipboardCopyElement; })); document.addEventListener('clipboard-copy', function(event) { const notice = event.target.querySelector('.notice') notice.hidden = false setTimeout(function() { notice.hidden = true }, 1000) }) pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight-ipynb .hll { background-color: var(--jp-cell-editor-active-background) } .highlight-ipynb { background: var(--jp-cell-editor-background); color: var(--jp-mirror-editor-variable-color) } .highlight-ipynb .c { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment */ .highlight-ipynb .err { color: var(--jp-mirror-editor-error-color) } /* Error */ .highlight-ipynb .k { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword */ .highlight-ipynb .o { color: var(--jp-mirror-editor-operator-color); font-weight: bold } /* Operator */ .highlight-ipynb .p { color: var(--jp-mirror-editor-punctuation-color) } /* Punctuation */ .highlight-ipynb .ch { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment.Hashbang */ .highlight-ipynb .cm { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment.Multiline */ .highlight-ipynb .cp { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment.Preproc */ .highlight-ipynb .cpf { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment.PreprocFile */ .highlight-ipynb .c1 { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment.Single */ .highlight-ipynb .cs { color: var(--jp-mirror-editor-comment-color); font-style: italic } /* Comment.Special */ .highlight-ipynb .kc { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword.Constant */ .highlight-ipynb .kd { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword.Declaration */ .highlight-ipynb .kn { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword.Namespace */ .highlight-ipynb .kp { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword.Pseudo */ .highlight-ipynb .kr { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword.Reserved */ .highlight-ipynb .kt { color: var(--jp-mirror-editor-keyword-color); font-weight: bold } /* Keyword.Type */ .highlight-ipynb .m { color: var(--jp-mirror-editor-number-color) } /* Literal.Number */ .highlight-ipynb .s { color: var(--jp-mirror-editor-string-color) } /* Literal.String */ .highlight-ipynb .ow { color: var(--jp-mirror-editor-operator-color); font-weight: bold } /* Operator.Word */ .highlight-ipynb .pm { color: var(--jp-mirror-editor-punctuation-color) } /* Punctuation.Marker */ .highlight-ipynb .w { color: var(--jp-mirror-editor-variable-color) } /* Text.Whitespace */ .highlight-ipynb .mb { color: var(--jp-mirror-editor-number-color) } /* Literal.Number.Bin */ .highlight-ipynb .mf { color: var(--jp-mirror-editor-number-color) } /* Literal.Number.Float */ .highlight-ipynb .mh { color: var(--jp-mirror-editor-number-color) } /* Literal.Number.Hex */ .highlight-ipynb .mi { color: var(--jp-mirror-editor-number-color) } /* Literal.Number.Integer */ .highlight-ipynb .mo { color: var(--jp-mirror-editor-number-color) } /* Literal.Number.Oct */ .highlight-ipynb .sa { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Affix */ .highlight-ipynb .sb { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Backtick */ .highlight-ipynb .sc { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Char */ .highlight-ipynb .dl { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Delimiter */ .highlight-ipynb .sd { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Doc */ .highlight-ipynb .s2 { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Double */ .highlight-ipynb .se { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Escape */ .highlight-ipynb .sh { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Heredoc */ .highlight-ipynb .si { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Interpol */ .highlight-ipynb .sx { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Other */ .highlight-ipynb .sr { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Regex */ .highlight-ipynb .s1 { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Single */ .highlight-ipynb .ss { color: var(--jp-mirror-editor-string-color) } /* Literal.String.Symbol */ .highlight-ipynb .il { color: var(--jp-mirror-editor-number-color) } /* Literal.Number.Integer.Long */ @charset \"UTF-8\";.jupyter-wrapper{--md-red-50: #ffebee;--md-red-100: #ffcdd2;--md-red-200: #ef9a9a;--md-red-300: #e57373;--md-red-400: #ef5350;--md-red-500: #f44336;--md-red-600: #e53935;--md-red-700: #d32f2f;--md-red-800: #c62828;--md-red-900: #b71c1c;--md-red-A100: #ff8a80;--md-red-A200: #ff5252;--md-red-A400: #ff1744;--md-red-A700: #d50000;--md-pink-50: #fce4ec;--md-pink-100: #f8bbd0;--md-pink-200: #f48fb1;--md-pink-300: #f06292;--md-pink-400: #ec407a;--md-pink-500: #e91e63;--md-pink-600: #d81b60;--md-pink-700: #c2185b;--md-pink-800: #ad1457;--md-pink-900: #880e4f;--md-pink-A100: #ff80ab;--md-pink-A200: #ff4081;--md-pink-A400: #f50057;--md-pink-A700: #c51162;--md-purple-50: #f3e5f5;--md-purple-100: #e1bee7;--md-purple-200: #ce93d8;--md-purple-300: #ba68c8;--md-purple-400: #ab47bc;--md-purple-500: #9c27b0;--md-purple-600: #8e24aa;--md-purple-700: #7b1fa2;--md-purple-800: #6a1b9a;--md-purple-900: #4a148c;--md-purple-A100: #ea80fc;--md-purple-A200: #e040fb;--md-purple-A400: #d500f9;--md-purple-A700: #aa00ff;--md-deep-purple-50: #ede7f6;--md-deep-purple-100: #d1c4e9;--md-deep-purple-200: #b39ddb;--md-deep-purple-300: #9575cd;--md-deep-purple-400: #7e57c2;--md-deep-purple-500: #673ab7;--md-deep-purple-600: #5e35b1;--md-deep-purple-700: #512da8;--md-deep-purple-800: #4527a0;--md-deep-purple-900: #311b92;--md-deep-purple-A100: #b388ff;--md-deep-purple-A200: #7c4dff;--md-deep-purple-A400: #651fff;--md-deep-purple-A700: #6200ea;--md-indigo-50: #e8eaf6;--md-indigo-100: #c5cae9;--md-indigo-200: #9fa8da;--md-indigo-300: #7986cb;--md-indigo-400: #5c6bc0;--md-indigo-500: #3f51b5;--md-indigo-600: #3949ab;--md-indigo-700: #303f9f;--md-indigo-800: #283593;--md-indigo-900: #1a237e;--md-indigo-A100: #8c9eff;--md-indigo-A200: #536dfe;--md-indigo-A400: #3d5afe;--md-indigo-A700: #304ffe;--md-blue-50: #e3f2fd;--md-blue-100: #bbdefb;--md-blue-200: #90caf9;--md-blue-300: #64b5f6;--md-blue-400: #42a5f5;--md-blue-500: #2196f3;--md-blue-600: #1e88e5;--md-blue-700: #1976d2;--md-blue-800: #1565c0;--md-blue-900: #0d47a1;--md-blue-A100: #82b1ff;--md-blue-A200: #448aff;--md-blue-A400: #2979ff;--md-blue-A700: #2962ff;--md-light-blue-50: #e1f5fe;--md-light-blue-100: #b3e5fc;--md-light-blue-200: #81d4fa;--md-light-blue-300: #4fc3f7;--md-light-blue-400: #29b6f6;--md-light-blue-500: #03a9f4;--md-light-blue-600: #039be5;--md-light-blue-700: #0288d1;--md-light-blue-800: #0277bd;--md-light-blue-900: #01579b;--md-light-blue-A100: #80d8ff;--md-light-blue-A200: #40c4ff;--md-light-blue-A400: #00b0ff;--md-light-blue-A700: #0091ea;--md-cyan-50: #e0f7fa;--md-cyan-100: #b2ebf2;--md-cyan-200: #80deea;--md-cyan-300: #4dd0e1;--md-cyan-400: #26c6da;--md-cyan-500: #00bcd4;--md-cyan-600: #00acc1;--md-cyan-700: #0097a7;--md-cyan-800: #00838f;--md-cyan-900: #006064;--md-cyan-A100: #84ffff;--md-cyan-A200: #18ffff;--md-cyan-A400: #00e5ff;--md-cyan-A700: #00b8d4;--md-teal-50: #e0f2f1;--md-teal-100: #b2dfdb;--md-teal-200: #80cbc4;--md-teal-300: #4db6ac;--md-teal-400: #26a69a;--md-teal-500: #009688;--md-teal-600: #00897b;--md-teal-700: #00796b;--md-teal-800: #00695c;--md-teal-900: #004d40;--md-teal-A100: #a7ffeb;--md-teal-A200: #64ffda;--md-teal-A400: #1de9b6;--md-teal-A700: #00bfa5;--md-green-50: #e8f5e9;--md-green-100: #c8e6c9;--md-green-200: #a5d6a7;--md-green-300: #81c784;--md-green-400: #66bb6a;--md-green-500: #4caf50;--md-green-600: #43a047;--md-green-700: #388e3c;--md-green-800: #2e7d32;--md-green-900: #1b5e20;--md-green-A100: #b9f6ca;--md-green-A200: #69f0ae;--md-green-A400: #00e676;--md-green-A700: #00c853;--md-light-green-50: #f1f8e9;--md-light-green-100: #dcedc8;--md-light-green-200: #c5e1a5;--md-light-green-300: #aed581;--md-light-green-400: #9ccc65;--md-light-green-500: #8bc34a;--md-light-green-600: #7cb342;--md-light-green-700: #689f38;--md-light-green-800: #558b2f;--md-light-green-900: #33691e;--md-light-green-A100: #ccff90;--md-light-green-A200: #b2ff59;--md-light-green-A400: #76ff03;--md-light-green-A700: #64dd17;--md-lime-50: #f9fbe7;--md-lime-100: #f0f4c3;--md-lime-200: #e6ee9c;--md-lime-300: #dce775;--md-lime-400: #d4e157;--md-lime-500: #cddc39;--md-lime-600: #c0ca33;--md-lime-700: #afb42b;--md-lime-800: #9e9d24;--md-lime-900: #827717;--md-lime-A100: #f4ff81;--md-lime-A200: #eeff41;--md-lime-A400: #c6ff00;--md-lime-A700: #aeea00;--md-yellow-50: #fffde7;--md-yellow-100: #fff9c4;--md-yellow-200: #fff59d;--md-yellow-300: #fff176;--md-yellow-400: #ffee58;--md-yellow-500: #ffeb3b;--md-yellow-600: #fdd835;--md-yellow-700: #fbc02d;--md-yellow-800: #f9a825;--md-yellow-900: #f57f17;--md-yellow-A100: #ffff8d;--md-yellow-A200: #ffff00;--md-yellow-A400: #ffea00;--md-yellow-A700: #ffd600;--md-amber-50: #fff8e1;--md-amber-100: #ffecb3;--md-amber-200: #ffe082;--md-amber-300: #ffd54f;--md-amber-400: #ffca28;--md-amber-500: #ffc107;--md-amber-600: #ffb300;--md-amber-700: #ffa000;--md-amber-800: #ff8f00;--md-amber-900: #ff6f00;--md-amber-A100: #ffe57f;--md-amber-A200: #ffd740;--md-amber-A400: #ffc400;--md-amber-A700: #ffab00;--md-orange-50: #fff3e0;--md-orange-100: #ffe0b2;--md-orange-200: #ffcc80;--md-orange-300: #ffb74d;--md-orange-400: #ffa726;--md-orange-500: #ff9800;--md-orange-600: #fb8c00;--md-orange-700: #f57c00;--md-orange-800: #ef6c00;--md-orange-900: #e65100;--md-orange-A100: #ffd180;--md-orange-A200: #ffab40;--md-orange-A400: #ff9100;--md-orange-A700: #ff6d00;--md-deep-orange-50: #fbe9e7;--md-deep-orange-100: #ffccbc;--md-deep-orange-200: #ffab91;--md-deep-orange-300: #ff8a65;--md-deep-orange-400: #ff7043;--md-deep-orange-500: #ff5722;--md-deep-orange-600: #f4511e;--md-deep-orange-700: #e64a19;--md-deep-orange-800: #d84315;--md-deep-orange-900: #bf360c;--md-deep-orange-A100: #ff9e80;--md-deep-orange-A200: #ff6e40;--md-deep-orange-A400: #ff3d00;--md-deep-orange-A700: #dd2c00;--md-brown-50: #efebe9;--md-brown-100: #d7ccc8;--md-brown-200: #bcaaa4;--md-brown-300: #a1887f;--md-brown-400: #8d6e63;--md-brown-500: #795548;--md-brown-600: #6d4c41;--md-brown-700: #5d4037;--md-brown-800: #4e342e;--md-brown-900: #3e2723;--md-grey-50: #fafafa;--md-grey-100: #f5f5f5;--md-grey-200: #eeeeee;--md-grey-300: #e0e0e0;--md-grey-400: #bdbdbd;--md-grey-500: #9e9e9e;--md-grey-600: #757575;--md-grey-700: #616161;--md-grey-800: #424242;--md-grey-900: #212121;--md-blue-grey-50: #eceff1;--md-blue-grey-100: #cfd8dc;--md-blue-grey-200: #b0bec5;--md-blue-grey-300: #90a4ae;--md-blue-grey-400: #78909c;--md-blue-grey-500: #607d8b;--md-blue-grey-600: #546e7a;--md-blue-grey-700: #455a64;--md-blue-grey-800: #37474f;--md-blue-grey-900: #263238}.jupyter-wrapper{--jp-shadow-base-lightness: 0;--jp-shadow-umbra-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .2 );--jp-shadow-penumbra-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .14 );--jp-shadow-ambient-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .12 );--jp-elevation-z0: none;--jp-elevation-z1: 0px 2px 1px -1px var(--jp-shadow-umbra-color), 0px 1px 1px 0px var(--jp-shadow-penumbra-color), 0px 1px 3px 0px var(--jp-shadow-ambient-color);--jp-elevation-z2: 0px 3px 1px -2px var(--jp-shadow-umbra-color), 0px 2px 2px 0px var(--jp-shadow-penumbra-color), 0px 1px 5px 0px var(--jp-shadow-ambient-color);--jp-elevation-z4: 0px 2px 4px -1px var(--jp-shadow-umbra-color), 0px 4px 5px 0px var(--jp-shadow-penumbra-color), 0px 1px 10px 0px var(--jp-shadow-ambient-color);--jp-elevation-z6: 0px 3px 5px -1px var(--jp-shadow-umbra-color), 0px 6px 10px 0px var(--jp-shadow-penumbra-color), 0px 1px 18px 0px var(--jp-shadow-ambient-color);--jp-elevation-z8: 0px 5px 5px -3px var(--jp-shadow-umbra-color), 0px 8px 10px 1px var(--jp-shadow-penumbra-color), 0px 3px 14px 2px var(--jp-shadow-ambient-color);--jp-elevation-z12: 0px 7px 8px -4px var(--jp-shadow-umbra-color), 0px 12px 17px 2px var(--jp-shadow-penumbra-color), 0px 5px 22px 4px var(--jp-shadow-ambient-color);--jp-elevation-z16: 0px 8px 10px -5px var(--jp-shadow-umbra-color), 0px 16px 24px 2px var(--jp-shadow-penumbra-color), 0px 6px 30px 5px var(--jp-shadow-ambient-color);--jp-elevation-z20: 0px 10px 13px -6px var(--jp-shadow-umbra-color), 0px 20px 31px 3px var(--jp-shadow-penumbra-color), 0px 8px 38px 7px var(--jp-shadow-ambient-color);--jp-elevation-z24: 0px 11px 15px -7px var(--jp-shadow-umbra-color), 0px 24px 38px 3px var(--jp-shadow-penumbra-color), 0px 9px 46px 8px var(--jp-shadow-ambient-color);--jp-border-width: 1px;--jp-border-color0: var(--md-grey-400);--jp-border-color1: var(--md-grey-400);--jp-border-color2: var(--md-grey-300);--jp-border-color3: var(--md-grey-200);--jp-inverse-border-color: var(--md-grey-600);--jp-border-radius: 2px;--jp-ui-font-scale-factor: 1.2;--jp-ui-font-size0: .83333em;--jp-ui-font-size1: 13px;--jp-ui-font-size2: 1.2em;--jp-ui-font-size3: 1.44em;--jp-ui-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--jp-ui-font-color0: rgba(0, 0, 0, 1);--jp-ui-font-color1: rgba(0, 0, 0, .87);--jp-ui-font-color2: rgba(0, 0, 0, .54);--jp-ui-font-color3: rgba(0, 0, 0, .38);--jp-ui-inverse-font-color0: rgba(255, 255, 255, 1);--jp-ui-inverse-font-color1: rgba(255, 255, 255, 1);--jp-ui-inverse-font-color2: rgba(255, 255, 255, .7);--jp-ui-inverse-font-color3: rgba(255, 255, 255, .5);--jp-content-line-height: 1.6;--jp-content-font-scale-factor: 1.2;--jp-content-font-size0: .83333em;--jp-content-font-size1: 14px;--jp-content-font-size2: 1.2em;--jp-content-font-size3: 1.44em;--jp-content-font-size4: 1.728em;--jp-content-font-size5: 2.0736em;--jp-content-presentation-font-size1: 17px;--jp-content-heading-line-height: 1;--jp-content-heading-margin-top: 1.2em;--jp-content-heading-margin-bottom: .8em;--jp-content-heading-font-weight: 500;--jp-content-font-color0: rgba(0, 0, 0, 1);--jp-content-font-color1: rgba(0, 0, 0, .87);--jp-content-font-color2: rgba(0, 0, 0, .54);--jp-content-font-color3: rgba(0, 0, 0, .38);--jp-content-link-color: var(--md-blue-700);--jp-content-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--jp-code-font-size: 13px;--jp-code-line-height: 1.3077;--jp-code-padding: 5px;--jp-code-font-family-default: Menlo, Consolas, \"DejaVu Sans Mono\", monospace;--jp-code-font-family: var(--jp-code-font-family-default);--jp-code-presentation-font-size: 16px;--jp-code-cursor-width0: 1.4px;--jp-code-cursor-width1: 2px;--jp-code-cursor-width2: 4px;--jp-layout-color0: white;--jp-layout-color1: white;--jp-layout-color2: var(--md-grey-200);--jp-layout-color3: var(--md-grey-400);--jp-layout-color4: var(--md-grey-600);--jp-inverse-layout-color0: #111111;--jp-inverse-layout-color1: var(--md-grey-900);--jp-inverse-layout-color2: var(--md-grey-800);--jp-inverse-layout-color3: var(--md-grey-700);--jp-inverse-layout-color4: var(--md-grey-600);--jp-brand-color0: var(--md-blue-900);--jp-brand-color1: var(--md-blue-700);--jp-brand-color2: var(--md-blue-300);--jp-brand-color3: var(--md-blue-100);--jp-brand-color4: var(--md-blue-50);--jp-accent-color0: var(--md-green-900);--jp-accent-color1: var(--md-green-700);--jp-accent-color2: var(--md-green-300);--jp-accent-color3: var(--md-green-100);--jp-warn-color0: var(--md-orange-900);--jp-warn-color1: var(--md-orange-700);--jp-warn-color2: var(--md-orange-300);--jp-warn-color3: var(--md-orange-100);--jp-error-color0: var(--md-red-900);--jp-error-color1: var(--md-red-700);--jp-error-color2: var(--md-red-300);--jp-error-color3: var(--md-red-100);--jp-success-color0: var(--md-green-900);--jp-success-color1: var(--md-green-700);--jp-success-color2: var(--md-green-300);--jp-success-color3: var(--md-green-100);--jp-info-color0: var(--md-cyan-900);--jp-info-color1: var(--md-cyan-700);--jp-info-color2: var(--md-cyan-300);--jp-info-color3: var(--md-cyan-100);--jp-cell-padding: 5px;--jp-cell-collapser-width: 8px;--jp-cell-collapser-min-height: 20px;--jp-cell-collapser-not-active-hover-opacity: .6;--jp-cell-editor-background: var(--md-grey-100);--jp-cell-editor-border-color: var(--md-grey-300);--jp-cell-editor-box-shadow: inset 0 0 2px var(--md-blue-300);--jp-cell-editor-active-background: var(--jp-layout-color0);--jp-cell-editor-active-border-color: var(--jp-brand-color1);--jp-cell-prompt-width: 64px;--jp-cell-prompt-font-family: var(--jp-code-font-family-default);--jp-cell-prompt-letter-spacing: 0px;--jp-cell-prompt-opacity: 1;--jp-cell-prompt-not-active-opacity: .5;--jp-cell-prompt-not-active-font-color: var(--md-grey-700);--jp-cell-inprompt-font-color: #307fc1;--jp-cell-outprompt-font-color: #bf5b3d;--jp-notebook-padding: 10px;--jp-notebook-select-background: var(--jp-layout-color1);--jp-notebook-multiselected-color: var(--md-blue-50);--jp-notebook-scroll-padding: calc( 100% - var(--jp-code-font-size) * var(--jp-code-line-height) - var(--jp-code-padding) - var(--jp-cell-padding) - 1px );--jp-rendermime-error-background: #fdd;--jp-rendermime-table-row-background: var(--md-grey-100);--jp-rendermime-table-row-hover-background: var(--md-light-blue-50);--jp-dialog-background: rgba(0, 0, 0, .25);--jp-console-padding: 10px;--jp-toolbar-border-color: var(--jp-border-color1);--jp-toolbar-micro-height: 8px;--jp-toolbar-background: var(--jp-layout-color1);--jp-toolbar-box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, .24);--jp-toolbar-header-margin: 4px 4px 0px 4px;--jp-toolbar-active-background: var(--md-grey-300);--jp-statusbar-height: 24px;--jp-input-box-shadow: inset 0 0 2px var(--md-blue-300);--jp-input-active-background: var(--jp-layout-color1);--jp-input-hover-background: var(--jp-layout-color1);--jp-input-background: var(--md-grey-100);--jp-input-border-color: var(--jp-inverse-border-color);--jp-input-active-border-color: var(--jp-brand-color1);--jp-input-active-box-shadow-color: rgba(19, 124, 189, .3);--jp-editor-selected-background: #d9d9d9;--jp-editor-selected-focused-background: #d7d4f0;--jp-editor-cursor-color: var(--jp-ui-font-color0);--jp-mirror-editor-keyword-color: #008000;--jp-mirror-editor-atom-color: #88f;--jp-mirror-editor-number-color: #080;--jp-mirror-editor-def-color: #00f;--jp-mirror-editor-variable-color: var(--md-grey-900);--jp-mirror-editor-variable-2-color: #05a;--jp-mirror-editor-variable-3-color: #085;--jp-mirror-editor-punctuation-color: #05a;--jp-mirror-editor-property-color: #05a;--jp-mirror-editor-operator-color: #aa22ff;--jp-mirror-editor-comment-color: #408080;--jp-mirror-editor-string-color: #ba2121;--jp-mirror-editor-string-2-color: #708;--jp-mirror-editor-meta-color: #aa22ff;--jp-mirror-editor-qualifier-color: #555;--jp-mirror-editor-builtin-color: #008000;--jp-mirror-editor-bracket-color: #997;--jp-mirror-editor-tag-color: #170;--jp-mirror-editor-attribute-color: #00c;--jp-mirror-editor-header-color: blue;--jp-mirror-editor-quote-color: #090;--jp-mirror-editor-link-color: #00c;--jp-mirror-editor-error-color: #f00;--jp-mirror-editor-hr-color: #999;--jp-collaborator-color1: #ffad8e;--jp-collaborator-color2: #dac83d;--jp-collaborator-color3: #72dd76;--jp-collaborator-color4: #00e4d0;--jp-collaborator-color5: #45d4ff;--jp-collaborator-color6: #e2b1ff;--jp-collaborator-color7: #ff9de6;--jp-vega-background: white;--jp-sidebar-min-width: 250px;--jp-search-toggle-off-opacity: .5;--jp-search-toggle-hover-opacity: .8;--jp-search-toggle-on-opacity: 1;--jp-search-selected-match-background-color: rgb(245, 200, 0);--jp-search-selected-match-color: black;--jp-search-unselected-match-background-color: var( --jp-inverse-layout-color0 );--jp-search-unselected-match-color: var(--jp-ui-inverse-font-color0);--jp-icon-contrast-color0: var(--md-purple-600);--jp-icon-contrast-color1: var(--md-green-600);--jp-icon-contrast-color2: var(--md-pink-600);--jp-icon-contrast-color3: var(--md-blue-600);--jp-jupyter-icon-color: #f37626;--jp-notebook-icon-color: #f37626;--jp-json-icon-color: var(--md-orange-700);--jp-console-icon-background-color: var(--md-blue-700);--jp-console-icon-color: white;--jp-terminal-icon-background-color: var(--md-grey-800);--jp-terminal-icon-color: var(--md-grey-200);--jp-text-editor-icon-color: var(--md-grey-700);--jp-inspector-icon-color: var(--md-grey-700);--jp-switch-color: var(--md-grey-400);--jp-switch-true-position-color: var(--md-orange-900)}[data-md-color-scheme=slate] .jupyter-wrapper{--jp-shadow-base-lightness: 32;--jp-shadow-umbra-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .2 );--jp-shadow-penumbra-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .14 );--jp-shadow-ambient-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .12 );--jp-elevation-z0: none;--jp-elevation-z1: 0px 2px 1px -1px var(--jp-shadow-umbra-color), 0px 1px 1px 0px var(--jp-shadow-penumbra-color), 0px 1px 3px 0px var(--jp-shadow-ambient-color);--jp-elevation-z2: 0px 3px 1px -2px var(--jp-shadow-umbra-color), 0px 2px 2px 0px var(--jp-shadow-penumbra-color), 0px 1px 5px 0px var(--jp-shadow-ambient-color);--jp-elevation-z4: 0px 2px 4px -1px var(--jp-shadow-umbra-color), 0px 4px 5px 0px var(--jp-shadow-penumbra-color), 0px 1px 10px 0px var(--jp-shadow-ambient-color);--jp-elevation-z6: 0px 3px 5px -1px var(--jp-shadow-umbra-color), 0px 6px 10px 0px var(--jp-shadow-penumbra-color), 0px 1px 18px 0px var(--jp-shadow-ambient-color);--jp-elevation-z8: 0px 5px 5px -3px var(--jp-shadow-umbra-color), 0px 8px 10px 1px var(--jp-shadow-penumbra-color), 0px 3px 14px 2px var(--jp-shadow-ambient-color);--jp-elevation-z12: 0px 7px 8px -4px var(--jp-shadow-umbra-color), 0px 12px 17px 2px var(--jp-shadow-penumbra-color), 0px 5px 22px 4px var(--jp-shadow-ambient-color);--jp-elevation-z16: 0px 8px 10px -5px var(--jp-shadow-umbra-color), 0px 16px 24px 2px var(--jp-shadow-penumbra-color), 0px 6px 30px 5px var(--jp-shadow-ambient-color);--jp-elevation-z20: 0px 10px 13px -6px var(--jp-shadow-umbra-color), 0px 20px 31px 3px var(--jp-shadow-penumbra-color), 0px 8px 38px 7px var(--jp-shadow-ambient-color);--jp-elevation-z24: 0px 11px 15px -7px var(--jp-shadow-umbra-color), 0px 24px 38px 3px var(--jp-shadow-penumbra-color), 0px 9px 46px 8px var(--jp-shadow-ambient-color);--jp-border-width: 1px;--jp-border-color0: var(--md-grey-700);--jp-border-color1: var(--md-grey-700);--jp-border-color2: var(--md-grey-800);--jp-border-color3: var(--md-grey-900);--jp-inverse-border-color: var(--md-grey-600);--jp-border-radius: 2px;--jp-ui-font-scale-factor: 1.2;--jp-ui-font-size0: .83333em;--jp-ui-font-size1: 13px;--jp-ui-font-size2: 1.2em;--jp-ui-font-size3: 1.44em;--jp-ui-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--jp-ui-font-color0: rgba(255, 255, 255, 1);--jp-ui-font-color1: rgba(255, 255, 255, .87);--jp-ui-font-color2: rgba(255, 255, 255, .54);--jp-ui-font-color3: rgba(255, 255, 255, .38);--jp-ui-inverse-font-color0: rgba(0, 0, 0, 1);--jp-ui-inverse-font-color1: rgba(0, 0, 0, .8);--jp-ui-inverse-font-color2: rgba(0, 0, 0, .5);--jp-ui-inverse-font-color3: rgba(0, 0, 0, .3);--jp-content-line-height: 1.6;--jp-content-font-scale-factor: 1.2;--jp-content-font-size0: .83333em;--jp-content-font-size1: 14px;--jp-content-font-size2: 1.2em;--jp-content-font-size3: 1.44em;--jp-content-font-size4: 1.728em;--jp-content-font-size5: 2.0736em;--jp-content-presentation-font-size1: 17px;--jp-content-heading-line-height: 1;--jp-content-heading-margin-top: 1.2em;--jp-content-heading-margin-bottom: .8em;--jp-content-heading-font-weight: 500;--jp-content-font-color0: rgba(255, 255, 255, 1);--jp-content-font-color1: rgba(255, 255, 255, 1);--jp-content-font-color2: rgba(255, 255, 255, .7);--jp-content-font-color3: rgba(255, 255, 255, .5);--jp-content-link-color: var(--md-blue-300);--jp-content-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--jp-code-font-size: 13px;--jp-code-line-height: 1.3077;--jp-code-padding: 5px;--jp-code-font-family-default: Menlo, Consolas, \"DejaVu Sans Mono\", monospace;--jp-code-font-family: var(--jp-code-font-family-default);--jp-code-presentation-font-size: 16px;--jp-code-cursor-width0: 1.4px;--jp-code-cursor-width1: 2px;--jp-code-cursor-width2: 4px;--jp-layout-color0: #111111;--jp-layout-color1: var(--md-grey-900);--jp-layout-color2: var(--md-grey-800);--jp-layout-color3: var(--md-grey-700);--jp-layout-color4: var(--md-grey-600);--jp-inverse-layout-color0: white;--jp-inverse-layout-color1: white;--jp-inverse-layout-color2: var(--md-grey-200);--jp-inverse-layout-color3: var(--md-grey-400);--jp-inverse-layout-color4: var(--md-grey-600);--jp-brand-color0: var(--md-blue-700);--jp-brand-color1: var(--md-blue-500);--jp-brand-color2: var(--md-blue-300);--jp-brand-color3: var(--md-blue-100);--jp-brand-color4: var(--md-blue-50);--jp-accent-color0: var(--md-green-700);--jp-accent-color1: var(--md-green-500);--jp-accent-color2: var(--md-green-300);--jp-accent-color3: var(--md-green-100);--jp-warn-color0: var(--md-orange-700);--jp-warn-color1: var(--md-orange-500);--jp-warn-color2: var(--md-orange-300);--jp-warn-color3: var(--md-orange-100);--jp-error-color0: var(--md-red-700);--jp-error-color1: var(--md-red-500);--jp-error-color2: var(--md-red-300);--jp-error-color3: var(--md-red-100);--jp-success-color0: var(--md-green-700);--jp-success-color1: var(--md-green-500);--jp-success-color2: var(--md-green-300);--jp-success-color3: var(--md-green-100);--jp-info-color0: var(--md-cyan-700);--jp-info-color1: var(--md-cyan-500);--jp-info-color2: var(--md-cyan-300);--jp-info-color3: var(--md-cyan-100);--jp-cell-padding: 5px;--jp-cell-collapser-width: 8px;--jp-cell-collapser-min-height: 20px;--jp-cell-collapser-not-active-hover-opacity: .6;--jp-cell-editor-background: var(--jp-layout-color1);--jp-cell-editor-border-color: var(--md-grey-700);--jp-cell-editor-box-shadow: inset 0 0 2px var(--md-blue-300);--jp-cell-editor-active-background: var(--jp-layout-color0);--jp-cell-editor-active-border-color: var(--jp-brand-color1);--jp-cell-prompt-width: 64px;--jp-cell-prompt-font-family: var(--jp-code-font-family-default);--jp-cell-prompt-letter-spacing: 0px;--jp-cell-prompt-opacity: 1;--jp-cell-prompt-not-active-opacity: 1;--jp-cell-prompt-not-active-font-color: var(--md-grey-300);--jp-cell-inprompt-font-color: #307fc1;--jp-cell-outprompt-font-color: #bf5b3d;--jp-notebook-padding: 10px;--jp-notebook-select-background: var(--jp-layout-color1);--jp-notebook-multiselected-color: rgba(33, 150, 243, .24);--jp-notebook-scroll-padding: calc( 100% - var(--jp-code-font-size) * var(--jp-code-line-height) - var(--jp-code-padding) - var(--jp-cell-padding) - 1px );--jp-rendermime-error-background: rgba(244, 67, 54, .28);--jp-rendermime-table-row-background: var(--md-grey-900);--jp-rendermime-table-row-hover-background: rgba(3, 169, 244, .2);--jp-dialog-background: rgba(0, 0, 0, .6);--jp-console-padding: 10px;--jp-toolbar-border-color: var(--jp-border-color2);--jp-toolbar-micro-height: 8px;--jp-toolbar-background: var(--jp-layout-color1);--jp-toolbar-box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, .8);--jp-toolbar-header-margin: 4px 4px 0px 4px;--jp-toolbar-active-background: var(--jp-layout-color0);--jp-statusbar-height: 24px;--jp-input-box-shadow: inset 0 0 2px var(--md-blue-300);--jp-input-active-background: var(--jp-layout-color0);--jp-input-hover-background: var(--jp-layout-color2);--jp-input-background: var(--md-grey-800);--jp-input-border-color: var(--jp-inverse-border-color);--jp-input-active-border-color: var(--jp-brand-color1);--jp-input-active-box-shadow-color: rgba(19, 124, 189, .3);--jp-editor-selected-background: var(--jp-layout-color2);--jp-editor-selected-focused-background: rgba(33, 150, 243, .24);--jp-editor-cursor-color: var(--jp-ui-font-color0);--jp-mirror-editor-keyword-color: var(--md-green-500);--jp-mirror-editor-atom-color: var(--md-blue-300);--jp-mirror-editor-number-color: var(--md-green-400);--jp-mirror-editor-def-color: var(--md-blue-600);--jp-mirror-editor-variable-color: var(--md-grey-300);--jp-mirror-editor-variable-2-color: var(--md-blue-400);--jp-mirror-editor-variable-3-color: var(--md-green-600);--jp-mirror-editor-punctuation-color: var(--md-blue-400);--jp-mirror-editor-property-color: var(--md-blue-400);--jp-mirror-editor-operator-color: #aa22ff;--jp-mirror-editor-comment-color: #408080;--jp-mirror-editor-string-color: #ff7070;--jp-mirror-editor-string-2-color: var(--md-purple-300);--jp-mirror-editor-meta-color: #aa22ff;--jp-mirror-editor-qualifier-color: #555;--jp-mirror-editor-builtin-color: var(--md-green-600);--jp-mirror-editor-bracket-color: #997;--jp-mirror-editor-tag-color: var(--md-green-700);--jp-mirror-editor-attribute-color: var(--md-blue-700);--jp-mirror-editor-header-color: var(--md-blue-500);--jp-mirror-editor-quote-color: var(--md-green-300);--jp-mirror-editor-link-color: var(--md-blue-700);--jp-mirror-editor-error-color: #f00;--jp-mirror-editor-hr-color: #999;--jp-collaborator-color1: #ad4a00;--jp-collaborator-color2: #7b6a00;--jp-collaborator-color3: #007e00;--jp-collaborator-color4: #008772;--jp-collaborator-color5: #0079b9;--jp-collaborator-color6: #8b45c6;--jp-collaborator-color7: #be208b;--jp-vega-background: var(--md-grey-400);--jp-sidebar-min-width: 250px;--jp-search-toggle-off-opacity: .6;--jp-search-toggle-hover-opacity: .8;--jp-search-toggle-on-opacity: 1;--jp-search-selected-match-background-color: rgb(255, 225, 0);--jp-search-selected-match-color: black;--jp-search-unselected-match-background-color: var( --jp-inverse-layout-color0 );--jp-search-unselected-match-color: var(--jp-ui-inverse-font-color0);--jp-scrollbar-background-color: #3f4244;--jp-scrollbar-thumb-color: 88, 96, 97;--jp-scrollbar-endpad: 3px;--jp-scrollbar-thumb-margin: 3.5px;--jp-scrollbar-thumb-radius: 9px;--jp-icon-contrast-color0: var(--md-purple-600);--jp-icon-contrast-color1: var(--md-green-600);--jp-icon-contrast-color2: var(--md-pink-600);--jp-icon-contrast-color3: var(--md-blue-600);--jp-jupyter-icon-color: #f37626;--jp-notebook-icon-color: #f37626;--jp-json-icon-color: var(--md-orange-500);--jp-console-icon-background-color: var(--md-blue-500);--jp-console-icon-color: white;--jp-terminal-icon-background-color: var(--md-grey-200);--jp-terminal-icon-color: var(--md-grey-800);--jp-text-editor-icon-color: var(--md-grey-200);--jp-inspector-icon-color: var(--md-grey-200);--jp-switch-color: var(--md-grey-400);--jp-switch-true-position-color: var(--md-orange-700)}.jupyter-wrapper [data-jp-theme-scrollbars=true]{scrollbar-color:rgb(var(--jp-scrollbar-thumb-color)) var(--jp-scrollbar-background-color)}.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-hscrollbar,.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-vscrollbar{scrollbar-color:rgba(var(--jp-scrollbar-thumb-color),.5) transparent}.jupyter-wrapper .jp-scrollbar-tiny{scrollbar-color:rgba(var(--jp-scrollbar-thumb-color),.5) transparent;scrollbar-width:thin}.jupyter-wrapper [data-jp-theme-scrollbars=true] ::-webkit-scrollbar,.jupyter-wrapper [data-jp-theme-scrollbars=true] ::-webkit-scrollbar-corner{background:var(--jp-scrollbar-background-color)}.jupyter-wrapper [data-jp-theme-scrollbars=true] ::-webkit-scrollbar-thumb{background:rgb(var(--jp-scrollbar-thumb-color));border:var(--jp-scrollbar-thumb-margin) solid transparent;background-clip:content-box;border-radius:var(--jp-scrollbar-thumb-radius)}.jupyter-wrapper [data-jp-theme-scrollbars=true] ::-webkit-scrollbar-track:horizontal{border-left:var(--jp-scrollbar-endpad) solid var(--jp-scrollbar-background-color);border-right:var(--jp-scrollbar-endpad) solid var(--jp-scrollbar-background-color)}.jupyter-wrapper [data-jp-theme-scrollbars=true] ::-webkit-scrollbar-track:vertical{border-top:var(--jp-scrollbar-endpad) solid var(--jp-scrollbar-background-color);border-bottom:var(--jp-scrollbar-endpad) solid var(--jp-scrollbar-background-color)}.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-hscrollbar::-webkit-scrollbar,.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-vscrollbar::-webkit-scrollbar,.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-hscrollbar::-webkit-scrollbar-corner,.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-vscrollbar::-webkit-scrollbar-corner{background-color:transparent}.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-hscrollbar::-webkit-scrollbar-thumb,.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-vscrollbar::-webkit-scrollbar-thumb{background:rgba(var(--jp-scrollbar-thumb-color),.5);border:var(--jp-scrollbar-thumb-margin) solid transparent;background-clip:content-box;border-radius:var(--jp-scrollbar-thumb-radius)}.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-hscrollbar::-webkit-scrollbar-track:horizontal{border-left:var(--jp-scrollbar-endpad) solid transparent;border-right:var(--jp-scrollbar-endpad) solid transparent}.jupyter-wrapper [data-jp-theme-scrollbars=true] .CodeMirror-vscrollbar::-webkit-scrollbar-track:vertical{border-top:var(--jp-scrollbar-endpad) solid transparent;border-bottom:var(--jp-scrollbar-endpad) solid transparent}.jupyter-wrapper .jp-scrollbar-tiny::-webkit-scrollbar,.jupyter-wrapper .jp-scrollbar-tiny::-webkit-scrollbar-corner{background-color:transparent;height:4px;width:4px}.jupyter-wrapper .jp-scrollbar-tiny::-webkit-scrollbar-thumb{background:rgba(var(--jp-scrollbar-thumb-color),.5)}.jupyter-wrapper .jp-scrollbar-tiny::-webkit-scrollbar-track:horizontal{border-left:0px solid transparent;border-right:0px solid transparent}.jupyter-wrapper .jp-scrollbar-tiny::-webkit-scrollbar-track:vertical{border-top:0px solid transparent;border-bottom:0px solid transparent}.jupyter-wrapper .lm-ScrollBar[data-orientation=horizontal]{min-height:16px;max-height:16px;min-width:45px;border-top:1px solid #a0a0a0}.jupyter-wrapper .lm-ScrollBar[data-orientation=vertical]{min-width:16px;max-width:16px;min-height:45px;border-left:1px solid #a0a0a0}.jupyter-wrapper .lm-ScrollBar-button{background-color:#f0f0f0;background-position:center center;min-height:15px;max-height:15px;min-width:15px;max-width:15px}.jupyter-wrapper .lm-ScrollBar-button:hover{background-color:#dadada}.jupyter-wrapper .lm-ScrollBar-button.lm-mod-active{background-color:#cdcdcd}.jupyter-wrapper .lm-ScrollBar-track{background:#f0f0f0}.jupyter-wrapper .lm-ScrollBar-thumb{background:#cdcdcd}.jupyter-wrapper .lm-ScrollBar-thumb:hover{background:#bababa}.jupyter-wrapper .lm-ScrollBar-thumb.lm-mod-active{background:#a0a0a0}.jupyter-wrapper .lm-ScrollBar[data-orientation=horizontal] .lm-ScrollBar-thumb{height:100%;min-width:15px;border-left:1px solid #a0a0a0;border-right:1px solid #a0a0a0}.jupyter-wrapper .lm-ScrollBar[data-orientation=vertical] .lm-ScrollBar-thumb{width:100%;min-height:15px;border-top:1px solid #a0a0a0;border-bottom:1px solid #a0a0a0}.jupyter-wrapper .lm-ScrollBar[data-orientation=horizontal] .lm-ScrollBar-button[data-action=decrement]{background-image:var(--jp-icon-caret-left);background-size:17px}.jupyter-wrapper .lm-ScrollBar[data-orientation=horizontal] .lm-ScrollBar-button[data-action=increment]{background-image:var(--jp-icon-caret-right);background-size:17px}.jupyter-wrapper .lm-ScrollBar[data-orientation=vertical] .lm-ScrollBar-button[data-action=decrement]{background-image:var(--jp-icon-caret-up);background-size:17px}.jupyter-wrapper .lm-ScrollBar[data-orientation=vertical] .lm-ScrollBar-button[data-action=increment]{background-image:var(--jp-icon-caret-down);background-size:17px}.jupyter-wrapper .p-Widget,.jupyter-wrapper .lm-Widget{box-sizing:border-box;position:relative;overflow:hidden;cursor:default}.jupyter-wrapper .p-Widget.p-mod-hidden,.jupyter-wrapper .lm-Widget.lm-mod-hidden{display:none!important}.jupyter-wrapper .lm-AccordionPanel[data-orientation=horizontal]>.lm-AccordionPanel-title{display:block;transform-origin:top left;transform:rotate(-90deg) translate(-100%)}.jupyter-wrapper .p-CommandPalette,.jupyter-wrapper .lm-CommandPalette{display:flex;flex-direction:column;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .p-CommandPalette-search,.jupyter-wrapper .lm-CommandPalette-search{flex:0 0 auto}.jupyter-wrapper .p-CommandPalette-content,.jupyter-wrapper .lm-CommandPalette-content{flex:1 1 auto;margin:0;padding:0;min-height:0;overflow:auto;list-style-type:none}.jupyter-wrapper .p-CommandPalette-header,.jupyter-wrapper .lm-CommandPalette-header{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.jupyter-wrapper .p-CommandPalette-item,.jupyter-wrapper .lm-CommandPalette-item{display:flex;flex-direction:row}.jupyter-wrapper .p-CommandPalette-itemIcon,.jupyter-wrapper .lm-CommandPalette-itemIcon{flex:0 0 auto}.jupyter-wrapper .p-CommandPalette-itemContent,.jupyter-wrapper .lm-CommandPalette-itemContent{flex:1 1 auto;overflow:hidden}.jupyter-wrapper .p-CommandPalette-itemShortcut,.jupyter-wrapper .lm-CommandPalette-itemShortcut{flex:0 0 auto}.jupyter-wrapper .p-CommandPalette-itemLabel,.jupyter-wrapper .lm-CommandPalette-itemLabel{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.jupyter-wrapper .lm-close-icon{border:1px solid transparent;background-color:transparent;position:absolute;z-index:1;right:3%;top:0;bottom:0;margin:auto;padding:7px 0;display:none;vertical-align:middle;outline:0;cursor:pointer}.jupyter-wrapper .lm-close-icon:after{content:\"X\";display:block;width:15px;height:15px;text-align:center;color:#000;font-weight:400;font-size:12px;cursor:pointer}.jupyter-wrapper .p-DockPanel,.jupyter-wrapper .lm-DockPanel,.jupyter-wrapper .p-DockPanel-widget,.jupyter-wrapper .lm-DockPanel-widget{z-index:0}.jupyter-wrapper .p-DockPanel-tabBar,.jupyter-wrapper .lm-DockPanel-tabBar{z-index:1}.jupyter-wrapper .p-DockPanel-handle,.jupyter-wrapper .lm-DockPanel-handle{z-index:2}.jupyter-wrapper .p-DockPanel-handle.p-mod-hidden,.jupyter-wrapper .lm-DockPanel-handle.lm-mod-hidden{display:none!important}.jupyter-wrapper .p-DockPanel-handle:after,.jupyter-wrapper .lm-DockPanel-handle:after{position:absolute;top:0;left:0;width:100%;height:100%;content:\"\"}.jupyter-wrapper .p-DockPanel-handle[data-orientation=horizontal],.jupyter-wrapper .lm-DockPanel-handle[data-orientation=horizontal]{cursor:ew-resize}.jupyter-wrapper .p-DockPanel-handle[data-orientation=vertical],.jupyter-wrapper .lm-DockPanel-handle[data-orientation=vertical]{cursor:ns-resize}.jupyter-wrapper .p-DockPanel-handle[data-orientation=horizontal]:after,.jupyter-wrapper .lm-DockPanel-handle[data-orientation=horizontal]:after{left:50%;min-width:8px;transform:translate(-50%)}.jupyter-wrapper .p-DockPanel-handle[data-orientation=vertical]:after,.jupyter-wrapper .lm-DockPanel-handle[data-orientation=vertical]:after{top:50%;min-height:8px;transform:translateY(-50%)}.jupyter-wrapper .p-DockPanel-overlay,.jupyter-wrapper .lm-DockPanel-overlay{z-index:3;box-sizing:border-box;pointer-events:none}.jupyter-wrapper .p-DockPanel-overlay.p-mod-hidden,.jupyter-wrapper .lm-DockPanel-overlay.lm-mod-hidden{display:none!important}.jupyter-wrapper .p-Menu,.jupyter-wrapper .lm-Menu{z-index:10000;position:absolute;white-space:nowrap;overflow-x:hidden;overflow-y:auto;outline:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .p-Menu-content,.jupyter-wrapper .lm-Menu-content{margin:0;padding:0;display:table;list-style-type:none}.jupyter-wrapper .p-Menu-item,.jupyter-wrapper .lm-Menu-item{display:table-row}.jupyter-wrapper .p-Menu-item.p-mod-hidden,.jupyter-wrapper .p-Menu-item.p-mod-collapsed,.jupyter-wrapper .lm-Menu-item.lm-mod-hidden,.jupyter-wrapper .lm-Menu-item.lm-mod-collapsed{display:none!important}.jupyter-wrapper .p-Menu-itemIcon,.jupyter-wrapper .p-Menu-itemSubmenuIcon,.jupyter-wrapper .lm-Menu-itemIcon,.jupyter-wrapper .lm-Menu-itemSubmenuIcon{display:table-cell;text-align:center}.jupyter-wrapper .p-Menu-itemLabel,.jupyter-wrapper .lm-Menu-itemLabel{display:table-cell;text-align:left}.jupyter-wrapper .p-Menu-itemShortcut,.jupyter-wrapper .lm-Menu-itemShortcut{display:table-cell;text-align:right}.jupyter-wrapper .p-MenuBar,.jupyter-wrapper .lm-MenuBar{outline:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .p-MenuBar-content,.jupyter-wrapper .lm-MenuBar-content{margin:0;padding:0;display:flex;flex-direction:row;list-style-type:none}.jupyter-wrapper .p--MenuBar-item,.jupyter-wrapper .lm-MenuBar-item{box-sizing:border-box}.jupyter-wrapper .p-MenuBar-itemIcon,.jupyter-wrapper .p-MenuBar-itemLabel,.jupyter-wrapper .lm-MenuBar-itemIcon,.jupyter-wrapper .lm-MenuBar-itemLabel{display:inline-block}.jupyter-wrapper .p-ScrollBar,.jupyter-wrapper .lm-ScrollBar{display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .p-ScrollBar[data-orientation=horizontal],.jupyter-wrapper .lm-ScrollBar[data-orientation=horizontal]{flex-direction:row}.jupyter-wrapper .p-ScrollBar[data-orientation=vertical],.jupyter-wrapper .lm-ScrollBar[data-orientation=vertical]{flex-direction:column}.jupyter-wrapper .p-ScrollBar-button,.jupyter-wrapper .lm-ScrollBar-button{box-sizing:border-box;flex:0 0 auto}.jupyter-wrapper .p-ScrollBar-track,.jupyter-wrapper .lm-ScrollBar-track{box-sizing:border-box;position:relative;overflow:hidden;flex:1 1 auto}.jupyter-wrapper .p-ScrollBar-thumb,.jupyter-wrapper .lm-ScrollBar-thumb{box-sizing:border-box;position:absolute}.jupyter-wrapper .p-SplitPanel-child,.jupyter-wrapper .lm-SplitPanel-child{z-index:0}.jupyter-wrapper .p-SplitPanel-handle,.jupyter-wrapper .lm-SplitPanel-handle{z-index:1}.jupyter-wrapper .p-SplitPanel-handle.p-mod-hidden,.jupyter-wrapper .lm-SplitPanel-handle.lm-mod-hidden{display:none!important}.jupyter-wrapper .p-SplitPanel-handle:after,.jupyter-wrapper .lm-SplitPanel-handle:after{position:absolute;top:0;left:0;width:100%;height:100%;content:\"\"}.jupyter-wrapper .p-SplitPanel[data-orientation=horizontal]>.p-SplitPanel-handle,.jupyter-wrapper .lm-SplitPanel[data-orientation=horizontal]>.lm-SplitPanel-handle{cursor:ew-resize}.jupyter-wrapper .p-SplitPanel[data-orientation=vertical]>.p-SplitPanel-handle,.jupyter-wrapper .lm-SplitPanel[data-orientation=vertical]>.lm-SplitPanel-handle{cursor:ns-resize}.jupyter-wrapper .p-SplitPanel[data-orientation=horizontal]>.p-SplitPanel-handle:after,.jupyter-wrapper .lm-SplitPanel[data-orientation=horizontal]>.lm-SplitPanel-handle:after{left:50%;min-width:8px;transform:translate(-50%)}.jupyter-wrapper .p-SplitPanel[data-orientation=vertical]>.p-SplitPanel-handle:after,.jupyter-wrapper .lm-SplitPanel[data-orientation=vertical]>.lm-SplitPanel-handle:after{top:50%;min-height:8px;transform:translateY(-50%)}.jupyter-wrapper .p-TabBar,.jupyter-wrapper .lm-TabBar{display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .p-TabBar[data-orientation=horizontal],.jupyter-wrapper .lm-TabBar[data-orientation=horizontal]{flex-direction:row;align-items:flex-end}.jupyter-wrapper .p-TabBar[data-orientation=vertical],.jupyter-wrapper .lm-TabBar[data-orientation=vertical]{flex-direction:column;align-items:flex-end}.jupyter-wrapper .p-TabBar-content,.jupyter-wrapper .lm-TabBar-content{margin:0;padding:0;display:flex;flex:1 1 auto;list-style-type:none}.jupyter-wrapper .p-TabBar[data-orientation=horizontal]>.p-TabBar-content,.jupyter-wrapper .lm-TabBar[data-orientation=horizontal]>.lm-TabBar-content{flex-direction:row}.jupyter-wrapper .p-TabBar[data-orientation=vertical]>.p-TabBar-content,.jupyter-wrapper .lm-TabBar[data-orientation=vertical]>.lm-TabBar-content{flex-direction:column}.jupyter-wrapper .p-TabBar-tab,.jupyter-wrapper .lm-TabBar-tab{display:flex;flex-direction:row;box-sizing:border-box;overflow:hidden;touch-action:none}.jupyter-wrapper .p-TabBar-tabIcon,.jupyter-wrapper .p-TabBar-tabCloseIcon,.jupyter-wrapper .lm-TabBar-tabIcon,.jupyter-wrapper .lm-TabBar-tabCloseIcon{flex:0 0 auto}.jupyter-wrapper .p-TabBar-tabLabel,.jupyter-wrapper .lm-TabBar-tabLabel{flex:1 1 auto;overflow:hidden;white-space:nowrap}.jupyter-wrapper .lm-TabBar-tabInput{-webkit-user-select:all;user-select:all;width:100%;box-sizing:border-box}.jupyter-wrapper .p-TabBar-tab.p-mod-hidden,.jupyter-wrapper .lm-TabBar-tab.lm-mod-hidden,.jupyter-wrapper .lm-TabBar-addButton.lm-mod-hidden{display:none!important}.jupyter-wrapper .p-TabBar.p-mod-dragging .p-TabBar-tab,.jupyter-wrapper .lm-TabBar.lm-mod-dragging .lm-TabBar-tab{position:relative}.jupyter-wrapper .p-TabBar.p-mod-dragging[data-orientation=horizontal] .p-TabBar-tab,.jupyter-wrapper .lm-TabBar.lm-mod-dragging[data-orientation=horizontal] .lm-TabBar-tab{left:0;transition:left .15s ease}.jupyter-wrapper .p-TabBar.p-mod-dragging[data-orientation=vertical] .p-TabBar-tab,.jupyter-wrapper .lm-TabBar.lm-mod-dragging[data-orientation=vertical] .lm-TabBar-tab{top:0;transition:top .15s ease}.jupyter-wrapper .p-TabBar.p-mod-dragging .p-TabBar-tab.p-mod-dragging,.jupyter-wrapper .lm-TabBar.lm-mod-dragging .lm-TabBar-tab.lm-mod-dragging{transition:none}.jupyter-wrapper .lm-TabBar-tabLabel .lm-TabBar-tabInput{-webkit-user-select:all;user-select:all;width:100%;box-sizing:border-box;background:inherit}.jupyter-wrapper .p-TabPanel-tabBar,.jupyter-wrapper .lm-TabPanel-tabBar{z-index:1}.jupyter-wrapper .p-TabPanel-stackedPanel,.jupyter-wrapper .lm-TabPanel-stackedPanel{z-index:0}.jupyter-wrapper html{-webkit-box-sizing:border-box;box-sizing:border-box}.jupyter-wrapper *,.jupyter-wrapper *:before,.jupyter-wrapper *:after{-webkit-box-sizing:inherit;box-sizing:inherit}.jupyter-wrapper body{font-size:14px;font-weight:400;letter-spacing:0;line-height:1.28581;text-transform:none;color:#182026;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,Icons16,sans-serif}.jupyter-wrapper p{margin-bottom:10px;margin-top:0}.jupyter-wrapper small{font-size:12px}.jupyter-wrapper strong{font-weight:600}.jupyter-wrapper ::-moz-selection{background:rgba(125,188,255,.6)}.jupyter-wrapper ::selection{background:rgba(125,188,255,.6)}.jupyter-wrapper .bp3-heading{color:#182026;font-weight:600;margin:0 0 10px;padding:0}.jupyter-wrapper .bp3-dark .bp3-heading{color:#f5f8fa}.jupyter-wrapper h1.bp3-heading,.jupyter-wrapper .bp3-running-text h1{font-size:36px;line-height:40px}.jupyter-wrapper h2.bp3-heading,.jupyter-wrapper .bp3-running-text h2{font-size:28px;line-height:32px}.jupyter-wrapper h3.bp3-heading,.jupyter-wrapper .bp3-running-text h3{font-size:22px;line-height:25px}.jupyter-wrapper h4.bp3-heading,.jupyter-wrapper .bp3-running-text h4{font-size:18px;line-height:21px}.jupyter-wrapper h5.bp3-heading,.jupyter-wrapper .bp3-running-text h5{font-size:16px;line-height:19px}.jupyter-wrapper h6.bp3-heading,.jupyter-wrapper .bp3-running-text h6{font-size:14px;line-height:16px}.jupyter-wrapper .bp3-ui-text{font-size:14px;font-weight:400;letter-spacing:0;line-height:1.28581;text-transform:none}.jupyter-wrapper .bp3-monospace-text{font-family:monospace;text-transform:none}.jupyter-wrapper .bp3-text-muted{color:#5c7080}.jupyter-wrapper .bp3-dark .bp3-text-muted{color:#a7b6c2}.jupyter-wrapper .bp3-text-disabled{color:#5c708099}.jupyter-wrapper .bp3-dark .bp3-text-disabled{color:#a7b6c299}.jupyter-wrapper .bp3-text-overflow-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal}.jupyter-wrapper .bp3-running-text{font-size:14px;line-height:1.5}.jupyter-wrapper .bp3-running-text h1{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.jupyter-wrapper .bp3-dark .bp3-running-text h1{color:#f5f8fa}.jupyter-wrapper .bp3-running-text h2{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.jupyter-wrapper .bp3-dark .bp3-running-text h2{color:#f5f8fa}.jupyter-wrapper .bp3-running-text h3{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.jupyter-wrapper .bp3-dark .bp3-running-text h3{color:#f5f8fa}.jupyter-wrapper .bp3-running-text h4{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.jupyter-wrapper .bp3-dark .bp3-running-text h4{color:#f5f8fa}.jupyter-wrapper .bp3-running-text h5{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.jupyter-wrapper .bp3-dark .bp3-running-text h5{color:#f5f8fa}.jupyter-wrapper .bp3-running-text h6{color:#182026;font-weight:600;margin-bottom:20px;margin-top:40px}.jupyter-wrapper .bp3-dark .bp3-running-text h6{color:#f5f8fa}.jupyter-wrapper .bp3-running-text hr{border:none;border-bottom:1px solid rgba(16,22,26,.15);margin:20px 0}.jupyter-wrapper .bp3-dark .bp3-running-text hr{border-color:#ffffff26}.jupyter-wrapper .bp3-running-text p{margin:0 0 10px;padding:0}.jupyter-wrapper .bp3-text-large{font-size:16px}.jupyter-wrapper .bp3-text-small{font-size:12px}.jupyter-wrapper a .bp3-icon,.jupyter-wrapper a .bp3-icon-standard,.jupyter-wrapper a .bp3-icon-large,.jupyter-wrapper a code,.jupyter-wrapper .bp3-dark a code{color:inherit}.jupyter-wrapper .bp3-dark a,.jupyter-wrapper .bp3-dark a:hover{color:#48aff0}.jupyter-wrapper .bp3-dark a .bp3-icon,.jupyter-wrapper .bp3-dark a .bp3-icon-standard,.jupyter-wrapper .bp3-dark a .bp3-icon-large,.jupyter-wrapper .bp3-dark a:hover .bp3-icon,.jupyter-wrapper .bp3-dark a:hover .bp3-icon-standard,.jupyter-wrapper .bp3-dark a:hover .bp3-icon-large{color:inherit}.jupyter-wrapper .bp3-running-text code,.jupyter-wrapper .bp3-code{font-family:monospace;text-transform:none;background:rgba(255,255,255,.7);border-radius:3px;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33;color:#5c7080;font-size:smaller;padding:2px 5px}.jupyter-wrapper .bp3-dark .bp3-running-text code,.jupyter-wrapper .bp3-running-text .bp3-dark code,.jupyter-wrapper .bp3-dark .bp3-code{background:rgba(16,22,26,.3);-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a66;color:#a7b6c2}.jupyter-wrapper .bp3-running-text a>code,.jupyter-wrapper a>.bp3-code{color:#137cbd}.jupyter-wrapper .bp3-dark .bp3-running-text a>code,.jupyter-wrapper .bp3-running-text .bp3-dark a>code,.jupyter-wrapper .bp3-dark a>.bp3-code{color:inherit}.jupyter-wrapper .bp3-running-text pre,.jupyter-wrapper .bp3-code-block{font-family:monospace;text-transform:none;background:rgba(255,255,255,.7);border-radius:3px;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.15);box-shadow:inset 0 0 0 1px #10161a26;color:#182026;display:block;font-size:13px;line-height:1.4;margin:10px 0;padding:13px 15px 12px;word-break:break-all;word-wrap:break-word}.jupyter-wrapper .bp3-dark .bp3-running-text pre,.jupyter-wrapper .bp3-running-text .bp3-dark pre,.jupyter-wrapper .bp3-dark .bp3-code-block{background:rgba(16,22,26,.3);-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-running-text pre>code,.jupyter-wrapper .bp3-code-block>code{background:none;-webkit-box-shadow:none;box-shadow:none;color:inherit;font-size:inherit;padding:0}.jupyter-wrapper .bp3-running-text kbd,.jupyter-wrapper .bp3-key{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background:#ffffff;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 0 #10161a00,0 1px 1px #10161a33;color:#5c7080;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-family:inherit;font-size:12px;height:24px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;line-height:24px;min-width:24px;padding:3px 6px;vertical-align:middle}.jupyter-wrapper .bp3-running-text kbd .bp3-icon,.jupyter-wrapper .bp3-key .bp3-icon,.jupyter-wrapper .bp3-running-text kbd .bp3-icon-standard,.jupyter-wrapper .bp3-key .bp3-icon-standard,.jupyter-wrapper .bp3-running-text kbd .bp3-icon-large,.jupyter-wrapper .bp3-key .bp3-icon-large{margin-right:5px}.jupyter-wrapper .bp3-dark .bp3-running-text kbd,.jupyter-wrapper .bp3-running-text .bp3-dark kbd,.jupyter-wrapper .bp3-dark .bp3-key{background:#394b59;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 0 #10161a00,0 1px 1px #10161a66;color:#a7b6c2}.jupyter-wrapper .bp3-running-text blockquote,.jupyter-wrapper .bp3-blockquote{border-left:solid 4px rgba(167,182,194,.5);margin:0 0 10px;padding:0 20px}.jupyter-wrapper .bp3-dark .bp3-running-text blockquote,.jupyter-wrapper .bp3-running-text .bp3-dark blockquote,.jupyter-wrapper .bp3-dark .bp3-blockquote{border-color:#73869480}.jupyter-wrapper .bp3-running-text ul,.jupyter-wrapper .bp3-running-text ol,.jupyter-wrapper .bp3-list{margin:10px 0;padding-left:30px}.jupyter-wrapper .bp3-running-text ul li:not(:last-child),.jupyter-wrapper .bp3-running-text ol li:not(:last-child),.jupyter-wrapper .bp3-list li:not(:last-child){margin-bottom:5px}.jupyter-wrapper .bp3-running-text ul ol,.jupyter-wrapper .bp3-running-text ol ol,.jupyter-wrapper .bp3-list ol,.jupyter-wrapper .bp3-running-text ul ul,.jupyter-wrapper .bp3-running-text ol ul,.jupyter-wrapper .bp3-list ul{margin-top:5px}.jupyter-wrapper .bp3-list-unstyled{list-style:none;margin:0;padding:0}.jupyter-wrapper .bp3-list-unstyled li{padding:0}.jupyter-wrapper .bp3-rtl{text-align:right}.jupyter-wrapper .bp3-dark{color:#f5f8fa}.jupyter-wrapper :focus{outline:rgba(19,124,189,.6) auto 2px;outline-offset:2px;-moz-outline-radius:6px}.jupyter-wrapper .bp3-focus-disabled :focus{outline:none!important}.jupyter-wrapper .bp3-focus-disabled :focus~.bp3-control-indicator{outline:none!important}.jupyter-wrapper .bp3-alert{max-width:400px;padding:20px}.jupyter-wrapper .bp3-alert-body{display:-webkit-box;display:-ms-flexbox;display:flex}.jupyter-wrapper .bp3-alert-body .bp3-icon{font-size:40px;margin-right:20px;margin-top:0}.jupyter-wrapper .bp3-alert-contents{word-break:break-word}.jupyter-wrapper .bp3-alert-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;margin-top:10px}.jupyter-wrapper .bp3-alert-footer .bp3-button{margin-left:10px}.jupyter-wrapper .bp3-breadcrumbs{-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:default;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;height:30px;list-style:none;margin:0;padding:0}.jupyter-wrapper .bp3-breadcrumbs>li{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.jupyter-wrapper .bp3-breadcrumbs>li:after{background:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.71 7.29l-4-4a1.003 1.003 0 00-1.42 1.42L8.59 8 5.3 11.29c-.19.18-.3.43-.3.71a1.003 1.003 0 001.71.71l4-4c.18-.18.29-.43.29-.71 0-.28-.11-.53-.29-.71z' fill='%235C7080'/%3e%3c/svg%3e\");content:\"\";display:block;height:16px;margin:0 5px;width:16px}.jupyter-wrapper .bp3-breadcrumbs>li:last-of-type:after{display:none}.jupyter-wrapper .bp3-breadcrumb,.jupyter-wrapper .bp3-breadcrumb-current,.jupyter-wrapper .bp3-breadcrumbs-collapsed{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:16px}.jupyter-wrapper .bp3-breadcrumb,.jupyter-wrapper .bp3-breadcrumbs-collapsed{color:#5c7080}.jupyter-wrapper .bp3-breadcrumb:hover{text-decoration:none}.jupyter-wrapper .bp3-breadcrumb.bp3-disabled{color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-breadcrumb .bp3-icon{margin-right:5px}.jupyter-wrapper .bp3-breadcrumb-current{color:inherit;font-weight:600}.jupyter-wrapper .bp3-breadcrumb-current .bp3-input{font-size:inherit;font-weight:inherit;vertical-align:baseline}.jupyter-wrapper .bp3-breadcrumbs-collapsed{background:#ced9e0;border:none;border-radius:3px;cursor:pointer;margin-right:2px;padding:1px 5px;vertical-align:text-bottom}.jupyter-wrapper .bp3-breadcrumbs-collapsed:before{background:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cg fill='%235C7080'%3e%3ccircle cx='2' cy='8.03' r='2'/%3e%3ccircle cx='14' cy='8.03' r='2'/%3e%3ccircle cx='8' cy='8.03' r='2'/%3e%3c/g%3e%3c/svg%3e\") center no-repeat;content:\"\";display:block;height:16px;width:16px}.jupyter-wrapper .bp3-breadcrumbs-collapsed:hover{background:#bfccd6;color:#182026;text-decoration:none}.jupyter-wrapper .bp3-dark .bp3-breadcrumb,.jupyter-wrapper .bp3-dark .bp3-breadcrumbs-collapsed{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-breadcrumbs>li:after{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-breadcrumb.bp3-disabled{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-breadcrumb-current{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-breadcrumbs-collapsed{background:rgba(16,22,26,.4)}.jupyter-wrapper .bp3-dark .bp3-breadcrumbs-collapsed:hover{background:rgba(16,22,26,.6);color:#f5f8fa}.jupyter-wrapper .bp3-button{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:none;border-radius:3px;cursor:pointer;font-size:14px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:5px 10px;text-align:left;vertical-align:middle;min-height:30px;min-width:30px}.jupyter-wrapper .bp3-button>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-button>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-button:before,.jupyter-wrapper .bp3-button>*{margin-right:7px}.jupyter-wrapper .bp3-button:empty:before,.jupyter-wrapper .bp3-button>:last-child{margin-right:0}.jupyter-wrapper .bp3-button:empty{padding:0!important}.jupyter-wrapper .bp3-button:disabled,.jupyter-wrapper .bp3-button.bp3-disabled{cursor:not-allowed}.jupyter-wrapper .bp3-button.bp3-fill{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%}.jupyter-wrapper .bp3-button.bp3-align-right,.jupyter-wrapper .bp3-align-right .bp3-button{text-align:right}.jupyter-wrapper .bp3-button.bp3-align-left,.jupyter-wrapper .bp3-align-left .bp3-button{text-align:left}.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]){background-color:#f5f8fa;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.8)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.8),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a;color:#182026}.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]):hover{background-clip:padding-box;background-color:#ebf1f5;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a}.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]):active,.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]).bp3-active{background-color:#d8e1e8;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]):disabled,.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]).bp3-disabled{background-color:#ced9e080;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;outline:none}.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]):disabled.bp3-active,.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]):disabled.bp3-active:hover,.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]).bp3-disabled.bp3-active,.jupyter-wrapper .bp3-button:not([class*=bp3-intent-]).bp3-disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.jupyter-wrapper .bp3-button.bp3-intent-primary{background-color:#137cbd;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.1)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.1),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33;color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-primary:hover,.jupyter-wrapper .bp3-button.bp3-intent-primary:active,.jupyter-wrapper .bp3-button.bp3-intent-primary.bp3-active{color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-primary:hover{background-color:#106ba3;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-primary:active,.jupyter-wrapper .bp3-button.bp3-intent-primary.bp3-active{background-color:#0e5a8a;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-button.bp3-intent-primary.bp3-disabled{background-color:#137cbd80;background-image:none;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff9}.jupyter-wrapper .bp3-button.bp3-intent-success{background-color:#0f9960;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.1)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.1),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33;color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-success:hover,.jupyter-wrapper .bp3-button.bp3-intent-success:active,.jupyter-wrapper .bp3-button.bp3-intent-success.bp3-active{color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-success:hover{background-color:#0d8050;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-success:active,.jupyter-wrapper .bp3-button.bp3-intent-success.bp3-active{background-color:#0a6640;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-success:disabled,.jupyter-wrapper .bp3-button.bp3-intent-success.bp3-disabled{background-color:#0f996080;background-image:none;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff9}.jupyter-wrapper .bp3-button.bp3-intent-warning{background-color:#d9822b;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.1)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.1),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33;color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-warning:hover,.jupyter-wrapper .bp3-button.bp3-intent-warning:active,.jupyter-wrapper .bp3-button.bp3-intent-warning.bp3-active{color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-warning:hover{background-color:#bf7326;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-warning:active,.jupyter-wrapper .bp3-button.bp3-intent-warning.bp3-active{background-color:#a66321;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-button.bp3-intent-warning.bp3-disabled{background-color:#d9822b80;background-image:none;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff9}.jupyter-wrapper .bp3-button.bp3-intent-danger{background-color:#db3737;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.1)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.1),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33;color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-danger:hover,.jupyter-wrapper .bp3-button.bp3-intent-danger:active,.jupyter-wrapper .bp3-button.bp3-intent-danger.bp3-active{color:#fff}.jupyter-wrapper .bp3-button.bp3-intent-danger:hover{background-color:#c23030;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-danger:active,.jupyter-wrapper .bp3-button.bp3-intent-danger.bp3-active{background-color:#a82a2a;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-button.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-button.bp3-intent-danger.bp3-disabled{background-color:#db373780;background-image:none;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff9}.jupyter-wrapper .bp3-button[class*=bp3-intent-] .bp3-button-spinner .bp3-spinner-head{stroke:#fff}.jupyter-wrapper .bp3-button.bp3-large,.jupyter-wrapper .bp3-large .bp3-button{min-height:40px;min-width:40px;font-size:16px;padding:5px 15px}.jupyter-wrapper .bp3-button.bp3-large:before,.jupyter-wrapper .bp3-button.bp3-large>*,.jupyter-wrapper .bp3-large .bp3-button:before,.jupyter-wrapper .bp3-large .bp3-button>*{margin-right:10px}.jupyter-wrapper .bp3-button.bp3-large:empty:before,.jupyter-wrapper .bp3-button.bp3-large>:last-child,.jupyter-wrapper .bp3-large .bp3-button:empty:before,.jupyter-wrapper .bp3-large .bp3-button>:last-child{margin-right:0}.jupyter-wrapper .bp3-button.bp3-small,.jupyter-wrapper .bp3-small .bp3-button{min-height:24px;min-width:24px;padding:0 7px}.jupyter-wrapper .bp3-button.bp3-loading{position:relative}.jupyter-wrapper .bp3-button.bp3-loading[class*=bp3-icon-]:before{visibility:hidden}.jupyter-wrapper .bp3-button.bp3-loading .bp3-button-spinner{margin:0;position:absolute}.jupyter-wrapper .bp3-button.bp3-loading>:not(.bp3-button-spinner){visibility:hidden}.jupyter-wrapper .bp3-button[class*=bp3-icon-]:before{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#5c7080}.jupyter-wrapper .bp3-button .bp3-icon,.jupyter-wrapper .bp3-button .bp3-icon-standard,.jupyter-wrapper .bp3-button .bp3-icon-large{color:#5c7080}.jupyter-wrapper .bp3-button .bp3-icon.bp3-align-right,.jupyter-wrapper .bp3-button .bp3-icon-standard.bp3-align-right,.jupyter-wrapper .bp3-button .bp3-icon-large.bp3-align-right{margin-left:7px}.jupyter-wrapper .bp3-button .bp3-icon:first-child:last-child,.jupyter-wrapper .bp3-button .bp3-spinner+.bp3-icon:last-child{margin:0 -7px}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]){background-color:#394b59;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.05)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.05),rgba(255,255,255,0));-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]):hover,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]):active,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-active{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]):hover{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]):active,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-active{background-color:#202b33;background-image:none;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a99,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]):disabled,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-disabled{background-color:#394b5980;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]):disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]).bp3-disabled.bp3-active{background:rgba(57,75,89,.7)}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-])[class*=bp3-icon-]:before{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-icon,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-icon-standard,.jupyter-wrapper .bp3-dark .bp3-button:not([class*=bp3-intent-]) .bp3-icon-large{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-],.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-]:hover{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-]:active,.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-].bp3-active{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-]:disabled,.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-].bp3-disabled{background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#ffffff4d}.jupyter-wrapper .bp3-dark .bp3-button[class*=bp3-intent-] .bp3-button-spinner .bp3-spinner-head{stroke:#8a9ba8}.jupyter-wrapper .bp3-button:disabled:before,.jupyter-wrapper .bp3-button:disabled .bp3-icon,.jupyter-wrapper .bp3-button:disabled .bp3-icon-standard,.jupyter-wrapper .bp3-button:disabled .bp3-icon-large,.jupyter-wrapper .bp3-button.bp3-disabled:before,.jupyter-wrapper .bp3-button.bp3-disabled .bp3-icon,.jupyter-wrapper .bp3-button.bp3-disabled .bp3-icon-standard,.jupyter-wrapper .bp3-button.bp3-disabled .bp3-icon-large,.jupyter-wrapper .bp3-button[class*=bp3-intent-]:before,.jupyter-wrapper .bp3-button[class*=bp3-intent-] .bp3-icon,.jupyter-wrapper .bp3-button[class*=bp3-intent-] .bp3-icon-standard,.jupyter-wrapper .bp3-button[class*=bp3-intent-] .bp3-icon-large{color:inherit!important}.jupyter-wrapper .bp3-button.bp3-minimal{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-button.bp3-minimal:hover{background:rgba(167,182,194,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026;text-decoration:none}.jupyter-wrapper .bp3-button.bp3-minimal:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-active{background:rgba(115,134,148,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026}.jupyter-wrapper .bp3-button.bp3-minimal:disabled,.jupyter-wrapper .bp3-button.bp3-minimal:disabled:hover,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-disabled,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-disabled:hover{background:none;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-button.bp3-minimal:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal:disabled:hover.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal{background:none;-webkit-box-shadow:none;box-shadow:none;color:inherit}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:hover,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:hover{background:rgba(138,155,168,.15)}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-active{background:rgba(138,155,168,.3);color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:disabled:hover,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-disabled:hover{background:none;color:#a7b6c299;cursor:not-allowed}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal:disabled:hover.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary{color:#106ba3}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary:hover,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#106ba3}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#106ba3}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled{background:none;color:#106ba380}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled{background:none;color:#48aff080}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success{color:#0d8050}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success:hover,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#0d8050}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#0d8050}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success:disabled,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled{background:none;color:#0d805080}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled{background:none;color:#3dcc9180}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning{color:#bf7326}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning:hover,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#bf7326}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#bf7326}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled{background:none;color:#bf732680}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled{background:none;color:#ffb36680}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger{color:#c23030}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger:hover,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#c23030}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger:active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#c23030}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled{background:none;color:#c2303080}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-button.bp3-minimal.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled{background:none;color:#ff737380}.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-minimal.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-button.bp3-outlined{background:none;-webkit-box-shadow:none;box-shadow:none;border:1px solid rgba(24,32,38,.2);-webkit-box-sizing:border-box;box-sizing:border-box}.jupyter-wrapper .bp3-button.bp3-outlined:hover{background:rgba(167,182,194,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026;text-decoration:none}.jupyter-wrapper .bp3-button.bp3-outlined:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-active{background:rgba(115,134,148,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026}.jupyter-wrapper .bp3-button.bp3-outlined:disabled,.jupyter-wrapper .bp3-button.bp3-outlined:disabled:hover,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-disabled:hover{background:none;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-button.bp3-outlined:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined:disabled:hover.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined{background:none;-webkit-box-shadow:none;box-shadow:none;color:inherit}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:hover,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:hover{background:rgba(138,155,168,.15)}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-active{background:rgba(138,155,168,.3);color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:disabled:hover,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-disabled:hover{background:none;color:#a7b6c299;cursor:not-allowed}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:disabled:hover.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary{color:#106ba3}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:hover,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#106ba3}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#106ba3}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled{background:none;color:#106ba380}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled{background:none;color:#48aff080}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success{color:#0d8050}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:hover,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#0d8050}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#0d8050}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled{background:none;color:#0d805080}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled{background:none;color:#3dcc9180}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning{color:#bf7326}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:hover,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#bf7326}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#bf7326}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled{background:none;color:#bf732680}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled{background:none;color:#ffb36680}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger{color:#c23030}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:hover,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#c23030}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#c23030}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled{background:none;color:#c2303080}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled{background:none;color:#ff737380}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-button.bp3-outlined:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-disabled,.jupyter-wrapper .bp3-button.bp3-outlined:disabled:hover,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-disabled:hover{border-color:#5c70801a}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined{border-color:#fff6}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined:disabled:hover,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-disabled:hover{border-color:#fff3}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary{border-color:#106ba399}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled{border-color:#106ba333}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary{border-color:#48aff099}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-primary.bp3-disabled{border-color:#48aff033}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success{border-color:#0d805099}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled{border-color:#0d805033}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success{border-color:#3dcc9199}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-success.bp3-disabled{border-color:#3dcc9133}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning{border-color:#bf732699}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled{border-color:#bf732633}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning{border-color:#ffb36699}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-warning.bp3-disabled{border-color:#ffb36633}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger{border-color:#c2303099}.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled{border-color:#c2303033}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger{border-color:#ff737399}.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-button.bp3-outlined.bp3-intent-danger.bp3-disabled{border-color:#ff737333}.jupyter-wrapper a.bp3-button{text-align:center;text-decoration:none;-webkit-transition:none;transition:none}.jupyter-wrapper a.bp3-button,.jupyter-wrapper a.bp3-button:hover,.jupyter-wrapper a.bp3-button:active{color:#182026}.jupyter-wrapper a.bp3-button.bp3-disabled{color:#5c708099}.jupyter-wrapper .bp3-button-text{-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto}.jupyter-wrapper .bp3-button.bp3-align-left .bp3-button-text,.jupyter-wrapper .bp3-button.bp3-align-right .bp3-button-text,.jupyter-wrapper .bp3-button-group.bp3-align-left .bp3-button-text,.jupyter-wrapper .bp3-button-group.bp3-align-right .bp3-button-text{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.jupyter-wrapper .bp3-button-group{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.jupyter-wrapper .bp3-button-group .bp3-button{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;position:relative;z-index:4}.jupyter-wrapper .bp3-button-group .bp3-button:focus{z-index:5}.jupyter-wrapper .bp3-button-group .bp3-button:hover{z-index:6}.jupyter-wrapper .bp3-button-group .bp3-button:active,.jupyter-wrapper .bp3-button-group .bp3-button.bp3-active{z-index:7}.jupyter-wrapper .bp3-button-group .bp3-button:disabled,.jupyter-wrapper .bp3-button-group .bp3-button.bp3-disabled{z-index:3}.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-]{z-index:9}.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-]:focus{z-index:10}.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-]:hover{z-index:11}.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-]:active,.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-].bp3-active{z-index:12}.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-]:disabled,.jupyter-wrapper .bp3-button-group .bp3-button[class*=bp3-intent-].bp3-disabled{z-index:8}.jupyter-wrapper .bp3-button-group:not(.bp3-minimal)>.bp3-popover-wrapper:not(:first-child) .bp3-button,.jupyter-wrapper .bp3-button-group:not(.bp3-minimal)>.bp3-button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.jupyter-wrapper .bp3-button-group:not(.bp3-minimal)>.bp3-popover-wrapper:not(:last-child) .bp3-button,.jupyter-wrapper .bp3-button-group:not(.bp3-minimal)>.bp3-button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button:hover{background:rgba(167,182,194,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026;text-decoration:none}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-active{background:rgba(115,134,148,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button:disabled,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button:disabled:hover,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover{background:none;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button:disabled.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button:disabled:hover.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button{background:none;-webkit-box-shadow:none;box-shadow:none;color:inherit}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:hover,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:hover{background:rgba(138,155,168,.15)}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-active{background:rgba(138,155,168,.3);color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled:hover,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover{background:none;color:#a7b6c299;cursor:not-allowed}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button:disabled:hover.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary{color:#106ba3}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:hover,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#106ba3}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#106ba3}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled{background:none;color:#106ba380}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled{background:none;color:#48aff080}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success{color:#0d8050}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:hover,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#0d8050}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#0d8050}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled{background:none;color:#0d805080}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled{background:none;color:#3dcc9180}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning{color:#bf7326}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:hover,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#bf7326}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#bf7326}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled{background:none;color:#bf732680}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled{background:none;color:#ffb36680}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger{color:#c23030}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:hover,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#c23030}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#c23030}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled{background:none;color:#c2303080}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled{background:none;color:#ff737380}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-minimal .bp3-button.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-button-group .bp3-popover-wrapper,.jupyter-wrapper .bp3-button-group .bp3-popover-target{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.jupyter-wrapper .bp3-button-group.bp3-fill{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%}.jupyter-wrapper .bp3-button-group .bp3-button.bp3-fill,.jupyter-wrapper .bp3-button-group.bp3-fill .bp3-button:not(.bp3-fixed){-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.jupyter-wrapper .bp3-button-group.bp3-vertical{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:top}.jupyter-wrapper .bp3-button-group.bp3-vertical.bp3-fill{height:100%;width:unset}.jupyter-wrapper .bp3-button-group.bp3-vertical .bp3-button{margin-right:0!important;width:100%}.jupyter-wrapper .bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-popover-wrapper:first-child .bp3-button,.jupyter-wrapper .bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-button:first-child{border-radius:3px 3px 0 0}.jupyter-wrapper .bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-popover-wrapper:last-child .bp3-button,.jupyter-wrapper .bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-button:last-child{border-radius:0 0 3px 3px}.jupyter-wrapper .bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-popover-wrapper:not(:last-child) .bp3-button,.jupyter-wrapper .bp3-button-group.bp3-vertical:not(.bp3-minimal)>.bp3-button:not(:last-child){margin-bottom:-1px}.jupyter-wrapper .bp3-button-group.bp3-align-left .bp3-button{text-align:left}.jupyter-wrapper .bp3-dark .bp3-button-group:not(.bp3-minimal)>.bp3-popover-wrapper:not(:last-child) .bp3-button,.jupyter-wrapper .bp3-dark .bp3-button-group:not(.bp3-minimal)>.bp3-button:not(:last-child){margin-right:1px}.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-vertical>.bp3-popover-wrapper:not(:last-child) .bp3-button,.jupyter-wrapper .bp3-dark .bp3-button-group.bp3-vertical>.bp3-button:not(:last-child){margin-bottom:1px}.jupyter-wrapper .bp3-callout{font-size:14px;line-height:1.5;background-color:#8a9ba826;border-radius:3px;padding:10px 12px 9px;position:relative;width:100%}.jupyter-wrapper .bp3-callout[class*=bp3-icon-]{padding-left:40px}.jupyter-wrapper .bp3-callout[class*=bp3-icon-]:before{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#5c7080;left:10px;position:absolute;top:10px}.jupyter-wrapper .bp3-callout.bp3-callout-icon{padding-left:40px}.jupyter-wrapper .bp3-callout.bp3-callout-icon>.bp3-icon:first-child{color:#5c7080;left:10px;position:absolute;top:10px}.jupyter-wrapper .bp3-callout .bp3-heading{line-height:20px;margin-bottom:5px;margin-top:0}.jupyter-wrapper .bp3-callout .bp3-heading:last-child{margin-bottom:0}.jupyter-wrapper .bp3-dark .bp3-callout{background-color:#8a9ba833}.jupyter-wrapper .bp3-dark .bp3-callout[class*=bp3-icon-]:before{color:#a7b6c2}.jupyter-wrapper .bp3-callout.bp3-intent-primary{background-color:#137cbd26}.jupyter-wrapper .bp3-callout.bp3-intent-primary[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-callout.bp3-intent-primary>.bp3-icon:first-child,.jupyter-wrapper .bp3-callout.bp3-intent-primary .bp3-heading{color:#106ba3}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-primary{background-color:#137cbd40}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-primary[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-primary>.bp3-icon:first-child,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-primary .bp3-heading{color:#48aff0}.jupyter-wrapper .bp3-callout.bp3-intent-success{background-color:#0f996026}.jupyter-wrapper .bp3-callout.bp3-intent-success[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-callout.bp3-intent-success>.bp3-icon:first-child,.jupyter-wrapper .bp3-callout.bp3-intent-success .bp3-heading{color:#0d8050}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-success{background-color:#0f996040}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-success[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-success>.bp3-icon:first-child,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-success .bp3-heading{color:#3dcc91}.jupyter-wrapper .bp3-callout.bp3-intent-warning{background-color:#d9822b26}.jupyter-wrapper .bp3-callout.bp3-intent-warning[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-callout.bp3-intent-warning>.bp3-icon:first-child,.jupyter-wrapper .bp3-callout.bp3-intent-warning .bp3-heading{color:#bf7326}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-warning{background-color:#d9822b40}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-warning[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-warning>.bp3-icon:first-child,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-warning .bp3-heading{color:#ffb366}.jupyter-wrapper .bp3-callout.bp3-intent-danger{background-color:#db373726}.jupyter-wrapper .bp3-callout.bp3-intent-danger[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-callout.bp3-intent-danger>.bp3-icon:first-child,.jupyter-wrapper .bp3-callout.bp3-intent-danger .bp3-heading{color:#c23030}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-danger{background-color:#db373740}.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-danger[class*=bp3-icon-]:before,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-danger>.bp3-icon:first-child,.jupyter-wrapper .bp3-dark .bp3-callout.bp3-intent-danger .bp3-heading{color:#ff7373}.jupyter-wrapper .bp3-running-text .bp3-callout{margin:20px 0}.jupyter-wrapper .bp3-card{background-color:#fff;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.15),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0);box-shadow:0 0 0 1px #10161a26,0 0 #10161a00,0 0 #10161a00;padding:20px;-webkit-transition:-webkit-transform .2s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .2s cubic-bezier(.4,1,.75,.9);transition:-webkit-transform .2s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .2s cubic-bezier(.4,1,.75,.9);transition:transform .2s cubic-bezier(.4,1,.75,.9),box-shadow .2s cubic-bezier(.4,1,.75,.9);transition:transform .2s cubic-bezier(.4,1,.75,.9),box-shadow .2s cubic-bezier(.4,1,.75,.9),-webkit-transform .2s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .2s cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-card.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-card{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0);box-shadow:0 0 0 1px #10161a66,0 0 #10161a00,0 0 #10161a00}.jupyter-wrapper .bp3-elevation-0{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.15),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0);box-shadow:0 0 0 1px #10161a26,0 0 #10161a00,0 0 #10161a00}.jupyter-wrapper .bp3-elevation-0.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-elevation-0{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4),0 0 0 rgba(16,22,26,0),0 0 0 rgba(16,22,26,0);box-shadow:0 0 0 1px #10161a66,0 0 #10161a00,0 0 #10161a00}.jupyter-wrapper .bp3-elevation-1{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 0 #10161a00,0 1px 1px #10161a33}.jupyter-wrapper .bp3-elevation-1.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-elevation-1{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 0 #10161a00,0 1px 1px #10161a66}.jupyter-wrapper .bp3-elevation-2{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 1px 1px rgba(16,22,26,.2),0 2px 6px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 1px 1px #10161a33,0 2px 6px #10161a33}.jupyter-wrapper .bp3-elevation-2.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-elevation-2{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.4),0 2px 6px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 1px 1px #10161a66,0 2px 6px #10161a66}.jupyter-wrapper .bp3-elevation-3{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33}.jupyter-wrapper .bp3-elevation-3.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-elevation-3{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66}.jupyter-wrapper .bp3-elevation-4{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 4px 8px #10161a33,0 18px 46px 6px #10161a33}.jupyter-wrapper .bp3-elevation-4.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-elevation-4{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 4px 8px #10161a66,0 18px 46px 6px #10161a66}.jupyter-wrapper .bp3-card.bp3-interactive:hover{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33;cursor:pointer}.jupyter-wrapper .bp3-card.bp3-interactive:hover.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-card.bp3-interactive:hover{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66}.jupyter-wrapper .bp3-card.bp3-interactive:active{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 0 #10161a00,0 1px 1px #10161a33;opacity:.9;-webkit-transition-duration:0;transition-duration:0}.jupyter-wrapper .bp3-card.bp3-interactive:active.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-card.bp3-interactive:active{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 0 #10161a00,0 1px 1px #10161a66}.jupyter-wrapper .bp3-collapse{height:0;overflow-y:hidden;-webkit-transition:height .2s cubic-bezier(.4,1,.75,.9);transition:height .2s cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-collapse .bp3-collapse-body{-webkit-transition:-webkit-transform .2s cubic-bezier(.4,1,.75,.9);transition:-webkit-transform .2s cubic-bezier(.4,1,.75,.9);transition:transform .2s cubic-bezier(.4,1,.75,.9);transition:transform .2s cubic-bezier(.4,1,.75,.9),-webkit-transform .2s cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-collapse .bp3-collapse-body[aria-hidden=true]{display:none}.jupyter-wrapper .bp3-context-menu .bp3-popover-target{display:block}.jupyter-wrapper .bp3-context-menu-popover-target{position:fixed}.jupyter-wrapper .bp3-dialog-container{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;min-height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.jupyter-wrapper .bp3-dialog-container.bp3-overlay-enter>.bp3-dialog,.jupyter-wrapper .bp3-dialog-container.bp3-overlay-appear>.bp3-dialog{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}.jupyter-wrapper .bp3-dialog-container.bp3-overlay-enter-active>.bp3-dialog,.jupyter-wrapper .bp3-dialog-container.bp3-overlay-appear-active>.bp3-dialog{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;transition-property:opacity,transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.54,1.12,.38,1.11);transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.jupyter-wrapper .bp3-dialog-container.bp3-overlay-exit>.bp3-dialog{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-dialog-container.bp3-overlay-exit-active>.bp3-dialog{opacity:0;-webkit-transform:scale(.5);transform:scale(.5);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;transition-property:opacity,transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.54,1.12,.38,1.11);transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.jupyter-wrapper .bp3-dialog{background:#ebf1f5;border-radius:6px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 4px 8px #10161a33,0 18px 46px 6px #10161a33;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:30px 0;padding-bottom:20px;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;width:500px}.jupyter-wrapper .bp3-dialog:focus{outline:0}.jupyter-wrapper .bp3-dialog.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-dialog{background:#293742;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 4px 8px #10161a66,0 18px 46px 6px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dialog-header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background:#ffffff;border-radius:6px 6px 0 0;-webkit-box-shadow:0 1px 0 rgba(16,22,26,.15);box-shadow:0 1px #10161a26;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;min-height:40px;padding-left:20px;padding-right:5px;z-index:30}.jupyter-wrapper .bp3-dialog-header .bp3-icon-large,.jupyter-wrapper .bp3-dialog-header .bp3-icon{color:#5c7080;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin-right:10px}.jupyter-wrapper .bp3-dialog-header .bp3-heading{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;line-height:inherit;margin:0}.jupyter-wrapper .bp3-dialog-header .bp3-heading:last-child{margin-right:20px}.jupyter-wrapper .bp3-dark .bp3-dialog-header{background:#30404d;-webkit-box-shadow:0 1px 0 rgba(16,22,26,.4);box-shadow:0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-dialog-header .bp3-icon-large,.jupyter-wrapper .bp3-dark .bp3-dialog-header .bp3-icon{color:#a7b6c2}.jupyter-wrapper .bp3-dialog-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;line-height:18px;margin:20px}.jupyter-wrapper .bp3-dialog-footer{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0 20px}.jupyter-wrapper .bp3-dialog-footer-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.jupyter-wrapper .bp3-dialog-footer-actions .bp3-button{margin-left:10px}.jupyter-wrapper .bp3-multistep-dialog-panels{display:-webkit-box;display:-ms-flexbox;display:flex}.jupyter-wrapper .bp3-multistep-dialog-left-panel{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.jupyter-wrapper .bp3-dark .bp3-multistep-dialog-left-panel{background:#202b33}.jupyter-wrapper .bp3-multistep-dialog-right-panel{background-color:#f5f8fa;border-left:1px solid rgba(16,22,26,.15);border-radius:0 0 6px;-webkit-box-flex:3;-ms-flex:3;flex:3;min-width:0}.jupyter-wrapper .bp3-dark .bp3-multistep-dialog-right-panel{background-color:#293742;border-left:1px solid rgba(16,22,26,.4)}.jupyter-wrapper .bp3-multistep-dialog-footer{background-color:#fff;border-radius:0 0 6px;border-top:1px solid rgba(16,22,26,.15);padding:10px}.jupyter-wrapper .bp3-dark .bp3-multistep-dialog-footer{background:#30404d;border-top:1px solid rgba(16,22,26,.4)}.jupyter-wrapper .bp3-dialog-step-container{background-color:#f5f8fa;border-bottom:1px solid rgba(16,22,26,.15)}.jupyter-wrapper .bp3-dark .bp3-dialog-step-container{background:#293742;border-bottom:1px solid rgba(16,22,26,.4)}.jupyter-wrapper .bp3-dialog-step-container.bp3-dialog-step-viewed{background-color:#fff}.jupyter-wrapper .bp3-dark .bp3-dialog-step-container.bp3-dialog-step-viewed{background:#30404d}.jupyter-wrapper .bp3-dialog-step{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#f5f8fa;border-radius:6px;cursor:not-allowed;display:-webkit-box;display:-ms-flexbox;display:flex;margin:4px;padding:6px 14px}.jupyter-wrapper .bp3-dark .bp3-dialog-step{background:#293742}.jupyter-wrapper .bp3-dialog-step-viewed .bp3-dialog-step{background-color:#fff;cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-dialog-step-viewed .bp3-dialog-step{background:#30404d}.jupyter-wrapper .bp3-dialog-step:hover{background-color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-dialog-step:hover{background:#293742}.jupyter-wrapper .bp3-dialog-step-icon{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#5c708099;border-radius:50%;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;height:25px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:25px}.jupyter-wrapper .bp3-dark .bp3-dialog-step-icon{background-color:#a7b6c299}.jupyter-wrapper .bp3-active.bp3-dialog-step-viewed .bp3-dialog-step-icon{background-color:#2b95d6}.jupyter-wrapper .bp3-dialog-step-viewed .bp3-dialog-step-icon{background-color:#8a9ba8}.jupyter-wrapper .bp3-dialog-step-title{color:#5c708099;-webkit-box-flex:1;-ms-flex:1;flex:1;padding-left:10px}.jupyter-wrapper .bp3-dark .bp3-dialog-step-title{color:#a7b6c299}.jupyter-wrapper .bp3-active.bp3-dialog-step-viewed .bp3-dialog-step-title{color:#2b95d6}.jupyter-wrapper .bp3-dialog-step-viewed:not(.bp3-active) .bp3-dialog-step-title{color:#182026}.jupyter-wrapper .bp3-dark .bp3-dialog-step-viewed:not(.bp3-active) .bp3-dialog-step-title{color:#f5f8fa}.jupyter-wrapper .bp3-drawer{background:#ffffff;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 4px 8px #10161a33,0 18px 46px 6px #10161a33;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0;padding:0}.jupyter-wrapper .bp3-drawer:focus{outline:0}.jupyter-wrapper .bp3-drawer.bp3-position-top{height:50%;left:0;right:0;top:0}.jupyter-wrapper .bp3-drawer.bp3-position-top.bp3-overlay-enter,.jupyter-wrapper .bp3-drawer.bp3-position-top.bp3-overlay-appear{-webkit-transform:translateY(-100%);transform:translateY(-100%)}.jupyter-wrapper .bp3-drawer.bp3-position-top.bp3-overlay-enter-active,.jupyter-wrapper .bp3-drawer.bp3-position-top.bp3-overlay-appear-active{-webkit-transform:translateY(0);transform:translateY(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-top.bp3-overlay-exit{-webkit-transform:translateY(0);transform:translateY(0)}.jupyter-wrapper .bp3-drawer.bp3-position-top.bp3-overlay-exit-active{-webkit-transform:translateY(-100%);transform:translateY(-100%);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-bottom{bottom:0;height:50%;left:0;right:0}.jupyter-wrapper .bp3-drawer.bp3-position-bottom.bp3-overlay-enter,.jupyter-wrapper .bp3-drawer.bp3-position-bottom.bp3-overlay-appear{-webkit-transform:translateY(100%);transform:translateY(100%)}.jupyter-wrapper .bp3-drawer.bp3-position-bottom.bp3-overlay-enter-active,.jupyter-wrapper .bp3-drawer.bp3-position-bottom.bp3-overlay-appear-active{-webkit-transform:translateY(0);transform:translateY(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-bottom.bp3-overlay-exit{-webkit-transform:translateY(0);transform:translateY(0)}.jupyter-wrapper .bp3-drawer.bp3-position-bottom.bp3-overlay-exit-active{-webkit-transform:translateY(100%);transform:translateY(100%);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-left{bottom:0;left:0;top:0;width:50%}.jupyter-wrapper .bp3-drawer.bp3-position-left.bp3-overlay-enter,.jupyter-wrapper .bp3-drawer.bp3-position-left.bp3-overlay-appear{-webkit-transform:translateX(-100%);transform:translate(-100%)}.jupyter-wrapper .bp3-drawer.bp3-position-left.bp3-overlay-enter-active,.jupyter-wrapper .bp3-drawer.bp3-position-left.bp3-overlay-appear-active{-webkit-transform:translateX(0);transform:translate(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-left.bp3-overlay-exit{-webkit-transform:translateX(0);transform:translate(0)}.jupyter-wrapper .bp3-drawer.bp3-position-left.bp3-overlay-exit-active{-webkit-transform:translateX(-100%);transform:translate(-100%);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-right{bottom:0;right:0;top:0;width:50%}.jupyter-wrapper .bp3-drawer.bp3-position-right.bp3-overlay-enter,.jupyter-wrapper .bp3-drawer.bp3-position-right.bp3-overlay-appear{-webkit-transform:translateX(100%);transform:translate(100%)}.jupyter-wrapper .bp3-drawer.bp3-position-right.bp3-overlay-enter-active,.jupyter-wrapper .bp3-drawer.bp3-position-right.bp3-overlay-appear-active{-webkit-transform:translateX(0);transform:translate(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-position-right.bp3-overlay-exit{-webkit-transform:translateX(0);transform:translate(0)}.jupyter-wrapper .bp3-drawer.bp3-position-right.bp3-overlay-exit-active{-webkit-transform:translateX(100%);transform:translate(100%);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical){bottom:0;right:0;top:0;width:50%}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-enter,.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-appear{-webkit-transform:translateX(100%);transform:translate(100%)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-enter-active,.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-appear-active{-webkit-transform:translateX(0);transform:translate(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-exit{-webkit-transform:translateX(0);transform:translate(0)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right):not(.bp3-vertical).bp3-overlay-exit-active{-webkit-transform:translateX(100%);transform:translate(100%);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical{bottom:0;height:50%;left:0;right:0}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-enter,.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-appear{-webkit-transform:translateY(100%);transform:translateY(100%)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-enter-active,.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-appear-active{-webkit-transform:translateY(0);transform:translateY(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-exit{-webkit-transform:translateY(0);transform:translateY(0)}.jupyter-wrapper .bp3-drawer:not(.bp3-position-top):not(.bp3-position-bottom):not(.bp3-position-left):not(.bp3-position-right).bp3-vertical.bp3-overlay-exit-active{-webkit-transform:translateY(100%);transform:translateY(100%);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-drawer.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-drawer{background:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 4px 8px #10161a66,0 18px 46px 6px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-drawer-header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(16,22,26,.15);box-shadow:0 1px #10161a26;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;min-height:40px;padding:5px 5px 5px 20px;position:relative}.jupyter-wrapper .bp3-drawer-header .bp3-icon-large,.jupyter-wrapper .bp3-drawer-header .bp3-icon{color:#5c7080;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin-right:10px}.jupyter-wrapper .bp3-drawer-header .bp3-heading{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;line-height:inherit;margin:0}.jupyter-wrapper .bp3-drawer-header .bp3-heading:last-child{margin-right:20px}.jupyter-wrapper .bp3-dark .bp3-drawer-header{-webkit-box-shadow:0 1px 0 rgba(16,22,26,.4);box-shadow:0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-drawer-header .bp3-icon-large,.jupyter-wrapper .bp3-dark .bp3-drawer-header .bp3-icon{color:#a7b6c2}.jupyter-wrapper .bp3-drawer-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;line-height:18px;overflow:auto}.jupyter-wrapper .bp3-drawer-footer{-webkit-box-shadow:inset 0 1px 0 rgba(16,22,26,.15);box-shadow:inset 0 1px #10161a26;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding:10px 20px;position:relative}.jupyter-wrapper .bp3-dark .bp3-drawer-footer{-webkit-box-shadow:inset 0 1px 0 rgba(16,22,26,.4);box-shadow:inset 0 1px #10161a66}.jupyter-wrapper .bp3-editable-text{cursor:text;display:inline-block;max-width:100%;position:relative;vertical-align:top;white-space:nowrap}.jupyter-wrapper .bp3-editable-text:before{bottom:-3px;left:-3px;position:absolute;right:-3px;top:-3px;border-radius:3px;content:\"\";-webkit-transition:background-color .1s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:background-color .1s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:background-color .1s cubic-bezier(.4,1,.75,.9),box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:background-color .1s cubic-bezier(.4,1,.75,.9),box-shadow .1s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-editable-text:hover:before{-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.15);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #10161a26}.jupyter-wrapper .bp3-editable-text.bp3-editable-text-editing:before{background-color:#fff;-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-editable-text.bp3-disabled:before{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-editable-text.bp3-intent-primary .bp3-editable-text-input,.jupyter-wrapper .bp3-editable-text.bp3-intent-primary .bp3-editable-text-content{color:#137cbd}.jupyter-wrapper .bp3-editable-text.bp3-intent-primary:hover:before{-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(19,124,189,.4);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #137cbd66}.jupyter-wrapper .bp3-editable-text.bp3-intent-primary.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-editable-text.bp3-intent-success .bp3-editable-text-input,.jupyter-wrapper .bp3-editable-text.bp3-intent-success .bp3-editable-text-content{color:#0f9960}.jupyter-wrapper .bp3-editable-text.bp3-intent-success:hover:before{-webkit-box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px rgba(15,153,96,.4);box-shadow:0 0 #0f996000,0 0 #0f996000,inset 0 0 0 1px #0f996066}.jupyter-wrapper .bp3-editable-text.bp3-intent-success.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #0f9960,0 0 0 3px #0f99604d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-editable-text.bp3-intent-warning .bp3-editable-text-input,.jupyter-wrapper .bp3-editable-text.bp3-intent-warning .bp3-editable-text-content{color:#d9822b}.jupyter-wrapper .bp3-editable-text.bp3-intent-warning:hover:before{-webkit-box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px rgba(217,130,43,.4);box-shadow:0 0 #d9822b00,0 0 #d9822b00,inset 0 0 0 1px #d9822b66}.jupyter-wrapper .bp3-editable-text.bp3-intent-warning.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #d9822b,0 0 0 3px #d9822b4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-editable-text.bp3-intent-danger .bp3-editable-text-input,.jupyter-wrapper .bp3-editable-text.bp3-intent-danger .bp3-editable-text-content{color:#db3737}.jupyter-wrapper .bp3-editable-text.bp3-intent-danger:hover:before{-webkit-box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px rgba(219,55,55,.4);box-shadow:0 0 #db373700,0 0 #db373700,inset 0 0 0 1px #db373766}.jupyter-wrapper .bp3-editable-text.bp3-intent-danger.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #db3737,0 0 0 3px #db37374d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-dark .bp3-editable-text:hover:before{-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(255,255,255,.15);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #ffffff26}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-editable-text-editing:before{background-color:#10161a4d;-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-disabled:before{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-primary .bp3-editable-text-content{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-primary:hover:before{-webkit-box-shadow:0 0 0 0 rgba(72,175,240,0),0 0 0 0 rgba(72,175,240,0),inset 0 0 0 1px rgba(72,175,240,.4);box-shadow:0 0 #48aff000,0 0 #48aff000,inset 0 0 0 1px #48aff066}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-primary.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #48aff0,0 0 0 3px rgba(72,175,240,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #48aff0,0 0 0 3px #48aff04d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-success .bp3-editable-text-content{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-success:hover:before{-webkit-box-shadow:0 0 0 0 rgba(61,204,145,0),0 0 0 0 rgba(61,204,145,0),inset 0 0 0 1px rgba(61,204,145,.4);box-shadow:0 0 #3dcc9100,0 0 #3dcc9100,inset 0 0 0 1px #3dcc9166}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-success.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #3dcc91,0 0 0 3px rgba(61,204,145,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #3dcc91,0 0 0 3px #3dcc914d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-warning .bp3-editable-text-content{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-warning:hover:before{-webkit-box-shadow:0 0 0 0 rgba(255,179,102,0),0 0 0 0 rgba(255,179,102,0),inset 0 0 0 1px rgba(255,179,102,.4);box-shadow:0 0 #ffb36600,0 0 #ffb36600,inset 0 0 0 1px #ffb36666}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-warning.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #ffb366,0 0 0 3px rgba(255,179,102,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #ffb366,0 0 0 3px #ffb3664d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-danger .bp3-editable-text-content{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-danger:hover:before{-webkit-box-shadow:0 0 0 0 rgba(255,115,115,0),0 0 0 0 rgba(255,115,115,0),inset 0 0 0 1px rgba(255,115,115,.4);box-shadow:0 0 #ff737300,0 0 #ff737300,inset 0 0 0 1px #ff737366}.jupyter-wrapper .bp3-dark .bp3-editable-text.bp3-intent-danger.bp3-editable-text-editing:before{-webkit-box-shadow:0 0 0 1px #ff7373,0 0 0 3px rgba(255,115,115,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #ff7373,0 0 0 3px #ff73734d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-editable-text-input,.jupyter-wrapper .bp3-editable-text-content{color:inherit;display:inherit;font:inherit;letter-spacing:inherit;max-width:inherit;min-width:inherit;position:relative;resize:none;text-transform:inherit;vertical-align:top}.jupyter-wrapper .bp3-editable-text-input{background:none;border:none;-webkit-box-shadow:none;box-shadow:none;padding:0;white-space:pre-wrap;width:100%}.jupyter-wrapper .bp3-editable-text-input::-webkit-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-editable-text-input::-moz-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-editable-text-input:-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-editable-text-input::-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-editable-text-input::placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-editable-text-input:focus{outline:none}.jupyter-wrapper .bp3-editable-text-input::-ms-clear{display:none}.jupyter-wrapper .bp3-editable-text-content{overflow:hidden;padding-right:2px;text-overflow:ellipsis;white-space:pre}.jupyter-wrapper .bp3-editable-text-editing>.bp3-editable-text-content{left:0;position:absolute;visibility:hidden}.jupyter-wrapper .bp3-editable-text-placeholder>.bp3-editable-text-content{color:#5c708099}.jupyter-wrapper .bp3-dark .bp3-editable-text-placeholder>.bp3-editable-text-content{color:#a7b6c299}.jupyter-wrapper .bp3-editable-text.bp3-multiline{display:block}.jupyter-wrapper .bp3-editable-text.bp3-multiline .bp3-editable-text-content{overflow:auto;white-space:pre-wrap;word-wrap:break-word}.jupyter-wrapper .bp3-divider{border-bottom:1px solid rgba(16,22,26,.15);border-right:1px solid rgba(16,22,26,.15);margin:5px}.jupyter-wrapper .bp3-dark .bp3-divider{border-color:#10161a66}.jupyter-wrapper .bp3-control-group{-webkit-transform:translateZ(0);transform:translateZ(0);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.jupyter-wrapper .bp3-control-group>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-control-group>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-control-group .bp3-button,.jupyter-wrapper .bp3-control-group .bp3-html-select,.jupyter-wrapper .bp3-control-group .bp3-input,.jupyter-wrapper .bp3-control-group .bp3-select{position:relative}.jupyter-wrapper .bp3-control-group .bp3-input{border-radius:inherit;z-index:2}.jupyter-wrapper .bp3-control-group .bp3-input:focus{border-radius:3px;z-index:14}.jupyter-wrapper .bp3-control-group .bp3-input[class*=bp3-intent]{z-index:13}.jupyter-wrapper .bp3-control-group .bp3-input[class*=bp3-intent]:focus{z-index:15}.jupyter-wrapper .bp3-control-group .bp3-input[readonly],.jupyter-wrapper .bp3-control-group .bp3-input:disabled,.jupyter-wrapper .bp3-control-group .bp3-input.bp3-disabled{z-index:1}.jupyter-wrapper .bp3-control-group .bp3-input-group[class*=bp3-intent] .bp3-input{z-index:13}.jupyter-wrapper .bp3-control-group .bp3-input-group[class*=bp3-intent] .bp3-input:focus{z-index:15}.jupyter-wrapper .bp3-control-group .bp3-button,.jupyter-wrapper .bp3-control-group .bp3-html-select select,.jupyter-wrapper .bp3-control-group .bp3-select select{-webkit-transform:translateZ(0);transform:translateZ(0);border-radius:inherit;z-index:4}.jupyter-wrapper .bp3-control-group .bp3-button:focus,.jupyter-wrapper .bp3-control-group .bp3-html-select select:focus,.jupyter-wrapper .bp3-control-group .bp3-select select:focus{z-index:5}.jupyter-wrapper .bp3-control-group .bp3-button:hover,.jupyter-wrapper .bp3-control-group .bp3-html-select select:hover,.jupyter-wrapper .bp3-control-group .bp3-select select:hover{z-index:6}.jupyter-wrapper .bp3-control-group .bp3-button:active,.jupyter-wrapper .bp3-control-group .bp3-html-select select:active,.jupyter-wrapper .bp3-control-group .bp3-select select:active{z-index:7}.jupyter-wrapper .bp3-control-group .bp3-button[readonly],.jupyter-wrapper .bp3-control-group .bp3-button:disabled,.jupyter-wrapper .bp3-control-group .bp3-button.bp3-disabled,.jupyter-wrapper .bp3-control-group .bp3-html-select select[readonly],.jupyter-wrapper .bp3-control-group .bp3-html-select select:disabled,.jupyter-wrapper .bp3-control-group .bp3-html-select select.bp3-disabled,.jupyter-wrapper .bp3-control-group .bp3-select select[readonly],.jupyter-wrapper .bp3-control-group .bp3-select select:disabled,.jupyter-wrapper .bp3-control-group .bp3-select select.bp3-disabled{z-index:3}.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent],.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent],.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent]{z-index:9}.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent]:focus,.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent]:focus,.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent]:focus{z-index:10}.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent]:hover,.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent]:hover,.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent]:hover{z-index:11}.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent]:active,.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent]:active,.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent]:active{z-index:12}.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent][readonly],.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent]:disabled,.jupyter-wrapper .bp3-control-group .bp3-button[class*=bp3-intent].bp3-disabled,.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent][readonly],.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent]:disabled,.jupyter-wrapper .bp3-control-group .bp3-html-select select[class*=bp3-intent].bp3-disabled,.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent][readonly],.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent]:disabled,.jupyter-wrapper .bp3-control-group .bp3-select select[class*=bp3-intent].bp3-disabled{z-index:8}.jupyter-wrapper .bp3-control-group .bp3-input-group>.bp3-icon,.jupyter-wrapper .bp3-control-group .bp3-input-group>.bp3-button,.jupyter-wrapper .bp3-control-group .bp3-input-group>.bp3-input-left-container,.jupyter-wrapper .bp3-control-group .bp3-input-group>.bp3-input-action{z-index:16}.jupyter-wrapper .bp3-control-group .bp3-select:after,.jupyter-wrapper .bp3-control-group .bp3-html-select:after,.jupyter-wrapper .bp3-control-group .bp3-select>.bp3-icon,.jupyter-wrapper .bp3-control-group .bp3-html-select>.bp3-icon{z-index:17}.jupyter-wrapper .bp3-control-group .bp3-select:focus-within{z-index:5}.jupyter-wrapper .bp3-control-group:not(.bp3-vertical)>*:not(.bp3-divider){margin-right:-1px}.jupyter-wrapper .bp3-control-group:not(.bp3-vertical)>.bp3-divider:not(:first-child){margin-left:6px}.jupyter-wrapper .bp3-dark .bp3-control-group:not(.bp3-vertical)>*:not(.bp3-divider){margin-right:0}.jupyter-wrapper .bp3-dark .bp3-control-group:not(.bp3-vertical)>.bp3-button+.bp3-button{margin-left:1px}.jupyter-wrapper .bp3-control-group .bp3-popover-wrapper,.jupyter-wrapper .bp3-control-group .bp3-popover-target{border-radius:inherit}.jupyter-wrapper .bp3-control-group>:first-child{border-radius:3px 0 0 3px}.jupyter-wrapper .bp3-control-group>:last-child{border-radius:0 3px 3px 0;margin-right:0}.jupyter-wrapper .bp3-control-group>:only-child{border-radius:3px;margin-right:0}.jupyter-wrapper .bp3-control-group .bp3-input-group .bp3-button{border-radius:3px}.jupyter-wrapper .bp3-control-group .bp3-numeric-input:not(:first-child) .bp3-input-group{border-bottom-left-radius:0;border-top-left-radius:0}.jupyter-wrapper .bp3-control-group.bp3-fill{width:100%}.jupyter-wrapper .bp3-control-group>.bp3-fill{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.jupyter-wrapper .bp3-control-group.bp3-fill>*:not(.bp3-fixed){-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.jupyter-wrapper .bp3-control-group.bp3-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.jupyter-wrapper .bp3-control-group.bp3-vertical>*{margin-top:-1px}.jupyter-wrapper .bp3-control-group.bp3-vertical>:first-child{border-radius:3px 3px 0 0;margin-top:0}.jupyter-wrapper .bp3-control-group.bp3-vertical>:last-child{border-radius:0 0 3px 3px}.jupyter-wrapper .bp3-control{cursor:pointer;display:block;margin-bottom:10px;position:relative;text-transform:none}.jupyter-wrapper .bp3-control input:checked~.bp3-control-indicator{background-color:#137cbd;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.1)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.1),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33;color:#fff}.jupyter-wrapper .bp3-control:hover input:checked~.bp3-control-indicator{background-color:#106ba3;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33}.jupyter-wrapper .bp3-control input:not(:disabled):active:checked~.bp3-control-indicator{background:#0e5a8a;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-control input:disabled:checked~.bp3-control-indicator{background:rgba(19,124,189,.5);-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-control input:checked~.bp3-control-indicator{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-control:hover input:checked~.bp3-control-indicator{background-color:#106ba3;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-control input:not(:disabled):active:checked~.bp3-control-indicator{background-color:#0e5a8a;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-control input:disabled:checked~.bp3-control-indicator{background:rgba(14,90,138,.5);-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-control:not(.bp3-align-right){padding-left:26px}.jupyter-wrapper .bp3-control:not(.bp3-align-right) .bp3-control-indicator{margin-left:-26px}.jupyter-wrapper .bp3-control.bp3-align-right{padding-right:26px}.jupyter-wrapper .bp3-control.bp3-align-right .bp3-control-indicator{margin-right:-26px}.jupyter-wrapper .bp3-control.bp3-disabled{color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-control.bp3-inline{display:inline-block;margin-right:20px}.jupyter-wrapper .bp3-control input{left:0;opacity:0;position:absolute;top:0;z-index:-1}.jupyter-wrapper .bp3-control .bp3-control-indicator{background-clip:padding-box;background-color:#f5f8fa;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.8)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.8),rgba(255,255,255,0));border:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a;cursor:pointer;display:inline-block;font-size:16px;height:1em;margin-right:10px;margin-top:-3px;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;width:1em}.jupyter-wrapper .bp3-control .bp3-control-indicator:before{content:\"\";display:block;height:1em;width:1em}.jupyter-wrapper .bp3-control:hover .bp3-control-indicator{background-color:#ebf1f5}.jupyter-wrapper .bp3-control input:not(:disabled):active~.bp3-control-indicator{background:#d8e1e8;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-control input:disabled~.bp3-control-indicator{background:rgba(206,217,224,.5);-webkit-box-shadow:none;box-shadow:none;cursor:not-allowed}.jupyter-wrapper .bp3-control input:focus~.bp3-control-indicator{outline:rgba(19,124,189,.6) auto 2px;outline-offset:2px;-moz-outline-radius:6px}.jupyter-wrapper .bp3-control.bp3-align-right .bp3-control-indicator{float:right;margin-left:10px;margin-top:1px}.jupyter-wrapper .bp3-control.bp3-large{font-size:16px}.jupyter-wrapper .bp3-control.bp3-large:not(.bp3-align-right){padding-left:30px}.jupyter-wrapper .bp3-control.bp3-large:not(.bp3-align-right) .bp3-control-indicator{margin-left:-30px}.jupyter-wrapper .bp3-control.bp3-large.bp3-align-right{padding-right:30px}.jupyter-wrapper .bp3-control.bp3-large.bp3-align-right .bp3-control-indicator{margin-right:-30px}.jupyter-wrapper .bp3-control.bp3-large .bp3-control-indicator{font-size:20px}.jupyter-wrapper .bp3-control.bp3-large.bp3-align-right .bp3-control-indicator{margin-top:0}.jupyter-wrapper .bp3-control.bp3-checkbox input:indeterminate~.bp3-control-indicator{background-color:#137cbd;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.1)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.1),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33;color:#fff}.jupyter-wrapper .bp3-control.bp3-checkbox:hover input:indeterminate~.bp3-control-indicator{background-color:#106ba3;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 -1px 0 rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 -1px #10161a33}.jupyter-wrapper .bp3-control.bp3-checkbox input:not(:disabled):active:indeterminate~.bp3-control-indicator{background:#0e5a8a;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-control.bp3-checkbox input:disabled:indeterminate~.bp3-control-indicator{background:rgba(19,124,189,.5);-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-control.bp3-checkbox input:indeterminate~.bp3-control-indicator{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-control.bp3-checkbox:hover input:indeterminate~.bp3-control-indicator{background-color:#106ba3;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-control.bp3-checkbox input:not(:disabled):active:indeterminate~.bp3-control-indicator{background-color:#0e5a8a;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a66,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-control.bp3-checkbox input:disabled:indeterminate~.bp3-control-indicator{background:rgba(14,90,138,.5);-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-control.bp3-checkbox .bp3-control-indicator{border-radius:3px}.jupyter-wrapper .bp3-control.bp3-checkbox input:checked~.bp3-control-indicator:before{background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M12 5c-.28 0-.53.11-.71.29L7 9.59l-2.29-2.3a1.003 1.003 0 00-1.42 1.42l3 3c.18.18.43.29.71.29s.53-.11.71-.29l5-5A1.003 1.003 0 0012 5z' fill='white'/%3e%3c/svg%3e\")}.jupyter-wrapper .bp3-control.bp3-checkbox input:indeterminate~.bp3-control-indicator:before{background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M11 7H5c-.55 0-1 .45-1 1s.45 1 1 1h6c.55 0 1-.45 1-1s-.45-1-1-1z' fill='white'/%3e%3c/svg%3e\")}.jupyter-wrapper .bp3-control.bp3-radio .bp3-control-indicator{border-radius:50%}.jupyter-wrapper .bp3-control.bp3-radio input:checked~.bp3-control-indicator:before{background-image:radial-gradient(#ffffff,#ffffff 28%,transparent 32%)}.jupyter-wrapper .bp3-control.bp3-radio input:checked:disabled~.bp3-control-indicator:before{opacity:.5}.jupyter-wrapper .bp3-control.bp3-radio input:focus~.bp3-control-indicator{-moz-outline-radius:16px}.jupyter-wrapper .bp3-control.bp3-switch input~.bp3-control-indicator{background:rgba(167,182,194,.5)}.jupyter-wrapper .bp3-control.bp3-switch:hover input~.bp3-control-indicator{background:rgba(115,134,148,.5)}.jupyter-wrapper .bp3-control.bp3-switch input:not(:disabled):active~.bp3-control-indicator{background:rgba(92,112,128,.5)}.jupyter-wrapper .bp3-control.bp3-switch input:disabled~.bp3-control-indicator{background:rgba(206,217,224,.5)}.jupyter-wrapper .bp3-control.bp3-switch input:disabled~.bp3-control-indicator:before{background:rgba(255,255,255,.8)}.jupyter-wrapper .bp3-control.bp3-switch input:checked~.bp3-control-indicator{background:#137cbd}.jupyter-wrapper .bp3-control.bp3-switch:hover input:checked~.bp3-control-indicator{background:#106ba3}.jupyter-wrapper .bp3-control.bp3-switch input:checked:not(:disabled):active~.bp3-control-indicator{background:#0e5a8a}.jupyter-wrapper .bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator{background:rgba(19,124,189,.5)}.jupyter-wrapper .bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator:before{background:rgba(255,255,255,.8)}.jupyter-wrapper .bp3-control.bp3-switch:not(.bp3-align-right){padding-left:38px}.jupyter-wrapper .bp3-control.bp3-switch:not(.bp3-align-right) .bp3-control-indicator{margin-left:-38px}.jupyter-wrapper .bp3-control.bp3-switch.bp3-align-right{padding-right:38px}.jupyter-wrapper .bp3-control.bp3-switch.bp3-align-right .bp3-control-indicator{margin-right:-38px}.jupyter-wrapper .bp3-control.bp3-switch .bp3-control-indicator{border:none;border-radius:1.75em;-webkit-box-shadow:none!important;box-shadow:none!important;min-width:1.75em;-webkit-transition:background-color .1s cubic-bezier(.4,1,.75,.9);transition:background-color .1s cubic-bezier(.4,1,.75,.9);width:auto}.jupyter-wrapper .bp3-control.bp3-switch .bp3-control-indicator:before{background:#ffffff;border-radius:50%;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a33,0 1px 1px #10161a33;height:calc(1em - 4px);left:0;margin:2px;position:absolute;-webkit-transition:left .1s cubic-bezier(.4,1,.75,.9);transition:left .1s cubic-bezier(.4,1,.75,.9);width:calc(1em - 4px)}.jupyter-wrapper .bp3-control.bp3-switch input:checked~.bp3-control-indicator:before{left:calc(100% - 1em)}.jupyter-wrapper .bp3-control.bp3-switch.bp3-large:not(.bp3-align-right){padding-left:45px}.jupyter-wrapper .bp3-control.bp3-switch.bp3-large:not(.bp3-align-right) .bp3-control-indicator{margin-left:-45px}.jupyter-wrapper .bp3-control.bp3-switch.bp3-large.bp3-align-right{padding-right:45px}.jupyter-wrapper .bp3-control.bp3-switch.bp3-large.bp3-align-right .bp3-control-indicator{margin-right:-45px}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input~.bp3-control-indicator{background:rgba(16,22,26,.5)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch:hover input~.bp3-control-indicator{background:rgba(16,22,26,.7)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:not(:disabled):active~.bp3-control-indicator{background:rgba(16,22,26,.9)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:disabled~.bp3-control-indicator{background:rgba(57,75,89,.5)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:disabled~.bp3-control-indicator:before{background:rgba(16,22,26,.4)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:checked~.bp3-control-indicator{background:#137cbd}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch:hover input:checked~.bp3-control-indicator{background:#106ba3}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:checked:not(:disabled):active~.bp3-control-indicator{background:#0e5a8a}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator{background:rgba(14,90,138,.5)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:checked:disabled~.bp3-control-indicator:before{background:rgba(16,22,26,.4)}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch .bp3-control-indicator:before{background:#394b59;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-control.bp3-switch input:checked~.bp3-control-indicator:before{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a66}.jupyter-wrapper .bp3-control.bp3-switch .bp3-switch-inner-text{font-size:.7em;text-align:center}.jupyter-wrapper .bp3-control.bp3-switch .bp3-control-indicator-child:first-child{line-height:0;margin-left:.5em;margin-right:1.2em;visibility:hidden}.jupyter-wrapper .bp3-control.bp3-switch .bp3-control-indicator-child:last-child{line-height:1em;margin-left:1.2em;margin-right:.5em;visibility:visible}.jupyter-wrapper .bp3-control.bp3-switch input:checked~.bp3-control-indicator .bp3-control-indicator-child:first-child{line-height:1em;visibility:visible}.jupyter-wrapper .bp3-control.bp3-switch input:checked~.bp3-control-indicator .bp3-control-indicator-child:last-child{line-height:0;visibility:hidden}.jupyter-wrapper .bp3-dark .bp3-control{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-control.bp3-disabled{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-control .bp3-control-indicator{background-color:#394b59;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.05)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.05),rgba(255,255,255,0));-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-control:hover .bp3-control-indicator{background-color:#30404d}.jupyter-wrapper .bp3-dark .bp3-control input:not(:disabled):active~.bp3-control-indicator{background:#202b33;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a99,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-control input:disabled~.bp3-control-indicator{background:rgba(57,75,89,.5);-webkit-box-shadow:none;box-shadow:none;cursor:not-allowed}.jupyter-wrapper .bp3-dark .bp3-control.bp3-checkbox input:disabled:checked~.bp3-control-indicator,.jupyter-wrapper .bp3-dark .bp3-control.bp3-checkbox input:disabled:indeterminate~.bp3-control-indicator{color:#a7b6c299}.jupyter-wrapper .bp3-file-input{cursor:pointer;display:inline-block;height:30px;position:relative}.jupyter-wrapper .bp3-file-input input{margin:0;min-width:200px;opacity:0}.jupyter-wrapper .bp3-file-input input:disabled+.bp3-file-upload-input,.jupyter-wrapper .bp3-file-input input.bp3-disabled+.bp3-file-upload-input{background:rgba(206,217,224,.5);-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;resize:none}.jupyter-wrapper .bp3-file-input input:disabled+.bp3-file-upload-input:after,.jupyter-wrapper .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after{background-color:#ced9e080;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;outline:none}.jupyter-wrapper .bp3-file-input input:disabled+.bp3-file-upload-input:after .bp3-active,.jupyter-wrapper .bp3-file-input input:disabled+.bp3-file-upload-input:after .bp3-active:hover,.jupyter-wrapper .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after .bp3-active,.jupyter-wrapper .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after .bp3-active:hover{background:rgba(206,217,224,.7)}.jupyter-wrapper .bp3-dark .bp3-file-input input:disabled+.bp3-file-upload-input,.jupyter-wrapper .bp3-dark .bp3-file-input input.bp3-disabled+.bp3-file-upload-input{background:rgba(57,75,89,.5);-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-input input:disabled+.bp3-file-upload-input:after,.jupyter-wrapper .bp3-dark .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after{background-color:#394b5980;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-input input:disabled+.bp3-file-upload-input:after .bp3-active,.jupyter-wrapper .bp3-dark .bp3-file-input input.bp3-disabled+.bp3-file-upload-input:after .bp3-active{background:rgba(57,75,89,.7)}.jupyter-wrapper .bp3-file-input.bp3-file-input-has-selection .bp3-file-upload-input{color:#182026}.jupyter-wrapper .bp3-dark .bp3-file-input.bp3-file-input-has-selection .bp3-file-upload-input{color:#f5f8fa}.jupyter-wrapper .bp3-file-input.bp3-fill{width:100%}.jupyter-wrapper .bp3-file-input.bp3-large,.jupyter-wrapper .bp3-large .bp3-file-input{height:40px}.jupyter-wrapper .bp3-file-input .bp3-file-upload-input-custom-text:after{content:attr(bp3-button-text)}.jupyter-wrapper .bp3-file-upload-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#ffffff;border:none;border-radius:3px;-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33;color:#182026;font-size:14px;font-weight:400;height:30px;line-height:30px;outline:none;padding:0 80px 0 10px;-webkit-transition:-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:box-shadow .1s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;color:#5c708099;left:0;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .bp3-file-upload-input::-webkit-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-file-upload-input::-moz-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-file-upload-input:-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-file-upload-input::-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-file-upload-input::placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-file-upload-input:focus,.jupyter-wrapper .bp3-file-upload-input.bp3-active{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-file-upload-input[type=search],.jupyter-wrapper .bp3-file-upload-input.bp3-round{border-radius:30px;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:10px}.jupyter-wrapper .bp3-file-upload-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.15);box-shadow:inset 0 0 0 1px #10161a26}.jupyter-wrapper .bp3-file-upload-input:disabled,.jupyter-wrapper .bp3-file-upload-input.bp3-disabled{background:rgba(206,217,224,.5);-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;resize:none}.jupyter-wrapper .bp3-file-upload-input:after{background-color:#f5f8fa;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.8)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.8),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a;color:#182026;min-height:24px;min-width:24px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;border-radius:3px;content:\"Browse\";line-height:24px;margin:3px;position:absolute;right:0;text-align:center;top:0;width:70px}.jupyter-wrapper .bp3-file-upload-input:after:hover{background-clip:padding-box;background-color:#ebf1f5;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a}.jupyter-wrapper .bp3-file-upload-input:after:active,.jupyter-wrapper .bp3-file-upload-input:after .bp3-active{background-color:#d8e1e8;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-file-upload-input:after:disabled,.jupyter-wrapper .bp3-file-upload-input:after .bp3-disabled{background-color:#ced9e080;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;outline:none}.jupyter-wrapper .bp3-file-upload-input:after:disabled .bp3-active,.jupyter-wrapper .bp3-file-upload-input:after:disabled .bp3-active:hover,.jupyter-wrapper .bp3-file-upload-input:after .bp3-disabled.bp3-active,.jupyter-wrapper .bp3-file-upload-input:after .bp3-disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.jupyter-wrapper .bp3-file-upload-input:hover:after{background-clip:padding-box;background-color:#ebf1f5;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a}.jupyter-wrapper .bp3-file-upload-input:active:after{background-color:#d8e1e8;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-large .bp3-file-upload-input{font-size:16px;height:40px;line-height:40px;padding-right:95px}.jupyter-wrapper .bp3-large .bp3-file-upload-input[type=search],.jupyter-wrapper .bp3-large .bp3-file-upload-input.bp3-round{padding:0 15px}.jupyter-wrapper .bp3-large .bp3-file-upload-input:after{min-height:30px;min-width:30px;line-height:30px;margin:5px;width:85px}.jupyter-wrapper .bp3-dark .bp3-file-upload-input{background:rgba(16,22,26,.3);-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #137cbd00,0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66;color:#f5f8fa;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input::-webkit-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input::-moz-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input::-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input::placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:focus{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-file-upload-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:disabled,.jupyter-wrapper .bp3-dark .bp3-file-upload-input.bp3-disabled{background:rgba(57,75,89,.5);-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after{background-color:#394b59;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.05)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.05),rgba(255,255,255,0));-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after:hover,.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after:active,.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after .bp3-active{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after:hover{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after:active,.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after .bp3-active{background-color:#202b33;background-image:none;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a99,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after:disabled,.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after .bp3-disabled{background-color:#394b5980;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after:disabled .bp3-active,.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after .bp3-disabled.bp3-active{background:rgba(57,75,89,.7)}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:after .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:hover:after{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-file-upload-input:active:after{background-color:#202b33;background-image:none;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a99,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-file-upload-input:after{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a}.jupyter-wrapper .bp3-form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 0 15px}.jupyter-wrapper .bp3-form-group label.bp3-label{margin-bottom:5px}.jupyter-wrapper .bp3-form-group .bp3-control{margin-top:7px}.jupyter-wrapper .bp3-form-group .bp3-form-helper-text{color:#5c7080;font-size:12px;margin-top:5px}.jupyter-wrapper .bp3-form-group.bp3-intent-primary .bp3-form-helper-text{color:#106ba3}.jupyter-wrapper .bp3-form-group.bp3-intent-success .bp3-form-helper-text{color:#0d8050}.jupyter-wrapper .bp3-form-group.bp3-intent-warning .bp3-form-helper-text{color:#bf7326}.jupyter-wrapper .bp3-form-group.bp3-intent-danger .bp3-form-helper-text{color:#c23030}.jupyter-wrapper .bp3-form-group.bp3-inline{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.jupyter-wrapper .bp3-form-group.bp3-inline.bp3-large label.bp3-label{line-height:40px;margin:0 10px 0 0}.jupyter-wrapper .bp3-form-group.bp3-inline label.bp3-label{line-height:30px;margin:0 10px 0 0}.jupyter-wrapper .bp3-form-group.bp3-disabled .bp3-label,.jupyter-wrapper .bp3-form-group.bp3-disabled .bp3-text-muted,.jupyter-wrapper .bp3-form-group.bp3-disabled .bp3-form-helper-text{color:#5c708099!important}.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-intent-primary .bp3-form-helper-text{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-intent-success .bp3-form-helper-text{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-intent-warning .bp3-form-helper-text{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-intent-danger .bp3-form-helper-text{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-form-group .bp3-form-helper-text{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-disabled .bp3-label,.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-disabled .bp3-text-muted,.jupyter-wrapper .bp3-dark .bp3-form-group.bp3-disabled .bp3-form-helper-text{color:#a7b6c299!important}.jupyter-wrapper .bp3-input-group{display:block;position:relative}.jupyter-wrapper .bp3-input-group .bp3-input{position:relative;width:100%}.jupyter-wrapper .bp3-input-group .bp3-input:not(:first-child){padding-left:30px}.jupyter-wrapper .bp3-input-group .bp3-input:not(:last-child){padding-right:30px}.jupyter-wrapper .bp3-input-group .bp3-input-action,.jupyter-wrapper .bp3-input-group>.bp3-input-left-container,.jupyter-wrapper .bp3-input-group>.bp3-button,.jupyter-wrapper .bp3-input-group>.bp3-icon{position:absolute;top:0}.jupyter-wrapper .bp3-input-group .bp3-input-action:first-child,.jupyter-wrapper .bp3-input-group>.bp3-input-left-container:first-child,.jupyter-wrapper .bp3-input-group>.bp3-button:first-child,.jupyter-wrapper .bp3-input-group>.bp3-icon:first-child{left:0}.jupyter-wrapper .bp3-input-group .bp3-input-action:last-child,.jupyter-wrapper .bp3-input-group>.bp3-input-left-container:last-child,.jupyter-wrapper .bp3-input-group>.bp3-button:last-child,.jupyter-wrapper .bp3-input-group>.bp3-icon:last-child{right:0}.jupyter-wrapper .bp3-input-group .bp3-button{min-height:24px;min-width:24px;margin:3px;padding:0 7px}.jupyter-wrapper .bp3-input-group .bp3-button:empty{padding:0}.jupyter-wrapper .bp3-input-group>.bp3-input-left-container,.jupyter-wrapper .bp3-input-group>.bp3-icon{z-index:1}.jupyter-wrapper .bp3-input-group>.bp3-input-left-container>.bp3-icon,.jupyter-wrapper .bp3-input-group>.bp3-icon{color:#5c7080}.jupyter-wrapper .bp3-input-group>.bp3-input-left-container>.bp3-icon:empty,.jupyter-wrapper .bp3-input-group>.bp3-icon:empty{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.jupyter-wrapper .bp3-input-group>.bp3-input-left-container>.bp3-icon,.jupyter-wrapper .bp3-input-group>.bp3-icon,.jupyter-wrapper .bp3-input-group .bp3-input-action>.bp3-spinner{margin:7px}.jupyter-wrapper .bp3-input-group .bp3-tag{margin:5px}.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus),.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus){color:#5c7080}.jupyter-wrapper .bp3-dark .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus),.jupyter-wrapper .bp3-dark .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus){color:#a7b6c2}.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-standard,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-large,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-standard,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:not(:hover):not(:focus) .bp3-icon-large{color:#5c7080}.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled{color:#5c708099!important}.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled .bp3-icon,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled .bp3-icon-standard,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-button.bp3-minimal:disabled .bp3-icon-large,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled .bp3-icon,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled .bp3-icon-standard,.jupyter-wrapper .bp3-input-group .bp3-input:not(:focus)+.bp3-input-action .bp3-button.bp3-minimal:disabled .bp3-icon-large{color:#5c708099!important}.jupyter-wrapper .bp3-input-group.bp3-disabled{cursor:not-allowed}.jupyter-wrapper .bp3-input-group.bp3-disabled .bp3-icon{color:#5c708099}.jupyter-wrapper .bp3-input-group.bp3-large .bp3-button{min-height:30px;min-width:30px;margin:5px}.jupyter-wrapper .bp3-input-group.bp3-large>.bp3-input-left-container>.bp3-icon,.jupyter-wrapper .bp3-input-group.bp3-large>.bp3-icon,.jupyter-wrapper .bp3-input-group.bp3-large .bp3-input-action>.bp3-spinner{margin:12px}.jupyter-wrapper .bp3-input-group.bp3-large .bp3-input{font-size:16px;height:40px;line-height:40px}.jupyter-wrapper .bp3-input-group.bp3-large .bp3-input[type=search],.jupyter-wrapper .bp3-input-group.bp3-large .bp3-input.bp3-round{padding:0 15px}.jupyter-wrapper .bp3-input-group.bp3-large .bp3-input:not(:first-child){padding-left:40px}.jupyter-wrapper .bp3-input-group.bp3-large .bp3-input:not(:last-child){padding-right:40px}.jupyter-wrapper .bp3-input-group.bp3-small .bp3-button,.jupyter-wrapper .bp3-input-group.bp3-small .bp3-tag{min-height:20px;min-width:20px;margin:2px}.jupyter-wrapper .bp3-input-group.bp3-small>.bp3-input-left-container>.bp3-icon,.jupyter-wrapper .bp3-input-group.bp3-small>.bp3-icon,.jupyter-wrapper .bp3-input-group.bp3-small .bp3-input-action>.bp3-spinner{margin:4px}.jupyter-wrapper .bp3-input-group.bp3-small .bp3-input{font-size:12px;height:24px;line-height:24px;padding-left:8px;padding-right:8px}.jupyter-wrapper .bp3-input-group.bp3-small .bp3-input[type=search],.jupyter-wrapper .bp3-input-group.bp3-small .bp3-input.bp3-round{padding:0 12px}.jupyter-wrapper .bp3-input-group.bp3-small .bp3-input:not(:first-child){padding-left:24px}.jupyter-wrapper .bp3-input-group.bp3-small .bp3-input:not(:last-child){padding-right:24px}.jupyter-wrapper .bp3-input-group.bp3-fill{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:100%}.jupyter-wrapper .bp3-input-group.bp3-round .bp3-button,.jupyter-wrapper .bp3-input-group.bp3-round .bp3-input,.jupyter-wrapper .bp3-input-group.bp3-round .bp3-tag{border-radius:30px}.jupyter-wrapper .bp3-dark .bp3-input-group .bp3-icon{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-input-group.bp3-disabled .bp3-icon{color:#a7b6c299}.jupyter-wrapper .bp3-input-group.bp3-intent-primary .bp3-input{-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px #137cbd,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #137cbd,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-primary .bp3-input:focus{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-primary .bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px #137cbd;box-shadow:inset 0 0 0 1px #137cbd}.jupyter-wrapper .bp3-input-group.bp3-intent-primary .bp3-input:disabled,.jupyter-wrapper .bp3-input-group.bp3-intent-primary .bp3-input.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input-group.bp3-intent-primary>.bp3-icon{color:#106ba3}.jupyter-wrapper .bp3-dark .bp3-input-group.bp3-intent-primary>.bp3-icon{color:#48aff0}.jupyter-wrapper .bp3-input-group.bp3-intent-success .bp3-input{-webkit-box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px #0f9960,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #0f996000,0 0 #0f996000,inset 0 0 0 1px #0f9960,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-success .bp3-input:focus{-webkit-box-shadow:0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #0f9960,0 0 0 3px #0f99604d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-success .bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px #0f9960;box-shadow:inset 0 0 0 1px #0f9960}.jupyter-wrapper .bp3-input-group.bp3-intent-success .bp3-input:disabled,.jupyter-wrapper .bp3-input-group.bp3-intent-success .bp3-input.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input-group.bp3-intent-success>.bp3-icon{color:#0d8050}.jupyter-wrapper .bp3-dark .bp3-input-group.bp3-intent-success>.bp3-icon{color:#3dcc91}.jupyter-wrapper .bp3-input-group.bp3-intent-warning .bp3-input{-webkit-box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px #d9822b,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #d9822b00,0 0 #d9822b00,inset 0 0 0 1px #d9822b,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-warning .bp3-input:focus{-webkit-box-shadow:0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #d9822b,0 0 0 3px #d9822b4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-warning .bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px #d9822b;box-shadow:inset 0 0 0 1px #d9822b}.jupyter-wrapper .bp3-input-group.bp3-intent-warning .bp3-input:disabled,.jupyter-wrapper .bp3-input-group.bp3-intent-warning .bp3-input.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input-group.bp3-intent-warning>.bp3-icon{color:#bf7326}.jupyter-wrapper .bp3-dark .bp3-input-group.bp3-intent-warning>.bp3-icon{color:#ffb366}.jupyter-wrapper .bp3-input-group.bp3-intent-danger .bp3-input{-webkit-box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px #db3737,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #db373700,0 0 #db373700,inset 0 0 0 1px #db3737,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-danger .bp3-input:focus{-webkit-box-shadow:0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #db3737,0 0 0 3px #db37374d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input-group.bp3-intent-danger .bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px #db3737;box-shadow:inset 0 0 0 1px #db3737}.jupyter-wrapper .bp3-input-group.bp3-intent-danger .bp3-input:disabled,.jupyter-wrapper .bp3-input-group.bp3-intent-danger .bp3-input.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input-group.bp3-intent-danger>.bp3-icon{color:#c23030}.jupyter-wrapper .bp3-dark .bp3-input-group.bp3-intent-danger>.bp3-icon{color:#ff7373}.jupyter-wrapper .bp3-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#ffffff;border:none;border-radius:3px;-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33;color:#182026;font-size:14px;font-weight:400;height:30px;line-height:30px;outline:none;padding:0 10px;-webkit-transition:-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:box-shadow .1s cubic-bezier(.4,1,.75,.9);transition:box-shadow .1s cubic-bezier(.4,1,.75,.9),-webkit-box-shadow .1s cubic-bezier(.4,1,.75,.9);vertical-align:middle}.jupyter-wrapper .bp3-input::-webkit-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input::-moz-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input:-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input::-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input::placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input:focus,.jupyter-wrapper .bp3-input.bp3-active{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input[type=search],.jupyter-wrapper .bp3-input.bp3-round{border-radius:30px;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:10px}.jupyter-wrapper .bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.15);box-shadow:inset 0 0 0 1px #10161a26}.jupyter-wrapper .bp3-input:disabled,.jupyter-wrapper .bp3-input.bp3-disabled{background:rgba(206,217,224,.5);-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;resize:none}.jupyter-wrapper .bp3-input.bp3-large{font-size:16px;height:40px;line-height:40px}.jupyter-wrapper .bp3-input.bp3-large[type=search],.jupyter-wrapper .bp3-input.bp3-large.bp3-round{padding:0 15px}.jupyter-wrapper .bp3-input.bp3-small{font-size:12px;height:24px;line-height:24px;padding-left:8px;padding-right:8px}.jupyter-wrapper .bp3-input.bp3-small[type=search],.jupyter-wrapper .bp3-input.bp3-small.bp3-round{padding:0 12px}.jupyter-wrapper .bp3-input.bp3-fill{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:100%}.jupyter-wrapper .bp3-dark .bp3-input{background:rgba(16,22,26,.3);-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #137cbd00,0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-input::-webkit-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-input::-moz-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-input:-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-input::-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-input::placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-input:focus{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input:disabled,.jupyter-wrapper .bp3-dark .bp3-input.bp3-disabled{background:rgba(57,75,89,.5);-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-input.bp3-intent-primary{-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px #137cbd,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #137cbd,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-primary:focus{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-primary[readonly]{-webkit-box-shadow:inset 0 0 0 1px #137cbd;box-shadow:inset 0 0 0 1px #137cbd}.jupyter-wrapper .bp3-input.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-input.bp3-intent-primary.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-primary{-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px #137cbd,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #137cbd00,0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #137cbd,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-primary:focus{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-primary[readonly]{-webkit-box-shadow:inset 0 0 0 1px #137cbd;box-shadow:inset 0 0 0 1px #137cbd}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-primary.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input.bp3-intent-success{-webkit-box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px #0f9960,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #0f996000,0 0 #0f996000,inset 0 0 0 1px #0f9960,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-success:focus{-webkit-box-shadow:0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #0f9960,0 0 0 3px #0f99604d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-success[readonly]{-webkit-box-shadow:inset 0 0 0 1px #0f9960;box-shadow:inset 0 0 0 1px #0f9960}.jupyter-wrapper .bp3-input.bp3-intent-success:disabled,.jupyter-wrapper .bp3-input.bp3-intent-success.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-success{-webkit-box-shadow:0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),0 0 0 0 rgba(15,153,96,0),inset 0 0 0 1px #0f9960,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #0f996000,0 0 #0f996000,0 0 #0f996000,inset 0 0 0 1px #0f9960,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-success:focus{-webkit-box-shadow:0 0 0 1px #0f9960,0 0 0 1px #0f9960,0 0 0 3px rgba(15,153,96,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #0f9960,0 0 0 1px #0f9960,0 0 0 3px #0f99604d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-success[readonly]{-webkit-box-shadow:inset 0 0 0 1px #0f9960;box-shadow:inset 0 0 0 1px #0f9960}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-success.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input.bp3-intent-warning{-webkit-box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px #d9822b,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #d9822b00,0 0 #d9822b00,inset 0 0 0 1px #d9822b,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-warning:focus{-webkit-box-shadow:0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #d9822b,0 0 0 3px #d9822b4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-warning[readonly]{-webkit-box-shadow:inset 0 0 0 1px #d9822b;box-shadow:inset 0 0 0 1px #d9822b}.jupyter-wrapper .bp3-input.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-input.bp3-intent-warning.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-warning{-webkit-box-shadow:0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),0 0 0 0 rgba(217,130,43,0),inset 0 0 0 1px #d9822b,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #d9822b00,0 0 #d9822b00,0 0 #d9822b00,inset 0 0 0 1px #d9822b,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-warning:focus{-webkit-box-shadow:0 0 0 1px #d9822b,0 0 0 1px #d9822b,0 0 0 3px rgba(217,130,43,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #d9822b,0 0 0 1px #d9822b,0 0 0 3px #d9822b4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-warning[readonly]{-webkit-box-shadow:inset 0 0 0 1px #d9822b;box-shadow:inset 0 0 0 1px #d9822b}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-warning.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input.bp3-intent-danger{-webkit-box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px #db3737,inset 0 0 0 1px rgba(16,22,26,.15),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 #db373700,0 0 #db373700,inset 0 0 0 1px #db3737,inset 0 0 0 1px #10161a26,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-danger:focus{-webkit-box-shadow:0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #db3737,0 0 0 3px #db37374d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-input.bp3-intent-danger[readonly]{-webkit-box-shadow:inset 0 0 0 1px #db3737;box-shadow:inset 0 0 0 1px #db3737}.jupyter-wrapper .bp3-input.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-input.bp3-intent-danger.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-danger{-webkit-box-shadow:0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),0 0 0 0 rgba(219,55,55,0),inset 0 0 0 1px #db3737,inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #db373700,0 0 #db373700,0 0 #db373700,inset 0 0 0 1px #db3737,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-danger:focus{-webkit-box-shadow:0 0 0 1px #db3737,0 0 0 1px #db3737,0 0 0 3px rgba(219,55,55,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #db3737,0 0 0 1px #db3737,0 0 0 3px #db37374d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-danger[readonly]{-webkit-box-shadow:inset 0 0 0 1px #db3737;box-shadow:inset 0 0 0 1px #db3737}.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-input.bp3-intent-danger.bp3-disabled{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-input::-ms-clear{display:none}.jupyter-wrapper textarea.bp3-input{max-width:100%;padding:10px}.jupyter-wrapper textarea.bp3-input,.jupyter-wrapper textarea.bp3-input.bp3-large,.jupyter-wrapper textarea.bp3-input.bp3-small{height:auto;line-height:inherit}.jupyter-wrapper textarea.bp3-input.bp3-small{padding:8px}.jupyter-wrapper .bp3-dark textarea.bp3-input{background:rgba(16,22,26,.3);-webkit-box-shadow:0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),0 0 0 0 rgba(19,124,189,0),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 #137cbd00,0 0 #137cbd00,0 0 #137cbd00,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dark textarea.bp3-input::-webkit-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark textarea.bp3-input::-moz-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark textarea.bp3-input:-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark textarea.bp3-input::-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark textarea.bp3-input::placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark textarea.bp3-input:focus{-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark textarea.bp3-input[readonly]{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark textarea.bp3-input:disabled,.jupyter-wrapper .bp3-dark textarea.bp3-input.bp3-disabled{background:rgba(57,75,89,.5);-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper label.bp3-label{display:block;margin-bottom:15px;margin-top:0}.jupyter-wrapper label.bp3-label .bp3-html-select,.jupyter-wrapper label.bp3-label .bp3-input,.jupyter-wrapper label.bp3-label .bp3-select,.jupyter-wrapper label.bp3-label .bp3-slider,.jupyter-wrapper label.bp3-label .bp3-popover-wrapper{display:block;margin-top:5px;text-transform:none}.jupyter-wrapper label.bp3-label .bp3-button-group{margin-top:5px}.jupyter-wrapper label.bp3-label .bp3-select select,.jupyter-wrapper label.bp3-label .bp3-html-select select{font-weight:400;vertical-align:top;width:100%}.jupyter-wrapper label.bp3-label.bp3-disabled,.jupyter-wrapper label.bp3-label.bp3-disabled .bp3-text-muted{color:#5c708099}.jupyter-wrapper label.bp3-label.bp3-inline{line-height:30px}.jupyter-wrapper label.bp3-label.bp3-inline .bp3-html-select,.jupyter-wrapper label.bp3-label.bp3-inline .bp3-input,.jupyter-wrapper label.bp3-label.bp3-inline .bp3-input-group,.jupyter-wrapper label.bp3-label.bp3-inline .bp3-select,.jupyter-wrapper label.bp3-label.bp3-inline .bp3-popover-wrapper{display:inline-block;margin:0 0 0 5px;vertical-align:top}.jupyter-wrapper label.bp3-label.bp3-inline .bp3-button-group{margin:0 0 0 5px}.jupyter-wrapper label.bp3-label.bp3-inline .bp3-input-group .bp3-input{margin-left:0}.jupyter-wrapper label.bp3-label.bp3-inline.bp3-large{line-height:40px}.jupyter-wrapper label.bp3-label:not(.bp3-inline) .bp3-popover-target{display:block}.jupyter-wrapper .bp3-dark label.bp3-label{color:#f5f8fa}.jupyter-wrapper .bp3-dark label.bp3-label.bp3-disabled,.jupyter-wrapper .bp3-dark label.bp3-label.bp3-disabled .bp3-text-muted{color:#a7b6c299}.jupyter-wrapper .bp3-numeric-input .bp3-button-group.bp3-vertical>.bp3-button{-webkit-box-flex:1;-ms-flex:1 1 14px;flex:1 1 14px;min-height:0;padding:0;width:30px}.jupyter-wrapper .bp3-numeric-input .bp3-button-group.bp3-vertical>.bp3-button:first-child{border-radius:0 3px 0 0}.jupyter-wrapper .bp3-numeric-input .bp3-button-group.bp3-vertical>.bp3-button:last-child{border-radius:0 0 3px}.jupyter-wrapper .bp3-numeric-input .bp3-button-group.bp3-vertical:first-child>.bp3-button:first-child{border-radius:3px 0 0}.jupyter-wrapper .bp3-numeric-input .bp3-button-group.bp3-vertical:first-child>.bp3-button:last-child{border-radius:0 0 0 3px}.jupyter-wrapper .bp3-numeric-input.bp3-large .bp3-button-group.bp3-vertical>.bp3-button{width:40px}.jupyter-wrapper form{display:block}.jupyter-wrapper .bp3-html-select select,.jupyter-wrapper .bp3-select select{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:none;cursor:pointer;font-size:14px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:left;vertical-align:middle;background-color:#f5f8fa;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.8)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.8),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a;color:#182026;-moz-appearance:none;-webkit-appearance:none;border-radius:3px;height:30px;padding:0 25px 0 10px;width:100%}.jupyter-wrapper .bp3-html-select select>*,.jupyter-wrapper .bp3-select select>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-html-select select>.bp3-fill,.jupyter-wrapper .bp3-select select>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-html-select select:before,.jupyter-wrapper .bp3-select select:before,.jupyter-wrapper .bp3-html-select select>*,.jupyter-wrapper .bp3-select select>*{margin-right:7px}.jupyter-wrapper .bp3-html-select select:empty:before,.jupyter-wrapper .bp3-select select:empty:before,.jupyter-wrapper .bp3-html-select select>:last-child,.jupyter-wrapper .bp3-select select>:last-child{margin-right:0}.jupyter-wrapper .bp3-html-select select:hover,.jupyter-wrapper .bp3-select select:hover{background-clip:padding-box;background-color:#ebf1f5;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a}.jupyter-wrapper .bp3-html-select select:active,.jupyter-wrapper .bp3-select select:active,.jupyter-wrapper .bp3-html-select select.bp3-active,.jupyter-wrapper .bp3-select select.bp3-active{background-color:#d8e1e8;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-html-select select:disabled,.jupyter-wrapper .bp3-select select:disabled,.jupyter-wrapper .bp3-html-select select.bp3-disabled,.jupyter-wrapper .bp3-select select.bp3-disabled{background-color:#ced9e080;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;outline:none}.jupyter-wrapper .bp3-html-select select:disabled.bp3-active,.jupyter-wrapper .bp3-select select:disabled.bp3-active,.jupyter-wrapper .bp3-html-select select:disabled.bp3-active:hover,.jupyter-wrapper .bp3-select select:disabled.bp3-active:hover,.jupyter-wrapper .bp3-html-select select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select select.bp3-disabled.bp3-active:hover,.jupyter-wrapper .bp3-select select.bp3-disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.jupyter-wrapper .bp3-html-select.bp3-minimal select,.jupyter-wrapper .bp3-select.bp3-minimal select{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-html-select.bp3-minimal select:hover,.jupyter-wrapper .bp3-select.bp3-minimal select:hover{background:rgba(167,182,194,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026;text-decoration:none}.jupyter-wrapper .bp3-html-select.bp3-minimal select:active,.jupyter-wrapper .bp3-select.bp3-minimal select:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-active{background:rgba(115,134,148,.3);-webkit-box-shadow:none;box-shadow:none;color:#182026}.jupyter-wrapper .bp3-html-select.bp3-minimal select:disabled,.jupyter-wrapper .bp3-select.bp3-minimal select:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal select:disabled:hover,.jupyter-wrapper .bp3-select.bp3-minimal select:disabled:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-disabled:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-disabled:hover{background:none;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-html-select.bp3-minimal select:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select:disabled:hover.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select:disabled:hover.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-disabled:hover.bp3-active{background:rgba(115,134,148,.3)}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select{background:none;-webkit-box-shadow:none;box-shadow:none;color:inherit}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:hover,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:hover{background:rgba(138,155,168,.15)}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-active{background:rgba(138,155,168,.3);color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:disabled,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:disabled:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:disabled:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:disabled:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:disabled:hover,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-disabled:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-disabled:hover{background:none;color:#a7b6c299;cursor:not-allowed}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select:disabled:hover.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select:disabled:hover.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select:disabled:hover.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select:disabled:hover.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-disabled:hover.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-disabled:hover.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-disabled:hover.bp3-active{background:rgba(138,155,168,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary{color:#106ba3}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#106ba3}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary:hover{background:rgba(19,124,189,.15);color:#106ba3}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#106ba3}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled{background:none;color:#106ba380}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-primary .bp3-button-spinner .bp3-spinner-head{stroke:#106ba3}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:hover{background:rgba(19,124,189,.2);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-active{background:rgba(19,124,189,.3);color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled{background:none;color:#48aff080}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-primary.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-primary.bp3-disabled.bp3-active{background:rgba(19,124,189,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success{color:#0d8050}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#0d8050}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success:hover{background:rgba(15,153,96,.15);color:#0d8050}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#0d8050}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success:disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled{background:none;color:#0d805080}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-success .bp3-button-spinner .bp3-spinner-head,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-success .bp3-button-spinner .bp3-spinner-head{stroke:#0d8050}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:hover{background:rgba(15,153,96,.2);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-active{background:rgba(15,153,96,.3);color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled{background:none;color:#3dcc9180}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-success.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-success.bp3-disabled.bp3-active{background:rgba(15,153,96,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning{color:#bf7326}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#bf7326}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning:hover{background:rgba(217,130,43,.15);color:#bf7326}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#bf7326}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled{background:none;color:#bf732680}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-warning .bp3-button-spinner .bp3-spinner-head{stroke:#bf7326}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:hover{background:rgba(217,130,43,.2);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-active{background:rgba(217,130,43,.3);color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled{background:none;color:#ffb36680}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-warning.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-warning.bp3-disabled.bp3-active{background:rgba(217,130,43,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger{color:#c23030}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-active{background:none;-webkit-box-shadow:none;box-shadow:none;color:#c23030}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger:hover,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger:hover{background:rgba(219,55,55,.15);color:#c23030}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger:active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger:active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#c23030}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled{background:none;color:#c2303080}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-html-select.bp3-minimal select.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head,.jupyter-wrapper .bp3-select.bp3-minimal select.bp3-intent-danger .bp3-button-spinner .bp3-spinner-head{stroke:#c23030}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:hover,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:hover,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:hover,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:hover{background:rgba(219,55,55,.2);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-active{background:rgba(219,55,55,.3);color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled{background:none;color:#ff737380}.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-html-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select.bp3-minimal select.bp3-intent-danger.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-select.bp3-minimal .bp3-dark select.bp3-intent-danger.bp3-disabled.bp3-active{background:rgba(219,55,55,.3)}.jupyter-wrapper .bp3-html-select.bp3-large select,.jupyter-wrapper .bp3-select.bp3-large select{font-size:16px;height:40px;padding-right:35px}.jupyter-wrapper .bp3-dark .bp3-html-select select,.jupyter-wrapper .bp3-dark .bp3-select select{background-color:#394b59;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.05)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.05),rgba(255,255,255,0));-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-html-select select:hover,.jupyter-wrapper .bp3-dark .bp3-select select:hover,.jupyter-wrapper .bp3-dark .bp3-html-select select:active,.jupyter-wrapper .bp3-dark .bp3-select select:active,.jupyter-wrapper .bp3-dark .bp3-html-select select.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select select.bp3-active{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-html-select select:hover,.jupyter-wrapper .bp3-dark .bp3-select select:hover{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-html-select select:active,.jupyter-wrapper .bp3-dark .bp3-select select:active,.jupyter-wrapper .bp3-dark .bp3-html-select select.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select select.bp3-active{background-color:#202b33;background-image:none;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a99,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-html-select select:disabled,.jupyter-wrapper .bp3-dark .bp3-select select:disabled,.jupyter-wrapper .bp3-dark .bp3-html-select select.bp3-disabled,.jupyter-wrapper .bp3-dark .bp3-select select.bp3-disabled{background-color:#394b5980;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-html-select select:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select select:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-html-select select.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-select select.bp3-disabled.bp3-active{background:rgba(57,75,89,.7)}.jupyter-wrapper .bp3-dark .bp3-html-select select .bp3-button-spinner .bp3-spinner-head,.jupyter-wrapper .bp3-dark .bp3-select select .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.jupyter-wrapper .bp3-html-select select:disabled,.jupyter-wrapper .bp3-select select:disabled{background-color:#ced9e080;-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-html-select .bp3-icon,.jupyter-wrapper .bp3-select .bp3-icon,.jupyter-wrapper .bp3-select:after{color:#5c7080;pointer-events:none;position:absolute;right:7px;top:7px}.jupyter-wrapper .bp3-html-select .bp3-disabled.bp3-icon,.jupyter-wrapper .bp3-select .bp3-disabled.bp3-icon,.jupyter-wrapper .bp3-disabled.bp3-select:after{color:#5c708099}.jupyter-wrapper .bp3-html-select,.jupyter-wrapper .bp3-select{display:inline-block;letter-spacing:normal;position:relative;vertical-align:middle}.jupyter-wrapper .bp3-html-select select::-ms-expand,.jupyter-wrapper .bp3-select select::-ms-expand{display:none}.jupyter-wrapper .bp3-html-select .bp3-icon,.jupyter-wrapper .bp3-select .bp3-icon{color:#5c7080}.jupyter-wrapper .bp3-html-select .bp3-icon:hover,.jupyter-wrapper .bp3-select .bp3-icon:hover{color:#182026}.jupyter-wrapper .bp3-dark .bp3-html-select .bp3-icon,.jupyter-wrapper .bp3-dark .bp3-select .bp3-icon{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-html-select .bp3-icon:hover,.jupyter-wrapper .bp3-dark .bp3-select .bp3-icon:hover{color:#f5f8fa}.jupyter-wrapper .bp3-html-select.bp3-large:after,.jupyter-wrapper .bp3-html-select.bp3-large .bp3-icon,.jupyter-wrapper .bp3-select.bp3-large:after,.jupyter-wrapper .bp3-select.bp3-large .bp3-icon{right:12px;top:12px}.jupyter-wrapper .bp3-html-select.bp3-fill,.jupyter-wrapper .bp3-html-select.bp3-fill select,.jupyter-wrapper .bp3-select.bp3-fill,.jupyter-wrapper .bp3-select.bp3-fill select{width:100%}.jupyter-wrapper .bp3-dark .bp3-html-select option,.jupyter-wrapper .bp3-dark .bp3-select option{background-color:#30404d;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-html-select option:disabled,.jupyter-wrapper .bp3-dark .bp3-select option:disabled{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-html-select:after,.jupyter-wrapper .bp3-dark .bp3-select:after{color:#a7b6c2}.jupyter-wrapper .bp3-select:after{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;content:\"\ue6c6\"}.jupyter-wrapper .bp3-running-text table,.jupyter-wrapper table.bp3-html-table{border-spacing:0;font-size:14px}.jupyter-wrapper .bp3-running-text table th,.jupyter-wrapper table.bp3-html-table th,.jupyter-wrapper .bp3-running-text table td,.jupyter-wrapper table.bp3-html-table td{padding:11px;text-align:left;vertical-align:top}.jupyter-wrapper .bp3-running-text table th,.jupyter-wrapper table.bp3-html-table th{color:#182026;font-weight:600}.jupyter-wrapper .bp3-running-text table td,.jupyter-wrapper table.bp3-html-table td{color:#182026}.jupyter-wrapper .bp3-running-text table tbody tr:first-child th,.jupyter-wrapper table.bp3-html-table tbody tr:first-child th,.jupyter-wrapper .bp3-running-text table tbody tr:first-child td,.jupyter-wrapper table.bp3-html-table tbody tr:first-child td,.jupyter-wrapper .bp3-running-text table tfoot tr:first-child th,.jupyter-wrapper table.bp3-html-table tfoot tr:first-child th,.jupyter-wrapper .bp3-running-text table tfoot tr:first-child td,.jupyter-wrapper table.bp3-html-table tfoot tr:first-child td{-webkit-box-shadow:inset 0 1px 0 0 rgba(16,22,26,.15);box-shadow:inset 0 1px #10161a26}.jupyter-wrapper .bp3-dark .bp3-running-text table th,.jupyter-wrapper .bp3-running-text .bp3-dark table th,.jupyter-wrapper .bp3-dark table.bp3-html-table th,.jupyter-wrapper .bp3-dark .bp3-running-text table td,.jupyter-wrapper .bp3-running-text .bp3-dark table td,.jupyter-wrapper .bp3-dark table.bp3-html-table td{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-running-text table tbody tr:first-child th,.jupyter-wrapper .bp3-running-text .bp3-dark table tbody tr:first-child th,.jupyter-wrapper .bp3-dark table.bp3-html-table tbody tr:first-child th,.jupyter-wrapper .bp3-dark .bp3-running-text table tbody tr:first-child td,.jupyter-wrapper .bp3-running-text .bp3-dark table tbody tr:first-child td,.jupyter-wrapper .bp3-dark table.bp3-html-table tbody tr:first-child td,.jupyter-wrapper .bp3-dark .bp3-running-text table tfoot tr:first-child th,.jupyter-wrapper .bp3-running-text .bp3-dark table tfoot tr:first-child th,.jupyter-wrapper .bp3-dark table.bp3-html-table tfoot tr:first-child th,.jupyter-wrapper .bp3-dark .bp3-running-text table tfoot tr:first-child td,.jupyter-wrapper .bp3-running-text .bp3-dark table tfoot tr:first-child td,.jupyter-wrapper .bp3-dark table.bp3-html-table tfoot tr:first-child td{-webkit-box-shadow:inset 0 1px 0 0 rgba(255,255,255,.15);box-shadow:inset 0 1px #ffffff26}.jupyter-wrapper table.bp3-html-table.bp3-html-table-condensed th,.jupyter-wrapper table.bp3-html-table.bp3-html-table-condensed td,.jupyter-wrapper table.bp3-html-table.bp3-small th,.jupyter-wrapper table.bp3-html-table.bp3-small td{padding-bottom:6px;padding-top:6px}.jupyter-wrapper table.bp3-html-table.bp3-html-table-striped tbody tr:nth-child(odd) td{background:rgba(191,204,214,.15)}.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered th:not(:first-child){-webkit-box-shadow:inset 1px 0 0 0 rgba(16,22,26,.15);box-shadow:inset 1px 0 #10161a26}.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered tbody tr td,.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered tfoot tr td{-webkit-box-shadow:inset 0 1px 0 0 rgba(16,22,26,.15);box-shadow:inset 0 1px #10161a26}.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered tbody tr td:not(:first-child),.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered tfoot tr td:not(:first-child){-webkit-box-shadow:inset 1px 1px 0 0 rgba(16,22,26,.15);box-shadow:inset 1px 1px #10161a26}.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td:not(:first-child){-webkit-box-shadow:inset 1px 0 0 0 rgba(16,22,26,.15);box-shadow:inset 1px 0 #10161a26}.jupyter-wrapper table.bp3-html-table.bp3-interactive tbody tr:hover td{background-color:#bfccd64d;cursor:pointer}.jupyter-wrapper table.bp3-html-table.bp3-interactive tbody tr:active td{background-color:#bfccd666}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-striped tbody tr:nth-child(odd) td{background:rgba(92,112,128,.15)}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered th:not(:first-child){-webkit-box-shadow:inset 1px 0 0 0 rgba(255,255,255,.15);box-shadow:inset 1px 0 #ffffff26}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered tbody tr td,.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered tfoot tr td{-webkit-box-shadow:inset 0 1px 0 0 rgba(255,255,255,.15);box-shadow:inset 0 1px #ffffff26}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered tbody tr td:not(:first-child),.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered tfoot tr td:not(:first-child){-webkit-box-shadow:inset 1px 1px 0 0 rgba(255,255,255,.15);box-shadow:inset 1px 1px #ffffff26}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td{-webkit-box-shadow:inset 1px 0 0 0 rgba(255,255,255,.15);box-shadow:inset 1px 0 #ffffff26}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-html-table-bordered.bp3-html-table-striped tbody tr:not(:first-child) td:first-child{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-interactive tbody tr:hover td{background-color:#5c70804d;cursor:pointer}.jupyter-wrapper .bp3-dark table.bp3-html-table.bp3-interactive tbody tr:active td{background-color:#5c708066}.jupyter-wrapper .bp3-key-combo{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.jupyter-wrapper .bp3-key-combo>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-key-combo>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-key-combo:before,.jupyter-wrapper .bp3-key-combo>*{margin-right:5px}.jupyter-wrapper .bp3-key-combo:empty:before,.jupyter-wrapper .bp3-key-combo>:last-child{margin-right:0}.jupyter-wrapper .bp3-hotkey-dialog{padding-bottom:0;top:40px}.jupyter-wrapper .bp3-hotkey-dialog .bp3-dialog-body{margin:0;padding:0}.jupyter-wrapper .bp3-hotkey-dialog .bp3-hotkey-label{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.jupyter-wrapper .bp3-hotkey-column{margin:auto;max-height:80vh;overflow-y:auto;padding:30px}.jupyter-wrapper .bp3-hotkey-column .bp3-heading{margin-bottom:20px}.jupyter-wrapper .bp3-hotkey-column .bp3-heading:not(:first-child){margin-top:40px}.jupyter-wrapper .bp3-hotkey{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin-left:0;margin-right:0}.jupyter-wrapper .bp3-hotkey:not(:last-child){margin-bottom:10px}.jupyter-wrapper .bp3-icon{display:inline-block;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;vertical-align:text-bottom}.jupyter-wrapper .bp3-icon:not(:empty):before{content:\"\"!important;content:unset!important}.jupyter-wrapper .bp3-icon>svg{display:block}.jupyter-wrapper .bp3-icon>svg:not([fill]){fill:currentColor}.jupyter-wrapper .bp3-icon.bp3-intent-primary,.jupyter-wrapper .bp3-icon-standard.bp3-intent-primary,.jupyter-wrapper .bp3-icon-large.bp3-intent-primary{color:#106ba3}.jupyter-wrapper .bp3-dark .bp3-icon.bp3-intent-primary,.jupyter-wrapper .bp3-dark .bp3-icon-standard.bp3-intent-primary,.jupyter-wrapper .bp3-dark .bp3-icon-large.bp3-intent-primary{color:#48aff0}.jupyter-wrapper .bp3-icon.bp3-intent-success,.jupyter-wrapper .bp3-icon-standard.bp3-intent-success,.jupyter-wrapper .bp3-icon-large.bp3-intent-success{color:#0d8050}.jupyter-wrapper .bp3-dark .bp3-icon.bp3-intent-success,.jupyter-wrapper .bp3-dark .bp3-icon-standard.bp3-intent-success,.jupyter-wrapper .bp3-dark .bp3-icon-large.bp3-intent-success{color:#3dcc91}.jupyter-wrapper .bp3-icon.bp3-intent-warning,.jupyter-wrapper .bp3-icon-standard.bp3-intent-warning,.jupyter-wrapper .bp3-icon-large.bp3-intent-warning{color:#bf7326}.jupyter-wrapper .bp3-dark .bp3-icon.bp3-intent-warning,.jupyter-wrapper .bp3-dark .bp3-icon-standard.bp3-intent-warning,.jupyter-wrapper .bp3-dark .bp3-icon-large.bp3-intent-warning{color:#ffb366}.jupyter-wrapper .bp3-icon.bp3-intent-danger,.jupyter-wrapper .bp3-icon-standard.bp3-intent-danger,.jupyter-wrapper .bp3-icon-large.bp3-intent-danger{color:#c23030}.jupyter-wrapper .bp3-dark .bp3-icon.bp3-intent-danger,.jupyter-wrapper .bp3-dark .bp3-icon-standard.bp3-intent-danger,.jupyter-wrapper .bp3-dark .bp3-icon-large.bp3-intent-danger{color:#ff7373}.jupyter-wrapper span.bp3-icon-standard{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block}.jupyter-wrapper span.bp3-icon-large{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block}.jupyter-wrapper span.bp3-icon:empty{font-family:Icons20;font-size:inherit;font-style:normal;font-weight:400;line-height:1}.jupyter-wrapper span.bp3-icon:empty:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.jupyter-wrapper .bp3-icon-add:before{content:\"\ue63e\"}.jupyter-wrapper .bp3-icon-add-column-left:before{content:\"\ue6f9\"}.jupyter-wrapper .bp3-icon-add-column-right:before{content:\"\ue6fa\"}.jupyter-wrapper .bp3-icon-add-row-bottom:before{content:\"\ue6f8\"}.jupyter-wrapper .bp3-icon-add-row-top:before{content:\"\ue6f7\"}.jupyter-wrapper .bp3-icon-add-to-artifact:before{content:\"\ue67c\"}.jupyter-wrapper .bp3-icon-add-to-folder:before{content:\"\ue6d2\"}.jupyter-wrapper .bp3-icon-airplane:before{content:\"\ue74b\"}.jupyter-wrapper .bp3-icon-align-center:before{content:\"\ue603\"}.jupyter-wrapper .bp3-icon-align-justify:before{content:\"\ue605\"}.jupyter-wrapper .bp3-icon-align-left:before{content:\"\ue602\"}.jupyter-wrapper .bp3-icon-align-right:before{content:\"\ue604\"}.jupyter-wrapper .bp3-icon-alignment-bottom:before{content:\"\ue727\"}.jupyter-wrapper .bp3-icon-alignment-horizontal-center:before{content:\"\ue726\"}.jupyter-wrapper .bp3-icon-alignment-left:before{content:\"\ue722\"}.jupyter-wrapper .bp3-icon-alignment-right:before{content:\"\ue724\"}.jupyter-wrapper .bp3-icon-alignment-top:before{content:\"\ue725\"}.jupyter-wrapper .bp3-icon-alignment-vertical-center:before{content:\"\ue723\"}.jupyter-wrapper .bp3-icon-annotation:before{content:\"\ue6f0\"}.jupyter-wrapper .bp3-icon-application:before{content:\"\ue735\"}.jupyter-wrapper .bp3-icon-applications:before{content:\"\ue621\"}.jupyter-wrapper .bp3-icon-archive:before{content:\"\ue907\"}.jupyter-wrapper .bp3-icon-arrow-bottom-left:before{content:\"\u2199\"}.jupyter-wrapper .bp3-icon-arrow-bottom-right:before{content:\"\u2198\"}.jupyter-wrapper .bp3-icon-arrow-down:before{content:\"\u2193\"}.jupyter-wrapper .bp3-icon-arrow-left:before{content:\"\u2190\"}.jupyter-wrapper .bp3-icon-arrow-right:before{content:\"\u2192\"}.jupyter-wrapper .bp3-icon-arrow-top-left:before{content:\"\u2196\"}.jupyter-wrapper .bp3-icon-arrow-top-right:before{content:\"\u2197\"}.jupyter-wrapper .bp3-icon-arrow-up:before{content:\"\u2191\"}.jupyter-wrapper .bp3-icon-arrows-horizontal:before{content:\"\u2194\"}.jupyter-wrapper .bp3-icon-arrows-vertical:before{content:\"\u2195\"}.jupyter-wrapper .bp3-icon-asterisk:before{content:\"*\"}.jupyter-wrapper .bp3-icon-automatic-updates:before{content:\"\ue65f\"}.jupyter-wrapper .bp3-icon-badge:before{content:\"\ue6e3\"}.jupyter-wrapper .bp3-icon-ban-circle:before{content:\"\ue69d\"}.jupyter-wrapper .bp3-icon-bank-account:before{content:\"\ue76f\"}.jupyter-wrapper .bp3-icon-barcode:before{content:\"\ue676\"}.jupyter-wrapper .bp3-icon-blank:before{content:\"\ue900\"}.jupyter-wrapper .bp3-icon-blocked-person:before{content:\"\ue768\"}.jupyter-wrapper .bp3-icon-bold:before{content:\"\ue606\"}.jupyter-wrapper .bp3-icon-book:before{content:\"\ue6b8\"}.jupyter-wrapper .bp3-icon-bookmark:before{content:\"\ue61a\"}.jupyter-wrapper .bp3-icon-box:before{content:\"\ue6bf\"}.jupyter-wrapper .bp3-icon-briefcase:before{content:\"\ue674\"}.jupyter-wrapper .bp3-icon-bring-data:before{content:\"\ue90a\"}.jupyter-wrapper .bp3-icon-build:before{content:\"\ue72d\"}.jupyter-wrapper .bp3-icon-calculator:before{content:\"\ue70b\"}.jupyter-wrapper .bp3-icon-calendar:before{content:\"\ue62b\"}.jupyter-wrapper .bp3-icon-camera:before{content:\"\ue69e\"}.jupyter-wrapper .bp3-icon-caret-down:before{content:\"\u2304\"}.jupyter-wrapper .bp3-icon-caret-left:before{content:\"\u2329\"}.jupyter-wrapper .bp3-icon-caret-right:before{content:\"\u232a\"}.jupyter-wrapper .bp3-icon-caret-up:before{content:\"\u2303\"}.jupyter-wrapper .bp3-icon-cell-tower:before{content:\"\ue770\"}.jupyter-wrapper .bp3-icon-MKDOCS_changes:before{content:\"\ue623\"}.jupyter-wrapper .bp3-icon-chart:before{content:\"\ue67e\"}.jupyter-wrapper .bp3-icon-chat:before{content:\"\ue689\"}.jupyter-wrapper .bp3-icon-chevron-backward:before{content:\"\ue6df\"}.jupyter-wrapper .bp3-icon-chevron-down:before{content:\"\ue697\"}.jupyter-wrapper .bp3-icon-chevron-forward:before{content:\"\ue6e0\"}.jupyter-wrapper .bp3-icon-chevron-left:before{content:\"\ue694\"}.jupyter-wrapper .bp3-icon-chevron-right:before{content:\"\ue695\"}.jupyter-wrapper .bp3-icon-chevron-up:before{content:\"\ue696\"}.jupyter-wrapper .bp3-icon-circle:before{content:\"\ue66a\"}.jupyter-wrapper .bp3-icon-circle-arrow-down:before{content:\"\ue68e\"}.jupyter-wrapper .bp3-icon-circle-arrow-left:before{content:\"\ue68c\"}.jupyter-wrapper .bp3-icon-circle-arrow-right:before{content:\"\ue68b\"}.jupyter-wrapper .bp3-icon-circle-arrow-up:before{content:\"\ue68d\"}.jupyter-wrapper .bp3-icon-citation:before{content:\"\ue61b\"}.jupyter-wrapper .bp3-icon-clean:before{content:\"\ue7c5\"}.jupyter-wrapper .bp3-icon-clipboard:before{content:\"\ue61d\"}.jupyter-wrapper .bp3-icon-cloud:before{content:\"\u2601\"}.jupyter-wrapper .bp3-icon-cloud-download:before{content:\"\ue690\"}.jupyter-wrapper .bp3-icon-cloud-upload:before{content:\"\ue691\"}.jupyter-wrapper .bp3-icon-code:before{content:\"\ue661\"}.jupyter-wrapper .bp3-icon-code-block:before{content:\"\ue6c5\"}.jupyter-wrapper .bp3-icon-cog:before{content:\"\ue645\"}.jupyter-wrapper .bp3-icon-collapse-all:before{content:\"\ue763\"}.jupyter-wrapper .bp3-icon-column-layout:before{content:\"\ue6da\"}.jupyter-wrapper .bp3-icon-comment:before{content:\"\ue68a\"}.jupyter-wrapper .bp3-icon-comparison:before{content:\"\ue637\"}.jupyter-wrapper .bp3-icon-compass:before{content:\"\ue79c\"}.jupyter-wrapper .bp3-icon-compressed:before{content:\"\ue6c0\"}.jupyter-wrapper .bp3-icon-confirm:before{content:\"\ue639\"}.jupyter-wrapper .bp3-icon-console:before{content:\"\ue79b\"}.jupyter-wrapper .bp3-icon-contrast:before{content:\"\ue6cb\"}.jupyter-wrapper .bp3-icon-control:before{content:\"\ue67f\"}.jupyter-wrapper .bp3-icon-credit-card:before{content:\"\ue649\"}.jupyter-wrapper .bp3-icon-cross:before{content:\"\u2717\"}.jupyter-wrapper .bp3-icon-crown:before{content:\"\ue7b4\"}.jupyter-wrapper .bp3-icon-cube:before{content:\"\ue7c8\"}.jupyter-wrapper .bp3-icon-cube-add:before{content:\"\ue7c9\"}.jupyter-wrapper .bp3-icon-cube-remove:before{content:\"\ue7d0\"}.jupyter-wrapper .bp3-icon-curved-range-chart:before{content:\"\ue71b\"}.jupyter-wrapper .bp3-icon-cut:before{content:\"\ue6ef\"}.jupyter-wrapper .bp3-icon-dashboard:before{content:\"\ue751\"}.jupyter-wrapper .bp3-icon-data-lineage:before{content:\"\ue908\"}.jupyter-wrapper .bp3-icon-database:before{content:\"\ue683\"}.jupyter-wrapper .bp3-icon-delete:before{content:\"\ue644\"}.jupyter-wrapper .bp3-icon-delta:before{content:\"\u0394\"}.jupyter-wrapper .bp3-icon-derive-column:before{content:\"\ue739\"}.jupyter-wrapper .bp3-icon-desktop:before{content:\"\ue6af\"}.jupyter-wrapper .bp3-icon-diagnosis:before{content:\"\ue90d\"}.jupyter-wrapper .bp3-icon-diagram-tree:before{content:\"\ue7b3\"}.jupyter-wrapper .bp3-icon-direction-left:before{content:\"\ue681\"}.jupyter-wrapper .bp3-icon-direction-right:before{content:\"\ue682\"}.jupyter-wrapper .bp3-icon-disable:before{content:\"\ue600\"}.jupyter-wrapper .bp3-icon-document:before{content:\"\ue630\"}.jupyter-wrapper .bp3-icon-document-open:before{content:\"\ue71e\"}.jupyter-wrapper .bp3-icon-document-share:before{content:\"\ue71f\"}.jupyter-wrapper .bp3-icon-dollar:before{content:\"$\"}.jupyter-wrapper .bp3-icon-dot:before{content:\"\u2022\"}.jupyter-wrapper .bp3-icon-double-caret-horizontal:before{content:\"\ue6c7\"}.jupyter-wrapper .bp3-icon-double-caret-vertical:before{content:\"\ue6c6\"}.jupyter-wrapper .bp3-icon-double-chevron-down:before{content:\"\ue703\"}.jupyter-wrapper .bp3-icon-double-chevron-left:before{content:\"\ue6ff\"}.jupyter-wrapper .bp3-icon-double-chevron-right:before{content:\"\ue701\"}.jupyter-wrapper .bp3-icon-double-chevron-up:before{content:\"\ue702\"}.jupyter-wrapper .bp3-icon-doughnut-chart:before{content:\"\ue6ce\"}.jupyter-wrapper .bp3-icon-download:before{content:\"\ue62f\"}.jupyter-wrapper .bp3-icon-drag-handle-horizontal:before{content:\"\ue716\"}.jupyter-wrapper .bp3-icon-drag-handle-vertical:before{content:\"\ue715\"}.jupyter-wrapper .bp3-icon-draw:before{content:\"\ue66b\"}.jupyter-wrapper .bp3-icon-drive-time:before{content:\"\ue615\"}.jupyter-wrapper .bp3-icon-duplicate:before{content:\"\ue69c\"}.jupyter-wrapper .bp3-icon-edit:before{content:\"\u270e\"}.jupyter-wrapper .bp3-icon-eject:before{content:\"\u23cf\"}.jupyter-wrapper .bp3-icon-endorsed:before{content:\"\ue75f\"}.jupyter-wrapper .bp3-icon-envelope:before{content:\"\u2709\"}.jupyter-wrapper .bp3-icon-equals:before{content:\"\ue7d9\"}.jupyter-wrapper .bp3-icon-eraser:before{content:\"\ue773\"}.jupyter-wrapper .bp3-icon-error:before{content:\"\ue648\"}.jupyter-wrapper .bp3-icon-euro:before{content:\"\u20ac\"}.jupyter-wrapper .bp3-icon-MKDOCS_exchange:before{content:\"\ue636\"}.jupyter-wrapper .bp3-icon-exclude-row:before{content:\"\ue6ea\"}.jupyter-wrapper .bp3-icon-expand-all:before{content:\"\ue764\"}.jupyter-wrapper .bp3-icon-export:before{content:\"\ue633\"}.jupyter-wrapper .bp3-icon-eye-off:before{content:\"\ue6cc\"}.jupyter-wrapper .bp3-icon-eye-on:before{content:\"\ue75a\"}.jupyter-wrapper .bp3-icon-eye-open:before{content:\"\ue66f\"}.jupyter-wrapper .bp3-icon-fast-backward:before{content:\"\ue6a8\"}.jupyter-wrapper .bp3-icon-fast-forward:before{content:\"\ue6ac\"}.jupyter-wrapper .bp3-icon-feed:before{content:\"\ue656\"}.jupyter-wrapper .bp3-icon-feed-subscribed:before{content:\"\ue78f\"}.jupyter-wrapper .bp3-icon-film:before{content:\"\ue6a1\"}.jupyter-wrapper .bp3-icon-filter:before{content:\"\ue638\"}.jupyter-wrapper .bp3-icon-filter-keep:before{content:\"\ue78c\"}.jupyter-wrapper .bp3-icon-filter-list:before{content:\"\ue6ee\"}.jupyter-wrapper .bp3-icon-filter-open:before{content:\"\ue7d7\"}.jupyter-wrapper .bp3-icon-filter-remove:before{content:\"\ue78d\"}.jupyter-wrapper .bp3-icon-flag:before{content:\"\u2691\"}.jupyter-wrapper .bp3-icon-flame:before{content:\"\ue7a9\"}.jupyter-wrapper .bp3-icon-flash:before{content:\"\ue6b3\"}.jupyter-wrapper .bp3-icon-floppy-disk:before{content:\"\ue6b7\"}.jupyter-wrapper .bp3-icon-flow-branch:before{content:\"\ue7c1\"}.jupyter-wrapper .bp3-icon-flow-end:before{content:\"\ue7c4\"}.jupyter-wrapper .bp3-icon-flow-linear:before{content:\"\ue7c0\"}.jupyter-wrapper .bp3-icon-flow-review:before{content:\"\ue7c2\"}.jupyter-wrapper .bp3-icon-flow-review-branch:before{content:\"\ue7c3\"}.jupyter-wrapper .bp3-icon-flows:before{content:\"\ue659\"}.jupyter-wrapper .bp3-icon-folder-close:before{content:\"\ue652\"}.jupyter-wrapper .bp3-icon-folder-new:before{content:\"\ue7b0\"}.jupyter-wrapper .bp3-icon-folder-open:before{content:\"\ue651\"}.jupyter-wrapper .bp3-icon-folder-shared:before{content:\"\ue653\"}.jupyter-wrapper .bp3-icon-folder-shared-open:before{content:\"\ue670\"}.jupyter-wrapper .bp3-icon-follower:before{content:\"\ue760\"}.jupyter-wrapper .bp3-icon-following:before{content:\"\ue761\"}.jupyter-wrapper .bp3-icon-font:before{content:\"\ue6b4\"}.jupyter-wrapper .bp3-icon-fork:before{content:\"\ue63a\"}.jupyter-wrapper .bp3-icon-form:before{content:\"\ue795\"}.jupyter-wrapper .bp3-icon-full-circle:before{content:\"\ue685\"}.jupyter-wrapper .bp3-icon-full-stacked-chart:before{content:\"\ue75e\"}.jupyter-wrapper .bp3-icon-fullscreen:before{content:\"\ue699\"}.jupyter-wrapper .bp3-icon-function:before{content:\"\ue6e5\"}.jupyter-wrapper .bp3-icon-gantt-chart:before{content:\"\ue6f4\"}.jupyter-wrapper .bp3-icon-geolocation:before{content:\"\ue640\"}.jupyter-wrapper .bp3-icon-geosearch:before{content:\"\ue613\"}.jupyter-wrapper .bp3-icon-git-branch:before{content:\"\ue72a\"}.jupyter-wrapper .bp3-icon-git-commit:before{content:\"\ue72b\"}.jupyter-wrapper .bp3-icon-git-merge:before{content:\"\ue729\"}.jupyter-wrapper .bp3-icon-git-new-branch:before{content:\"\ue749\"}.jupyter-wrapper .bp3-icon-git-pull:before{content:\"\ue728\"}.jupyter-wrapper .bp3-icon-git-push:before{content:\"\ue72c\"}.jupyter-wrapper .bp3-icon-git-repo:before{content:\"\ue748\"}.jupyter-wrapper .bp3-icon-glass:before{content:\"\ue6b1\"}.jupyter-wrapper .bp3-icon-globe:before{content:\"\ue666\"}.jupyter-wrapper .bp3-icon-globe-network:before{content:\"\ue7b5\"}.jupyter-wrapper .bp3-icon-graph:before{content:\"\ue673\"}.jupyter-wrapper .bp3-icon-graph-remove:before{content:\"\ue609\"}.jupyter-wrapper .bp3-icon-greater-than:before{content:\"\ue7e1\"}.jupyter-wrapper .bp3-icon-greater-than-or-equal-to:before{content:\"\ue7e2\"}.jupyter-wrapper .bp3-icon-grid:before{content:\"\ue6d0\"}.jupyter-wrapper .bp3-icon-grid-view:before{content:\"\ue6e4\"}.jupyter-wrapper .bp3-icon-group-objects:before{content:\"\ue60a\"}.jupyter-wrapper .bp3-icon-grouped-bar-chart:before{content:\"\ue75d\"}.jupyter-wrapper .bp3-icon-hand:before{content:\"\ue6de\"}.jupyter-wrapper .bp3-icon-hand-down:before{content:\"\ue6bb\"}.jupyter-wrapper .bp3-icon-hand-left:before{content:\"\ue6bc\"}.jupyter-wrapper .bp3-icon-hand-right:before{content:\"\ue6b9\"}.jupyter-wrapper .bp3-icon-hand-up:before{content:\"\ue6ba\"}.jupyter-wrapper .bp3-icon-header:before{content:\"\ue6b5\"}.jupyter-wrapper .bp3-icon-header-one:before{content:\"\ue793\"}.jupyter-wrapper .bp3-icon-header-two:before{content:\"\ue794\"}.jupyter-wrapper .bp3-icon-headset:before{content:\"\ue6dc\"}.jupyter-wrapper .bp3-icon-heart:before{content:\"\u2665\"}.jupyter-wrapper .bp3-icon-heart-broken:before{content:\"\ue7a2\"}.jupyter-wrapper .bp3-icon-heat-grid:before{content:\"\ue6f3\"}.jupyter-wrapper .bp3-icon-heatmap:before{content:\"\ue614\"}.jupyter-wrapper .bp3-icon-help:before{content:\"?\"}.jupyter-wrapper .bp3-icon-helper-management:before{content:\"\ue66d\"}.jupyter-wrapper .bp3-icon-highlight:before{content:\"\ue6ed\"}.jupyter-wrapper .bp3-icon-history:before{content:\"\ue64a\"}.jupyter-wrapper .bp3-icon-home:before{content:\"\u2302\"}.jupyter-wrapper .bp3-icon-horizontal-bar-chart:before{content:\"\ue70c\"}.jupyter-wrapper .bp3-icon-horizontal-bar-chart-asc:before{content:\"\ue75c\"}.jupyter-wrapper .bp3-icon-horizontal-bar-chart-desc:before{content:\"\ue71d\"}.jupyter-wrapper .bp3-icon-horizontal-distribution:before{content:\"\ue720\"}.jupyter-wrapper .bp3-icon-id-number:before{content:\"\ue771\"}.jupyter-wrapper .bp3-icon-image-rotate-left:before{content:\"\ue73a\"}.jupyter-wrapper .bp3-icon-image-rotate-right:before{content:\"\ue73b\"}.jupyter-wrapper .bp3-icon-import:before{content:\"\ue632\"}.jupyter-wrapper .bp3-icon-inbox:before{content:\"\ue629\"}.jupyter-wrapper .bp3-icon-inbox-filtered:before{content:\"\ue7d1\"}.jupyter-wrapper .bp3-icon-inbox-geo:before{content:\"\ue7d2\"}.jupyter-wrapper .bp3-icon-inbox-search:before{content:\"\ue7d3\"}.jupyter-wrapper .bp3-icon-inbox-update:before{content:\"\ue7d4\"}.jupyter-wrapper .bp3-icon-info-sign:before{content:\"\u2139\"}.jupyter-wrapper .bp3-icon-inheritance:before{content:\"\ue7d5\"}.jupyter-wrapper .bp3-icon-inner-join:before{content:\"\ue7a3\"}.jupyter-wrapper .bp3-icon-insert:before{content:\"\ue66c\"}.jupyter-wrapper .bp3-icon-intersection:before{content:\"\ue765\"}.jupyter-wrapper .bp3-icon-ip-address:before{content:\"\ue772\"}.jupyter-wrapper .bp3-icon-issue:before{content:\"\ue774\"}.jupyter-wrapper .bp3-icon-issue-closed:before{content:\"\ue776\"}.jupyter-wrapper .bp3-icon-issue-new:before{content:\"\ue775\"}.jupyter-wrapper .bp3-icon-italic:before{content:\"\ue607\"}.jupyter-wrapper .bp3-icon-join-table:before{content:\"\ue738\"}.jupyter-wrapper .bp3-icon-key:before{content:\"\ue78e\"}.jupyter-wrapper .bp3-icon-key-backspace:before{content:\"\ue707\"}.jupyter-wrapper .bp3-icon-key-command:before{content:\"\ue705\"}.jupyter-wrapper .bp3-icon-key-control:before{content:\"\ue704\"}.jupyter-wrapper .bp3-icon-key-delete:before{content:\"\ue708\"}.jupyter-wrapper .bp3-icon-key-enter:before{content:\"\ue70a\"}.jupyter-wrapper .bp3-icon-key-escape:before{content:\"\ue709\"}.jupyter-wrapper .bp3-icon-key-option:before{content:\"\ue742\"}.jupyter-wrapper .bp3-icon-key-shift:before{content:\"\ue706\"}.jupyter-wrapper .bp3-icon-key-tab:before{content:\"\ue757\"}.jupyter-wrapper .bp3-icon-known-vehicle:before{content:\"\ue73c\"}.jupyter-wrapper .bp3-icon-lab-test:before{content:\"\ue90e\"}.jupyter-wrapper .bp3-icon-label:before{content:\"\ue665\"}.jupyter-wrapper .bp3-icon-layer:before{content:\"\ue6cf\"}.jupyter-wrapper .bp3-icon-layers:before{content:\"\ue618\"}.jupyter-wrapper .bp3-icon-layout:before{content:\"\ue60c\"}.jupyter-wrapper .bp3-icon-layout-auto:before{content:\"\ue60d\"}.jupyter-wrapper .bp3-icon-layout-balloon:before{content:\"\ue6d3\"}.jupyter-wrapper .bp3-icon-layout-circle:before{content:\"\ue60e\"}.jupyter-wrapper .bp3-icon-layout-grid:before{content:\"\ue610\"}.jupyter-wrapper .bp3-icon-layout-group-by:before{content:\"\ue611\"}.jupyter-wrapper .bp3-icon-layout-hierarchy:before{content:\"\ue60f\"}.jupyter-wrapper .bp3-icon-layout-linear:before{content:\"\ue6c3\"}.jupyter-wrapper .bp3-icon-layout-skew-grid:before{content:\"\ue612\"}.jupyter-wrapper .bp3-icon-layout-sorted-clusters:before{content:\"\ue6d4\"}.jupyter-wrapper .bp3-icon-learning:before{content:\"\ue904\"}.jupyter-wrapper .bp3-icon-left-join:before{content:\"\ue7a4\"}.jupyter-wrapper .bp3-icon-less-than:before{content:\"\ue7e3\"}.jupyter-wrapper .bp3-icon-less-than-or-equal-to:before{content:\"\ue7e4\"}.jupyter-wrapper .bp3-icon-lifesaver:before{content:\"\ue7c7\"}.jupyter-wrapper .bp3-icon-lightbulb:before{content:\"\ue6b0\"}.jupyter-wrapper .bp3-icon-link:before{content:\"\ue62d\"}.jupyter-wrapper .bp3-icon-list:before{content:\"\u2630\"}.jupyter-wrapper .bp3-icon-list-columns:before{content:\"\ue7b9\"}.jupyter-wrapper .bp3-icon-list-detail-view:before{content:\"\ue743\"}.jupyter-wrapper .bp3-icon-locate:before{content:\"\ue619\"}.jupyter-wrapper .bp3-icon-lock:before{content:\"\ue625\"}.jupyter-wrapper .bp3-icon-log-in:before{content:\"\ue69a\"}.jupyter-wrapper .bp3-icon-log-out:before{content:\"\ue64c\"}.jupyter-wrapper .bp3-icon-manual:before{content:\"\ue6f6\"}.jupyter-wrapper .bp3-icon-manually-entered-data:before{content:\"\ue74a\"}.jupyter-wrapper .bp3-icon-map:before{content:\"\ue662\"}.jupyter-wrapper .bp3-icon-map-create:before{content:\"\ue741\"}.jupyter-wrapper .bp3-icon-map-marker:before{content:\"\ue67d\"}.jupyter-wrapper .bp3-icon-maximize:before{content:\"\ue635\"}.jupyter-wrapper .bp3-icon-media:before{content:\"\ue62c\"}.jupyter-wrapper .bp3-icon-menu:before{content:\"\ue762\"}.jupyter-wrapper .bp3-icon-menu-closed:before{content:\"\ue655\"}.jupyter-wrapper .bp3-icon-menu-open:before{content:\"\ue654\"}.jupyter-wrapper .bp3-icon-merge-columns:before{content:\"\ue74f\"}.jupyter-wrapper .bp3-icon-merge-links:before{content:\"\ue60b\"}.jupyter-wrapper .bp3-icon-minimize:before{content:\"\ue634\"}.jupyter-wrapper .bp3-icon-minus:before{content:\"\u2212\"}.jupyter-wrapper .bp3-icon-mobile-phone:before{content:\"\ue717\"}.jupyter-wrapper .bp3-icon-mobile-video:before{content:\"\ue69f\"}.jupyter-wrapper .bp3-icon-moon:before{content:\"\ue754\"}.jupyter-wrapper .bp3-icon-more:before{content:\"\ue62a\"}.jupyter-wrapper .bp3-icon-mountain:before{content:\"\ue7b1\"}.jupyter-wrapper .bp3-icon-move:before{content:\"\ue693\"}.jupyter-wrapper .bp3-icon-mugshot:before{content:\"\ue6db\"}.jupyter-wrapper .bp3-icon-multi-select:before{content:\"\ue680\"}.jupyter-wrapper .bp3-icon-music:before{content:\"\ue6a6\"}.jupyter-wrapper .bp3-icon-new-drawing:before{content:\"\ue905\"}.jupyter-wrapper .bp3-icon-new-grid-item:before{content:\"\ue747\"}.jupyter-wrapper .bp3-icon-new-layer:before{content:\"\ue902\"}.jupyter-wrapper .bp3-icon-new-layers:before{content:\"\ue903\"}.jupyter-wrapper .bp3-icon-new-link:before{content:\"\ue65c\"}.jupyter-wrapper .bp3-icon-new-object:before{content:\"\ue65d\"}.jupyter-wrapper .bp3-icon-new-person:before{content:\"\ue6e9\"}.jupyter-wrapper .bp3-icon-new-prescription:before{content:\"\ue78b\"}.jupyter-wrapper .bp3-icon-new-text-box:before{content:\"\ue65b\"}.jupyter-wrapper .bp3-icon-ninja:before{content:\"\ue675\"}.jupyter-wrapper .bp3-icon-not-equal-to:before{content:\"\ue7e0\"}.jupyter-wrapper .bp3-icon-notifications:before{content:\"\ue624\"}.jupyter-wrapper .bp3-icon-notifications-updated:before{content:\"\ue7b8\"}.jupyter-wrapper .bp3-icon-numbered-list:before{content:\"\ue746\"}.jupyter-wrapper .bp3-icon-numerical:before{content:\"\ue756\"}.jupyter-wrapper .bp3-icon-office:before{content:\"\ue69b\"}.jupyter-wrapper .bp3-icon-offline:before{content:\"\ue67a\"}.jupyter-wrapper .bp3-icon-oil-field:before{content:\"\ue73f\"}.jupyter-wrapper .bp3-icon-one-column:before{content:\"\ue658\"}.jupyter-wrapper .bp3-icon-outdated:before{content:\"\ue7a8\"}.jupyter-wrapper .bp3-icon-page-layout:before{content:\"\ue660\"}.jupyter-wrapper .bp3-icon-panel-stats:before{content:\"\ue777\"}.jupyter-wrapper .bp3-icon-panel-table:before{content:\"\ue778\"}.jupyter-wrapper .bp3-icon-paperclip:before{content:\"\ue664\"}.jupyter-wrapper .bp3-icon-paragraph:before{content:\"\ue76c\"}.jupyter-wrapper .bp3-icon-path:before{content:\"\ue753\"}.jupyter-wrapper .bp3-icon-path-search:before{content:\"\ue65e\"}.jupyter-wrapper .bp3-icon-pause:before{content:\"\ue6a9\"}.jupyter-wrapper .bp3-icon-people:before{content:\"\ue63d\"}.jupyter-wrapper .bp3-icon-percentage:before{content:\"\ue76a\"}.jupyter-wrapper .bp3-icon-person:before{content:\"\ue63c\"}.jupyter-wrapper .bp3-icon-phone:before{content:\"\u260e\"}.jupyter-wrapper .bp3-icon-pie-chart:before{content:\"\ue684\"}.jupyter-wrapper .bp3-icon-pin:before{content:\"\ue646\"}.jupyter-wrapper .bp3-icon-pivot:before{content:\"\ue6f1\"}.jupyter-wrapper .bp3-icon-pivot-table:before{content:\"\ue6eb\"}.jupyter-wrapper .bp3-icon-play:before{content:\"\ue6ab\"}.jupyter-wrapper .bp3-icon-plus:before{content:\"+\"}.jupyter-wrapper .bp3-icon-polygon-filter:before{content:\"\ue6d1\"}.jupyter-wrapper .bp3-icon-power:before{content:\"\ue6d9\"}.jupyter-wrapper .bp3-icon-predictive-analysis:before{content:\"\ue617\"}.jupyter-wrapper .bp3-icon-prescription:before{content:\"\ue78a\"}.jupyter-wrapper .bp3-icon-presentation:before{content:\"\ue687\"}.jupyter-wrapper .bp3-icon-print:before{content:\"\u2399\"}.jupyter-wrapper .bp3-icon-projects:before{content:\"\ue622\"}.jupyter-wrapper .bp3-icon-properties:before{content:\"\ue631\"}.jupyter-wrapper .bp3-icon-property:before{content:\"\ue65a\"}.jupyter-wrapper .bp3-icon-publish-function:before{content:\"\ue752\"}.jupyter-wrapper .bp3-icon-pulse:before{content:\"\ue6e8\"}.jupyter-wrapper .bp3-icon-random:before{content:\"\ue698\"}.jupyter-wrapper .bp3-icon-record:before{content:\"\ue6ae\"}.jupyter-wrapper .bp3-icon-redo:before{content:\"\ue6c4\"}.jupyter-wrapper .bp3-icon-refresh:before{content:\"\ue643\"}.jupyter-wrapper .bp3-icon-regression-chart:before{content:\"\ue758\"}.jupyter-wrapper .bp3-icon-remove:before{content:\"\ue63f\"}.jupyter-wrapper .bp3-icon-remove-column:before{content:\"\ue755\"}.jupyter-wrapper .bp3-icon-remove-column-left:before{content:\"\ue6fd\"}.jupyter-wrapper .bp3-icon-remove-column-right:before{content:\"\ue6fe\"}.jupyter-wrapper .bp3-icon-remove-row-bottom:before{content:\"\ue6fc\"}.jupyter-wrapper .bp3-icon-remove-row-top:before{content:\"\ue6fb\"}.jupyter-wrapper .bp3-icon-repeat:before{content:\"\ue692\"}.jupyter-wrapper .bp3-icon-reset:before{content:\"\ue7d6\"}.jupyter-wrapper .bp3-icon-resolve:before{content:\"\ue672\"}.jupyter-wrapper .bp3-icon-rig:before{content:\"\ue740\"}.jupyter-wrapper .bp3-icon-right-join:before{content:\"\ue7a5\"}.jupyter-wrapper .bp3-icon-ring:before{content:\"\ue6f2\"}.jupyter-wrapper .bp3-icon-rotate-document:before{content:\"\ue6e1\"}.jupyter-wrapper .bp3-icon-rotate-page:before{content:\"\ue6e2\"}.jupyter-wrapper .bp3-icon-satellite:before{content:\"\ue76b\"}.jupyter-wrapper .bp3-icon-saved:before{content:\"\ue6b6\"}.jupyter-wrapper .bp3-icon-scatter-plot:before{content:\"\ue73e\"}.jupyter-wrapper .bp3-icon-search:before{content:\"\ue64b\"}.jupyter-wrapper .bp3-icon-search-around:before{content:\"\ue608\"}.jupyter-wrapper .bp3-icon-search-template:before{content:\"\ue628\"}.jupyter-wrapper .bp3-icon-search-text:before{content:\"\ue663\"}.jupyter-wrapper .bp3-icon-segmented-control:before{content:\"\ue6ec\"}.jupyter-wrapper .bp3-icon-select:before{content:\"\ue616\"}.jupyter-wrapper .bp3-icon-selection:before{content:\"\u29bf\"}.jupyter-wrapper .bp3-icon-send-to:before{content:\"\ue66e\"}.jupyter-wrapper .bp3-icon-send-to-graph:before{content:\"\ue736\"}.jupyter-wrapper .bp3-icon-send-to-map:before{content:\"\ue737\"}.jupyter-wrapper .bp3-icon-series-add:before{content:\"\ue796\"}.jupyter-wrapper .bp3-icon-series-configuration:before{content:\"\ue79a\"}.jupyter-wrapper .bp3-icon-series-derived:before{content:\"\ue799\"}.jupyter-wrapper .bp3-icon-series-filtered:before{content:\"\ue798\"}.jupyter-wrapper .bp3-icon-series-search:before{content:\"\ue797\"}.jupyter-wrapper .bp3-icon-settings:before{content:\"\ue6a2\"}.jupyter-wrapper .bp3-icon-share:before{content:\"\ue62e\"}.jupyter-wrapper .bp3-icon-shield:before{content:\"\ue7b2\"}.jupyter-wrapper .bp3-icon-shop:before{content:\"\ue6c2\"}.jupyter-wrapper .bp3-icon-shopping-cart:before{content:\"\ue6c1\"}.jupyter-wrapper .bp3-icon-signal-search:before{content:\"\ue909\"}.jupyter-wrapper .bp3-icon-sim-card:before{content:\"\ue718\"}.jupyter-wrapper .bp3-icon-slash:before{content:\"\ue769\"}.jupyter-wrapper .bp3-icon-small-cross:before{content:\"\ue6d7\"}.jupyter-wrapper .bp3-icon-small-minus:before{content:\"\ue70e\"}.jupyter-wrapper .bp3-icon-small-plus:before{content:\"\ue70d\"}.jupyter-wrapper .bp3-icon-small-tick:before{content:\"\ue6d8\"}.jupyter-wrapper .bp3-icon-snowflake:before{content:\"\ue7b6\"}.jupyter-wrapper .bp3-icon-social-media:before{content:\"\ue671\"}.jupyter-wrapper .bp3-icon-sort:before{content:\"\ue64f\"}.jupyter-wrapper .bp3-icon-sort-alphabetical:before{content:\"\ue64d\"}.jupyter-wrapper .bp3-icon-sort-alphabetical-desc:before{content:\"\ue6c8\"}.jupyter-wrapper .bp3-icon-sort-asc:before{content:\"\ue6d5\"}.jupyter-wrapper .bp3-icon-sort-desc:before{content:\"\ue6d6\"}.jupyter-wrapper .bp3-icon-sort-numerical:before{content:\"\ue64e\"}.jupyter-wrapper .bp3-icon-sort-numerical-desc:before{content:\"\ue6c9\"}.jupyter-wrapper .bp3-icon-split-columns:before{content:\"\ue750\"}.jupyter-wrapper .bp3-icon-square:before{content:\"\ue686\"}.jupyter-wrapper .bp3-icon-stacked-chart:before{content:\"\ue6e7\"}.jupyter-wrapper .bp3-icon-star:before{content:\"\u2605\"}.jupyter-wrapper .bp3-icon-star-empty:before{content:\"\u2606\"}.jupyter-wrapper .bp3-icon-step-backward:before{content:\"\ue6a7\"}.jupyter-wrapper .bp3-icon-step-chart:before{content:\"\ue70f\"}.jupyter-wrapper .bp3-icon-step-forward:before{content:\"\ue6ad\"}.jupyter-wrapper .bp3-icon-stop:before{content:\"\ue6aa\"}.jupyter-wrapper .bp3-icon-stopwatch:before{content:\"\ue901\"}.jupyter-wrapper .bp3-icon-strikethrough:before{content:\"\ue7a6\"}.jupyter-wrapper .bp3-icon-style:before{content:\"\ue601\"}.jupyter-wrapper .bp3-icon-swap-horizontal:before{content:\"\ue745\"}.jupyter-wrapper .bp3-icon-swap-vertical:before{content:\"\ue744\"}.jupyter-wrapper .bp3-icon-symbol-circle:before{content:\"\ue72e\"}.jupyter-wrapper .bp3-icon-symbol-cross:before{content:\"\ue731\"}.jupyter-wrapper .bp3-icon-symbol-diamond:before{content:\"\ue730\"}.jupyter-wrapper .bp3-icon-symbol-square:before{content:\"\ue72f\"}.jupyter-wrapper .bp3-icon-symbol-triangle-down:before{content:\"\ue733\"}.jupyter-wrapper .bp3-icon-symbol-triangle-up:before{content:\"\ue732\"}.jupyter-wrapper .bp3-icon-tag:before{content:\"\ue61c\"}.jupyter-wrapper .bp3-icon-take-action:before{content:\"\ue6ca\"}.jupyter-wrapper .bp3-icon-taxi:before{content:\"\ue79e\"}.jupyter-wrapper .bp3-icon-text-highlight:before{content:\"\ue6dd\"}.jupyter-wrapper .bp3-icon-th:before{content:\"\ue667\"}.jupyter-wrapper .bp3-icon-th-derived:before{content:\"\ue669\"}.jupyter-wrapper .bp3-icon-th-disconnect:before{content:\"\ue7d8\"}.jupyter-wrapper .bp3-icon-th-filtered:before{content:\"\ue7c6\"}.jupyter-wrapper .bp3-icon-th-list:before{content:\"\ue668\"}.jupyter-wrapper .bp3-icon-thumbs-down:before{content:\"\ue6be\"}.jupyter-wrapper .bp3-icon-thumbs-up:before{content:\"\ue6bd\"}.jupyter-wrapper .bp3-icon-tick:before{content:\"\u2713\"}.jupyter-wrapper .bp3-icon-tick-circle:before{content:\"\ue779\"}.jupyter-wrapper .bp3-icon-time:before{content:\"\u23f2\"}.jupyter-wrapper .bp3-icon-timeline-area-chart:before{content:\"\ue6cd\"}.jupyter-wrapper .bp3-icon-timeline-bar-chart:before{content:\"\ue620\"}.jupyter-wrapper .bp3-icon-timeline-events:before{content:\"\ue61e\"}.jupyter-wrapper .bp3-icon-timeline-line-chart:before{content:\"\ue61f\"}.jupyter-wrapper .bp3-icon-tint:before{content:\"\ue6b2\"}.jupyter-wrapper .bp3-icon-torch:before{content:\"\ue677\"}.jupyter-wrapper .bp3-icon-tractor:before{content:\"\ue90c\"}.jupyter-wrapper .bp3-icon-train:before{content:\"\ue79f\"}.jupyter-wrapper .bp3-icon-translate:before{content:\"\ue759\"}.jupyter-wrapper .bp3-icon-trash:before{content:\"\ue63b\"}.jupyter-wrapper .bp3-icon-tree:before{content:\"\ue7b7\"}.jupyter-wrapper .bp3-icon-trending-down:before{content:\"\ue71a\"}.jupyter-wrapper .bp3-icon-trending-up:before{content:\"\ue719\"}.jupyter-wrapper .bp3-icon-truck:before{content:\"\ue90b\"}.jupyter-wrapper .bp3-icon-two-columns:before{content:\"\ue657\"}.jupyter-wrapper .bp3-icon-unarchive:before{content:\"\ue906\"}.jupyter-wrapper .bp3-icon-underline:before{content:\"\u2381\"}.jupyter-wrapper .bp3-icon-undo:before{content:\"\u238c\"}.jupyter-wrapper .bp3-icon-ungroup-objects:before{content:\"\ue688\"}.jupyter-wrapper .bp3-icon-unknown-vehicle:before{content:\"\ue73d\"}.jupyter-wrapper .bp3-icon-unlock:before{content:\"\ue626\"}.jupyter-wrapper .bp3-icon-unpin:before{content:\"\ue650\"}.jupyter-wrapper .bp3-icon-unresolve:before{content:\"\ue679\"}.jupyter-wrapper .bp3-icon-updated:before{content:\"\ue7a7\"}.jupyter-wrapper .bp3-icon-upload:before{content:\"\ue68f\"}.jupyter-wrapper .bp3-icon-user:before{content:\"\ue627\"}.jupyter-wrapper .bp3-icon-variable:before{content:\"\ue6f5\"}.jupyter-wrapper .bp3-icon-vertical-bar-chart-asc:before{content:\"\ue75b\"}.jupyter-wrapper .bp3-icon-vertical-bar-chart-desc:before{content:\"\ue71c\"}.jupyter-wrapper .bp3-icon-vertical-distribution:before{content:\"\ue721\"}.jupyter-wrapper .bp3-icon-video:before{content:\"\ue6a0\"}.jupyter-wrapper .bp3-icon-volume-down:before{content:\"\ue6a4\"}.jupyter-wrapper .bp3-icon-volume-off:before{content:\"\ue6a3\"}.jupyter-wrapper .bp3-icon-volume-up:before{content:\"\ue6a5\"}.jupyter-wrapper .bp3-icon-walk:before{content:\"\ue79d\"}.jupyter-wrapper .bp3-icon-warning-sign:before{content:\"\ue647\"}.jupyter-wrapper .bp3-icon-waterfall-chart:before{content:\"\ue6e6\"}.jupyter-wrapper .bp3-icon-widget:before{content:\"\ue678\"}.jupyter-wrapper .bp3-icon-widget-button:before{content:\"\ue790\"}.jupyter-wrapper .bp3-icon-widget-footer:before{content:\"\ue792\"}.jupyter-wrapper .bp3-icon-widget-header:before{content:\"\ue791\"}.jupyter-wrapper .bp3-icon-wrench:before{content:\"\ue734\"}.jupyter-wrapper .bp3-icon-zoom-in:before{content:\"\ue641\"}.jupyter-wrapper .bp3-icon-zoom-out:before{content:\"\ue642\"}.jupyter-wrapper .bp3-icon-zoom-to-fit:before{content:\"\ue67b\"}.jupyter-wrapper .bp3-submenu>.bp3-popover-wrapper{display:block}.jupyter-wrapper .bp3-submenu .bp3-popover-target{display:block}.jupyter-wrapper .bp3-submenu.bp3-popover{-webkit-box-shadow:none;box-shadow:none;padding:0 5px}.jupyter-wrapper .bp3-submenu.bp3-popover>.bp3-popover-content{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33}.jupyter-wrapper .bp3-dark .bp3-submenu.bp3-popover,.jupyter-wrapper .bp3-submenu.bp3-popover.bp3-dark{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-dark .bp3-submenu.bp3-popover>.bp3-popover-content,.jupyter-wrapper .bp3-submenu.bp3-popover.bp3-dark>.bp3-popover-content{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66}.jupyter-wrapper .bp3-menu{background:#ffffff;border-radius:3px;color:#182026;list-style:none;margin:0;min-width:180px;padding:5px;text-align:left}.jupyter-wrapper .bp3-menu-divider{border-top:1px solid rgba(16,22,26,.15);display:block;margin:5px}.jupyter-wrapper .bp3-dark .bp3-menu-divider{border-top-color:#ffffff26}.jupyter-wrapper .bp3-menu-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;border-radius:2px;color:inherit;line-height:20px;padding:5px 7px;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .bp3-menu-item>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-menu-item>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-menu-item:before,.jupyter-wrapper .bp3-menu-item>*{margin-right:7px}.jupyter-wrapper .bp3-menu-item:empty:before,.jupyter-wrapper .bp3-menu-item>:last-child{margin-right:0}.jupyter-wrapper .bp3-menu-item>.bp3-fill{word-break:break-word}.jupyter-wrapper .bp3-menu-item:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-menu-item{background-color:#a7b6c24d;cursor:pointer;text-decoration:none}.jupyter-wrapper .bp3-menu-item.bp3-disabled{background-color:inherit;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-dark .bp3-menu-item{color:inherit}.jupyter-wrapper .bp3-dark .bp3-menu-item:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-menu-item{background-color:#8a9ba826;color:inherit}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-disabled{background-color:inherit;color:#a7b6c299}.jupyter-wrapper .bp3-menu-item.bp3-intent-primary{color:#106ba3}.jupyter-wrapper .bp3-menu-item.bp3-intent-primary .bp3-icon{color:inherit}.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary .bp3-menu-item-label{color:#106ba3}.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary.bp3-active{background-color:#137cbd}.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:active{background-color:#106ba3}.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:hover:before,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:hover:after,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:active,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary:active .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary.bp3-active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary.bp3-active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-primary.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-menu-item.bp3-intent-success{color:#0d8050}.jupyter-wrapper .bp3-menu-item.bp3-intent-success .bp3-icon{color:inherit}.jupyter-wrapper .bp3-menu-item.bp3-intent-success:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-success .bp3-menu-item-label{color:#0d8050}.jupyter-wrapper .bp3-menu-item.bp3-intent-success:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-success.bp3-active{background-color:#0f9960}.jupyter-wrapper .bp3-menu-item.bp3-intent-success:active{background-color:#0d8050}.jupyter-wrapper .bp3-menu-item.bp3-intent-success:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:hover:before,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:hover:after,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:active,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-success:active .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-menu-item.bp3-intent-success.bp3-active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-success.bp3-active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-success.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-menu-item.bp3-intent-warning{color:#bf7326}.jupyter-wrapper .bp3-menu-item.bp3-intent-warning .bp3-icon{color:inherit}.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning .bp3-menu-item-label{color:#bf7326}.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning.bp3-active{background-color:#d9822b}.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:active{background-color:#bf7326}.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:hover:before,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:hover:after,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:active,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning:active .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning.bp3-active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning.bp3-active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-warning.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-menu-item.bp3-intent-danger{color:#c23030}.jupyter-wrapper .bp3-menu-item.bp3-intent-danger .bp3-icon{color:inherit}.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger .bp3-menu-item-label{color:#c23030}.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger.bp3-active{background-color:#db3737}.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:active{background-color:#c23030}.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:hover:before,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:hover:after,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:active,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger:active .bp3-menu-item-label,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger.bp3-active:before,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger.bp3-active:after,.jupyter-wrapper .bp3-menu-item.bp3-intent-danger.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-menu-item:before{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;margin-right:7px}.jupyter-wrapper .bp3-menu-item:before,.jupyter-wrapper .bp3-menu-item>.bp3-icon{color:#5c7080;margin-top:2px}.jupyter-wrapper .bp3-menu-item .bp3-menu-item-label{color:#5c7080}.jupyter-wrapper .bp3-menu-item:hover,.jupyter-wrapper .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-menu-item{color:inherit}.jupyter-wrapper .bp3-menu-item.bp3-active,.jupyter-wrapper .bp3-menu-item:active{background-color:#7386944d}.jupyter-wrapper .bp3-menu-item.bp3-disabled{background-color:inherit!important;color:#5c708099!important;cursor:not-allowed!important;outline:none!important}.jupyter-wrapper .bp3-menu-item.bp3-disabled:before,.jupyter-wrapper .bp3-menu-item.bp3-disabled>.bp3-icon,.jupyter-wrapper .bp3-menu-item.bp3-disabled .bp3-menu-item-label{color:#5c708099!important}.jupyter-wrapper .bp3-large .bp3-menu-item{font-size:16px;line-height:22px;padding:9px 7px}.jupyter-wrapper .bp3-large .bp3-menu-item .bp3-icon{margin-top:3px}.jupyter-wrapper .bp3-large .bp3-menu-item:before{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;margin-right:10px;margin-top:1px}.jupyter-wrapper button.bp3-menu-item{background:none;border:none;text-align:left;width:100%}.jupyter-wrapper .bp3-menu-header{border-top:1px solid rgba(16,22,26,.15);display:block;margin:5px;cursor:default;padding-left:2px}.jupyter-wrapper .bp3-dark .bp3-menu-header{border-top-color:#ffffff26}.jupyter-wrapper .bp3-menu-header:first-of-type{border-top:none}.jupyter-wrapper .bp3-menu-header>h6{color:#182026;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;line-height:17px;margin:0;padding:10px 7px 0 1px}.jupyter-wrapper .bp3-menu-header:first-of-type>h6{padding-top:0}.jupyter-wrapper .bp3-large .bp3-menu-header>h6{font-size:18px;padding-bottom:5px;padding-top:15px}.jupyter-wrapper .bp3-large .bp3-menu-header:first-of-type>h6{padding-top:0}.jupyter-wrapper .bp3-dark .bp3-menu{background:#30404d;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary .bp3-icon{color:inherit}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary .bp3-menu-item-label{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active{background-color:#137cbd}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:active{background-color:#106ba3}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:hover:before,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:before,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:hover:after,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:after,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-primary.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary:active .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-primary.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success .bp3-icon{color:inherit}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success .bp3-menu-item-label{color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active{background-color:#0f9960}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:active{background-color:#0d8050}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:hover:before,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:before,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:hover:after,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:after,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-success.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success:active .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-success.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning .bp3-icon{color:inherit}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning .bp3-menu-item-label{color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active{background-color:#d9822b}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:active{background-color:#bf7326}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:hover:before,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:before,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:hover:after,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:after,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-warning.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning:active .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-warning.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger .bp3-icon{color:inherit}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger .bp3-menu-item-label{color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active{background-color:#db3737}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:active{background-color:#c23030}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:hover,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:hover:before,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:before,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:hover:after,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:after,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:hover .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-submenu .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-submenu .bp3-dark .bp3-popover-target.bp3-popover-open>.bp3-intent-danger.bp3-menu-item .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger:active .bp3-menu-item-label,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active:after,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-intent-danger.bp3-active .bp3-menu-item-label{color:#fff}.jupyter-wrapper .bp3-dark .bp3-menu-item:before,.jupyter-wrapper .bp3-dark .bp3-menu-item>.bp3-icon{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-menu-item .bp3-menu-item-label{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-active,.jupyter-wrapper .bp3-dark .bp3-menu-item:active{background-color:#8a9ba84d}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-disabled{color:#a7b6c299!important}.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-disabled:before,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-disabled>.bp3-icon,.jupyter-wrapper .bp3-dark .bp3-menu-item.bp3-disabled .bp3-menu-item-label{color:#a7b6c299!important}.jupyter-wrapper .bp3-dark .bp3-menu-divider,.jupyter-wrapper .bp3-dark .bp3-menu-header{border-color:#ffffff26}.jupyter-wrapper .bp3-dark .bp3-menu-header>h6{color:#f5f8fa}.jupyter-wrapper .bp3-label .bp3-menu{margin-top:5px}.jupyter-wrapper .bp3-navbar{background-color:#fff;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 0 #10161a00,0 1px 1px #10161a33;height:50px;padding:0 15px;position:relative;width:100%;z-index:10}.jupyter-wrapper .bp3-navbar.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-navbar{background-color:#394b59}.jupyter-wrapper .bp3-navbar.bp3-dark{-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4);box-shadow:inset 0 0 0 1px #10161a33,0 0 #10161a00,0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-navbar{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 0 0 rgba(16,22,26,0),0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 0 #10161a00,0 1px 1px #10161a66}.jupyter-wrapper .bp3-navbar.bp3-fixed-top{left:0;position:fixed;right:0;top:0}.jupyter-wrapper .bp3-navbar-heading{font-size:16px;margin-right:15px}.jupyter-wrapper .bp3-navbar-group{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;height:50px}.jupyter-wrapper .bp3-navbar-group.bp3-align-left{float:left}.jupyter-wrapper .bp3-navbar-group.bp3-align-right{float:right}.jupyter-wrapper .bp3-navbar-divider{border-left:1px solid rgba(16,22,26,.15);height:20px;margin:0 10px}.jupyter-wrapper .bp3-dark .bp3-navbar-divider{border-left-color:#ffffff26}.jupyter-wrapper .bp3-non-ideal-state{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:100%;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center;width:100%}.jupyter-wrapper .bp3-non-ideal-state>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-non-ideal-state>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-non-ideal-state:before,.jupyter-wrapper .bp3-non-ideal-state>*{margin-bottom:20px}.jupyter-wrapper .bp3-non-ideal-state:empty:before,.jupyter-wrapper .bp3-non-ideal-state>:last-child{margin-bottom:0}.jupyter-wrapper .bp3-non-ideal-state>*{max-width:400px}.jupyter-wrapper .bp3-non-ideal-state-visual{color:#5c708099;font-size:60px}.jupyter-wrapper .bp3-dark .bp3-non-ideal-state-visual{color:#a7b6c299}.jupyter-wrapper .bp3-overflow-list{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;min-width:0}.jupyter-wrapper .bp3-overflow-list-spacer{-ms-flex-negative:1;flex-shrink:1;width:1px}.jupyter-wrapper body.bp3-overlay-open{overflow:hidden}.jupyter-wrapper .bp3-overlay{bottom:0;left:0;position:static;right:0;top:0;z-index:20}.jupyter-wrapper .bp3-overlay:not(.bp3-overlay-open){pointer-events:none}.jupyter-wrapper .bp3-overlay.bp3-overlay-container{overflow:hidden;position:fixed}.jupyter-wrapper .bp3-overlay.bp3-overlay-container.bp3-overlay-inline{position:absolute}.jupyter-wrapper .bp3-overlay.bp3-overlay-scroll-container{overflow:auto;position:fixed}.jupyter-wrapper .bp3-overlay.bp3-overlay-scroll-container.bp3-overlay-inline{position:absolute}.jupyter-wrapper .bp3-overlay.bp3-overlay-inline{display:inline;overflow:visible}.jupyter-wrapper .bp3-overlay-content{position:fixed;z-index:20}.jupyter-wrapper .bp3-overlay-inline .bp3-overlay-content,.jupyter-wrapper .bp3-overlay-scroll-container .bp3-overlay-content{position:absolute}.jupyter-wrapper .bp3-overlay-backdrop{bottom:0;left:0;position:fixed;right:0;top:0;opacity:1;background-color:#10161ab3;overflow:auto;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:20}.jupyter-wrapper .bp3-overlay-backdrop.bp3-overlay-enter,.jupyter-wrapper .bp3-overlay-backdrop.bp3-overlay-appear{opacity:0}.jupyter-wrapper .bp3-overlay-backdrop.bp3-overlay-enter-active,.jupyter-wrapper .bp3-overlay-backdrop.bp3-overlay-appear-active{opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-overlay-backdrop.bp3-overlay-exit{opacity:1}.jupyter-wrapper .bp3-overlay-backdrop.bp3-overlay-exit-active{opacity:0;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-overlay-backdrop:focus{outline:none}.jupyter-wrapper .bp3-overlay-inline .bp3-overlay-backdrop{position:absolute}.jupyter-wrapper .bp3-panel-stack{overflow:hidden;position:relative}.jupyter-wrapper .bp3-panel-stack-header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 1px rgba(16,22,26,.15);box-shadow:0 1px #10161a26;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-negative:0;flex-shrink:0;height:30px;z-index:1}.jupyter-wrapper .bp3-dark .bp3-panel-stack-header{-webkit-box-shadow:0 1px rgba(255,255,255,.15);box-shadow:0 1px #ffffff26}.jupyter-wrapper .bp3-panel-stack-header>span{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1}.jupyter-wrapper .bp3-panel-stack-header .bp3-heading{margin:0 5px}.jupyter-wrapper .bp3-button.bp3-panel-stack-header-back{margin-left:5px;padding-left:0;white-space:nowrap}.jupyter-wrapper .bp3-button.bp3-panel-stack-header-back .bp3-icon{margin:0 2px}.jupyter-wrapper .bp3-panel-stack-view{bottom:0;left:0;position:absolute;right:0;top:0;background-color:#fff;border-right:1px solid rgba(16,22,26,.15);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:-1px;overflow-y:auto;z-index:1}.jupyter-wrapper .bp3-dark .bp3-panel-stack-view{background-color:#30404d}.jupyter-wrapper .bp3-panel-stack-view:nth-last-child(n+4){display:none}.jupyter-wrapper .bp3-panel-stack-push .bp3-panel-stack-enter,.jupyter-wrapper .bp3-panel-stack-push .bp3-panel-stack-appear{-webkit-transform:translateX(100%);transform:translate(100%);opacity:0}.jupyter-wrapper .bp3-panel-stack-push .bp3-panel-stack-enter-active,.jupyter-wrapper .bp3-panel-stack-push .bp3-panel-stack-appear-active{-webkit-transform:translate(0%);transform:translate(0);opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack-push .bp3-panel-stack-exit{-webkit-transform:translate(0%);transform:translate(0);opacity:1}.jupyter-wrapper .bp3-panel-stack-push .bp3-panel-stack-exit-active{-webkit-transform:translateX(-50%);transform:translate(-50%);opacity:0;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack-pop .bp3-panel-stack-enter,.jupyter-wrapper .bp3-panel-stack-pop .bp3-panel-stack-appear{-webkit-transform:translateX(-50%);transform:translate(-50%);opacity:0}.jupyter-wrapper .bp3-panel-stack-pop .bp3-panel-stack-enter-active,.jupyter-wrapper .bp3-panel-stack-pop .bp3-panel-stack-appear-active{-webkit-transform:translate(0%);transform:translate(0);opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack-pop .bp3-panel-stack-exit{-webkit-transform:translate(0%);transform:translate(0);opacity:1}.jupyter-wrapper .bp3-panel-stack-pop .bp3-panel-stack-exit-active{-webkit-transform:translateX(100%);transform:translate(100%);opacity:0;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack2{overflow:hidden;position:relative}.jupyter-wrapper .bp3-panel-stack2-header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-shadow:0 1px rgba(16,22,26,.15);box-shadow:0 1px #10161a26;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-negative:0;flex-shrink:0;height:30px;z-index:1}.jupyter-wrapper .bp3-dark .bp3-panel-stack2-header{-webkit-box-shadow:0 1px rgba(255,255,255,.15);box-shadow:0 1px #ffffff26}.jupyter-wrapper .bp3-panel-stack2-header>span{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1}.jupyter-wrapper .bp3-panel-stack2-header .bp3-heading{margin:0 5px}.jupyter-wrapper .bp3-button.bp3-panel-stack2-header-back{margin-left:5px;padding-left:0;white-space:nowrap}.jupyter-wrapper .bp3-button.bp3-panel-stack2-header-back .bp3-icon{margin:0 2px}.jupyter-wrapper .bp3-panel-stack2-view{bottom:0;left:0;position:absolute;right:0;top:0;background-color:#fff;border-right:1px solid rgba(16,22,26,.15);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:-1px;overflow-y:auto;z-index:1}.jupyter-wrapper .bp3-dark .bp3-panel-stack2-view{background-color:#30404d}.jupyter-wrapper .bp3-panel-stack2-view:nth-last-child(n+4){display:none}.jupyter-wrapper .bp3-panel-stack2-push .bp3-panel-stack2-enter,.jupyter-wrapper .bp3-panel-stack2-push .bp3-panel-stack2-appear{-webkit-transform:translateX(100%);transform:translate(100%);opacity:0}.jupyter-wrapper .bp3-panel-stack2-push .bp3-panel-stack2-enter-active,.jupyter-wrapper .bp3-panel-stack2-push .bp3-panel-stack2-appear-active{-webkit-transform:translate(0%);transform:translate(0);opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack2-push .bp3-panel-stack2-exit{-webkit-transform:translate(0%);transform:translate(0);opacity:1}.jupyter-wrapper .bp3-panel-stack2-push .bp3-panel-stack2-exit-active{-webkit-transform:translateX(-50%);transform:translate(-50%);opacity:0;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack2-pop .bp3-panel-stack2-enter,.jupyter-wrapper .bp3-panel-stack2-pop .bp3-panel-stack2-appear{-webkit-transform:translateX(-50%);transform:translate(-50%);opacity:0}.jupyter-wrapper .bp3-panel-stack2-pop .bp3-panel-stack2-enter-active,.jupyter-wrapper .bp3-panel-stack2-pop .bp3-panel-stack2-appear-active{-webkit-transform:translate(0%);transform:translate(0);opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-panel-stack2-pop .bp3-panel-stack2-exit{-webkit-transform:translate(0%);transform:translate(0);opacity:1}.jupyter-wrapper .bp3-panel-stack2-pop .bp3-panel-stack2-exit-active{-webkit-transform:translateX(100%);transform:translate(100%);opacity:0;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.4s;transition-duration:.4s;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-transition-timing-function:ease;transition-timing-function:ease}.jupyter-wrapper .bp3-popover{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33;-webkit-transform:scale(1);transform:scale(1);border-radius:3px;display:inline-block;z-index:20}.jupyter-wrapper .bp3-popover .bp3-popover-arrow{height:30px;position:absolute;width:30px}.jupyter-wrapper .bp3-popover .bp3-popover-arrow:before{height:20px;margin:5px;width:20px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-popover{margin-bottom:17px;margin-top:-17px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-popover>.bp3-popover-arrow{bottom:-11px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-popover>.bp3-popover-arrow svg{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-popover{margin-left:17px}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-popover>.bp3-popover-arrow{left:-11px}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-popover>.bp3-popover-arrow svg{-webkit-transform:rotate(0);transform:rotate(0)}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-popover{margin-top:17px}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-popover>.bp3-popover-arrow{top:-11px}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-popover>.bp3-popover-arrow svg{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-popover{margin-left:-17px;margin-right:17px}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-popover>.bp3-popover-arrow{right:-11px}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-popover>.bp3-popover-arrow svg{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.jupyter-wrapper .bp3-tether-element-attached-middle>.bp3-popover>.bp3-popover-arrow{top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.jupyter-wrapper .bp3-tether-element-attached-center>.bp3-popover>.bp3-popover-arrow{right:50%;-webkit-transform:translateX(50%);transform:translate(50%)}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-top>.bp3-popover>.bp3-popover-arrow{top:-.3934px}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-right>.bp3-popover>.bp3-popover-arrow{right:-.3934px}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-left>.bp3-popover>.bp3-popover-arrow{left:-.3934px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-bottom>.bp3-popover>.bp3-popover-arrow{bottom:-.3934px}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-element-attached-left>.bp3-popover{-webkit-transform-origin:top left;transform-origin:top left}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-element-attached-center>.bp3-popover{-webkit-transform-origin:top center;transform-origin:top center}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-element-attached-right>.bp3-popover{-webkit-transform-origin:top right;transform-origin:top right}.jupyter-wrapper .bp3-tether-element-attached-middle.bp3-tether-element-attached-left>.bp3-popover{-webkit-transform-origin:center left;transform-origin:center left}.jupyter-wrapper .bp3-tether-element-attached-middle.bp3-tether-element-attached-center>.bp3-popover{-webkit-transform-origin:center center;transform-origin:center center}.jupyter-wrapper .bp3-tether-element-attached-middle.bp3-tether-element-attached-right>.bp3-popover{-webkit-transform-origin:center right;transform-origin:center right}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-element-attached-left>.bp3-popover{-webkit-transform-origin:bottom left;transform-origin:bottom left}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-element-attached-center>.bp3-popover{-webkit-transform-origin:bottom center;transform-origin:bottom center}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-element-attached-right>.bp3-popover{-webkit-transform-origin:bottom right;transform-origin:bottom right}.jupyter-wrapper .bp3-popover .bp3-popover-content{background:#ffffff;color:inherit}.jupyter-wrapper .bp3-popover .bp3-popover-arrow:before{-webkit-box-shadow:1px 1px 6px rgba(16,22,26,.2);box-shadow:1px 1px 6px #10161a33}.jupyter-wrapper .bp3-popover .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.1}.jupyter-wrapper .bp3-popover .bp3-popover-arrow-fill{fill:#fff}.jupyter-wrapper .bp3-popover-enter>.bp3-popover,.jupyter-wrapper .bp3-popover-appear>.bp3-popover{-webkit-transform:scale(.3);transform:scale(.3)}.jupyter-wrapper .bp3-popover-enter-active>.bp3-popover,.jupyter-wrapper .bp3-popover-appear-active>.bp3-popover{-webkit-transform:scale(1);transform:scale(1);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.54,1.12,.38,1.11);transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.jupyter-wrapper .bp3-popover-exit>.bp3-popover{-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-popover-exit-active>.bp3-popover{-webkit-transform:scale(.3);transform:scale(.3);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.54,1.12,.38,1.11);transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.jupyter-wrapper .bp3-popover .bp3-popover-content{border-radius:3px;position:relative}.jupyter-wrapper .bp3-popover.bp3-popover-content-sizing .bp3-popover-content{max-width:350px;padding:20px}.jupyter-wrapper .bp3-popover-target+.bp3-overlay .bp3-popover.bp3-popover-content-sizing{width:350px}.jupyter-wrapper .bp3-popover.bp3-minimal{margin:0!important}.jupyter-wrapper .bp3-popover.bp3-minimal .bp3-popover-arrow{display:none}.jupyter-wrapper .bp3-popover.bp3-minimal.bp3-popover{-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-popover-enter>.bp3-popover.bp3-minimal.bp3-popover,.jupyter-wrapper .bp3-popover-appear>.bp3-popover.bp3-minimal.bp3-popover{-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-popover-enter-active>.bp3-popover.bp3-minimal.bp3-popover,.jupyter-wrapper .bp3-popover-appear-active>.bp3-popover.bp3-minimal.bp3-popover{-webkit-transform:scale(1);transform:scale(1);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-popover-exit>.bp3-popover.bp3-minimal.bp3-popover{-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-popover-exit-active>.bp3-popover.bp3-minimal.bp3-popover{-webkit-transform:scale(1);transform:scale(1);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-popover.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-popover{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66}.jupyter-wrapper .bp3-popover.bp3-dark .bp3-popover-content,.jupyter-wrapper .bp3-dark .bp3-popover .bp3-popover-content{background:#30404d;color:inherit}.jupyter-wrapper .bp3-popover.bp3-dark .bp3-popover-arrow:before,.jupyter-wrapper .bp3-dark .bp3-popover .bp3-popover-arrow:before{-webkit-box-shadow:1px 1px 6px rgba(16,22,26,.4);box-shadow:1px 1px 6px #10161a66}.jupyter-wrapper .bp3-popover.bp3-dark .bp3-popover-arrow-border,.jupyter-wrapper .bp3-dark .bp3-popover .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.2}.jupyter-wrapper .bp3-popover.bp3-dark .bp3-popover-arrow-fill,.jupyter-wrapper .bp3-dark .bp3-popover .bp3-popover-arrow-fill{fill:#30404d}.jupyter-wrapper .bp3-popover-arrow:before{border-radius:2px;content:\"\";display:block;position:absolute;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.jupyter-wrapper .bp3-tether-pinned .bp3-popover-arrow{display:none}.jupyter-wrapper .bp3-popover-backdrop{background:rgba(255,255,255,0)}.jupyter-wrapper .bp3-transition-container{opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;z-index:20}.jupyter-wrapper .bp3-transition-container.bp3-popover-enter,.jupyter-wrapper .bp3-transition-container.bp3-popover-appear{opacity:0}.jupyter-wrapper .bp3-transition-container.bp3-popover-enter-active,.jupyter-wrapper .bp3-transition-container.bp3-popover-appear-active{opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-transition-container.bp3-popover-exit{opacity:1}.jupyter-wrapper .bp3-transition-container.bp3-popover-exit-active{opacity:0;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-transition-container:focus{outline:none}.jupyter-wrapper .bp3-transition-container.bp3-popover-leave .bp3-popover-content{pointer-events:none}.jupyter-wrapper .bp3-transition-container[data-x-out-of-boundaries]{display:none}.jupyter-wrapper span.bp3-popover-target{display:inline-block}.jupyter-wrapper .bp3-popover-wrapper.bp3-fill{width:100%}.jupyter-wrapper .bp3-portal{left:0;position:absolute;right:0;top:0}@-webkit-keyframes linear-progress-bar-stripes{0%{background-position:0 0}to{background-position:30px 0}}@keyframes linear-progress-bar-stripes{0%{background-position:0 0}to{background-position:30px 0}}.jupyter-wrapper .bp3-progress-bar{background:rgba(92,112,128,.2);border-radius:40px;display:block;height:8px;overflow:hidden;position:relative;width:100%}.jupyter-wrapper .bp3-progress-bar .bp3-progress-meter{background:linear-gradient(-45deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%);background-color:#5c7080cc;background-size:30px 30px;border-radius:40px;height:100%;position:absolute;-webkit-transition:width .2s cubic-bezier(.4,1,.75,.9);transition:width .2s cubic-bezier(.4,1,.75,.9);width:100%}.jupyter-wrapper .bp3-progress-bar:not(.bp3-no-animation):not(.bp3-no-stripes) .bp3-progress-meter{animation:linear-progress-bar-stripes .3s linear infinite reverse}.jupyter-wrapper .bp3-progress-bar.bp3-no-stripes .bp3-progress-meter{background-image:none}.jupyter-wrapper .bp3-dark .bp3-progress-bar{background:rgba(16,22,26,.5)}.jupyter-wrapper .bp3-dark .bp3-progress-bar .bp3-progress-meter{background-color:#8a9ba8}.jupyter-wrapper .bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{background-color:#137cbd}.jupyter-wrapper .bp3-progress-bar.bp3-intent-success .bp3-progress-meter{background-color:#0f9960}.jupyter-wrapper .bp3-progress-bar.bp3-intent-warning .bp3-progress-meter{background-color:#d9822b}.jupyter-wrapper .bp3-progress-bar.bp3-intent-danger .bp3-progress-meter{background-color:#db3737}@-webkit-keyframes skeleton-glow{0%{background:rgba(206,217,224,.2);border-color:#ced9e033}to{background:rgba(92,112,128,.2);border-color:#5c708033}}@keyframes skeleton-glow{0%{background:rgba(206,217,224,.2);border-color:#ced9e033}to{background:rgba(92,112,128,.2);border-color:#5c708033}}.jupyter-wrapper .bp3-skeleton{-webkit-animation:1s linear infinite alternate skeleton-glow;animation:1s linear infinite alternate skeleton-glow;background:rgba(206,217,224,.2);background-clip:padding-box!important;border-color:#ced9e033!important;border-radius:2px;-webkit-box-shadow:none!important;box-shadow:none!important;color:transparent!important;cursor:default;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .bp3-skeleton:before,.jupyter-wrapper .bp3-skeleton:after,.jupyter-wrapper .bp3-skeleton *{visibility:hidden!important}.jupyter-wrapper .bp3-slider{height:40px;min-width:150px;width:100%;cursor:default;outline:none;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .bp3-slider:hover{cursor:pointer}.jupyter-wrapper .bp3-slider:active{cursor:-webkit-grabbing;cursor:grabbing}.jupyter-wrapper .bp3-slider.bp3-disabled{cursor:not-allowed;opacity:.5}.jupyter-wrapper .bp3-slider.bp3-slider-unlabeled{height:16px}.jupyter-wrapper .bp3-slider-track,.jupyter-wrapper .bp3-slider-progress{height:6px;left:0;right:0;top:5px;position:absolute}.jupyter-wrapper .bp3-slider-track{border-radius:3px;overflow:hidden}.jupyter-wrapper .bp3-slider-progress{background:rgba(92,112,128,.2)}.jupyter-wrapper .bp3-dark .bp3-slider-progress{background:rgba(16,22,26,.5)}.jupyter-wrapper .bp3-slider-progress.bp3-intent-primary{background-color:#137cbd}.jupyter-wrapper .bp3-slider-progress.bp3-intent-success{background-color:#0f9960}.jupyter-wrapper .bp3-slider-progress.bp3-intent-warning{background-color:#d9822b}.jupyter-wrapper .bp3-slider-progress.bp3-intent-danger{background-color:#db3737}.jupyter-wrapper .bp3-slider-handle{background-color:#f5f8fa;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.8)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.8),rgba(255,255,255,0));-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a;color:#182026;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a33,0 1px 1px #10161a33;cursor:pointer;height:16px;left:0;position:absolute;top:0;width:16px}.jupyter-wrapper .bp3-slider-handle:hover{background-clip:padding-box;background-color:#ebf1f5;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a}.jupyter-wrapper .bp3-slider-handle:active,.jupyter-wrapper .bp3-slider-handle.bp3-active{background-color:#d8e1e8;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-slider-handle:disabled,.jupyter-wrapper .bp3-slider-handle.bp3-disabled{background-color:#ced9e080;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#5c708099;cursor:not-allowed;outline:none}.jupyter-wrapper .bp3-slider-handle:disabled.bp3-active,.jupyter-wrapper .bp3-slider-handle:disabled.bp3-active:hover,.jupyter-wrapper .bp3-slider-handle.bp3-disabled.bp3-active,.jupyter-wrapper .bp3-slider-handle.bp3-disabled.bp3-active:hover{background:rgba(206,217,224,.7)}.jupyter-wrapper .bp3-slider-handle:focus{z-index:1}.jupyter-wrapper .bp3-slider-handle:hover{background-clip:padding-box;background-color:#ebf1f5;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 -1px 0 rgba(16,22,26,.1);box-shadow:inset 0 0 0 1px #10161a33,inset 0 -1px #10161a1a;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a33,0 1px 1px #10161a33;cursor:-webkit-grab;cursor:grab;z-index:2}.jupyter-wrapper .bp3-slider-handle.bp3-active{background-color:#d8e1e8;background-image:none;-webkit-box-shadow:inset 0 0 0 1px rgba(16,22,26,.2),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:inset 0 0 0 1px #10161a33,inset 0 1px 2px #10161a33;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),inset 0 1px 1px rgba(16,22,26,.1);box-shadow:0 0 0 1px #10161a33,inset 0 1px 1px #10161a1a;cursor:-webkit-grabbing;cursor:grabbing}.jupyter-wrapper .bp3-disabled .bp3-slider-handle{background:#bfccd6;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.jupyter-wrapper .bp3-dark .bp3-slider-handle{background-color:#394b59;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.05)),to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom,rgba(255,255,255,.05),rgba(255,255,255,0));-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66;color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-slider-handle:hover,.jupyter-wrapper .bp3-dark .bp3-slider-handle:active,.jupyter-wrapper .bp3-dark .bp3-slider-handle.bp3-active{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-slider-handle:hover{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-slider-handle:active,.jupyter-wrapper .bp3-dark .bp3-slider-handle.bp3-active{background-color:#202b33;background-image:none;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.6),inset 0 1px 2px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a99,inset 0 1px 2px #10161a33}.jupyter-wrapper .bp3-dark .bp3-slider-handle:disabled,.jupyter-wrapper .bp3-dark .bp3-slider-handle.bp3-disabled{background-color:#394b5980;background-image:none;-webkit-box-shadow:none;box-shadow:none;color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-slider-handle:disabled.bp3-active,.jupyter-wrapper .bp3-dark .bp3-slider-handle.bp3-disabled.bp3-active{background:rgba(57,75,89,.7)}.jupyter-wrapper .bp3-dark .bp3-slider-handle .bp3-button-spinner .bp3-spinner-head{background:rgba(16,22,26,.5);stroke:#8a9ba8}.jupyter-wrapper .bp3-dark .bp3-slider-handle,.jupyter-wrapper .bp3-dark .bp3-slider-handle:hover{background-color:#394b59}.jupyter-wrapper .bp3-dark .bp3-slider-handle.bp3-active{background-color:#293742}.jupyter-wrapper .bp3-dark .bp3-disabled .bp3-slider-handle{background:#5c7080;border-color:#5c7080;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-slider-handle .bp3-slider-label{background:#394b59;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33;color:#f5f8fa;margin-left:8px}.jupyter-wrapper .bp3-dark .bp3-slider-handle .bp3-slider-label{background:#e1e8ed;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66;color:#394b59}.jupyter-wrapper .bp3-disabled .bp3-slider-handle .bp3-slider-label{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-slider-handle.bp3-start,.jupyter-wrapper .bp3-slider-handle.bp3-end{width:8px}.jupyter-wrapper .bp3-slider-handle.bp3-start{border-bottom-right-radius:0;border-top-right-radius:0}.jupyter-wrapper .bp3-slider-handle.bp3-end{border-bottom-left-radius:0;border-top-left-radius:0;margin-left:8px}.jupyter-wrapper .bp3-slider-handle.bp3-end .bp3-slider-label{margin-left:0}.jupyter-wrapper .bp3-slider-label{-webkit-transform:translate(-50%,20px);transform:translate(-50%,20px);display:inline-block;font-size:12px;line-height:1;padding:2px 5px;position:absolute;vertical-align:top}.jupyter-wrapper .bp3-slider.bp3-vertical{height:150px;min-width:40px;width:40px}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-track,.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-progress{bottom:0;height:auto;left:5px;top:0;width:6px}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-progress{top:auto}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-label{-webkit-transform:translate(20px,50%);transform:translate(20px,50%)}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle{top:auto}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle .bp3-slider-label{margin-left:0;margin-top:-8px}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle.bp3-end,.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle.bp3-start{height:8px;margin-left:0;width:16px}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle.bp3-start{border-bottom-right-radius:3px;border-top-left-radius:0}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle.bp3-start .bp3-slider-label{-webkit-transform:translate(20px);transform:translate(20px)}.jupyter-wrapper .bp3-slider.bp3-vertical .bp3-slider-handle.bp3-end{border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:3px;margin-bottom:8px}@-webkit-keyframes pt-spinner-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes pt-spinner-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.jupyter-wrapper .bp3-spinner{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;overflow:visible;vertical-align:middle}.jupyter-wrapper .bp3-spinner svg{display:block}.jupyter-wrapper .bp3-spinner path{fill-opacity:0}.jupyter-wrapper .bp3-spinner .bp3-spinner-head{stroke:#5c7080cc;stroke-linecap:round;-webkit-transform-origin:center;transform-origin:center;-webkit-transition:stroke-dashoffset .2s cubic-bezier(.4,1,.75,.9);transition:stroke-dashoffset .2s cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-spinner .bp3-spinner-track{stroke:#5c708033}.jupyter-wrapper .bp3-spinner-animation{-webkit-animation:pt-spinner-animation .5s linear infinite;animation:pt-spinner-animation .5s linear infinite}.jupyter-wrapper .bp3-no-spin>.bp3-spinner-animation{-webkit-animation:none;animation:none}.jupyter-wrapper .bp3-dark .bp3-spinner .bp3-spinner-head{stroke:#8a9ba8}.jupyter-wrapper .bp3-dark .bp3-spinner .bp3-spinner-track{stroke:#10161a80}.jupyter-wrapper .bp3-spinner.bp3-intent-primary .bp3-spinner-head{stroke:#137cbd}.jupyter-wrapper .bp3-spinner.bp3-intent-success .bp3-spinner-head{stroke:#0f9960}.jupyter-wrapper .bp3-spinner.bp3-intent-warning .bp3-spinner-head{stroke:#d9822b}.jupyter-wrapper .bp3-spinner.bp3-intent-danger .bp3-spinner-head{stroke:#db3737}.jupyter-wrapper .bp3-tabs.bp3-vertical{display:-webkit-box;display:-ms-flexbox;display:flex}.jupyter-wrapper .bp3-tabs.bp3-vertical>.bp3-tab-list{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.jupyter-wrapper .bp3-tabs.bp3-vertical>.bp3-tab-list .bp3-tab{border-radius:3px;padding:0 10px;width:100%}.jupyter-wrapper .bp3-tabs.bp3-vertical>.bp3-tab-list .bp3-tab[aria-selected=true]{background-color:#137cbd33;-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-tabs.bp3-vertical>.bp3-tab-list .bp3-tab-indicator-wrapper .bp3-tab-indicator{background-color:#137cbd33;border-radius:3px;bottom:0;height:auto;left:0;right:0;top:0}.jupyter-wrapper .bp3-tabs.bp3-vertical>.bp3-tab-panel{margin-top:0;padding-left:20px}.jupyter-wrapper .bp3-tab-list{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;border:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;list-style:none;margin:0;padding:0;position:relative}.jupyter-wrapper .bp3-tab-list>*:not(:last-child){margin-right:20px}.jupyter-wrapper .bp3-tab{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;color:#182026;cursor:pointer;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;font-size:14px;line-height:30px;max-width:100%;position:relative;vertical-align:top}.jupyter-wrapper .bp3-tab a{color:inherit;display:block;text-decoration:none}.jupyter-wrapper .bp3-tab-indicator-wrapper~.bp3-tab{background-color:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}.jupyter-wrapper .bp3-tab[aria-disabled=true]{color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-tab[aria-selected=true]{border-radius:0;-webkit-box-shadow:inset 0 -3px 0 #106ba3;box-shadow:inset 0 -3px #106ba3}.jupyter-wrapper .bp3-tab[aria-selected=true],.jupyter-wrapper .bp3-tab:not([aria-disabled=true]):hover{color:#106ba3}.jupyter-wrapper .bp3-tab:focus{-moz-outline-radius:0}.jupyter-wrapper .bp3-large>.bp3-tab{font-size:16px;line-height:40px}.jupyter-wrapper .bp3-tab-panel{margin-top:20px}.jupyter-wrapper .bp3-tab-panel[aria-hidden=true]{display:none}.jupyter-wrapper .bp3-tab-indicator-wrapper{left:0;pointer-events:none;position:absolute;top:0;-webkit-transform:translateX(0),translateY(0);transform:translate(0),translateY(0);-webkit-transition:height,width,-webkit-transform;transition:height,width,-webkit-transform;transition:height,transform,width;transition:height,transform,width,-webkit-transform;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-tab-indicator-wrapper .bp3-tab-indicator{background-color:#106ba3;bottom:0;height:3px;left:0;position:absolute;right:0}.jupyter-wrapper .bp3-tab-indicator-wrapper.bp3-no-animation{-webkit-transition:none;transition:none}.jupyter-wrapper .bp3-dark .bp3-tab{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-tab[aria-disabled=true]{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-tab[aria-selected=true]{-webkit-box-shadow:inset 0 -3px 0 #48aff0;box-shadow:inset 0 -3px #48aff0}.jupyter-wrapper .bp3-dark .bp3-tab[aria-selected=true],.jupyter-wrapper .bp3-dark .bp3-tab:not([aria-disabled=true]):hover{color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-tab-indicator{background-color:#48aff0}.jupyter-wrapper .bp3-flex-expander{-webkit-box-flex:1;-ms-flex:1 1;flex:1 1}.jupyter-wrapper .bp3-tag{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#5c7080;border:none;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;color:#f5f8fa;font-size:12px;line-height:16px;max-width:100%;min-height:20px;min-width:20px;padding:2px 6px;position:relative}.jupyter-wrapper .bp3-tag.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-interactive:hover{background-color:#5c7080d9}.jupyter-wrapper .bp3-tag.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-interactive:active{background-color:#5c7080b3}.jupyter-wrapper .bp3-tag>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-tag>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-tag:before,.jupyter-wrapper .bp3-tag>*{margin-right:4px}.jupyter-wrapper .bp3-tag:empty:before,.jupyter-wrapper .bp3-tag>:last-child{margin-right:0}.jupyter-wrapper .bp3-tag:focus{outline:rgba(19,124,189,.6) auto 2px;outline-offset:0;-moz-outline-radius:6px}.jupyter-wrapper .bp3-tag.bp3-round{border-radius:30px;padding-left:8px;padding-right:8px}.jupyter-wrapper .bp3-dark .bp3-tag{background-color:#bfccd6;color:#182026}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-interactive:hover{background-color:#bfccd6d9}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-interactive:active{background-color:#bfccd6b3}.jupyter-wrapper .bp3-dark .bp3-tag>.bp3-icon,.jupyter-wrapper .bp3-dark .bp3-tag .bp3-icon-standard,.jupyter-wrapper .bp3-dark .bp3-tag .bp3-icon-large{fill:currentColor}.jupyter-wrapper .bp3-tag>.bp3-icon,.jupyter-wrapper .bp3-tag .bp3-icon-standard,.jupyter-wrapper .bp3-tag .bp3-icon-large{fill:#fff}.jupyter-wrapper .bp3-tag.bp3-large,.jupyter-wrapper .bp3-large .bp3-tag{font-size:14px;line-height:20px;min-height:30px;min-width:30px;padding:5px 10px}.jupyter-wrapper .bp3-tag.bp3-large:before,.jupyter-wrapper .bp3-tag.bp3-large>*,.jupyter-wrapper .bp3-large .bp3-tag:before,.jupyter-wrapper .bp3-large .bp3-tag>*{margin-right:7px}.jupyter-wrapper .bp3-tag.bp3-large:empty:before,.jupyter-wrapper .bp3-tag.bp3-large>:last-child,.jupyter-wrapper .bp3-large .bp3-tag:empty:before,.jupyter-wrapper .bp3-large .bp3-tag>:last-child{margin-right:0}.jupyter-wrapper .bp3-tag.bp3-large.bp3-round,.jupyter-wrapper .bp3-large .bp3-tag.bp3-round{padding-left:12px;padding-right:12px}.jupyter-wrapper .bp3-tag.bp3-intent-primary{background:#137cbd;color:#fff}.jupyter-wrapper .bp3-tag.bp3-intent-primary.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-intent-primary.bp3-interactive:hover{background-color:#137cbdd9}.jupyter-wrapper .bp3-tag.bp3-intent-primary.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-intent-primary.bp3-interactive:active{background-color:#137cbdb3}.jupyter-wrapper .bp3-tag.bp3-intent-success{background:#0f9960;color:#fff}.jupyter-wrapper .bp3-tag.bp3-intent-success.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-intent-success.bp3-interactive:hover{background-color:#0f9960d9}.jupyter-wrapper .bp3-tag.bp3-intent-success.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-intent-success.bp3-interactive:active{background-color:#0f9960b3}.jupyter-wrapper .bp3-tag.bp3-intent-warning{background:#d9822b;color:#fff}.jupyter-wrapper .bp3-tag.bp3-intent-warning.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-intent-warning.bp3-interactive:hover{background-color:#d9822bd9}.jupyter-wrapper .bp3-tag.bp3-intent-warning.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-intent-warning.bp3-interactive:active{background-color:#d9822bb3}.jupyter-wrapper .bp3-tag.bp3-intent-danger{background:#db3737;color:#fff}.jupyter-wrapper .bp3-tag.bp3-intent-danger.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-intent-danger.bp3-interactive:hover{background-color:#db3737d9}.jupyter-wrapper .bp3-tag.bp3-intent-danger.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-intent-danger.bp3-interactive:active{background-color:#db3737b3}.jupyter-wrapper .bp3-tag.bp3-fill{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%}.jupyter-wrapper .bp3-tag.bp3-minimal>.bp3-icon,.jupyter-wrapper .bp3-tag.bp3-minimal .bp3-icon-standard,.jupyter-wrapper .bp3-tag.bp3-minimal .bp3-icon-large{fill:#5c7080}.jupyter-wrapper .bp3-tag.bp3-minimal:not([class*=bp3-intent-]){background-color:#8a9ba833;color:#182026}.jupyter-wrapper .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:hover{background-color:#5c70804d}.jupyter-wrapper .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:active{background-color:#5c708066}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]){color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:hover{background-color:#bfccd64d}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive.bp3-active,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]).bp3-interactive:active{background-color:#bfccd666}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-])>.bp3-icon,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]) .bp3-icon-standard,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal:not([class*=bp3-intent-]) .bp3-icon-large{fill:#a7b6c2}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary{background-color:#137cbd26;color:#106ba3}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:hover{background-color:#137cbd40}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:active{background-color:#137cbd59}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary>.bp3-icon,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary .bp3-icon-standard,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-primary .bp3-icon-large{fill:#137cbd}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary{background-color:#137cbd40;color:#48aff0}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:hover{background-color:#137cbd59}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-primary.bp3-interactive:active{background-color:#137cbd73}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success{background-color:#0f996026;color:#0d8050}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:hover{background-color:#0f996040}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:active{background-color:#0f996059}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success>.bp3-icon,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success .bp3-icon-standard,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-success .bp3-icon-large{fill:#0f9960}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success{background-color:#0f996040;color:#3dcc91}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:hover{background-color:#0f996059}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-success.bp3-interactive:active{background-color:#0f996073}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning{background-color:#d9822b26;color:#bf7326}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:hover{background-color:#d9822b40}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:active{background-color:#d9822b59}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning>.bp3-icon,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning .bp3-icon-standard,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-warning .bp3-icon-large{fill:#d9822b}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning{background-color:#d9822b40;color:#ffb366}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:hover{background-color:#d9822b59}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-warning.bp3-interactive:active{background-color:#d9822b73}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger{background-color:#db373726;color:#c23030}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:hover{background-color:#db373740}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:active{background-color:#db373759}.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger>.bp3-icon,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger .bp3-icon-standard,.jupyter-wrapper .bp3-tag.bp3-minimal.bp3-intent-danger .bp3-icon-large{fill:#db3737}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger{background-color:#db373740;color:#ff7373}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive{cursor:pointer}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:hover{background-color:#db373759}.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive.bp3-active,.jupyter-wrapper .bp3-dark .bp3-tag.bp3-minimal.bp3-intent-danger.bp3-interactive:active{background-color:#db373773}.jupyter-wrapper .bp3-tag-remove{background:none;border:none;color:inherit;cursor:pointer;display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:-2px;margin-right:-6px!important;margin-top:-2px;opacity:.5;padding:2px 2px 2px 0}.jupyter-wrapper .bp3-tag-remove:hover{background:none;opacity:.8;text-decoration:none}.jupyter-wrapper .bp3-tag-remove:active{opacity:1}.jupyter-wrapper .bp3-tag-remove:empty:before{font-family:Icons16,sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;content:\"\ue6d7\"}.jupyter-wrapper .bp3-large .bp3-tag-remove{margin-right:-10px!important;padding:0 5px 0 0}.jupyter-wrapper .bp3-large .bp3-tag-remove:empty:before{font-family:Icons20,sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1}.jupyter-wrapper .bp3-tag-input{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;cursor:text;height:auto;line-height:inherit;min-height:30px;padding-left:5px;padding-right:0}.jupyter-wrapper .bp3-tag-input>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-tag-input>.bp3-tag-input-values{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-icon{color:#5c7080;margin-left:2px;margin-right:7px;margin-top:7px}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-item-align:stretch;align-self:stretch;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:7px;margin-top:5px;min-width:0}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values>*{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values>.bp3-fill{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values:before,.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values>*{margin-right:5px}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values:empty:before,.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values>:last-child{margin-right:0}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values:first-child .bp3-input-ghost:first-child{padding-left:5px}.jupyter-wrapper .bp3-tag-input .bp3-tag-input-values>*{margin-bottom:5px}.jupyter-wrapper .bp3-tag-input .bp3-tag{overflow-wrap:break-word}.jupyter-wrapper .bp3-tag-input .bp3-tag.bp3-active{outline:rgba(19,124,189,.6) auto 2px;outline-offset:0;-moz-outline-radius:6px}.jupyter-wrapper .bp3-tag-input .bp3-input-ghost{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;line-height:20px;width:80px}.jupyter-wrapper .bp3-tag-input .bp3-input-ghost:disabled,.jupyter-wrapper .bp3-tag-input .bp3-input-ghost.bp3-disabled{cursor:not-allowed}.jupyter-wrapper .bp3-tag-input .bp3-button,.jupyter-wrapper .bp3-tag-input .bp3-spinner{margin:3px 3px 3px 0}.jupyter-wrapper .bp3-tag-input .bp3-button{min-height:24px;min-width:24px;padding:0 7px}.jupyter-wrapper .bp3-tag-input.bp3-large{height:auto;min-height:40px}.jupyter-wrapper .bp3-tag-input.bp3-large:before,.jupyter-wrapper .bp3-tag-input.bp3-large>*{margin-right:10px}.jupyter-wrapper .bp3-tag-input.bp3-large:empty:before,.jupyter-wrapper .bp3-tag-input.bp3-large>:last-child{margin-right:0}.jupyter-wrapper .bp3-tag-input.bp3-large .bp3-tag-input-icon{margin-left:5px;margin-top:10px}.jupyter-wrapper .bp3-tag-input.bp3-large .bp3-input-ghost{line-height:30px}.jupyter-wrapper .bp3-tag-input.bp3-large .bp3-button{min-height:30px;min-width:30px;padding:5px 10px;margin:5px 5px 5px 0}.jupyter-wrapper .bp3-tag-input.bp3-large .bp3-spinner{margin:8px 8px 8px 0}.jupyter-wrapper .bp3-tag-input.bp3-active{background-color:#fff;-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-tag-input.bp3-active.bp3-intent-primary{-webkit-box-shadow:0 0 0 1px #106ba3,0 0 0 3px rgba(16,107,163,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #106ba3,0 0 0 3px #106ba34d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-tag-input.bp3-active.bp3-intent-success{-webkit-box-shadow:0 0 0 1px #0d8050,0 0 0 3px rgba(13,128,80,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #0d8050,0 0 0 3px #0d80504d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-tag-input.bp3-active.bp3-intent-warning{-webkit-box-shadow:0 0 0 1px #bf7326,0 0 0 3px rgba(191,115,38,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #bf7326,0 0 0 3px #bf73264d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-tag-input.bp3-active.bp3-intent-danger{-webkit-box-shadow:0 0 0 1px #c23030,0 0 0 3px rgba(194,48,48,.3),inset 0 1px 1px rgba(16,22,26,.2);box-shadow:0 0 0 1px #c23030,0 0 0 3px #c230304d,inset 0 1px 1px #10161a33}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-tag-input-icon,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-tag-input-icon{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-input-ghost,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-input-ghost{color:#f5f8fa}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-input-ghost::-webkit-input-placeholder,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-input-ghost::-webkit-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-input-ghost::-moz-placeholder,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-input-ghost::-moz-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-input-ghost:-ms-input-placeholder,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-input-ghost:-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-input-ghost::-ms-input-placeholder,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-input-ghost::-ms-input-placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-tag-input .bp3-input-ghost::placeholder,.jupyter-wrapper .bp3-tag-input.bp3-dark .bp3-input-ghost::placeholder{color:#a7b6c299}.jupyter-wrapper .bp3-dark .bp3-tag-input.bp3-active,.jupyter-wrapper .bp3-tag-input.bp3-dark.bp3-active{background-color:#10161a4d;-webkit-box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px rgba(19,124,189,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #137cbd,0 0 0 1px #137cbd,0 0 0 3px #137cbd4d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-tag-input.bp3-active.bp3-intent-primary,.jupyter-wrapper .bp3-tag-input.bp3-dark.bp3-active.bp3-intent-primary{-webkit-box-shadow:0 0 0 1px #106ba3,0 0 0 3px rgba(16,107,163,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #106ba3,0 0 0 3px #106ba34d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-tag-input.bp3-active.bp3-intent-success,.jupyter-wrapper .bp3-tag-input.bp3-dark.bp3-active.bp3-intent-success{-webkit-box-shadow:0 0 0 1px #0d8050,0 0 0 3px rgba(13,128,80,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #0d8050,0 0 0 3px #0d80504d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-tag-input.bp3-active.bp3-intent-warning,.jupyter-wrapper .bp3-tag-input.bp3-dark.bp3-active.bp3-intent-warning{-webkit-box-shadow:0 0 0 1px #bf7326,0 0 0 3px rgba(191,115,38,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #bf7326,0 0 0 3px #bf73264d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-dark .bp3-tag-input.bp3-active.bp3-intent-danger,.jupyter-wrapper .bp3-tag-input.bp3-dark.bp3-active.bp3-intent-danger{-webkit-box-shadow:0 0 0 1px #c23030,0 0 0 3px rgba(194,48,48,.3),inset 0 0 0 1px rgba(16,22,26,.3),inset 0 1px 1px rgba(16,22,26,.4);box-shadow:0 0 0 1px #c23030,0 0 0 3px #c230304d,inset 0 0 0 1px #10161a4d,inset 0 1px 1px #10161a66}.jupyter-wrapper .bp3-input-ghost{background:none;border:none;-webkit-box-shadow:none;box-shadow:none;padding:0}.jupyter-wrapper .bp3-input-ghost::-webkit-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input-ghost::-moz-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input-ghost:-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input-ghost::-ms-input-placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input-ghost::placeholder{color:#5c708099;opacity:1}.jupyter-wrapper .bp3-input-ghost:focus{outline:none!important}.jupyter-wrapper .bp3-toast{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;background-color:#fff;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33;display:-webkit-box;display:-ms-flexbox;display:flex;margin:20px 0 0;max-width:500px;min-width:300px;pointer-events:all;position:relative!important}.jupyter-wrapper .bp3-toast.bp3-toast-enter,.jupyter-wrapper .bp3-toast.bp3-toast-appear{-webkit-transform:translateY(-40px);transform:translateY(-40px)}.jupyter-wrapper .bp3-toast.bp3-toast-enter-active,.jupyter-wrapper .bp3-toast.bp3-toast-appear-active{-webkit-transform:translateY(0);transform:translateY(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.54,1.12,.38,1.11);transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.jupyter-wrapper .bp3-toast.bp3-toast-enter~.bp3-toast,.jupyter-wrapper .bp3-toast.bp3-toast-appear~.bp3-toast{-webkit-transform:translateY(-40px);transform:translateY(-40px)}.jupyter-wrapper .bp3-toast.bp3-toast-enter-active~.bp3-toast,.jupyter-wrapper .bp3-toast.bp3-toast-appear-active~.bp3-toast{-webkit-transform:translateY(0);transform:translateY(0);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.54,1.12,.38,1.11);transition-timing-function:cubic-bezier(.54,1.12,.38,1.11)}.jupyter-wrapper .bp3-toast.bp3-toast-exit{opacity:1;-webkit-filter:blur(0);filter:blur(0)}.jupyter-wrapper .bp3-toast.bp3-toast-exit-active{opacity:0;-webkit-filter:blur(10px);filter:blur(10px);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:opacity,-webkit-filter;transition-property:opacity,-webkit-filter;transition-property:opacity,filter;transition-property:opacity,filter,-webkit-filter;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-toast.bp3-toast-exit~.bp3-toast{-webkit-transform:translateY(0);transform:translateY(0)}.jupyter-wrapper .bp3-toast.bp3-toast-exit-active~.bp3-toast{-webkit-transform:translateY(-40px);transform:translateY(-40px);-webkit-transition-delay:50ms;transition-delay:50ms;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-toast .bp3-button-group{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding:5px 5px 5px 0}.jupyter-wrapper .bp3-toast>.bp3-icon{color:#5c7080;margin:12px 0 12px 12px}.jupyter-wrapper .bp3-toast.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-toast{background-color:#394b59;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66}.jupyter-wrapper .bp3-toast.bp3-dark>.bp3-icon,.jupyter-wrapper .bp3-dark .bp3-toast>.bp3-icon{color:#a7b6c2}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] a{color:#ffffffb3}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] a:hover{color:#fff}.jupyter-wrapper .bp3-toast[class*=bp3-intent-]>.bp3-icon{color:#fff}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button,.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button:before,.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button .bp3-icon,.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button:active{color:#ffffffb3!important}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button:focus{outline-color:#ffffff80}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button:hover{background-color:#ffffff26!important;color:#fff!important}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button:active{background-color:#ffffff4d!important;color:#fff!important}.jupyter-wrapper .bp3-toast[class*=bp3-intent-] .bp3-button:after{background:rgba(255,255,255,.3)!important}.jupyter-wrapper .bp3-toast.bp3-intent-primary{background-color:#137cbd;color:#fff}.jupyter-wrapper .bp3-toast.bp3-intent-success{background-color:#0f9960;color:#fff}.jupyter-wrapper .bp3-toast.bp3-intent-warning{background-color:#d9822b;color:#fff}.jupyter-wrapper .bp3-toast.bp3-intent-danger{background-color:#db3737;color:#fff}.jupyter-wrapper .bp3-toast-message{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:11px;word-break:break-word}.jupyter-wrapper .bp3-toast-container{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;left:0;overflow:hidden;padding:0 20px 20px;pointer-events:none;right:0;z-index:40}.jupyter-wrapper .bp3-toast-container.bp3-toast-container-in-portal{position:fixed}.jupyter-wrapper .bp3-toast-container.bp3-toast-container-inline{position:absolute}.jupyter-wrapper .bp3-toast-container.bp3-toast-container-top{top:0}.jupyter-wrapper .bp3-toast-container.bp3-toast-container-bottom{bottom:0;-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse;top:auto}.jupyter-wrapper .bp3-toast-container.bp3-toast-container-left{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.jupyter-wrapper .bp3-toast-container.bp3-toast-container-right{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.jupyter-wrapper .bp3-toast-container-bottom .bp3-toast.bp3-toast-enter:not(.bp3-toast-enter-active),.jupyter-wrapper .bp3-toast-container-bottom .bp3-toast.bp3-toast-enter:not(.bp3-toast-enter-active)~.bp3-toast,.jupyter-wrapper .bp3-toast-container-bottom .bp3-toast.bp3-toast-appear:not(.bp3-toast-appear-active),.jupyter-wrapper .bp3-toast-container-bottom .bp3-toast.bp3-toast-appear:not(.bp3-toast-appear-active)~.bp3-toast,.jupyter-wrapper .bp3-toast-container-bottom .bp3-toast.bp3-toast-exit-active~.bp3-toast,.jupyter-wrapper .bp3-toast-container-bottom .bp3-toast.bp3-toast-leave-active~.bp3-toast{-webkit-transform:translateY(60px);transform:translateY(60px)}.jupyter-wrapper .bp3-tooltip{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 2px 4px rgba(16,22,26,.2),0 8px 24px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 2px 4px #10161a33,0 8px 24px #10161a33;-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-tooltip .bp3-popover-arrow{height:22px;position:absolute;width:22px}.jupyter-wrapper .bp3-tooltip .bp3-popover-arrow:before{height:14px;margin:4px;width:14px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-tooltip{margin-bottom:11px;margin-top:-11px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-tooltip>.bp3-popover-arrow{bottom:-8px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-top>.bp3-tooltip>.bp3-popover-arrow svg{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-tooltip{margin-left:11px}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-tooltip>.bp3-popover-arrow{left:-8px}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-right>.bp3-tooltip>.bp3-popover-arrow svg{-webkit-transform:rotate(0);transform:rotate(0)}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-tooltip{margin-top:11px}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-tooltip>.bp3-popover-arrow{top:-8px}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-bottom>.bp3-tooltip>.bp3-popover-arrow svg{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-tooltip{margin-left:-11px;margin-right:11px}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-tooltip>.bp3-popover-arrow{right:-8px}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-left>.bp3-tooltip>.bp3-popover-arrow svg{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.jupyter-wrapper .bp3-tether-element-attached-middle>.bp3-tooltip>.bp3-popover-arrow{top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.jupyter-wrapper .bp3-tether-element-attached-center>.bp3-tooltip>.bp3-popover-arrow{right:50%;-webkit-transform:translateX(50%);transform:translate(50%)}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-target-attached-top>.bp3-tooltip>.bp3-popover-arrow{top:-.22183px}.jupyter-wrapper .bp3-tether-element-attached-right.bp3-tether-target-attached-right>.bp3-tooltip>.bp3-popover-arrow{right:-.22183px}.jupyter-wrapper .bp3-tether-element-attached-left.bp3-tether-target-attached-left>.bp3-tooltip>.bp3-popover-arrow{left:-.22183px}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-target-attached-bottom>.bp3-tooltip>.bp3-popover-arrow{bottom:-.22183px}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-element-attached-left>.bp3-tooltip{-webkit-transform-origin:top left;transform-origin:top left}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-element-attached-center>.bp3-tooltip{-webkit-transform-origin:top center;transform-origin:top center}.jupyter-wrapper .bp3-tether-element-attached-top.bp3-tether-element-attached-right>.bp3-tooltip{-webkit-transform-origin:top right;transform-origin:top right}.jupyter-wrapper .bp3-tether-element-attached-middle.bp3-tether-element-attached-left>.bp3-tooltip{-webkit-transform-origin:center left;transform-origin:center left}.jupyter-wrapper .bp3-tether-element-attached-middle.bp3-tether-element-attached-center>.bp3-tooltip{-webkit-transform-origin:center center;transform-origin:center center}.jupyter-wrapper .bp3-tether-element-attached-middle.bp3-tether-element-attached-right>.bp3-tooltip{-webkit-transform-origin:center right;transform-origin:center right}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-element-attached-left>.bp3-tooltip{-webkit-transform-origin:bottom left;transform-origin:bottom left}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-element-attached-center>.bp3-tooltip{-webkit-transform-origin:bottom center;transform-origin:bottom center}.jupyter-wrapper .bp3-tether-element-attached-bottom.bp3-tether-element-attached-right>.bp3-tooltip{-webkit-transform-origin:bottom right;transform-origin:bottom right}.jupyter-wrapper .bp3-tooltip .bp3-popover-content{background:#394b59;color:#f5f8fa}.jupyter-wrapper .bp3-tooltip .bp3-popover-arrow:before{-webkit-box-shadow:1px 1px 6px rgba(16,22,26,.2);box-shadow:1px 1px 6px #10161a33}.jupyter-wrapper .bp3-tooltip .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.1}.jupyter-wrapper .bp3-tooltip .bp3-popover-arrow-fill{fill:#394b59}.jupyter-wrapper .bp3-popover-enter>.bp3-tooltip,.jupyter-wrapper .bp3-popover-appear>.bp3-tooltip{-webkit-transform:scale(.8);transform:scale(.8)}.jupyter-wrapper .bp3-popover-enter-active>.bp3-tooltip,.jupyter-wrapper .bp3-popover-appear-active>.bp3-tooltip{-webkit-transform:scale(1);transform:scale(1);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-popover-exit>.bp3-tooltip{-webkit-transform:scale(1);transform:scale(1)}.jupyter-wrapper .bp3-popover-exit-active>.bp3-tooltip{-webkit-transform:scale(.8);transform:scale(.8);-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.1s;transition-duration:.1s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-tooltip .bp3-popover-content{padding:10px 12px}.jupyter-wrapper .bp3-tooltip.bp3-dark,.jupyter-wrapper .bp3-dark .bp3-tooltip{-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 2px 4px rgba(16,22,26,.4),0 8px 24px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 2px 4px #10161a66,0 8px 24px #10161a66}.jupyter-wrapper .bp3-tooltip.bp3-dark .bp3-popover-content,.jupyter-wrapper .bp3-dark .bp3-tooltip .bp3-popover-content{background:#e1e8ed;color:#394b59}.jupyter-wrapper .bp3-tooltip.bp3-dark .bp3-popover-arrow:before,.jupyter-wrapper .bp3-dark .bp3-tooltip .bp3-popover-arrow:before{-webkit-box-shadow:1px 1px 6px rgba(16,22,26,.4);box-shadow:1px 1px 6px #10161a66}.jupyter-wrapper .bp3-tooltip.bp3-dark .bp3-popover-arrow-border,.jupyter-wrapper .bp3-dark .bp3-tooltip .bp3-popover-arrow-border{fill:#10161a;fill-opacity:.2}.jupyter-wrapper .bp3-tooltip.bp3-dark .bp3-popover-arrow-fill,.jupyter-wrapper .bp3-dark .bp3-tooltip .bp3-popover-arrow-fill{fill:#e1e8ed}.jupyter-wrapper .bp3-tooltip.bp3-intent-primary .bp3-popover-content{background:#137cbd;color:#fff}.jupyter-wrapper .bp3-tooltip.bp3-intent-primary .bp3-popover-arrow-fill{fill:#137cbd}.jupyter-wrapper .bp3-tooltip.bp3-intent-success .bp3-popover-content{background:#0f9960;color:#fff}.jupyter-wrapper .bp3-tooltip.bp3-intent-success .bp3-popover-arrow-fill{fill:#0f9960}.jupyter-wrapper .bp3-tooltip.bp3-intent-warning .bp3-popover-content{background:#d9822b;color:#fff}.jupyter-wrapper .bp3-tooltip.bp3-intent-warning .bp3-popover-arrow-fill{fill:#d9822b}.jupyter-wrapper .bp3-tooltip.bp3-intent-danger .bp3-popover-content{background:#db3737;color:#fff}.jupyter-wrapper .bp3-tooltip.bp3-intent-danger .bp3-popover-arrow-fill{fill:#db3737}.jupyter-wrapper .bp3-tooltip-indicator{border-bottom:dotted 1px;cursor:help}.jupyter-wrapper .bp3-tree .bp3-icon,.jupyter-wrapper .bp3-tree .bp3-icon-standard,.jupyter-wrapper .bp3-tree .bp3-icon-large{color:#5c7080}.jupyter-wrapper .bp3-tree .bp3-icon.bp3-intent-primary,.jupyter-wrapper .bp3-tree .bp3-icon-standard.bp3-intent-primary,.jupyter-wrapper .bp3-tree .bp3-icon-large.bp3-intent-primary{color:#137cbd}.jupyter-wrapper .bp3-tree .bp3-icon.bp3-intent-success,.jupyter-wrapper .bp3-tree .bp3-icon-standard.bp3-intent-success,.jupyter-wrapper .bp3-tree .bp3-icon-large.bp3-intent-success{color:#0f9960}.jupyter-wrapper .bp3-tree .bp3-icon.bp3-intent-warning,.jupyter-wrapper .bp3-tree .bp3-icon-standard.bp3-intent-warning,.jupyter-wrapper .bp3-tree .bp3-icon-large.bp3-intent-warning{color:#d9822b}.jupyter-wrapper .bp3-tree .bp3-icon.bp3-intent-danger,.jupyter-wrapper .bp3-tree .bp3-icon-standard.bp3-intent-danger,.jupyter-wrapper .bp3-tree .bp3-icon-large.bp3-intent-danger{color:#db3737}.jupyter-wrapper .bp3-tree-node-list{list-style:none;margin:0;padding-left:0}.jupyter-wrapper .bp3-tree-root{background-color:transparent;cursor:default;padding-left:0;position:relative}.jupyter-wrapper .bp3-tree-node-content-0{padding-left:0}.jupyter-wrapper .bp3-tree-node-content-1{padding-left:23px}.jupyter-wrapper .bp3-tree-node-content-2{padding-left:46px}.jupyter-wrapper .bp3-tree-node-content-3{padding-left:69px}.jupyter-wrapper .bp3-tree-node-content-4{padding-left:92px}.jupyter-wrapper .bp3-tree-node-content-5{padding-left:115px}.jupyter-wrapper .bp3-tree-node-content-6{padding-left:138px}.jupyter-wrapper .bp3-tree-node-content-7{padding-left:161px}.jupyter-wrapper .bp3-tree-node-content-8{padding-left:184px}.jupyter-wrapper .bp3-tree-node-content-9{padding-left:207px}.jupyter-wrapper .bp3-tree-node-content-10{padding-left:230px}.jupyter-wrapper .bp3-tree-node-content-11{padding-left:253px}.jupyter-wrapper .bp3-tree-node-content-12{padding-left:276px}.jupyter-wrapper .bp3-tree-node-content-13{padding-left:299px}.jupyter-wrapper .bp3-tree-node-content-14{padding-left:322px}.jupyter-wrapper .bp3-tree-node-content-15{padding-left:345px}.jupyter-wrapper .bp3-tree-node-content-16{padding-left:368px}.jupyter-wrapper .bp3-tree-node-content-17{padding-left:391px}.jupyter-wrapper .bp3-tree-node-content-18{padding-left:414px}.jupyter-wrapper .bp3-tree-node-content-19{padding-left:437px}.jupyter-wrapper .bp3-tree-node-content-20{padding-left:460px}.jupyter-wrapper .bp3-tree-node-content{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;height:30px;padding-right:5px;width:100%}.jupyter-wrapper .bp3-tree-node-content:hover{background-color:#bfccd666}.jupyter-wrapper .bp3-tree-node-caret,.jupyter-wrapper .bp3-tree-node-caret-none{min-width:30px}.jupyter-wrapper .bp3-tree-node-caret{color:#5c7080;cursor:pointer;padding:7px;-webkit-transform:rotate(0deg);transform:rotate(0);-webkit-transition:-webkit-transform .2s cubic-bezier(.4,1,.75,.9);transition:-webkit-transform .2s cubic-bezier(.4,1,.75,.9);transition:transform .2s cubic-bezier(.4,1,.75,.9);transition:transform .2s cubic-bezier(.4,1,.75,.9),-webkit-transform .2s cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-tree-node-caret:hover{color:#182026}.jupyter-wrapper .bp3-dark .bp3-tree-node-caret{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-tree-node-caret:hover{color:#f5f8fa}.jupyter-wrapper .bp3-tree-node-caret.bp3-tree-node-caret-open{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.jupyter-wrapper .bp3-tree-node-caret.bp3-icon-standard:before{content:\"\ue695\"}.jupyter-wrapper .bp3-tree-node-icon{margin-right:7px;position:relative}.jupyter-wrapper .bp3-tree-node-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .bp3-tree-node-label span{display:inline}.jupyter-wrapper .bp3-tree-node-secondary-label{padding:0 5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .bp3-tree-node-secondary-label .bp3-popover-wrapper,.jupyter-wrapper .bp3-tree-node-secondary-label .bp3-popover-target{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.jupyter-wrapper .bp3-tree-node.bp3-disabled .bp3-tree-node-content{background-color:inherit;color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-tree-node.bp3-disabled .bp3-tree-node-caret,.jupyter-wrapper .bp3-tree-node.bp3-disabled .bp3-tree-node-icon{color:#5c708099;cursor:not-allowed}.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content{background-color:#137cbd}.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content,.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-icon,.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-icon-standard,.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-icon-large{color:#fff}.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-tree-node-caret:before{color:#ffffffb3}.jupyter-wrapper .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content .bp3-tree-node-caret:hover:before{color:#fff}.jupyter-wrapper .bp3-dark .bp3-tree-node-content:hover{background-color:#5c70804d}.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-standard,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-large{color:#a7b6c2}.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon.bp3-intent-primary,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-primary,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-primary{color:#137cbd}.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon.bp3-intent-success,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-success,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-success{color:#0f9960}.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon.bp3-intent-warning,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-warning,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-warning{color:#d9822b}.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon.bp3-intent-danger,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-standard.bp3-intent-danger,.jupyter-wrapper .bp3-dark .bp3-tree .bp3-icon-large.bp3-intent-danger{color:#db3737}.jupyter-wrapper .bp3-dark .bp3-tree-node.bp3-tree-node-selected>.bp3-tree-node-content{background-color:#137cbd}.jupyter-wrapper .bp3-omnibar{-webkit-filter:blur(0);filter:blur(0);opacity:1;background-color:#fff;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.1),0 4px 8px rgba(16,22,26,.2),0 18px 46px 6px rgba(16,22,26,.2);box-shadow:0 0 0 1px #10161a1a,0 4px 8px #10161a33,0 18px 46px 6px #10161a33;left:calc(50% - 250px);top:20vh;width:500px;z-index:21}.jupyter-wrapper .bp3-omnibar.bp3-overlay-enter,.jupyter-wrapper .bp3-omnibar.bp3-overlay-appear{-webkit-filter:blur(20px);filter:blur(20px);opacity:.2}.jupyter-wrapper .bp3-omnibar.bp3-overlay-enter-active,.jupyter-wrapper .bp3-omnibar.bp3-overlay-appear-active{-webkit-filter:blur(0);filter:blur(0);opacity:1;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:opacity,-webkit-filter;transition-property:opacity,-webkit-filter;transition-property:filter,opacity;transition-property:filter,opacity,-webkit-filter;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-omnibar.bp3-overlay-exit{-webkit-filter:blur(0);filter:blur(0);opacity:1}.jupyter-wrapper .bp3-omnibar.bp3-overlay-exit-active{-webkit-filter:blur(20px);filter:blur(20px);opacity:.2;-webkit-transition-delay:0;transition-delay:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:opacity,-webkit-filter;transition-property:opacity,-webkit-filter;transition-property:filter,opacity;transition-property:filter,opacity,-webkit-filter;-webkit-transition-timing-function:cubic-bezier(.4,1,.75,.9);transition-timing-function:cubic-bezier(.4,1,.75,.9)}.jupyter-wrapper .bp3-omnibar .bp3-input{background-color:transparent;border-radius:0}.jupyter-wrapper .bp3-omnibar .bp3-input,.jupyter-wrapper .bp3-omnibar .bp3-input:focus{-webkit-box-shadow:none;box-shadow:none}.jupyter-wrapper .bp3-omnibar .bp3-menu{background-color:transparent;border-radius:0;-webkit-box-shadow:inset 0 1px 0 rgba(16,22,26,.15);box-shadow:inset 0 1px #10161a26;max-height:calc(60vh - 40px);overflow:auto}.jupyter-wrapper .bp3-omnibar .bp3-menu:empty{display:none}.jupyter-wrapper .bp3-dark .bp3-omnibar,.jupyter-wrapper .bp3-omnibar.bp3-dark{background-color:#30404d;-webkit-box-shadow:0 0 0 1px rgba(16,22,26,.2),0 4px 8px rgba(16,22,26,.4),0 18px 46px 6px rgba(16,22,26,.4);box-shadow:0 0 0 1px #10161a33,0 4px 8px #10161a66,0 18px 46px 6px #10161a66}.jupyter-wrapper .bp3-omnibar-overlay .bp3-overlay-backdrop{background-color:#10161a33}.jupyter-wrapper .bp3-multi-select{min-width:150px}.jupyter-wrapper .bp3-multi-select-popover .bp3-menu{max-height:300px;max-width:400px;overflow:auto}.jupyter-wrapper .bp3-select-popover .bp3-popover-content{padding:5px}.jupyter-wrapper .bp3-select-popover .bp3-input-group{margin-bottom:0}.jupyter-wrapper .bp3-select-popover .bp3-menu{max-height:300px;max-width:400px;overflow:auto;padding:0}.jupyter-wrapper .bp3-select-popover .bp3-menu:not(:first-child){padding-top:5px}.jupyter-wrapper :root{--jp-icon-add-above: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzEzN18xOTQ5MikiPgo8cGF0aCBjbGFzcz0ianAtaWNvbjMiIGQ9Ik00Ljc1IDQuOTMwNjZINi42MjVWNi44MDU2NkM2LjYyNSA3LjAxMTkxIDYuNzkzNzUgNy4xODA2NiA3IDcuMTgwNjZDNy4yMDYyNSA3LjE4MDY2IDcuMzc1IDcuMDExOTEgNy4zNzUgNi44MDU2NlY0LjkzMDY2SDkuMjVDOS40NTYyNSA0LjkzMDY2IDkuNjI1IDQuNzYxOTEgOS42MjUgNC41NTU2NkM5LjYyNSA0LjM0OTQxIDkuNDU2MjUgNC4xODA2NiA5LjI1IDQuMTgwNjZINy4zNzVWMi4zMDU2NkM3LjM3NSAyLjA5OTQxIDcuMjA2MjUgMS45MzA2NiA3IDEuOTMwNjZDNi43OTM3NSAxLjkzMDY2IDYuNjI1IDIuMDk5NDEgNi42MjUgMi4zMDU2NlY0LjE4MDY2SDQuNzVDNC41NDM3NSA0LjE4MDY2IDQuMzc1IDQuMzQ5NDEgNC4zNzUgNC41NTU2NkM0LjM3NSA0Ljc2MTkxIDQuNTQzNzUgNC45MzA2NiA0Ljc1IDQuOTMwNjZaIiBmaWxsPSIjNjE2MTYxIiBzdHJva2U9IiM2MTYxNjEiIHN0cm9rZS13aWR0aD0iMC43Ii8+CjwvZz4KPHBhdGggY2xhc3M9ImpwLWljb24zIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTExLjUgOS41VjExLjVMMi41IDExLjVWOS41TDExLjUgOS41Wk0xMiA4QzEyLjU1MjMgOCAxMyA4LjQ0NzcyIDEzIDlWMTJDMTMgMTIuNTUyMyAxMi41NTIzIDEzIDEyIDEzTDIgMTNDMS40NDc3MiAxMyAxIDEyLjU1MjMgMSAxMlY5QzEgOC40NDc3MiAxLjQ0NzcxIDggMiA4TDEyIDhaIiBmaWxsPSIjNjE2MTYxIi8+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzEzN18xOTQ5MiI+CjxyZWN0IGNsYXNzPSJqcC1pY29uMyIgd2lkdGg9IjYiIGhlaWdodD0iNiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0ibWF0cml4KC0xIDAgMCAxIDEwIDEuNTU1NjYpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==);--jp-icon-add-below: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzEzN18xOTQ5OCkiPgo8cGF0aCBjbGFzcz0ianAtaWNvbjMiIGQ9Ik05LjI1IDEwLjA2OTNMNy4zNzUgMTAuMDY5M0w3LjM3NSA4LjE5NDM0QzcuMzc1IDcuOTg4MDkgNy4yMDYyNSA3LjgxOTM0IDcgNy44MTkzNEM2Ljc5Mzc1IDcuODE5MzQgNi42MjUgNy45ODgwOSA2LjYyNSA4LjE5NDM0TDYuNjI1IDEwLjA2OTNMNC43NSAxMC4wNjkzQzQuNTQzNzUgMTAuMDY5MyA0LjM3NSAxMC4yMzgxIDQuMzc1IDEwLjQ0NDNDNC4zNzUgMTAuNjUwNiA0LjU0Mzc1IDEwLjgxOTMgNC43NSAxMC44MTkzTDYuNjI1IDEwLjgxOTNMNi42MjUgMTIuNjk0M0M2LjYyNSAxMi45MDA2IDYuNzkzNzUgMTMuMDY5MyA3IDEzLjA2OTNDNy4yMDYyNSAxMy4wNjkzIDcuMzc1IDEyLjkwMDYgNy4zNzUgMTIuNjk0M0w3LjM3NSAxMC44MTkzTDkuMjUgMTAuODE5M0M5LjQ1NjI1IDEwLjgxOTMgOS42MjUgMTAuNjUwNiA5LjYyNSAxMC40NDQzQzkuNjI1IDEwLjIzODEgOS40NTYyNSAxMC4wNjkzIDkuMjUgMTAuMDY5M1oiIGZpbGw9IiM2MTYxNjEiIHN0cm9rZT0iIzYxNjE2MSIgc3Ryb2tlLXdpZHRoPSIwLjciLz4KPC9nPgo8cGF0aCBjbGFzcz0ianAtaWNvbjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMi41IDUuNUwyLjUgMy41TDExLjUgMy41TDExLjUgNS41TDIuNSA1LjVaTTIgN0MxLjQ0NzcyIDcgMSA2LjU1MjI4IDEgNkwxIDNDMSAyLjQ0NzcyIDEuNDQ3NzIgMiAyIDJMMTIgMkMxMi41NTIzIDIgMTMgMi40NDc3MiAxMyAzTDEzIDZDMTMgNi41NTIyOSAxMi41NTIzIDcgMTIgN0wyIDdaIiBmaWxsPSIjNjE2MTYxIi8+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzEzN18xOTQ5OCI+CjxyZWN0IGNsYXNzPSJqcC1pY29uMyIgd2lkdGg9IjYiIGhlaWdodD0iNiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0ibWF0cml4KDEgMS43NDg0NmUtMDcgMS43NDg0NmUtMDcgLTEgNCAxMy40NDQzKSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=);--jp-icon-add: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTE5IDEzaC02djZoLTJ2LTZINXYtMmg2VjVoMnY2aDZ2MnoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-bell: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE2IDE2IiB2ZXJzaW9uPSIxLjEiPgogICA8cGF0aCBjbGFzcz0ianAtaWNvbjIganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjMzMzMzMzIgogICAgICBkPSJtOCAwLjI5Yy0xLjQgMC0yLjcgMC43My0zLjYgMS44LTEuMiAxLjUtMS40IDMuNC0xLjUgNS4yLTAuMTggMi4yLTAuNDQgNC0yLjMgNS4zbDAuMjggMS4zaDVjMC4wMjYgMC42NiAwLjMyIDEuMSAwLjcxIDEuNSAwLjg0IDAuNjEgMiAwLjYxIDIuOCAwIDAuNTItMC40IDAuNi0xIDAuNzEtMS41aDVsMC4yOC0xLjNjLTEuOS0wLjk3LTIuMi0zLjMtMi4zLTUuMy0wLjEzLTEuOC0wLjI2LTMuNy0xLjUtNS4yLTAuODUtMS0yLjItMS44LTMuNi0xLjh6bTAgMS40YzAuODggMCAxLjkgMC41NSAyLjUgMS4zIDAuODggMS4xIDEuMSAyLjcgMS4yIDQuNCAwLjEzIDEuNyAwLjIzIDMuNiAxLjMgNS4yaC0xMGMxLjEtMS42IDEuMi0zLjQgMS4zLTUuMiAwLjEzLTEuNyAwLjMtMy4zIDEuMi00LjQgMC41OS0wLjcyIDEuNi0xLjMgMi41LTEuM3ptLTAuNzQgMTJoMS41Yy0wLjAwMTUgMC4yOCAwLjAxNSAwLjc5LTAuNzQgMC43OS0wLjczIDAuMDAxNi0wLjcyLTAuNTMtMC43NC0wLjc5eiIgLz4KPC9zdmc+Cg==);--jp-icon-bug-dot: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiM2MTYxNjEiPgogICAgICAgIDxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTcuMTkgOEgyMFYxMEgxNy45MUMxNy45NiAxMC4zMyAxOCAxMC42NiAxOCAxMVYxMkgyMFYxNEgxOC41SDE4VjE0LjAyNzVDMTUuNzUgMTQuMjc2MiAxNCAxNi4xODM3IDE0IDE4LjVDMTQgMTkuMjA4IDE0LjE2MzUgMTkuODc3OSAxNC40NTQ5IDIwLjQ3MzlDMTMuNzA2MyAyMC44MTE3IDEyLjg3NTcgMjEgMTIgMjFDOS43OCAyMSA3Ljg1IDE5Ljc5IDYuODEgMThINFYxNkg2LjA5QzYuMDQgMTUuNjcgNiAxNS4zNCA2IDE1VjE0SDRWMTJINlYxMUM2IDEwLjY2IDYuMDQgMTAuMzMgNi4wOSAxMEg0VjhINi44MUM3LjI2IDcuMjIgNy44OCA2LjU1IDguNjIgNi4wNEw3IDQuNDFMOC40MSAzTDEwLjU5IDUuMTdDMTEuMDQgNS4wNiAxMS41MSA1IDEyIDVDMTIuNDkgNSAxMi45NiA1LjA2IDEzLjQyIDUuMTdMMTUuNTkgM0wxNyA0LjQxTDE1LjM3IDYuMDRDMTYuMTIgNi41NSAxNi43NCA3LjIyIDE3LjE5IDhaTTEwIDE2SDE0VjE0SDEwVjE2Wk0xMCAxMkgxNFYxMEgxMFYxMloiIGZpbGw9IiM2MTYxNjEiLz4KICAgICAgICA8cGF0aCBkPSJNMjIgMTguNUMyMiAyMC40MzMgMjAuNDMzIDIyIDE4LjUgMjJDMTYuNTY3IDIyIDE1IDIwLjQzMyAxNSAxOC41QzE1IDE2LjU2NyAxNi41NjcgMTUgMTguNSAxNUMyMC40MzMgMTUgMjIgMTYuNTY3IDIyIDE4LjVaIiBmaWxsPSIjNjE2MTYxIi8+CiAgICA8L2c+Cjwvc3ZnPgo=);--jp-icon-bug: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIj4KICAgIDxwYXRoIGQ9Ik0yMCA4aC0yLjgxYy0uNDUtLjc4LTEuMDctMS40NS0xLjgyLTEuOTZMMTcgNC40MSAxNS41OSAzbC0yLjE3IDIuMTdDMTIuOTYgNS4wNiAxMi40OSA1IDEyIDVjLS40OSAwLS45Ni4wNi0xLjQxLjE3TDguNDEgMyA3IDQuNDFsMS42MiAxLjYzQzcuODggNi41NSA3LjI2IDcuMjIgNi44MSA4SDR2MmgyLjA5Yy0uMDUuMzMtLjA5LjY2LS4wOSAxdjFINHYyaDJ2MWMwIC4zNC4wNC42Ny4wOSAxSDR2MmgyLjgxYzEuMDQgMS43OSAyLjk3IDMgNS4xOSAzczQuMTUtMS4yMSA1LjE5LTNIMjB2LTJoLTIuMDljLjA1LS4zMy4wOS0uNjYuMDktMXYtMWgydi0yaC0ydi0xYzAtLjM0LS4wNC0uNjctLjA5LTFIMjBWOHptLTYgOGgtNHYtMmg0djJ6bTAtNGgtNHYtMmg0djJ6Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-build: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTE0LjkgMTcuNDVDMTYuMjUgMTcuNDUgMTcuMzUgMTYuMzUgMTcuMzUgMTVDMTcuMzUgMTMuNjUgMTYuMjUgMTIuNTUgMTQuOSAxMi41NUMxMy41NCAxMi41NSAxMi40NSAxMy42NSAxMi40NSAxNUMxMi40NSAxNi4zNSAxMy41NCAxNy40NSAxNC45IDE3LjQ1Wk0yMC4xIDE1LjY4TDIxLjU4IDE2Ljg0QzIxLjcxIDE2Ljk1IDIxLjc1IDE3LjEzIDIxLjY2IDE3LjI5TDIwLjI2IDE5LjcxQzIwLjE3IDE5Ljg2IDIwIDE5LjkyIDE5LjgzIDE5Ljg2TDE4LjA5IDE5LjE2QzE3LjczIDE5LjQ0IDE3LjMzIDE5LjY3IDE2LjkxIDE5Ljg1TDE2LjY0IDIxLjdDMTYuNjIgMjEuODcgMTYuNDcgMjIgMTYuMyAyMkgxMy41QzEzLjMyIDIyIDEzLjE4IDIxLjg3IDEzLjE1IDIxLjdMMTIuODkgMTkuODVDMTIuNDYgMTkuNjcgMTIuMDcgMTkuNDQgMTEuNzEgMTkuMTZMOS45NjAwMiAxOS44NkM5LjgxMDAyIDE5LjkyIDkuNjIwMDIgMTkuODYgOS41NDAwMiAxOS43MUw4LjE0MDAyIDE3LjI5QzguMDUwMDIgMTcuMTMgOC4wOTAwMiAxNi45NSA4LjIyMDAyIDE2Ljg0TDkuNzAwMDIgMTUuNjhMOS42NTAwMSAxNUw5LjcwMDAyIDE0LjMxTDguMjIwMDIgMTMuMTZDOC4wOTAwMiAxMy4wNSA4LjA1MDAyIDEyLjg2IDguMTQwMDIgMTIuNzFMOS41NDAwMiAxMC4yOUM5LjYyMDAyIDEwLjEzIDkuODEwMDIgMTAuMDcgOS45NjAwMiAxMC4xM0wxMS43MSAxMC44NEMxMi4wNyAxMC41NiAxMi40NiAxMC4zMiAxMi44OSAxMC4xNUwxMy4xNSA4LjI4OTk4QzEzLjE4IDguMTI5OTggMTMuMzIgNy45OTk5OCAxMy41IDcuOTk5OThIMTYuM0MxNi40NyA3Ljk5OTk4IDE2LjYyIDguMTI5OTggMTYuNjQgOC4yODk5OEwxNi45MSAxMC4xNUMxNy4zMyAxMC4zMiAxNy43MyAxMC41NiAxOC4wOSAxMC44NEwxOS44MyAxMC4xM0MyMCAxMC4wNyAyMC4xNyAxMC4xMyAyMC4yNiAxMC4yOUwyMS42NiAxMi43MUMyMS43NSAxMi44NiAyMS43MSAxMy4wNSAyMS41OCAxMy4xNkwyMC4xIDE0LjMxTDIwLjE1IDE1TDIwLjEgMTUuNjhaIi8+CiAgICA8cGF0aCBkPSJNNy4zMjk2NiA3LjQ0NDU0QzguMDgzMSA3LjAwOTU0IDguMzM5MzIgNi4wNTMzMiA3LjkwNDMyIDUuMjk5ODhDNy40NjkzMiA0LjU0NjQzIDYuNTA4MSA0LjI4MTU2IDUuNzU0NjYgNC43MTY1NkM1LjM5MTc2IDQuOTI2MDggNS4xMjY5NSA1LjI3MTE4IDUuMDE4NDkgNS42NzU5NEM0LjkxMDA0IDYuMDgwNzEgNC45NjY4MiA2LjUxMTk4IDUuMTc2MzQgNi44NzQ4OEM1LjYxMTM0IDcuNjI4MzIgNi41NzYyMiA3Ljg3OTU0IDcuMzI5NjYgNy40NDQ1NFpNOS42NTcxOCA0Ljc5NTkzTDEwLjg2NzIgNC45NTE3OUMxMC45NjI4IDQuOTc3NDEgMTEuMDQwMiA1LjA3MTMzIDExLjAzODIgNS4xODc5M0wxMS4wMzg4IDYuOTg4OTNDMTEuMDQ1NSA3LjEwMDU0IDEwLjk2MTYgNy4xOTUxOCAxMC44NTUgNy4yMTA1NEw5LjY2MDAxIDcuMzgwODNMOS4yMzkxNSA4LjEzMTg4TDkuNjY5NjEgOS4yNTc0NUM5LjcwNzI5IDkuMzYyNzEgOS42NjkzNCA5LjQ3Njk5IDkuNTc0MDggOS41MzE5OUw4LjAxNTIzIDEwLjQzMkM3LjkxMTMxIDEwLjQ5MiA3Ljc5MzM3IDEwLjQ2NzcgNy43MjEwNSAxMC4zODI0TDYuOTg3NDggOS40MzE4OEw2LjEwOTMxIDkuNDMwODNMNS4zNDcwNCAxMC4zOTA1QzUuMjg5MDkgMTAuNDcwMiA1LjE3MzgzIDEwLjQ5MDUgNS4wNzE4NyAxMC40MzM5TDMuNTEyNDUgOS41MzI5M0MzLjQxMDQ5IDkuNDc2MzMgMy4zNzY0NyA5LjM1NzQxIDMuNDEwNzUgOS4yNTY3OUwzLjg2MzQ3IDguMTQwOTNMMy42MTc0OSA3Ljc3NDg4TDMuNDIzNDcgNy4zNzg4M0wyLjIzMDc1IDcuMjEyOTdDMi4xMjY0NyA3LjE5MjM1IDIuMDQwNDkgNy4xMDM0MiAyLjA0MjQ1IDYuOTg2ODJMMi4wNDE4NyA1LjE4NTgyQzIuMDQzODMgNS4wNjkyMiAyLjExOTA5IDQuOTc5NTggMi4yMTcwNCA0Ljk2OTIyTDMuNDIwNjUgNC43OTM5M0wzLjg2NzQ5IDQuMDI3ODhMMy40MTEwNSAyLjkxNzMxQzMuMzczMzcgMi44MTIwNCAzLjQxMTMxIDIuNjk3NzYgMy41MTUyMyAyLjYzNzc2TDUuMDc0MDggMS43Mzc3NkM1LjE2OTM0IDEuNjgyNzYgNS4yODcyOSAxLjcwNzA0IDUuMzU5NjEgMS43OTIzMUw2LjExOTE1IDIuNzI3ODhMNi45ODAwMSAyLjczODkzTDcuNzI0OTYgMS43ODkyMkM3Ljc5MTU2IDEuNzA0NTggNy45MTU0OCAxLjY3OTIyIDguMDA4NzkgMS43NDA4Mkw5LjU2ODIxIDIuNjQxODJDOS42NzAxNyAyLjY5ODQyIDkuNzEyODUgMi44MTIzNCA5LjY4NzIzIDIuOTA3OTdMOS4yMTcxOCA0LjAzMzgzTDkuNDYzMTYgNC4zOTk4OEw5LjY1NzE4IDQuNzk1OTNaIi8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-caret-down-empty-thin: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIwIDIwIj4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSIgc2hhcGUtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iPgoJCTxwb2x5Z29uIGNsYXNzPSJzdDEiIHBvaW50cz0iOS45LDEzLjYgMy42LDcuNCA0LjQsNi42IDkuOSwxMi4yIDE1LjQsNi43IDE2LjEsNy40ICIvPgoJPC9nPgo8L3N2Zz4K);--jp-icon-caret-down-empty: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE4IDE4Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KICAgIDxwYXRoIGQ9Ik01LjIsNS45TDksOS43bDMuOC0zLjhsMS4yLDEuMmwtNC45LDVsLTQuOS01TDUuMiw1Ljl6Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-caret-down: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE4IDE4Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KICAgIDxwYXRoIGQ9Ik01LjIsNy41TDksMTEuMmwzLjgtMy44SDUuMnoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-caret-left: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE4IDE4Ij4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSIgc2hhcGUtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iPgoJCTxwYXRoIGQ9Ik0xMC44LDEyLjhMNy4xLDlsMy44LTMuOGwwLDcuNkgxMC44eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-caret-right: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE4IDE4Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KICAgIDxwYXRoIGQ9Ik03LjIsNS4yTDEwLjksOWwtMy44LDMuOFY1LjJINy4yeiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-caret-up-empty-thin: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIwIDIwIj4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSIgc2hhcGUtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iPgoJCTxwb2x5Z29uIGNsYXNzPSJzdDEiIHBvaW50cz0iMTUuNCwxMy4zIDkuOSw3LjcgNC40LDEzLjIgMy42LDEyLjUgOS45LDYuMyAxNi4xLDEyLjYgIi8+Cgk8L2c+Cjwvc3ZnPgo=);--jp-icon-caret-up: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE4IDE4Ij4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSIgc2hhcGUtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iPgoJCTxwYXRoIGQ9Ik01LjIsMTAuNUw5LDYuOGwzLjgsMy44SDUuMnoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-case-sensitive: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIwIDIwIj4KICA8ZyBjbGFzcz0ianAtaWNvbjIiIGZpbGw9IiM0MTQxNDEiPgogICAgPHJlY3QgeD0iMiIgeT0iMiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+CiAgPC9nPgogIDxnIGNsYXNzPSJqcC1pY29uLWFjY2VudDIiIGZpbGw9IiNGRkYiPgogICAgPHBhdGggZD0iTTcuNiw4aDAuOWwzLjUsOGgtMS4xTDEwLDE0SDZsLTAuOSwySDRMNy42LDh6IE04LDkuMUw2LjQsMTNoMy4yTDgsOS4xeiIvPgogICAgPHBhdGggZD0iTTE2LjYsOS44Yy0wLjIsMC4xLTAuNCwwLjEtMC43LDAuMWMtMC4yLDAtMC40LTAuMS0wLjYtMC4yYy0wLjEtMC4xLTAuMi0wLjQtMC4yLTAuNyBjLTAuMywwLjMtMC42LDAuNS0wLjksMC43Yy0wLjMsMC4xLTAuNywwLjItMS4xLDAuMmMtMC4zLDAtMC41LDAtMC43LTAuMWMtMC4yLTAuMS0wLjQtMC4yLTAuNi0wLjNjLTAuMi0wLjEtMC4zLTAuMy0wLjQtMC41IGMtMC4xLTAuMi0wLjEtMC40LTAuMS0wLjdjMC0wLjMsMC4xLTAuNiwwLjItMC44YzAuMS0wLjIsMC4zLTAuNCwwLjQtMC41QzEyLDcsMTIuMiw2LjksMTIuNSw2LjhjMC4yLTAuMSwwLjUtMC4xLDAuNy0wLjIgYzAuMy0wLjEsMC41LTAuMSwwLjctMC4xYzAuMiwwLDAuNC0wLjEsMC42LTAuMWMwLjIsMCwwLjMtMC4xLDAuNC0wLjJjMC4xLTAuMSwwLjItMC4yLDAuMi0wLjRjMC0xLTEuMS0xLTEuMy0xIGMtMC40LDAtMS40LDAtMS40LDEuMmgtMC45YzAtMC40LDAuMS0wLjcsMC4yLTFjMC4xLTAuMiwwLjMtMC40LDAuNS0wLjZjMC4yLTAuMiwwLjUtMC4zLDAuOC0wLjNDMTMuMyw0LDEzLjYsNCwxMy45LDQgYzAuMywwLDAuNSwwLDAuOCwwLjFjMC4zLDAsMC41LDAuMSwwLjcsMC4yYzAuMiwwLjEsMC40LDAuMywwLjUsMC41QzE2LDUsMTYsNS4yLDE2LDUuNnYyLjljMCwwLjIsMCwwLjQsMCwwLjUgYzAsMC4xLDAuMSwwLjIsMC4zLDAuMmMwLjEsMCwwLjIsMCwwLjMsMFY5Ljh6IE0xNS4yLDYuOWMtMS4yLDAuNi0zLjEsMC4yLTMuMSwxLjRjMCwxLjQsMy4xLDEsMy4xLTAuNVY2Ljl6Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-check: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIj4KICAgIDxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-circle-empty: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTEyIDJDNi40NyAyIDIgNi40NyAyIDEyczQuNDcgMTAgMTAgMTAgMTAtNC40NyAxMC0xMFMxNy41MyAyIDEyIDJ6bTAgMThjLTQuNDEgMC04LTMuNTktOC04czMuNTktOCA4LTggOCAzLjU5IDggOC0zLjU5IDgtOCA4eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-circle: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTggMTgiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPGNpcmNsZSBjeD0iOSIgY3k9IjkiIHI9IjgiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-clear: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8bWFzayBpZD0iZG9udXRIb2xlIj4KICAgIDxyZWN0IHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0id2hpdGUiIC8+CiAgICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSI4IiBmaWxsPSJibGFjayIvPgogIDwvbWFzaz4KCiAgPGcgY2xhc3M9ImpwLWljb24zIiBmaWxsPSIjNjE2MTYxIj4KICAgIDxyZWN0IGhlaWdodD0iMTgiIHdpZHRoPSIyIiB4PSIxMSIgeT0iMyIgdHJhbnNmb3JtPSJyb3RhdGUoMzE1LCAxMiwgMTIpIi8+CiAgICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIgbWFzaz0idXJsKCNkb251dEhvbGUpIi8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-close: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbi1ub25lIGpwLWljb24tc2VsZWN0YWJsZS1pbnZlcnNlIGpwLWljb24zLWhvdmVyIiBmaWxsPSJub25lIj4KICAgIDxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjExIi8+CiAgPC9nPgoKICA8ZyBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIGpwLWljb24tYWNjZW50Mi1ob3ZlciIgZmlsbD0iIzYxNjE2MSI+CiAgICA8cGF0aCBkPSJNMTkgNi40MUwxNy41OSA1IDEyIDEwLjU5IDYuNDEgNSA1IDYuNDEgMTAuNTkgMTIgNSAxNy41OSA2LjQxIDE5IDEyIDEzLjQxIDE3LjU5IDE5IDE5IDE3LjU5IDEzLjQxIDEyeiIvPgogIDwvZz4KCiAgPGcgY2xhc3M9ImpwLWljb24tbm9uZSBqcC1pY29uLWJ1c3kiIGZpbGw9Im5vbmUiPgogICAgPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iNyIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-code: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyOCAyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CgkJPHBhdGggZD0iTTExLjQgMTguNkw2LjggMTRMMTEuNCA5LjRMMTAgOEw0IDE0TDEwIDIwTDExLjQgMTguNlpNMTYuNiAxOC42TDIxLjIgMTRMMTYuNiA5LjRMMTggOEwyNCAxNEwxOCAyMEwxNi42IDE4LjZWMTguNloiLz4KCTwvZz4KPC9zdmc+Cg==);--jp-icon-console: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIwMCAyMDAiPgogIDxnIGNsYXNzPSJqcC1jb25zb2xlLWljb24tYmFja2dyb3VuZC1jb2xvciBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiMwMjg4RDEiPgogICAgPHBhdGggZD0iTTIwIDE5LjhoMTYwdjE1OS45SDIweiIvPgogIDwvZz4KICA8ZyBjbGFzcz0ianAtY29uc29sZS1pY29uLWNvbG9yIGpwLWljb24tc2VsZWN0YWJsZS1pbnZlcnNlIiBmaWxsPSIjZmZmIj4KICAgIDxwYXRoIGQ9Ik0xMDUgMTI3LjNoNDB2MTIuOGgtNDB6TTUxLjEgNzdMNzQgOTkuOWwtMjMuMyAyMy4zIDEwLjUgMTAuNSAyMy4zLTIzLjNMOTUgOTkuOSA4NC41IDg5LjQgNjEuNiA2Ni41eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-copy: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTggMTgiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTExLjksMUgzLjJDMi40LDEsMS43LDEuNywxLjcsMi41djEwLjJoMS41VjIuNWg4LjdWMXogTTE0LjEsMy45aC04Yy0wLjgsMC0xLjUsMC43LTEuNSwxLjV2MTAuMmMwLDAuOCwwLjcsMS41LDEuNSwxLjVoOCBjMC44LDAsMS41LTAuNywxLjUtMS41VjUuNEMxNS41LDQuNiwxNC45LDMuOSwxNC4xLDMuOXogTTE0LjEsMTUuNWgtOFY1LjRoOFYxNS41eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-copyright: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCI+CiAgPGcgY2xhc3M9ImpwLWljb24zIiBmaWxsPSIjNjE2MTYxIj4KICAgIDxwYXRoIGQ9Ik0xMS44OCw5LjE0YzEuMjgsMC4wNiwxLjYxLDEuMTUsMS42MywxLjY2aDEuNzljLTAuMDgtMS45OC0xLjQ5LTMuMTktMy40NS0zLjE5QzkuNjQsNy42MSw4LDksOCwxMi4xNCBjMCwxLjk0LDAuOTMsNC4yNCwzLjg0LDQuMjRjMi4yMiwwLDMuNDEtMS42NSwzLjQ0LTIuOTVoLTEuNzljLTAuMDMsMC41OS0wLjQ1LDEuMzgtMS42MywxLjQ0QzEwLjU1LDE0LjgzLDEwLDEzLjgxLDEwLDEyLjE0IEMxMCw5LjI1LDExLjI4LDkuMTYsMTEuODgsOS4xNHogTTEyLDJDNi40OCwyLDIsNi40OCwyLDEyczQuNDgsMTAsMTAsMTBzMTAtNC40OCwxMC0xMFMxNy41MiwyLDEyLDJ6IE0xMiwyMGMtNC40MSwwLTgtMy41OS04LTggczMuNTktOCw4LThzOCwzLjU5LDgsOFMxNi40MSwyMCwxMiwyMHoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-cut: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTkuNjQgNy42NGMuMjMtLjUuMzYtMS4wNS4zNi0xLjY0IDAtMi4yMS0xLjc5LTQtNC00UzIgMy43OSAyIDZzMS43OSA0IDQgNGMuNTkgMCAxLjE0LS4xMyAxLjY0LS4zNkwxMCAxMmwtMi4zNiAyLjM2QzcuMTQgMTQuMTMgNi41OSAxNCA2IDE0Yy0yLjIxIDAtNCAxLjc5LTQgNHMxLjc5IDQgNCA0IDQtMS43OSA0LTRjMC0uNTktLjEzLTEuMTQtLjM2LTEuNjRMMTIgMTRsNyA3aDN2LTFMOS42NCA3LjY0ek02IDhjLTEuMSAwLTItLjg5LTItMnMuOS0yIDItMiAyIC44OSAyIDItLjkgMi0yIDJ6bTAgMTJjLTEuMSAwLTItLjg5LTItMnMuOS0yIDItMiAyIC44OSAyIDItLjkgMi0yIDJ6bTYtNy41Yy0uMjggMC0uNS0uMjItLjUtLjVzLjIyLS41LjUtLjUuNS4yMi41LjUtLjIyLjUtLjUuNXpNMTkgM2wtNiA2IDIgMiA3LTdWM3oiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-delete: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCI+CiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPgogICAgPHBhdGggY2xhc3M9ImpwLWljb24zIiBmaWxsPSIjNjI2MjYyIiBkPSJNNiAxOWMwIDEuMS45IDIgMiAyaDhjMS4xIDAgMi0uOSAyLTJWN0g2djEyek0xOSA0aC0zLjVsLTEtMWgtNWwtMSAxSDV2MmgxNFY0eiIgLz4KPC9zdmc+Cg==);--jp-icon-download: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTE5IDloLTRWM0g5djZINWw3IDcgNy03ek01IDE4djJoMTR2LTJINXoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-duplicate: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggY2xhc3M9ImpwLWljb24zIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIuNzk5OTggMC44NzVIOC44OTU4MkM5LjIwMDYxIDAuODc1IDkuNDQ5OTggMS4xMzkxNCA5LjQ0OTk4IDEuNDYxOThDOS40NDk5OCAxLjc4NDgyIDkuMjAwNjEgMi4wNDg5NiA4Ljg5NTgyIDIuMDQ4OTZIMy4zNTQxNUMzLjA0OTM2IDIuMDQ4OTYgMi43OTk5OCAyLjMxMzEgMi43OTk5OCAyLjYzNTk0VjkuNjc5NjlDMi43OTk5OCAxMC4wMDI1IDIuNTUwNjEgMTAuMjY2NyAyLjI0NTgyIDEwLjI2NjdDMS45NDEwMyAxMC4yNjY3IDEuNjkxNjUgMTAuMDAyNSAxLjY5MTY1IDkuNjc5NjlWMi4wNDg5NkMxLjY5MTY1IDEuNDAzMjggMi4xOTA0IDAuODc1IDIuNzk5OTggMC44NzVaTTUuMzY2NjUgMTEuOVY0LjU1SDExLjA4MzNWMTEuOUg1LjM2NjY1Wk00LjE0MTY1IDQuMTQxNjdDNC4xNDE2NSAzLjY5MDYzIDQuNTA3MjggMy4zMjUgNC45NTgzMiAzLjMyNUgxMS40OTE3QzExLjk0MjcgMy4zMjUgMTIuMzA4MyAzLjY5MDYzIDEyLjMwODMgNC4xNDE2N1YxMi4zMDgzQzEyLjMwODMgMTIuNzU5NCAxMS45NDI3IDEzLjEyNSAxMS40OTE3IDEzLjEyNUg0Ljk1ODMyQzQuNTA3MjggMTMuMTI1IDQuMTQxNjUgMTIuNzU5NCA0LjE0MTY1IDEyLjMwODNWNC4xNDE2N1oiIGZpbGw9IiM2MTYxNjEiLz4KPHBhdGggY2xhc3M9ImpwLWljb24zIiBkPSJNOS40MzU3NCA4LjI2NTA3SDguMzY0MzFWOS4zMzY1QzguMzY0MzEgOS40NTQzNSA4LjI2Nzg4IDkuNTUwNzggOC4xNTAwMiA5LjU1MDc4QzguMDMyMTcgOS41NTA3OCA3LjkzNTc0IDkuNDU0MzUgNy45MzU3NCA5LjMzNjVWOC4yNjUwN0g2Ljg2NDMxQzYuNzQ2NDUgOC4yNjUwNyA2LjY1MDAyIDguMTY4NjQgNi42NTAwMiA4LjA1MDc4QzYuNjUwMDIgNy45MzI5MiA2Ljc0NjQ1IDcuODM2NSA2Ljg2NDMxIDcuODM2NUg3LjkzNTc0VjYuNzY1MDdDNy45MzU3NCA2LjY0NzIxIDguMDMyMTcgNi41NTA3OCA4LjE1MDAyIDYuNTUwNzhDOC4yNjc4OCA2LjU1MDc4IDguMzY0MzEgNi42NDcyMSA4LjM2NDMxIDYuNzY1MDdWNy44MzY1SDkuNDM1NzRDOS41NTM2IDcuODM2NSA5LjY1MDAyIDcuOTMyOTIgOS42NTAwMiA4LjA1MDc4QzkuNjUwMDIgOC4xNjg2NCA5LjU1MzYgOC4yNjUwNyA5LjQzNTc0IDguMjY1MDdaIiBmaWxsPSIjNjE2MTYxIiBzdHJva2U9IiM2MTYxNjEiIHN0cm9rZS13aWR0aD0iMC41Ii8+Cjwvc3ZnPgo=);--jp-icon-edit: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTMgMTcuMjVWMjFoMy43NUwxNy44MSA5Ljk0bC0zLjc1LTMuNzVMMyAxNy4yNXpNMjAuNzEgNy4wNGMuMzktLjM5LjM5LTEuMDIgMC0xLjQxbC0yLjM0LTIuMzRjLS4zOS0uMzktMS4wMi0uMzktMS40MSAwbC0xLjgzIDEuODMgMy43NSAzLjc1IDEuODMtMS44M3oiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-ellipses: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPGNpcmNsZSBjeD0iNSIgY3k9IjEyIiByPSIyIi8+CiAgICA8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIyIi8+CiAgICA8Y2lyY2xlIGN4PSIxOSIgY3k9IjEyIiByPSIyIi8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-extension: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTIwLjUgMTFIMTlWN2MwLTEuMS0uOS0yLTItMmgtNFYzLjVDMTMgMi4xMiAxMS44OCAxIDEwLjUgMVM4IDIuMTIgOCAzLjVWNUg0Yy0xLjEgMC0xLjk5LjktMS45OSAydjMuOEgzLjVjMS40OSAwIDIuNyAxLjIxIDIuNyAyLjdzLTEuMjEgMi43LTIuNyAyLjdIMlYyMGMwIDEuMS45IDIgMiAyaDMuOHYtMS41YzAtMS40OSAxLjIxLTIuNyAyLjctMi43IDEuNDkgMCAyLjcgMS4yMSAyLjcgMi43VjIySDE3YzEuMSAwIDItLjkgMi0ydi00aDEuNWMxLjM4IDAgMi41LTEuMTIgMi41LTIuNVMyMS44OCAxMSAyMC41IDExeiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-fast-forward: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTQgMThsOC41LTZMNCA2djEyem05LTEydjEybDguNS02TDEzIDZ6Ii8+CiAgICA8L2c+Cjwvc3ZnPgo=);--jp-icon-file-upload: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTkgMTZoNnYtNmg0bC03LTctNyA3aDR6bS00IDJoMTR2Mkg1eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-file: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8cGF0aCBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIiBkPSJNMTkuMyA4LjJsLTUuNS01LjVjLS4zLS4zLS43LS41LTEuMi0uNUgzLjljLS44LjEtMS42LjktMS42IDEuOHYxNC4xYzAgLjkuNyAxLjYgMS42IDEuNmgxNC4yYy45IDAgMS42LS43IDEuNi0xLjZWOS40Yy4xLS41LS4xLS45LS40LTEuMnptLTUuOC0zLjNsMy40IDMuNmgtMy40VjQuOXptMy45IDEyLjdINC43Yy0uMSAwLS4yIDAtLjItLjJWNC43YzAtLjIuMS0uMy4yLS4zaDcuMnY0LjRzMCAuOC4zIDEuMWMuMy4zIDEuMS4zIDEuMS4zaDQuM3Y3LjJzLS4xLjItLjIuMnoiLz4KPC9zdmc+Cg==);--jp-icon-filter-list: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTEwIDE4aDR2LTJoLTR2MnpNMyA2djJoMThWNkgzem0zIDdoMTJ2LTJINnYyeiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-folder-favorite: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+CiAgPHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggY2xhc3M9ImpwLWljb24zIGpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iIzYxNjE2MSIgZD0iTTIwIDZoLThsLTItMkg0Yy0xLjEgMC0yIC45LTIgMnYxMmMwIDEuMS45IDIgMiAyaDE2YzEuMSAwIDItLjkgMi0yVjhjMC0xLjEtLjktMi0yLTJ6bS0yLjA2IDExTDE1IDE1LjI4IDEyLjA2IDE3bC43OC0zLjMzLTIuNTktMi4yNCAzLjQxLS4yOUwxNSA4bDEuMzQgMy4xNCAzLjQxLjI5LTIuNTkgMi4yNC43OCAzLjMzeiIvPgo8L3N2Zz4K);--jp-icon-folder: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8cGF0aCBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIiBkPSJNMTAgNEg0Yy0xLjEgMC0xLjk5LjktMS45OSAyTDIgMThjMCAxLjEuOSAyIDIgMmgxNmMxLjEgMCAyLS45IDItMlY4YzAtMS4xLS45LTItMi0yaC04bC0yLTJ6Ii8+Cjwvc3ZnPgo=);--jp-icon-home: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+CiAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPjxwYXRoIGNsYXNzPSJqcC1pY29uMyBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiM2MTYxNjEiIGQ9Ik0xMCAyMHYtNmg0djZoNXYtOGgzTDEyIDMgMiAxMmgzdjh6Ii8+Cjwvc3ZnPgo=);--jp-icon-html5: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDUxMiA1MTIiPgogIDxwYXRoIGNsYXNzPSJqcC1pY29uMCBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiMwMDAiIGQ9Ik0xMDguNCAwaDIzdjIyLjhoMjEuMlYwaDIzdjY5aC0yM1Y0NmgtMjF2MjNoLTIzLjJNMjA2IDIzaC0yMC4zVjBoNjMuN3YyM0gyMjl2NDZoLTIzbTUzLjUtNjloMjQuMWwxNC44IDI0LjNMMzEzLjIgMGgyNC4xdjY5aC0yM1YzNC44bC0xNi4xIDI0LjgtMTYuMS0yNC44VjY5aC0yMi42bTg5LjItNjloMjN2NDYuMmgzMi42VjY5aC01NS42Ii8+CiAgPHBhdGggY2xhc3M9ImpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iI2U0NGQyNiIgZD0iTTEwNy42IDQ3MWwtMzMtMzcwLjRoMzYyLjhsLTMzIDM3MC4yTDI1NS43IDUxMiIvPgogIDxwYXRoIGNsYXNzPSJqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiNmMTY1MjkiIGQ9Ik0yNTYgNDgwLjVWMTMxaDE0OC4zTDM3NiA0NDciLz4KICA8cGF0aCBjbGFzcz0ianAtaWNvbi1zZWxlY3RhYmxlLWludmVyc2UiIGZpbGw9IiNlYmViZWIiIGQ9Ik0xNDIgMTc2LjNoMTE0djQ1LjRoLTY0LjJsNC4yIDQ2LjVoNjB2NDUuM0gxNTQuNG0yIDIyLjhIMjAybDMuMiAzNi4zIDUwLjggMTMuNnY0Ny40bC05My4yLTI2Ii8+CiAgPHBhdGggY2xhc3M9ImpwLWljb24tc2VsZWN0YWJsZS1pbnZlcnNlIiBmaWxsPSIjZmZmIiBkPSJNMzY5LjYgMTc2LjNIMjU1Ljh2NDUuNGgxMDkuNm0tNC4xIDQ2LjVIMjU1Ljh2NDUuNGg1NmwtNS4zIDU5LTUwLjcgMTMuNnY0Ny4ybDkzLTI1LjgiLz4KPC9zdmc+Cg==);--jp-icon-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8cGF0aCBjbGFzcz0ianAtaWNvbi1icmFuZDQganAtaWNvbi1zZWxlY3RhYmxlLWludmVyc2UiIGZpbGw9IiNGRkYiIGQ9Ik0yLjIgMi4yaDE3LjV2MTcuNUgyLjJ6Ii8+CiAgPHBhdGggY2xhc3M9ImpwLWljb24tYnJhbmQwIGpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iIzNGNTFCNSIgZD0iTTIuMiAyLjJ2MTcuNWgxNy41bC4xLTE3LjVIMi4yem0xMi4xIDIuMmMxLjIgMCAyLjIgMSAyLjIgMi4ycy0xIDIuMi0yLjIgMi4yLTIuMi0xLTIuMi0yLjIgMS0yLjIgMi4yLTIuMnpNNC40IDE3LjZsMy4zLTguOCAzLjMgNi42IDIuMi0zLjIgNC40IDUuNEg0LjR6Ii8+Cjwvc3ZnPgo=);--jp-icon-inspector: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8cGF0aCBjbGFzcz0ianAtaW5zcGVjdG9yLWljb24tY29sb3IganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIiBkPSJNMjAgNEg0Yy0xLjEgMC0xLjk5LjktMS45OSAyTDIgMThjMCAxLjEuOSAyIDIgMmgxNmMxLjEgMCAyLS45IDItMlY2YzAtMS4xLS45LTItMi0yem0tNSAxNEg0di00aDExdjR6bTAtNUg0VjloMTF2NHptNSA1aC00VjloNHY5eiIvPgo8L3N2Zz4K);--jp-icon-json: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8ZyBjbGFzcz0ianAtanNvbi1pY29uLWNvbG9yIGpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iI0Y5QTgyNSI+CiAgICA8cGF0aCBkPSJNMjAuMiAxMS44Yy0xLjYgMC0xLjcuNS0xLjcgMSAwIC40LjEuOS4xIDEuMy4xLjUuMS45LjEgMS4zIDAgMS43LTEuNCAyLjMtMy41IDIuM2gtLjl2LTEuOWguNWMxLjEgMCAxLjQgMCAxLjQtLjggMC0uMyAwLS42LS4xLTEgMC0uNC0uMS0uOC0uMS0xLjIgMC0xLjMgMC0xLjggMS4zLTItMS4zLS4yLTEuMy0uNy0xLjMtMiAwLS40LjEtLjguMS0xLjIuMS0uNC4xLS43LjEtMSAwLS44LS40LS43LTEuNC0uOGgtLjVWNC4xaC45YzIuMiAwIDMuNS43IDMuNSAyLjMgMCAuNC0uMS45LS4xIDEuMy0uMS41LS4xLjktLjEgMS4zIDAgLjUuMiAxIDEuNyAxdjEuOHpNMS44IDEwLjFjMS42IDAgMS43LS41IDEuNy0xIDAtLjQtLjEtLjktLjEtMS4zLS4xLS41LS4xLS45LS4xLTEuMyAwLTEuNiAxLjQtMi4zIDMuNS0yLjNoLjl2MS45aC0uNWMtMSAwLTEuNCAwLTEuNC44IDAgLjMgMCAuNi4xIDEgMCAuMi4xLjYuMSAxIDAgMS4zIDAgMS44LTEuMyAyQzYgMTEuMiA2IDExLjcgNiAxM2MwIC40LS4xLjgtLjEgMS4yLS4xLjMtLjEuNy0uMSAxIDAgLjguMy44IDEuNC44aC41djEuOWgtLjljLTIuMSAwLTMuNS0uNi0zLjUtMi4zIDAtLjQuMS0uOS4xLTEuMy4xLS41LjEtLjkuMS0xLjMgMC0uNS0uMi0xLTEuNy0xdi0xLjl6Ii8+CiAgICA8Y2lyY2xlIGN4PSIxMSIgY3k9IjEzLjgiIHI9IjIuMSIvPgogICAgPGNpcmNsZSBjeD0iMTEiIGN5PSI4LjIiIHI9IjIuMSIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-julia: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDMyNSAzMDAiPgogIDxnIGNsYXNzPSJqcC1icmFuZDAganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjY2IzYzMzIj4KICAgIDxwYXRoIGQ9Ik0gMTUwLjg5ODQzOCAyMjUgQyAxNTAuODk4NDM4IDI2Ni40MjE4NzUgMTE3LjMyMDMxMiAzMDAgNzUuODk4NDM4IDMwMCBDIDM0LjQ3NjU2MiAzMDAgMC44OTg0MzggMjY2LjQyMTg3NSAwLjg5ODQzOCAyMjUgQyAwLjg5ODQzOCAxODMuNTc4MTI1IDM0LjQ3NjU2MiAxNTAgNzUuODk4NDM4IDE1MCBDIDExNy4zMjAzMTIgMTUwIDE1MC44OTg0MzggMTgzLjU3ODEyNSAxNTAuODk4NDM4IDIyNSIvPgogIDwvZz4KICA8ZyBjbGFzcz0ianAtYnJhbmQwIGpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iIzM4OTgyNiI+CiAgICA8cGF0aCBkPSJNIDIzNy41IDc1IEMgMjM3LjUgMTE2LjQyMTg3NSAyMDMuOTIxODc1IDE1MCAxNjIuNSAxNTAgQyAxMjEuMDc4MTI1IDE1MCA4Ny41IDExNi40MjE4NzUgODcuNSA3NSBDIDg3LjUgMzMuNTc4MTI1IDEyMS4wNzgxMjUgMCAxNjIuNSAwIEMgMjAzLjkyMTg3NSAwIDIzNy41IDMzLjU3ODEyNSAyMzcuNSA3NSIvPgogIDwvZz4KICA8ZyBjbGFzcz0ianAtYnJhbmQwIGpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iIzk1NThiMiI+CiAgICA8cGF0aCBkPSJNIDMyNC4xMDE1NjIgMjI1IEMgMzI0LjEwMTU2MiAyNjYuNDIxODc1IDI5MC41MjM0MzggMzAwIDI0OS4xMDE1NjIgMzAwIEMgMjA3LjY3OTY4OCAzMDAgMTc0LjEwMTU2MiAyNjYuNDIxODc1IDE3NC4xMDE1NjIgMjI1IEMgMTc0LjEwMTU2MiAxODMuNTc4MTI1IDIwNy42Nzk2ODggMTUwIDI0OS4xMDE1NjIgMTUwIEMgMjkwLjUyMzQzOCAxNTAgMzI0LjEwMTU2MiAxODMuNTc4MTI1IDMyNC4xMDE1NjIgMjI1Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-jupyter-favicon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUyIiBoZWlnaHQ9IjE2NSIgdmlld0JveD0iMCAwIDE1MiAxNjUiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgPGcgY2xhc3M9ImpwLWp1cHl0ZXItaWNvbi1jb2xvciIgZmlsbD0iI0YzNzcyNiI+CiAgICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLjA3ODk0NywgMTEwLjU4MjkyNykiIGQ9Ik03NS45NDIyODQyLDI5LjU4MDQ1NjEgQzQzLjMwMjM5NDcsMjkuNTgwNDU2MSAxNC43OTY3ODMyLDE3LjY1MzQ2MzQgMCwwIEM1LjUxMDgzMjExLDE1Ljg0MDY4MjkgMTUuNzgxNTM4OSwyOS41NjY3NzMyIDI5LjM5MDQ5NDcsMzkuMjc4NDE3MSBDNDIuOTk5Nyw0OC45ODk4NTM3IDU5LjI3MzcsNTQuMjA2NzgwNSA3NS45NjA1Nzg5LDU0LjIwNjc4MDUgQzkyLjY0NzQ1NzksNTQuMjA2NzgwNSAxMDguOTIxNDU4LDQ4Ljk4OTg1MzcgMTIyLjUzMDY2MywzOS4yNzg0MTcxIEMxMzYuMTM5NDUzLDI5LjU2Njc3MzIgMTQ2LjQxMDI4NCwxNS44NDA2ODI5IDE1MS45MjExNTgsMCBDMTM3LjA4Nzg2OCwxNy42NTM0NjM0IDEwOC41ODI1ODksMjkuNTgwNDU2MSA3NS45NDIyODQyLDI5LjU4MDQ1NjEgTDc1Ljk0MjI4NDIsMjkuNTgwNDU2MSBaIiAvPgogICAgPHBhdGggdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMzczNjgsIDAuNzA0ODc4KSIgZD0iTTc1Ljk3ODQ1NzksMjQuNjI2NDA3MyBDMTA4LjYxODc2MywyNC42MjY0MDczIDEzNy4xMjQ0NTgsMzYuNTUzNDQxNSAxNTEuOTIxMTU4LDU0LjIwNjc4MDUgQzE0Ni40MTAyODQsMzguMzY2MjIyIDEzNi4xMzk0NTMsMjQuNjQwMTMxNyAxMjIuNTMwNjYzLDE0LjkyODQ4NzggQzEwOC45MjE0NTgsNS4yMTY4NDM5IDkyLjY0NzQ1NzksMCA3NS45NjA1Nzg5LDAgQzU5LjI3MzcsMCA0Mi45OTk3LDUuMjE2ODQzOSAyOS4zOTA0OTQ3LDE0LjkyODQ4NzggQzE1Ljc4MTUzODksMjQuNjQwMTMxNyA1LjUxMDgzMjExLDM4LjM2NjIyMiAwLDU0LjIwNjc4MDUgQzE0LjgzMzA4MTYsMzYuNTg5OTI5MyA0My4zMzg1Njg0LDI0LjYyNjQwNzMgNzUuOTc4NDU3OSwyNC42MjY0MDczIEw3NS45Nzg0NTc5LDI0LjYyNjQwNzMgWiIgLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-jupyter: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzkiIGhlaWdodD0iNTEiIHZpZXdCb3g9IjAgMCAzOSA1MSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTYzOCAtMjI4MSkiPgogICAgIDxnIGNsYXNzPSJqcC1qdXB5dGVyLWljb24tY29sb3IiIGZpbGw9IiNGMzc3MjYiPgogICAgICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjM5Ljc0IDIzMTEuOTgpIiBkPSJNIDE4LjI2NDYgNy4xMzQxMUMgMTAuNDE0NSA3LjEzNDExIDMuNTU4NzIgNC4yNTc2IDAgMEMgMS4zMjUzOSAzLjgyMDQgMy43OTU1NiA3LjEzMDgxIDcuMDY4NiA5LjQ3MzAzQyAxMC4zNDE3IDExLjgxNTIgMTQuMjU1NyAxMy4wNzM0IDE4LjI2OSAxMy4wNzM0QyAyMi4yODIzIDEzLjA3MzQgMjYuMTk2MyAxMS44MTUyIDI5LjQ2OTQgOS40NzMwM0MgMzIuNzQyNCA3LjEzMDgxIDM1LjIxMjYgMy44MjA0IDM2LjUzOCAwQyAzMi45NzA1IDQuMjU3NiAyNi4xMTQ4IDcuMTM0MTEgMTguMjY0NiA3LjEzNDExWiIvPgogICAgICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjM5LjczIDIyODUuNDgpIiBkPSJNIDE4LjI3MzMgNS45MzkzMUMgMjYuMTIzNSA1LjkzOTMxIDMyLjk3OTMgOC44MTU4MyAzNi41MzggMTMuMDczNEMgMzUuMjEyNiA5LjI1MzAzIDMyLjc0MjQgNS45NDI2MiAyOS40Njk0IDMuNjAwNEMgMjYuMTk2MyAxLjI1ODE4IDIyLjI4MjMgMCAxOC4yNjkgMEMgMTQuMjU1NyAwIDEwLjM0MTcgMS4yNTgxOCA3LjA2ODYgMy42MDA0QyAzLjc5NTU2IDUuOTQyNjIgMS4zMjUzOSA5LjI1MzAzIDAgMTMuMDczNEMgMy41Njc0NSA4LjgyNDYzIDEwLjQyMzIgNS45MzkzMSAxOC4yNzMzIDUuOTM5MzFaIi8+CiAgICA8L2c+CiAgICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjY5LjMgMjI4MS4zMSkiIGQ9Ik0gNS44OTM1MyAyLjg0NEMgNS45MTg4OSAzLjQzMTY1IDUuNzcwODUgNC4wMTM2NyA1LjQ2ODE1IDQuNTE2NDVDIDUuMTY1NDUgNS4wMTkyMiA0LjcyMTY4IDUuNDIwMTUgNC4xOTI5OSA1LjY2ODUxQyAzLjY2NDMgNS45MTY4OCAzLjA3NDQ0IDYuMDAxNTEgMi40OTgwNSA1LjkxMTcxQyAxLjkyMTY2IDUuODIxOSAxLjM4NDYzIDUuNTYxNyAwLjk1NDg5OCA1LjE2NDAxQyAwLjUyNTE3IDQuNzY2MzMgMC4yMjIwNTYgNC4yNDkwMyAwLjA4MzkwMzcgMy42Nzc1N0MgLTAuMDU0MjQ4MyAzLjEwNjExIC0wLjAyMTIzIDIuNTA2MTcgMC4xNzg3ODEgMS45NTM2NEMgMC4zNzg3OTMgMS40MDExIDAuNzM2ODA5IDAuOTIwODE3IDEuMjA3NTQgMC41NzM1MzhDIDEuNjc4MjYgMC4yMjYyNTkgMi4yNDA1NSAwLjAyNzU5MTkgMi44MjMyNiAwLjAwMjY3MjI5QyAzLjYwMzg5IC0wLjAzMDcxMTUgNC4zNjU3MyAwLjI0OTc4OSA0Ljk0MTQyIDAuNzgyNTUxQyA1LjUxNzExIDEuMzE1MzEgNS44NTk1NiAyLjA1Njc2IDUuODkzNTMgMi44NDRaIi8+CiAgICAgIDxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE2MzkuOCAyMzIzLjgxKSIgZD0iTSA3LjQyNzg5IDMuNTgzMzhDIDcuNDYwMDggNC4zMjQzIDcuMjczNTUgNS4wNTgxOSA2Ljg5MTkzIDUuNjkyMTNDIDYuNTEwMzEgNi4zMjYwNyA1Ljk1MDc1IDYuODMxNTYgNS4yODQxMSA3LjE0NDZDIDQuNjE3NDcgNy40NTc2MyAzLjg3MzcxIDcuNTY0MTQgMy4xNDcwMiA3LjQ1MDYzQyAyLjQyMDMyIDcuMzM3MTIgMS43NDMzNiA3LjAwODcgMS4yMDE4NCA2LjUwNjk1QyAwLjY2MDMyOCA2LjAwNTIgMC4yNzg2MSA1LjM1MjY4IDAuMTA1MDE3IDQuNjMyMDJDIC0wLjA2ODU3NTcgMy45MTEzNSAtMC4wMjYyMzYxIDMuMTU0OTQgMC4yMjY2NzUgMi40NTg1NkMgMC40Nzk1ODcgMS43NjIxNyAwLjkzMTY5NyAxLjE1NzEzIDEuNTI1NzYgMC43MjAwMzNDIDIuMTE5ODMgMC4yODI5MzUgMi44MjkxNCAwLjAzMzQzOTUgMy41NjM4OSAwLjAwMzEzMzQ0QyA0LjU0NjY3IC0wLjAzNzQwMzMgNS41MDUyOSAwLjMxNjcwNiA2LjIyOTYxIDAuOTg3ODM1QyA2Ljk1MzkzIDEuNjU4OTYgNy4zODQ4NCAyLjU5MjM1IDcuNDI3ODkgMy41ODMzOEwgNy40Mjc4OSAzLjU4MzM4WiIvPgogICAgICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjM4LjM2IDIyODYuMDYpIiBkPSJNIDIuMjc0NzEgNC4zOTYyOUMgMS44NDM2MyA0LjQxNTA4IDEuNDE2NzEgNC4zMDQ0NSAxLjA0Nzk5IDQuMDc4NDNDIDAuNjc5MjY4IDMuODUyNCAwLjM4NTMyOCAzLjUyMTE0IDAuMjAzMzcxIDMuMTI2NTZDIDAuMDIxNDEzNiAyLjczMTk4IC0wLjA0MDM3OTggMi4yOTE4MyAwLjAyNTgxMTYgMS44NjE4MUMgMC4wOTIwMDMxIDEuNDMxOCAwLjI4MzIwNCAxLjAzMTI2IDAuNTc1MjEzIDAuNzEwODgzQyAwLjg2NzIyMiAwLjM5MDUxIDEuMjQ2OTEgMC4xNjQ3MDggMS42NjYyMiAwLjA2MjA1OTJDIDIuMDg1NTMgLTAuMDQwNTg5NyAyLjUyNTYxIC0wLjAxNTQ3MTQgMi45MzA3NiAwLjEzNDIzNUMgMy4zMzU5MSAwLjI4Mzk0MSAzLjY4NzkyIDAuNTUxNTA1IDMuOTQyMjIgMC45MDMwNkMgNC4xOTY1MiAxLjI1NDYyIDQuMzQxNjkgMS42NzQzNiA0LjM1OTM1IDIuMTA5MTZDIDQuMzgyOTkgMi42OTEwNyA0LjE3Njc4IDMuMjU4NjkgMy43ODU5NyAzLjY4NzQ2QyAzLjM5NTE2IDQuMTE2MjQgMi44NTE2NiA0LjM3MTE2IDIuMjc0NzEgNC4zOTYyOUwgMi4yNzQ3MSA0LjM5NjI5WiIvPgogICAgPC9nPgogIDwvZz4+Cjwvc3ZnPgo=);--jp-icon-jupyterlab-wordmark: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIHZpZXdCb3g9IjAgMCAxODYwLjggNDc1Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjIiIGZpbGw9IiM0RTRFNEUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQ4MC4xMzY0MDEsIDY0LjI3MTQ5MykiPgogICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDU4Ljg3NTU2NikiPgogICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLjA4NzYwMywgMC4xNDAyOTQpIj4KICAgICAgICA8cGF0aCBkPSJNLTQyNi45LDE2OS44YzAsNDguNy0zLjcsNjQuNy0xMy42LDc2LjRjLTEwLjgsMTAtMjUsMTUuNS0zOS43LDE1LjVsMy43LDI5IGMyMi44LDAuMyw0NC44LTcuOSw2MS45LTIzLjFjMTcuOC0xOC41LDI0LTQ0LjEsMjQtODMuM1YwSC00Mjd2MTcwLjFMLTQyNi45LDE2OS44TC00MjYuOSwxNjkuOHoiLz4KICAgICAgPC9nPgogICAgPC9nPgogICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTU1LjA0NTI5NiwgNTYuODM3MTA0KSI+CiAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEuNTYyNDUzLCAxLjc5OTg0MikiPgogICAgICAgIDxwYXRoIGQ9Ik0tMzEyLDE0OGMwLDIxLDAsMzkuNSwxLjcsNTUuNGgtMzEuOGwtMi4xLTMzLjNoLTAuOGMtNi43LDExLjYtMTYuNCwyMS4zLTI4LDI3LjkgYy0xMS42LDYuNi0yNC44LDEwLTM4LjIsOS44Yy0zMS40LDAtNjktMTcuNy02OS04OVYwaDM2LjR2MTEyLjdjMCwzOC43LDExLjYsNjQuNyw0NC42LDY0LjdjMTAuMy0wLjIsMjAuNC0zLjUsMjguOS05LjQgYzguNS01LjksMTUuMS0xNC4zLDE4LjktMjMuOWMyLjItNi4xLDMuMy0xMi41LDMuMy0xOC45VjAuMmgzNi40VjE0OEgtMzEyTC0zMTIsMTQ4eiIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzOTAuMDEzMzIyLCA1My40Nzk2MzgpIj4KICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS43MDY0NTgsIDAuMjMxNDI1KSI+CiAgICAgICAgPHBhdGggZD0iTS00NzguNiw3MS40YzAtMjYtMC44LTQ3LTEuNy02Ni43aDMyLjdsMS43LDM0LjhoMC44YzcuMS0xMi41LDE3LjUtMjIuOCwzMC4xLTI5LjcgYzEyLjUtNywyNi43LTEwLjMsNDEtOS44YzQ4LjMsMCw4NC43LDQxLjcsODQuNywxMDMuM2MwLDczLjEtNDMuNywxMDkuMi05MSwxMDkuMmMtMTIuMSwwLjUtMjQuMi0yLjItMzUtNy44IGMtMTAuOC01LjYtMTkuOS0xMy45LTI2LjYtMjQuMmgtMC44VjI5MWgtMzZ2LTIyMEwtNDc4LjYsNzEuNEwtNDc4LjYsNzEuNHogTS00NDIuNiwxMjUuNmMwLjEsNS4xLDAuNiwxMC4xLDEuNywxNS4xIGMzLDEyLjMsOS45LDIzLjMsMTkuOCwzMS4xYzkuOSw3LjgsMjIuMSwxMi4xLDM0LjcsMTIuMWMzOC41LDAsNjAuNy0zMS45LDYwLjctNzguNWMwLTQwLjctMjEuMS03NS42LTU5LjUtNzUuNiBjLTEyLjksMC40LTI1LjMsNS4xLTM1LjMsMTMuNGMtOS45LDguMy0xNi45LDE5LjctMTkuNiwzMi40Yy0xLjUsNC45LTIuMywxMC0yLjUsMTUuMVYxMjUuNkwtNDQyLjYsMTI1LjZMLTQ0Mi42LDEyNS42eiIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MDYuNzQwNzI2LCA1Ni44MzcxMDQpIj4KICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC43NTEyMjYsIDEuOTg5Mjk5KSI+CiAgICAgICAgPHBhdGggZD0iTS00NDAuOCwwbDQzLjcsMTIwLjFjNC41LDEzLjQsOS41LDI5LjQsMTIuOCw0MS43aDAuOGMzLjctMTIuMiw3LjktMjcuNywxMi44LTQyLjQgbDM5LjctMTE5LjJoMzguNUwtMzQ2LjksMTQ1Yy0yNiw2OS43LTQzLjcsMTA1LjQtNjguNiwxMjcuMmMtMTIuNSwxMS43LTI3LjksMjAtNDQuNiwyMy45bC05LjEtMzEuMSBjMTEuNy0zLjksMjIuNS0xMC4xLDMxLjgtMTguMWMxMy4yLTExLjEsMjMuNy0yNS4yLDMwLjYtNDEuMmMxLjUtMi44LDIuNS01LjcsMi45LTguOGMtMC4zLTMuMy0xLjItNi42LTIuNS05LjdMLTQ4MC4yLDAuMSBoMzkuN0wtNDQwLjgsMEwtNDQwLjgsMHoiLz4KICAgICAgPC9nPgogICAgPC9nPgogICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODIyLjc0ODEwNCwgMC4wMDAwMDApIj4KICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS40NjQwNTAsIDAuMzc4OTE0KSI+CiAgICAgICAgPHBhdGggZD0iTS00MTMuNywwdjU4LjNoNTJ2MjguMmgtNTJWMTk2YzAsMjUsNywzOS41LDI3LjMsMzkuNWM3LjEsMC4xLDE0LjItMC43LDIxLjEtMi41IGwxLjcsMjcuN2MtMTAuMywzLjctMjEuMyw1LjQtMzIuMiw1Yy03LjMsMC40LTE0LjYtMC43LTIxLjMtMy40Yy02LjgtMi43LTEyLjktNi44LTE3LjktMTIuMWMtMTAuMy0xMC45LTE0LjEtMjktMTQuMS01Mi45IFY4Ni41aC0zMVY1OC4zaDMxVjkuNkwtNDEzLjcsMEwtNDEzLjcsMHoiLz4KICAgICAgPC9nPgogICAgPC9nPgogICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTc0LjQzMzI4NiwgNTMuNDc5NjM4KSI+CiAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuOTkwMDM0LCAwLjYxMDMzOSkiPgogICAgICAgIDxwYXRoIGQ9Ik0tNDQ1LjgsMTEzYzAuOCw1MCwzMi4yLDcwLjYsNjguNiw3MC42YzE5LDAuNiwzNy45LTMsNTUuMy0xMC41bDYuMiwyNi40IGMtMjAuOSw4LjktNDMuNSwxMy4xLTY2LjIsMTIuNmMtNjEuNSwwLTk4LjMtNDEuMi05OC4zLTEwMi41Qy00ODAuMiw0OC4yLTQ0NC43LDAtMzg2LjUsMGM2NS4yLDAsODIuNyw1OC4zLDgyLjcsOTUuNyBjLTAuMSw1LjgtMC41LDExLjUtMS4yLDE3LjJoLTE0MC42SC00NDUuOEwtNDQ1LjgsMTEzeiBNLTMzOS4yLDg2LjZjMC40LTIzLjUtOS41LTYwLjEtNTAuNC02MC4xIGMtMzYuOCwwLTUyLjgsMzQuNC01NS43LDYwLjFILTMzOS4yTC0zMzkuMiw4Ni42TC0zMzkuMiw4Ni42eiIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjAxLjk2MTA1OCwgNTMuNDc5NjM4KSI+CiAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEuMTc5NjQwLCAwLjcwNTA2OCkiPgogICAgICAgIDxwYXRoIGQ9Ik0tNDc4LjYsNjhjMC0yMy45LTAuNC00NC41LTEuNy02My40aDMxLjhsMS4yLDM5LjloMS43YzkuMS0yNy4zLDMxLTQ0LjUsNTUuMy00NC41IGMzLjUtMC4xLDcsMC40LDEwLjMsMS4ydjM0LjhjLTQuMS0wLjktOC4yLTEuMy0xMi40LTEuMmMtMjUuNiwwLTQzLjcsMTkuNy00OC43LDQ3LjRjLTEsNS43LTEuNiwxMS41LTEuNywxNy4ydjEwOC4zaC0zNlY2OCBMLTQ3OC42LDY4eiIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgoKICA8ZyBjbGFzcz0ianAtaWNvbi13YXJuMCIgZmlsbD0iI0YzNzcyNiI+CiAgICA8cGF0aCBkPSJNMTM1Mi4zLDMyNi4yaDM3VjI4aC0zN1YzMjYuMnogTTE2MDQuOCwzMjYuMmMtMi41LTEzLjktMy40LTMxLjEtMy40LTQ4Ljd2LTc2IGMwLTQwLjctMTUuMS04My4xLTc3LjMtODMuMWMtMjUuNiwwLTUwLDcuMS02Ni44LDE4LjFsOC40LDI0LjRjMTQuMy05LjIsMzQtMTUuMSw1My0xNS4xYzQxLjYsMCw0Ni4yLDMwLjIsNDYuMiw0N3Y0LjIgYy03OC42LTAuNC0xMjIuMywyNi41LTEyMi4zLDc1LjZjMCwyOS40LDIxLDU4LjQsNjIuMiw1OC40YzI5LDAsNTAuOS0xNC4zLDYyLjItMzAuMmgxLjNsMi45LDI1LjZIMTYwNC44eiBNMTU2NS43LDI1Ny43IGMwLDMuOC0wLjgsOC0yLjEsMTEuOGMtNS45LDE3LjItMjIuNywzNC00OS4yLDM0Yy0xOC45LDAtMzQuOS0xMS4zLTM0LjktMzUuM2MwLTM5LjUsNDUuOC00Ni42LDg2LjItNDUuOFYyNTcuN3ogTTE2OTguNSwzMjYuMiBsMS43LTMzLjZoMS4zYzE1LjEsMjYuOSwzOC43LDM4LjIsNjguMSwzOC4yYzQ1LjQsMCw5MS4yLTM2LjEsOTEuMi0xMDguOGMwLjQtNjEuNy0zNS4zLTEwMy43LTg1LjctMTAzLjcgYy0zMi44LDAtNTYuMywxNC43LTY5LjMsMzcuNGgtMC44VjI4aC0zNi42djI0NS43YzAsMTguMS0wLjgsMzguNi0xLjcsNTIuNUgxNjk4LjV6IE0xNzA0LjgsMjA4LjJjMC01LjksMS4zLTEwLjksMi4xLTE1LjEgYzcuNi0yOC4xLDMxLjEtNDUuNCw1Ni4zLTQ1LjRjMzkuNSwwLDYwLjUsMzQuOSw2MC41LDc1LjZjMCw0Ni42LTIzLjEsNzguMS02MS44LDc4LjFjLTI2LjksMC00OC4zLTE3LjYtNTUuNS00My4zIGMtMC44LTQuMi0xLjctOC44LTEuNy0xMy40VjIwOC4yeiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-kernel: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgIDxwYXRoIGNsYXNzPSJqcC1pY29uMiIgZmlsbD0iIzYxNjE2MSIgZD0iTTE1IDlIOXY2aDZWOXptLTIgNGgtMnYtMmgydjJ6bTgtMlY5aC0yVjdjMC0xLjEtLjktMi0yLTJoLTJWM2gtMnYyaC0yVjNIOXYySDdjLTEuMSAwLTIgLjktMiAydjJIM3YyaDJ2MkgzdjJoMnYyYzAgMS4xLjkgMiAyIDJoMnYyaDJ2LTJoMnYyaDJ2LTJoMmMxLjEgMCAyLS45IDItMnYtMmgydi0yaC0ydi0yaDJ6bS00IDZIN1Y3aDEwdjEweiIvPgo8L3N2Zz4K);--jp-icon-keyboard: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8cGF0aCBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIiBkPSJNMjAgNUg0Yy0xLjEgMC0xLjk5LjktMS45OSAyTDIgMTdjMCAxLjEuOSAyIDIgMmgxNmMxLjEgMCAyLS45IDItMlY3YzAtMS4xLS45LTItMi0yem0tOSAzaDJ2MmgtMlY4em0wIDNoMnYyaC0ydi0yek04IDhoMnYySDhWOHptMCAzaDJ2Mkg4di0yem0tMSAySDV2LTJoMnYyem0wLTNINVY4aDJ2MnptOSA3SDh2LTJoOHYyem0wLTRoLTJ2LTJoMnYyem0wLTNoLTJWOGgydjJ6bTMgM2gtMnYtMmgydjJ6bTAtM2gtMlY4aDJ2MnoiLz4KPC9zdmc+Cg==);--jp-icon-launch: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIj4KICAgIDxwYXRoIGQ9Ik0yNiwyOEg2YTIuMDAyNywyLjAwMjcsMCwwLDEtMi0yVjZBMi4wMDI3LDIuMDAyNywwLDAsMSw2LDRIMTZWNkg2VjI2SDI2VjE2aDJWMjZBMi4wMDI3LDIuMDAyNywwLDAsMSwyNiwyOFoiLz4KICAgIDxwb2x5Z29uIHBvaW50cz0iMjAgMiAyMCA0IDI2LjU4NiA0IDE4IDEyLjU4NiAxOS40MTQgMTQgMjggNS40MTQgMjggMTIgMzAgMTIgMzAgMiAyMCAyIi8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-launcher: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8cGF0aCBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIiBkPSJNMTkgMTlINVY1aDdWM0g1YTIgMiAwIDAwLTIgMnYxNGEyIDIgMCAwMDIgMmgxNGMxLjEgMCAyLS45IDItMnYtN2gtMnY3ek0xNCAzdjJoMy41OWwtOS44MyA5LjgzIDEuNDEgMS40MUwxOSA2LjQxVjEwaDJWM2gtN3oiLz4KPC9zdmc+Cg==);--jp-icon-line-form: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgIDxwYXRoIGZpbGw9IndoaXRlIiBkPSJNNS44OCA0LjEyTDEzLjc2IDEybC03Ljg4IDcuODhMOCAyMmwxMC0xMEw4IDJ6Ii8+Cjwvc3ZnPgo=);--jp-icon-link: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTMuOSAxMmMwLTEuNzEgMS4zOS0zLjEgMy4xLTMuMWg0VjdIN2MtMi43NiAwLTUgMi4yNC01IDVzMi4yNCA1IDUgNWg0di0xLjlIN2MtMS43MSAwLTMuMS0xLjM5LTMuMS0zLjF6TTggMTNoOHYtMkg4djJ6bTktNmgtNHYxLjloNGMxLjcxIDAgMy4xIDEuMzkgMy4xIDMuMXMtMS4zOSAzLjEtMy4xIDMuMWgtNFYxN2g0YzIuNzYgMCA1LTIuMjQgNS01cy0yLjI0LTUtNS01eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-list: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgIDxwYXRoIGNsYXNzPSJqcC1pY29uMiBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiM2MTYxNjEiIGQ9Ik0xOSA1djE0SDVWNWgxNG0xLjEtMkgzLjljLS41IDAtLjkuNC0uOS45djE2LjJjMCAuNC40LjkuOS45aDE2LjJjLjQgMCAuOS0uNS45LS45VjMuOWMwLS41LS41LS45LS45LS45ek0xMSA3aDZ2MmgtNlY3em0wIDRoNnYyaC02di0yem0wIDRoNnYyaC02ek03IDdoMnYySDd6bTAgNGgydjJIN3ptMCA0aDJ2Mkg3eiIvPgo8L3N2Zz4=);--jp-icon-listings-info: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MC45NzggNTAuOTc4IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MC45NzggNTAuOTc4OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+Cgk8Zz4KCQk8cGF0aCBzdHlsZT0iZmlsbDojMDEwMDAyOyIgZD0iTTQzLjUyLDcuNDU4QzM4LjcxMSwyLjY0OCwzMi4zMDcsMCwyNS40ODksMEMxOC42NywwLDEyLjI2NiwyLjY0OCw3LjQ1OCw3LjQ1OAoJCQljLTkuOTQzLDkuOTQxLTkuOTQzLDI2LjExOSwwLDM2LjA2MmM0LjgwOSw0LjgwOSwxMS4yMTIsNy40NTYsMTguMDMxLDcuNDU4YzAsMCwwLjAwMSwwLDAuMDAyLDAKCQkJYzYuODE2LDAsMTMuMjIxLTIuNjQ4LDE4LjAyOS03LjQ1OGM0LjgwOS00LjgwOSw3LjQ1Ny0xMS4yMTIsNy40NTctMTguMDNDNTAuOTc3LDE4LjY3LDQ4LjMyOCwxMi4yNjYsNDMuNTIsNy40NTh6CgkJCSBNNDIuMTA2LDQyLjEwNWMtNC40MzIsNC40MzEtMTAuMzMyLDYuODcyLTE2LjYxNSw2Ljg3MmgtMC4wMDJjLTYuMjg1LTAuMDAxLTEyLjE4Ny0yLjQ0MS0xNi42MTctNi44NzIKCQkJYy05LjE2Mi05LjE2My05LjE2Mi0yNC4wNzEsMC0zMy4yMzNDMTMuMzAzLDQuNDQsMTkuMjA0LDIsMjUuNDg5LDJjNi4yODQsMCwxMi4xODYsMi40NCwxNi42MTcsNi44NzIKCQkJYzQuNDMxLDQuNDMxLDYuODcxLDEwLjMzMiw2Ljg3MSwxNi42MTdDNDguOTc3LDMxLjc3Miw0Ni41MzYsMzcuNjc1LDQyLjEwNiw0Mi4xMDV6Ii8+CgkJPHBhdGggc3R5bGU9ImZpbGw6IzAxMDAwMjsiIGQ9Ik0yMy41NzgsMzIuMjE4Yy0wLjAyMy0xLjczNCwwLjE0My0zLjA1OSwwLjQ5Ni0zLjk3MmMwLjM1My0wLjkxMywxLjExLTEuOTk3LDIuMjcyLTMuMjUzCgkJCWMwLjQ2OC0wLjUzNiwwLjkyMy0xLjA2MiwxLjM2Ny0xLjU3NWMwLjYyNi0wLjc1MywxLjEwNC0xLjQ3OCwxLjQzNi0yLjE3NWMwLjMzMS0wLjcwNywwLjQ5NS0xLjU0MSwwLjQ5NS0yLjUKCQkJYzAtMS4wOTYtMC4yNi0yLjA4OC0wLjc3OS0yLjk3OWMtMC41NjUtMC44NzktMS41MDEtMS4zMzYtMi44MDYtMS4zNjljLTEuODAyLDAuMDU3LTIuOTg1LDAuNjY3LTMuNTUsMS44MzIKCQkJYy0wLjMwMSwwLjUzNS0wLjUwMywxLjE0MS0wLjYwNywxLjgxNGMtMC4xMzksMC43MDctMC4yMDcsMS40MzItMC4yMDcsMi4xNzRoLTIuOTM3Yy0wLjA5MS0yLjIwOCwwLjQwNy00LjExNCwxLjQ5My01LjcxOQoJCQljMS4wNjItMS42NCwyLjg1NS0yLjQ4MSw1LjM3OC0yLjUyN2MyLjE2LDAuMDIzLDMuODc0LDAuNjA4LDUuMTQxLDEuNzU4YzEuMjc4LDEuMTYsMS45MjksMi43NjQsMS45NSw0LjgxMQoJCQljMCwxLjE0Mi0wLjEzNywyLjExMS0wLjQxLDIuOTExYy0wLjMwOSwwLjg0NS0wLjczMSwxLjU5My0xLjI2OCwyLjI0M2MtMC40OTIsMC42NS0xLjA2OCwxLjMxOC0xLjczLDIuMDAyCgkJCWMtMC42NSwwLjY5Ny0xLjMxMywxLjQ3OS0xLjk4NywyLjM0NmMtMC4yMzksMC4zNzctMC40MjksMC43NzctMC41NjUsMS4xOTljLTAuMTYsMC45NTktMC4yMTcsMS45NTEtMC4xNzEsMi45NzkKCQkJQzI2LjU4OSwzMi4yMTgsMjMuNTc4LDMyLjIxOCwyMy41NzgsMzIuMjE4eiBNMjMuNTc4LDM4LjIydi0zLjQ4NGgzLjA3NnYzLjQ4NEgyMy41Nzh6Ii8+Cgk8L2c+Cjwvc3ZnPgo=);--jp-icon-markdown: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8cGF0aCBjbGFzcz0ianAtaWNvbi1jb250cmFzdDAganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjN0IxRkEyIiBkPSJNNSAxNC45aDEybC02LjEgNnptOS40LTYuOGMwLTEuMy0uMS0yLjktLjEtNC41LS40IDEuNC0uOSAyLjktMS4zIDQuM2wtMS4zIDQuM2gtMkw4LjUgNy45Yy0uNC0xLjMtLjctMi45LTEtNC4zLS4xIDEuNi0uMSAzLjItLjIgNC42TDcgMTIuNEg0LjhsLjctMTFoMy4zTDEwIDVjLjQgMS4yLjcgMi43IDEgMy45LjMtMS4yLjctMi42IDEtMy45bDEuMi0zLjdoMy4zbC42IDExaC0yLjRsLS4zLTQuMnoiLz4KPC9zdmc+Cg==);--jp-icon-move-down: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggY2xhc3M9ImpwLWljb24zIiBkPSJNMTIuNDcxIDcuNTI4OTlDMTIuNzYzMiA3LjIzNjg0IDEyLjc2MzIgNi43NjMxNiAxMi40NzEgNi40NzEwMVY2LjQ3MTAxQzEyLjE3OSA2LjE3OTA1IDExLjcwNTcgNi4xNzg4NCAxMS40MTM1IDYuNDcwNTRMNy43NSAxMC4xMjc1VjEuNzVDNy43NSAxLjMzNTc5IDcuNDE0MjEgMSA3IDFWMUM2LjU4NTc5IDEgNi4yNSAxLjMzNTc5IDYuMjUgMS43NVYxMC4xMjc1TDIuNTk3MjYgNi40NjgyMkMyLjMwMzM4IDYuMTczODEgMS44MjY0MSA2LjE3MzU5IDEuNTMyMjYgNi40Njc3NFY2LjQ2Nzc0QzEuMjM4MyA2Ljc2MTcgMS4yMzgzIDcuMjM4MyAxLjUzMjI2IDcuNTMyMjZMNi4yOTI4OSAxMi4yOTI5QzYuNjgzNDIgMTIuNjgzNCA3LjMxNjU4IDEyLjY4MzQgNy43MDcxMSAxMi4yOTI5TDEyLjQ3MSA3LjUyODk5WiIgZmlsbD0iIzYxNjE2MSIvPgo8L3N2Zz4K);--jp-icon-move-up: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggY2xhc3M9ImpwLWljb24zIiBkPSJNMS41Mjg5OSA2LjQ3MTAxQzEuMjM2ODQgNi43NjMxNiAxLjIzNjg0IDcuMjM2ODQgMS41Mjg5OSA3LjUyODk5VjcuNTI4OTlDMS44MjA5NSA3LjgyMDk1IDIuMjk0MjYgNy44MjExNiAyLjU4NjQ5IDcuNTI5NDZMNi4yNSAzLjg3MjVWMTIuMjVDNi4yNSAxMi42NjQyIDYuNTg1NzkgMTMgNyAxM1YxM0M3LjQxNDIxIDEzIDcuNzUgMTIuNjY0MiA3Ljc1IDEyLjI1VjMuODcyNUwxMS40MDI3IDcuNTMxNzhDMTEuNjk2NiA3LjgyNjE5IDEyLjE3MzYgNy44MjY0MSAxMi40Njc3IDcuNTMyMjZWNy41MzIyNkMxMi43NjE3IDcuMjM4MyAxMi43NjE3IDYuNzYxNyAxMi40Njc3IDYuNDY3NzRMNy43MDcxMSAxLjcwNzExQzcuMzE2NTggMS4zMTY1OCA2LjY4MzQyIDEuMzE2NTggNi4yOTI4OSAxLjcwNzExTDEuNTI4OTkgNi40NzEwMVoiIGZpbGw9IiM2MTYxNjEiLz4KPC9zdmc+Cg==);--jp-icon-new-folder: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTIwIDZoLThsLTItMkg0Yy0xLjExIDAtMS45OS44OS0xLjk5IDJMMiAxOGMwIDEuMTEuODkgMiAyIDJoMTZjMS4xMSAwIDItLjg5IDItMlY4YzAtMS4xMS0uODktMi0yLTJ6bS0xIDhoLTN2M2gtMnYtM2gtM3YtMmgzVjloMnYzaDN2MnoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-not-trusted: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI1IDI1Ij4KICAgIDxwYXRoIGNsYXNzPSJqcC1pY29uMiIgc3Ryb2tlPSIjMzMzMzMzIiBzdHJva2Utd2lkdGg9IjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMgMykiIGQ9Ik0xLjg2MDk0IDExLjQ0MDlDMC44MjY0NDggOC43NzAyNyAwLjg2Mzc3OSA2LjA1NzY0IDEuMjQ5MDcgNC4xOTkzMkMyLjQ4MjA2IDMuOTMzNDcgNC4wODA2OCAzLjQwMzQ3IDUuNjAxMDIgMi44NDQ5QzcuMjM1NDkgMi4yNDQ0IDguODU2NjYgMS41ODE1IDkuOTg3NiAxLjA5NTM5QzExLjA1OTcgMS41ODM0MSAxMi42MDk0IDIuMjQ0NCAxNC4yMTggMi44NDMzOUMxNS43NTAzIDMuNDEzOTQgMTcuMzk5NSAzLjk1MjU4IDE4Ljc1MzkgNC4yMTM4NUMxOS4xMzY0IDYuMDcxNzcgMTkuMTcwOSA4Ljc3NzIyIDE4LjEzOSAxMS40NDA5QzE3LjAzMDMgMTQuMzAzMiAxNC42NjY4IDE3LjE4NDQgOS45OTk5OSAxOC45MzU0QzUuMzMzMTkgMTcuMTg0NCAyLjk2OTY4IDE0LjMwMzIgMS44NjA5NCAxMS40NDA5WiIvPgogICAgPHBhdGggY2xhc3M9ImpwLWljb24yIiBzdHJva2U9IiMzMzMzMzMiIHN0cm9rZS13aWR0aD0iMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOS4zMTU5MiA5LjMyMDMxKSIgZD0iTTcuMzY4NDIgMEwwIDcuMzY0NzkiLz4KICAgIDxwYXRoIGNsYXNzPSJqcC1pY29uMiIgc3Ryb2tlPSIjMzMzMzMzIiBzdHJva2Utd2lkdGg9IjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkuMzE1OTIgMTYuNjgzNikgc2NhbGUoMSAtMSkiIGQ9Ik03LjM2ODQyIDBMMCA3LjM2NDc5Ii8+Cjwvc3ZnPgo=);--jp-icon-notebook: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8ZyBjbGFzcz0ianAtbm90ZWJvb2staWNvbi1jb2xvciBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiNFRjZDMDAiPgogICAgPHBhdGggZD0iTTE4LjcgMy4zdjE1LjRIMy4zVjMuM2gxNS40bTEuNS0xLjVIMS44djE4LjNoMTguM2wuMS0xOC4zeiIvPgogICAgPHBhdGggZD0iTTE2LjUgMTYuNWwtNS40LTQuMy01LjYgNC4zdi0xMWgxMXoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-numbering: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyOCAyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CgkJPHBhdGggZD0iTTQgMTlINlYxOS41SDVWMjAuNUg2VjIxSDRWMjJIN1YxOEg0VjE5Wk01IDEwSDZWNkg0VjdINVYxMFpNNCAxM0g1LjhMNCAxNS4xVjE2SDdWMTVINS4yTDcgMTIuOVYxMkg0VjEzWk05IDdWOUgyM1Y3SDlaTTkgMjFIMjNWMTlIOVYyMVpNOSAxNUgyM1YxM0g5VjE1WiIvPgoJPC9nPgo8L3N2Zz4K);--jp-icon-offline-bolt: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTEyIDIuMDJjLTUuNTEgMC05Ljk4IDQuNDctOS45OCA5Ljk4czQuNDcgOS45OCA5Ljk4IDkuOTggOS45OC00LjQ3IDkuOTgtOS45OFMxNy41MSAyLjAyIDEyIDIuMDJ6TTExLjQ4IDIwdi02LjI2SDhMMTMgNHY2LjI2aDMuMzVMMTEuNDggMjB6Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-palette: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTE4IDEzVjIwSDRWNkg5LjAyQzkuMDcgNS4yOSA5LjI0IDQuNjIgOS41IDRINEMyLjkgNCAyIDQuOSAyIDZWMjBDMiAyMS4xIDIuOSAyMiA0IDIySDE4QzE5LjEgMjIgMjAgMjEuMSAyMCAyMFYxNUwxOCAxM1pNMTkuMyA4Ljg5QzE5Ljc0IDguMTkgMjAgNy4zOCAyMCA2LjVDMjAgNC4wMSAxNy45OSAyIDE1LjUgMkMxMy4wMSAyIDExIDQuMDEgMTEgNi41QzExIDguOTkgMTMuMDEgMTEgMTUuNDkgMTFDMTYuMzcgMTEgMTcuMTkgMTAuNzQgMTcuODggMTAuM0wyMSAxMy40MkwyMi40MiAxMkwxOS4zIDguODlaTTE1LjUgOUMxNC4xMiA5IDEzIDcuODggMTMgNi41QzEzIDUuMTIgMTQuMTIgNCAxNS41IDRDMTYuODggNCAxOCA1LjEyIDE4IDYuNUMxOCA3Ljg4IDE2Ljg4IDkgMTUuNSA5WiIvPgogICAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDZIOS4wMTg5NEM5LjAwNjM5IDYuMTY1MDIgOSA2LjMzMTc2IDkgNi41QzkgOC44MTU3NyAxMC4yMTEgMTAuODQ4NyAxMi4wMzQzIDEySDlWMTRIMTZWMTIuOTgxMUMxNi41NzAzIDEyLjkzNzcgMTcuMTIgMTIuODIwNyAxNy42Mzk2IDEyLjYzOTZMMTggMTNWMjBINFY2Wk04IDhINlYxMEg4VjhaTTYgMTJIOFYxNEg2VjEyWk04IDE2SDZWMThIOFYxNlpNOSAxNkgxNlYxOEg5VjE2WiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-paste: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTE5IDJoLTQuMThDMTQuNC44NCAxMy4zIDAgMTIgMGMtMS4zIDAtMi40Ljg0LTIuODIgMkg1Yy0xLjEgMC0yIC45LTIgMnYxNmMwIDEuMS45IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjRjMC0xLjEtLjktMi0yLTJ6bS03IDBjLjU1IDAgMSAuNDUgMSAxcy0uNDUgMS0xIDEtMS0uNDUtMS0xIC40NS0xIDEtMXptNyAxOEg1VjRoMnYzaDEwVjRoMnYxNnoiLz4KICAgIDwvZz4KPC9zdmc+Cg==);--jp-icon-pdf: url(data:image/svg+xml;base64,PHN2ZwogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMiAyMiIgd2lkdGg9IjE2Ij4KICAgIDxwYXRoIHRyYW5zZm9ybT0icm90YXRlKDQ1KSIgY2xhc3M9ImpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iI0ZGMkEyQSIKICAgICAgIGQ9Im0gMjIuMzQ0MzY5LC0zLjAxNjM2NDIgaCA1LjYzODYwNCB2IDEuNTc5MjQzMyBoIC0zLjU0OTIyNyB2IDEuNTA4NjkyOTkgaCAzLjMzNzU3NiBWIDEuNjUwODE1NCBoIC0zLjMzNzU3NiB2IDMuNDM1MjYxMyBoIC0yLjA4OTM3NyB6IG0gLTcuMTM2NDQ0LDEuNTc5MjQzMyB2IDQuOTQzOTU0MyBoIDAuNzQ4OTIgcSAxLjI4MDc2MSwwIDEuOTUzNzAzLC0wLjYzNDk1MzUgMC42NzgzNjksLTAuNjM0OTUzNSAwLjY3ODM2OSwtMS44NDUxNjQxIDAsLTEuMjA0NzgzNTUgLTAuNjcyOTQyLC0xLjgzNDMxMDExIC0wLjY3Mjk0MiwtMC42Mjk1MjY1OSAtMS45NTkxMywtMC42Mjk1MjY1OSB6IG0gLTIuMDg5Mzc3LC0xLjU3OTI0MzMgaCAyLjIwMzM0MyBxIDEuODQ1MTY0LDAgMi43NDYwMzksMC4yNjU5MjA3IDAuOTA2MzAxLDAuMjYwNDkzNyAxLjU1MjEwOCwwLjg5MDAyMDMgMC41Njk4MywwLjU0ODEyMjMgMC44NDY2MDUsMS4yNjQ0ODAwNiAwLjI3Njc3NCwwLjcxNjM1NzgxIDAuMjc2Nzc0LDEuNjIyNjU4OTQgMCwwLjkxNzE1NTEgLTAuMjc2Nzc0LDEuNjM4OTM5OSAtMC4yNzY3NzUsMC43MTYzNTc4IC0wLjg0NjYwNSwxLjI2NDQ4IC0wLjY1MTIzNCwwLjYyOTUyNjYgLTEuNTYyOTYyLDAuODk1NDQ3MyAtMC45MTE3MjgsMC4yNjA0OTM3IC0yLjczNTE4NSwwLjI2MDQ5MzcgaCAtMi4yMDMzNDMgeiBtIC04LjE0NTg1NjUsMCBoIDMuNDY3ODIzIHEgMS41NDY2ODE2LDAgMi4zNzE1Nzg1LDAuNjg5MjIzIDAuODMwMzI0LDAuNjgzNzk2MSAwLjgzMDMyNCwxLjk1MzcwMzE0IDAsMS4yNzUzMzM5NyAtMC44MzAzMjQsMS45NjQ1NTcwNiBRIDkuOTg3MTk2MSwyLjI3NDkxNSA4LjQ0MDUxNDUsMi4yNzQ5MTUgSCA3LjA2MjA2ODQgViA1LjA4NjA3NjcgSCA0Ljk3MjY5MTUgWiBtIDIuMDg5Mzc2OSwxLjUxNDExOTkgdiAyLjI2MzAzOTQzIGggMS4xNTU5NDEgcSAwLjYwNzgxODgsMCAwLjkzODg2MjksLTAuMjkzMDU1NDcgMC4zMzEwNDQxLC0wLjI5ODQ4MjQxIDAuMzMxMDQ0MSwtMC44NDExNzc3MiAwLC0wLjU0MjY5NTMxIC0wLjMzMTA0NDEsLTAuODM1NzUwNzQgLTAuMzMxMDQ0MSwtMC4yOTMwNTU1IC0wLjkzODg2MjksLTAuMjkzMDU1NSB6IgovPgo8L3N2Zz4K);--jp-icon-python: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iLTEwIC0xMCAxMzEuMTYxMzYxNjk0MzM1OTQgMTMyLjM4ODk5OTkzODk2NDg0Ij4KICA8cGF0aCBjbGFzcz0ianAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjMzA2OTk4IiBkPSJNIDU0LjkxODc4NSw5LjE5Mjc0MjFlLTQgQyA1MC4zMzUxMzIsMC4wMjIyMTcyNyA0NS45NTc4NDYsMC40MTMxMzY5NyA0Mi4xMDYyODUsMS4wOTQ2NjkzIDMwLjc2MDA2OSwzLjA5OTE3MzEgMjguNzAwMDM2LDcuMjk0NzcxNCAyOC43MDAwMzUsMTUuMDMyMTY5IHYgMTAuMjE4NzUgaCAyNi44MTI1IHYgMy40MDYyNSBoIC0yNi44MTI1IC0xMC4wNjI1IGMgLTcuNzkyNDU5LDAgLTE0LjYxNTc1ODgsNC42ODM3MTcgLTE2Ljc0OTk5OTgsMTMuNTkzNzUgLTIuNDYxODE5OTgsMTAuMjEyOTY2IC0yLjU3MTAxNTA4LDE2LjU4NjAyMyAwLDI3LjI1IDEuOTA1OTI4Myw3LjkzNzg1MiA2LjQ1NzU0MzIsMTMuNTkzNzQ4IDE0LjI0OTk5OTgsMTMuNTkzNzUgaCA5LjIxODc1IHYgLTEyLjI1IGMgMCwtOC44NDk5MDIgNy42NTcxNDQsLTE2LjY1NjI0OCAxNi43NSwtMTYuNjU2MjUgaCAyNi43ODEyNSBjIDcuNDU0OTUxLDAgMTMuNDA2MjUzLC02LjEzODE2NCAxMy40MDYyNSwtMTMuNjI1IHYgLTI1LjUzMTI1IGMgMCwtNy4yNjYzMzg2IC02LjEyOTk4LC0xMi43MjQ3NzcxIC0xMy40MDYyNSwtMTMuOTM3NDk5NyBDIDY0LjI4MTU0OCwwLjMyNzk0Mzk3IDU5LjUwMjQzOCwtMC4wMjAzNzkwMyA1NC45MTg3ODUsOS4xOTI3NDIxZS00IFogbSAtMTQuNSw4LjIxODc1MDEyNTc5IGMgMi43Njk1NDcsMCA1LjAzMTI1LDIuMjk4NjQ1NiA1LjAzMTI1LDUuMTI0OTk5NiAtMmUtNiwyLjgxNjMzNiAtMi4yNjE3MDMsNS4wOTM3NSAtNS4wMzEyNSw1LjA5Mzc1IC0yLjc3OTQ3NiwtMWUtNiAtNS4wMzEyNSwtMi4yNzc0MTUgLTUuMDMxMjUsLTUuMDkzNzUgLTEwZS03LC0yLjgyNjM1MyAyLjI1MTc3NCwtNS4xMjQ5OTk2IDUuMDMxMjUsLTUuMTI0OTk5NiB6Ii8+CiAgPHBhdGggY2xhc3M9ImpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iI2ZmZDQzYiIgZD0ibSA4NS42Mzc1MzUsMjguNjU3MTY5IHYgMTEuOTA2MjUgYyAwLDkuMjMwNzU1IC03LjgyNTg5NSwxNi45OTk5OTkgLTE2Ljc1LDE3IGggLTI2Ljc4MTI1IGMgLTcuMzM1ODMzLDAgLTEzLjQwNjI0OSw2LjI3ODQ4MyAtMTMuNDA2MjUsMTMuNjI1IHYgMjUuNTMxMjQ3IGMgMCw3LjI2NjM0NCA2LjMxODU4OCwxMS41NDAzMjQgMTMuNDA2MjUsMTMuNjI1MDA0IDguNDg3MzMxLDIuNDk1NjEgMTYuNjI2MjM3LDIuOTQ2NjMgMjYuNzgxMjUsMCA2Ljc1MDE1NSwtMS45NTQzOSAxMy40MDYyNTMsLTUuODg3NjEgMTMuNDA2MjUsLTEzLjYyNTAwNCBWIDg2LjUwMDkxOSBoIC0yNi43ODEyNSB2IC0zLjQwNjI1IGggMjYuNzgxMjUgMTMuNDA2MjU0IGMgNy43OTI0NjEsMCAxMC42OTYyNTEsLTUuNDM1NDA4IDEzLjQwNjI0MSwtMTMuNTkzNzUgMi43OTkzMywtOC4zOTg4ODYgMi42ODAyMiwtMTYuNDc1Nzc2IDAsLTI3LjI1IC0xLjkyNTc4LC03Ljc1NzQ0MSAtNS42MDM4NywtMTMuNTkzNzUgLTEzLjQwNjI0MSwtMTMuNTkzNzUgeiBtIC0xNS4wNjI1LDY0LjY1NjI1IGMgMi43Nzk0NzgsM2UtNiA1LjAzMTI1LDIuMjc3NDE3IDUuMDMxMjUsNS4wOTM3NDcgLTJlLTYsMi44MjYzNTQgLTIuMjUxNzc1LDUuMTI1MDA0IC01LjAzMTI1LDUuMTI1MDA0IC0yLjc2OTU1LDAgLTUuMDMxMjUsLTIuMjk4NjUgLTUuMDMxMjUsLTUuMTI1MDA0IDJlLTYsLTIuODE2MzMgMi4yNjE2OTcsLTUuMDkzNzQ3IDUuMDMxMjUsLTUuMDkzNzQ3IHoiLz4KPC9zdmc+Cg==);--jp-icon-r-kernel: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8cGF0aCBjbGFzcz0ianAtaWNvbi1jb250cmFzdDMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjMjE5NkYzIiBkPSJNNC40IDIuNWMxLjItLjEgMi45LS4zIDQuOS0uMyAyLjUgMCA0LjEuNCA1LjIgMS4zIDEgLjcgMS41IDEuOSAxLjUgMy41IDAgMi0xLjQgMy41LTIuOSA0LjEgMS4yLjQgMS43IDEuNiAyLjIgMyAuNiAxLjkgMSAzLjkgMS4zIDQuNmgtMy44Yy0uMy0uNC0uOC0xLjctMS4yLTMuN3MtMS4yLTIuNi0yLjYtMi42aC0uOXY2LjRINC40VjIuNXptMy43IDYuOWgxLjRjMS45IDAgMi45LS45IDIuOS0yLjNzLTEtMi4zLTIuOC0yLjNjLS43IDAtMS4zIDAtMS42LjJ2NC41aC4xdi0uMXoiLz4KPC9zdmc+Cg==);--jp-icon-react: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMTUwIDE1MCA1NDEuOSAyOTUuMyI+CiAgPGcgY2xhc3M9ImpwLWljb24tYnJhbmQyIGpwLWljb24tc2VsZWN0YWJsZSIgZmlsbD0iIzYxREFGQiI+CiAgICA8cGF0aCBkPSJNNjY2LjMgMjk2LjVjMC0zMi41LTQwLjctNjMuMy0xMDMuMS04Mi40IDE0LjQtNjMuNiA4LTExNC4yLTIwLjItMTMwLjQtNi41LTMuOC0xNC4xLTUuNi0yMi40LTUuNnYyMi4zYzQuNiAwIDguMy45IDExLjQgMi42IDEzLjYgNy44IDE5LjUgMzcuNSAxNC45IDc1LjctMS4xIDkuNC0yLjkgMTkuMy01LjEgMjkuNC0xOS42LTQuOC00MS04LjUtNjMuNS0xMC45LTEzLjUtMTguNS0yNy41LTM1LjMtNDEuNi01MCAzMi42LTMwLjMgNjMuMi00Ni45IDg0LTQ2LjlWNzhjLTI3LjUgMC02My41IDE5LjYtOTkuOSA1My42LTM2LjQtMzMuOC03Mi40LTUzLjItOTkuOS01My4ydjIyLjNjMjAuNyAwIDUxLjQgMTYuNSA4NCA0Ni42LTE0IDE0LjctMjggMzEuNC00MS4zIDQ5LjktMjIuNiAyLjQtNDQgNi4xLTYzLjYgMTEtMi4zLTEwLTQtMTkuNy01LjItMjktNC43LTM4LjIgMS4xLTY3LjkgMTQuNi03NS44IDMtMS44IDYuOS0yLjYgMTEuNS0yLjZWNzguNWMtOC40IDAtMTYgMS44LTIyLjYgNS42LTI4LjEgMTYuMi0zNC40IDY2LjctMTkuOSAxMzAuMS02Mi4yIDE5LjItMTAyLjcgNDkuOS0xMDIuNyA4Mi4zIDAgMzIuNSA0MC43IDYzLjMgMTAzLjEgODIuNC0xNC40IDYzLjYtOCAxMTQuMiAyMC4yIDEzMC40IDYuNSAzLjggMTQuMSA1LjYgMjIuNSA1LjYgMjcuNSAwIDYzLjUtMTkuNiA5OS45LTUzLjYgMzYuNCAzMy44IDcyLjQgNTMuMiA5OS45IDUzLjIgOC40IDAgMTYtMS44IDIyLjYtNS42IDI4LjEtMTYuMiAzNC40LTY2LjcgMTkuOS0xMzAuMSA2Mi0xOS4xIDEwMi41LTQ5LjkgMTAyLjUtODIuM3ptLTEzMC4yLTY2LjdjLTMuNyAxMi45LTguMyAyNi4yLTEzLjUgMzkuNS00LjEtOC04LjQtMTYtMTMuMS0yNC00LjYtOC05LjUtMTUuOC0xNC40LTIzLjQgMTQuMiAyLjEgMjcuOSA0LjcgNDEgNy45em0tNDUuOCAxMDYuNWMtNy44IDEzLjUtMTUuOCAyNi4zLTI0LjEgMzguMi0xNC45IDEuMy0zMCAyLTQ1LjIgMi0xNS4xIDAtMzAuMi0uNy00NS0xLjktOC4zLTExLjktMTYuNC0yNC42LTI0LjItMzgtNy42LTEzLjEtMTQuNS0yNi40LTIwLjgtMzkuOCA2LjItMTMuNCAxMy4yLTI2LjggMjAuNy0zOS45IDcuOC0xMy41IDE1LjgtMjYuMyAyNC4xLTM4LjIgMTQuOS0xLjMgMzAtMiA0NS4yLTIgMTUuMSAwIDMwLjIuNyA0NSAxLjkgOC4zIDExLjkgMTYuNCAyNC42IDI0LjIgMzggNy42IDEzLjEgMTQuNSAyNi40IDIwLjggMzkuOC02LjMgMTMuNC0xMy4yIDI2LjgtMjAuNyAzOS45em0zMi4zLTEzYzUuNCAxMy40IDEwIDI2LjggMTMuOCAzOS44LTEzLjEgMy4yLTI2LjkgNS45LTQxLjIgOCA0LjktNy43IDkuOC0xNS42IDE0LjQtMjMuNyA0LjYtOCA4LjktMTYuMSAxMy0yNC4xek00MjEuMiA0MzBjLTkuMy05LjYtMTguNi0yMC4zLTI3LjgtMzIgOSAuNCAxOC4yLjcgMjcuNS43IDkuNCAwIDE4LjctLjIgMjcuOC0uNy05IDExLjctMTguMyAyMi40LTI3LjUgMzJ6bS03NC40LTU4LjljLTE0LjItMi4xLTI3LjktNC43LTQxLTcuOSAzLjctMTIuOSA4LjMtMjYuMiAxMy41LTM5LjUgNC4xIDggOC40IDE2IDEzLjEgMjQgNC43IDggOS41IDE1LjggMTQuNCAyMy40ek00MjAuNyAxNjNjOS4zIDkuNiAxOC42IDIwLjMgMjcuOCAzMi05LS40LTE4LjItLjctMjcuNS0uNy05LjQgMC0xOC43LjItMjcuOC43IDktMTEuNyAxOC4zLTIyLjQgMjcuNS0zMnptLTc0IDU4LjljLTQuOSA3LjctOS44IDE1LjYtMTQuNCAyMy43LTQuNiA4LTguOSAxNi0xMyAyNC01LjQtMTMuNC0xMC0yNi44LTEzLjgtMzkuOCAxMy4xLTMuMSAyNi45LTUuOCA0MS4yLTcuOXptLTkwLjUgMTI1LjJjLTM1LjQtMTUuMS01OC4zLTM0LjktNTguMy01MC42IDAtMTUuNyAyMi45LTM1LjYgNTguMy01MC42IDguNi0zLjcgMTgtNyAyNy43LTEwLjEgNS43IDE5LjYgMTMuMiA0MCAyMi41IDYwLjktOS4yIDIwLjgtMTYuNiA0MS4xLTIyLjIgNjAuNi05LjktMy4xLTE5LjMtNi41LTI4LTEwLjJ6TTMxMCA0OTBjLTEzLjYtNy44LTE5LjUtMzcuNS0xNC45LTc1LjcgMS4xLTkuNCAyLjktMTkuMyA1LjEtMjkuNCAxOS42IDQuOCA0MSA4LjUgNjMuNSAxMC45IDEzLjUgMTguNSAyNy41IDM1LjMgNDEuNiA1MC0zMi42IDMwLjMtNjMuMiA0Ni45LTg0IDQ2LjktNC41LS4xLTguMy0xLTExLjMtMi43em0yMzcuMi03Ni4yYzQuNyAzOC4yLTEuMSA2Ny45LTE0LjYgNzUuOC0zIDEuOC02LjkgMi42LTExLjUgMi42LTIwLjcgMC01MS40LTE2LjUtODQtNDYuNiAxNC0xNC43IDI4LTMxLjQgNDEuMy00OS45IDIyLjYtMi40IDQ0LTYuMSA2My42LTExIDIuMyAxMC4xIDQuMSAxOS44IDUuMiAyOS4xem0zOC41LTY2LjdjLTguNiAzLjctMTggNy0yNy43IDEwLjEtNS43LTE5LjYtMTMuMi00MC0yMi41LTYwLjkgOS4yLTIwLjggMTYuNi00MS4xIDIyLjItNjAuNiA5LjkgMy4xIDE5LjMgNi41IDI4LjEgMTAuMiAzNS40IDE1LjEgNTguMyAzNC45IDU4LjMgNTAuNi0uMSAxNS43LTIzIDM1LjYtNTguNCA1MC42ek0zMjAuOCA3OC40eiIvPgogICAgPGNpcmNsZSBjeD0iNDIwLjkiIGN5PSIyOTYuNSIgcj0iNDUuNyIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-redo: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE4LjQgMTAuNkMxNi41NSA4Ljk5IDE0LjE1IDggMTEuNSA4Yy00LjY1IDAtOC41OCAzLjAzLTkuOTYgNy4yMkwzLjkgMTZjMS4wNS0zLjE5IDQuMDUtNS41IDcuNi01LjUgMS45NSAwIDMuNzMuNzIgNS4xMiAxLjg4TDEzIDE2aDlWN2wtMy42IDMuNnoiLz4KICA8L2c+Cjwvc3ZnPgo=);--jp-icon-refresh: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDE4IDE4Ij4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTkgMTMuNWMtMi40OSAwLTQuNS0yLjAxLTQuNS00LjVTNi41MSA0LjUgOSA0LjVjMS4yNCAwIDIuMzYuNTIgMy4xNyAxLjMzTDEwIDhoNVYzbC0xLjc2IDEuNzZDMTIuMTUgMy42OCAxMC42NiAzIDkgMyA1LjY5IDMgMy4wMSA1LjY5IDMuMDEgOVM1LjY5IDE1IDkgMTVjMi45NyAwIDUuNDMtMi4xNiA1LjktNWgtMS41MmMtLjQ2IDItMi4yNCAzLjUtNC4zOCAzLjV6Ii8+CiAgICA8L2c+Cjwvc3ZnPgo=);--jp-icon-regex: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIwIDIwIj4KICA8ZyBjbGFzcz0ianAtaWNvbjIiIGZpbGw9IiM0MTQxNDEiPgogICAgPHJlY3QgeD0iMiIgeT0iMiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+CiAgPC9nPgoKICA8ZyBjbGFzcz0ianAtaWNvbi1hY2NlbnQyIiBmaWxsPSIjRkZGIj4KICAgIDxjaXJjbGUgY2xhc3M9InN0MiIgY3g9IjUuNSIgY3k9IjE0LjUiIHI9IjEuNSIvPgogICAgPHJlY3QgeD0iMTIiIHk9IjQiIGNsYXNzPSJzdDIiIHdpZHRoPSIxIiBoZWlnaHQ9IjgiLz4KICAgIDxyZWN0IHg9IjguNSIgeT0iNy41IiB0cmFuc2Zvcm09Im1hdHJpeCgwLjg2NiAtMC41IDAuNSAwLjg2NiAtMi4zMjU1IDcuMzIxOSkiIGNsYXNzPSJzdDIiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiLz4KICAgIDxyZWN0IHg9IjEyIiB5PSI0IiB0cmFuc2Zvcm09Im1hdHJpeCgwLjUgLTAuODY2IDAuODY2IDAuNSAtMC42Nzc5IDE0LjgyNTIpIiBjbGFzcz0ic3QyIiB3aWR0aD0iMSIgaGVpZ2h0PSI4Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-run: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTggNXYxNGwxMS03eiIvPgogICAgPC9nPgo8L3N2Zz4K);--jp-icon-running: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDUxMiA1MTIiPgogIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICA8cGF0aCBkPSJNMjU2IDhDMTE5IDggOCAxMTkgOCAyNTZzMTExIDI0OCAyNDggMjQ4IDI0OC0xMTEgMjQ4LTI0OFMzOTMgOCAyNTYgOHptOTYgMzI4YzAgOC44LTcuMiAxNi0xNiAxNkgxNzZjLTguOCAwLTE2LTcuMi0xNi0xNlYxNzZjMC04LjggNy4yLTE2IDE2LTE2aDE2MGM4LjggMCAxNiA3LjIgMTYgMTZ2MTYweiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-save: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTE3IDNINWMtMS4xMSAwLTIgLjktMiAydjE0YzAgMS4xLjg5IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjdsLTQtNHptLTUgMTZjLTEuNjYgMC0zLTEuMzQtMy0zczEuMzQtMyAzLTMgMyAxLjM0IDMgMy0xLjM0IDMtMyAzem0zLTEwSDVWNWgxMHY0eiIvPgogICAgPC9nPgo8L3N2Zz4K);--jp-icon-search: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTggMTgiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTEyLjEsMTAuOWgtMC43bC0wLjItMC4yYzAuOC0wLjksMS4zLTIuMiwxLjMtMy41YzAtMy0yLjQtNS40LTUuNC01LjRTMS44LDQuMiwxLjgsNy4xczIuNCw1LjQsNS40LDUuNCBjMS4zLDAsMi41LTAuNSwzLjUtMS4zbDAuMiwwLjJ2MC43bDQuMSw0LjFsMS4yLTEuMkwxMi4xLDEwLjl6IE03LjEsMTAuOWMtMi4xLDAtMy43LTEuNy0zLjctMy43czEuNy0zLjcsMy43LTMuN3MzLjcsMS43LDMuNywzLjcgUzkuMiwxMC45LDcuMSwxMC45eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-settings: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8cGF0aCBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIiBkPSJNMTkuNDMgMTIuOThjLjA0LS4zMi4wNy0uNjQuMDctLjk4cy0uMDMtLjY2LS4wNy0uOThsMi4xMS0xLjY1Yy4xOS0uMTUuMjQtLjQyLjEyLS42NGwtMi0zLjQ2Yy0uMTItLjIyLS4zOS0uMy0uNjEtLjIybC0yLjQ5IDFjLS41Mi0uNC0xLjA4LS43My0xLjY5LS45OGwtLjM4LTIuNjVBLjQ4OC40ODggMCAwMDE0IDJoLTRjLS4yNSAwLS40Ni4xOC0uNDkuNDJsLS4zOCAyLjY1Yy0uNjEuMjUtMS4xNy41OS0xLjY5Ljk4bC0yLjQ5LTFjLS4yMy0uMDktLjQ5IDAtLjYxLjIybC0yIDMuNDZjLS4xMy4yMi0uMDcuNDkuMTIuNjRsMi4xMSAxLjY1Yy0uMDQuMzItLjA3LjY1LS4wNy45OHMuMDMuNjYuMDcuOThsLTIuMTEgMS42NWMtLjE5LjE1LS4yNC40Mi0uMTIuNjRsMiAzLjQ2Yy4xMi4yMi4zOS4zLjYxLjIybDIuNDktMWMuNTIuNCAxLjA4LjczIDEuNjkuOThsLjM4IDIuNjVjLjAzLjI0LjI0LjQyLjQ5LjQyaDRjLjI1IDAgLjQ2LS4xOC40OS0uNDJsLjM4LTIuNjVjLjYxLS4yNSAxLjE3LS41OSAxLjY5LS45OGwyLjQ5IDFjLjIzLjA5LjQ5IDAgLjYxLS4yMmwyLTMuNDZjLjEyLS4yMi4wNy0uNDktLjEyLS42NGwtMi4xMS0xLjY1ek0xMiAxNS41Yy0xLjkzIDAtMy41LTEuNTctMy41LTMuNXMxLjU3LTMuNSAzLjUtMy41IDMuNSAxLjU3IDMuNSAzLjUtMS41NyAzLjUtMy41IDMuNXoiLz4KPC9zdmc+Cg==);--jp-icon-share: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTSAxOCAyIEMgMTYuMzU0OTkgMiAxNSAzLjM1NDk5MDQgMTUgNSBDIDE1IDUuMTkwOTUyOSAxNS4wMjE3OTEgNS4zNzcxMjI0IDE1LjA1NjY0MSA1LjU1ODU5MzggTCA3LjkyMTg3NSA5LjcyMDcwMzEgQyA3LjM5ODUzOTkgOS4yNzc4NTM5IDYuNzMyMDc3MSA5IDYgOSBDIDQuMzU0OTkwNCA5IDMgMTAuMzU0OTkgMyAxMiBDIDMgMTMuNjQ1MDEgNC4zNTQ5OTA0IDE1IDYgMTUgQyA2LjczMjA3NzEgMTUgNy4zOTg1Mzk5IDE0LjcyMjE0NiA3LjkyMTg3NSAxNC4yNzkyOTcgTCAxNS4wNTY2NDEgMTguNDM5NDUzIEMgMTUuMDIxNTU1IDE4LjYyMTUxNCAxNSAxOC44MDgzODYgMTUgMTkgQyAxNSAyMC42NDUwMSAxNi4zNTQ5OSAyMiAxOCAyMiBDIDE5LjY0NTAxIDIyIDIxIDIwLjY0NTAxIDIxIDE5IEMgMjEgMTcuMzU0OTkgMTkuNjQ1MDEgMTYgMTggMTYgQyAxNy4yNjc0OCAxNiAxNi42MDE1OTMgMTYuMjc5MzI4IDE2LjA3ODEyNSAxNi43MjI2NTYgTCA4Ljk0MzM1OTQgMTIuNTU4NTk0IEMgOC45NzgyMDk1IDEyLjM3NzEyMiA5IDEyLjE5MDk1MyA5IDEyIEMgOSAxMS44MDkwNDcgOC45NzgyMDk1IDExLjYyMjg3OCA4Ljk0MzM1OTQgMTEuNDQxNDA2IEwgMTYuMDc4MTI1IDcuMjc5Mjk2OSBDIDE2LjYwMTQ2IDcuNzIyMTQ2MSAxNy4yNjc5MjMgOCAxOCA4IEMgMTkuNjQ1MDEgOCAyMSA2LjY0NTAwOTYgMjEgNSBDIDIxIDMuMzU0OTkwNCAxOS42NDUwMSAyIDE4IDIgeiBNIDE4IDQgQyAxOC41NjQxMjkgNCAxOSA0LjQzNTg3MDYgMTkgNSBDIDE5IDUuNTY0MTI5NCAxOC41NjQxMjkgNiAxOCA2IEMgMTcuNDM1ODcxIDYgMTcgNS41NjQxMjk0IDE3IDUgQyAxNyA0LjQzNTg3MDYgMTcuNDM1ODcxIDQgMTggNCB6IE0gNiAxMSBDIDYuNTY0MTI5NCAxMSA3IDExLjQzNTg3MSA3IDEyIEMgNyAxMi41NjQxMjkgNi41NjQxMjk0IDEzIDYgMTMgQyA1LjQzNTg3MDYgMTMgNSAxMi41NjQxMjkgNSAxMiBDIDUgMTEuNDM1ODcxIDUuNDM1ODcwNiAxMSA2IDExIHogTSAxOCAxOCBDIDE4LjU2NDEyOSAxOCAxOSAxOC40MzU4NzEgMTkgMTkgQyAxOSAxOS41NjQxMjkgMTguNTY0MTI5IDIwIDE4IDIwIEMgMTcuNDM1ODcxIDIwIDE3IDE5LjU2NDEyOSAxNyAxOSBDIDE3IDE4LjQzNTg3MSAxNy40MzU4NzEgMTggMTggMTggeiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-spreadsheet: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8cGF0aCBjbGFzcz0ianAtaWNvbi1jb250cmFzdDEganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNENBRjUwIiBkPSJNMi4yIDIuMnYxNy42aDE3LjZWMi4ySDIuMnptMTUuNCA3LjdoLTUuNVY0LjRoNS41djUuNXpNOS45IDQuNHY1LjVINC40VjQuNGg1LjV6bS01LjUgNy43aDUuNXY1LjVINC40di01LjV6bTcuNyA1LjV2LTUuNWg1LjV2NS41aC01LjV6Ii8+Cjwvc3ZnPgo=);--jp-icon-stop: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPgogICAgICAgIDxwYXRoIGQ9Ik02IDZoMTJ2MTJINnoiLz4KICAgIDwvZz4KPC9zdmc+Cg==);--jp-icon-tab: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTIxIDNIM2MtMS4xIDAtMiAuOS0yIDJ2MTRjMCAxLjEuOSAyIDIgMmgxOGMxLjEgMCAyLS45IDItMlY1YzAtMS4xLS45LTItMi0yem0wIDE2SDNWNWgxMHY0aDh2MTB6Ii8+CiAgPC9nPgo8L3N2Zz4K);--jp-icon-table-rows: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPgogICAgICAgIDxwYXRoIGQ9Ik0yMSw4SDNWNGgxOFY4eiBNMjEsMTBIM3Y0aDE4VjEweiBNMjEsMTZIM3Y0aDE4VjE2eiIvPgogICAgPC9nPgo8L3N2Zz4=);--jp-icon-tag: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjgiIGhlaWdodD0iMjgiIHZpZXdCb3g9IjAgMCA0MyAyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CgkJPHBhdGggZD0iTTI4LjgzMzIgMTIuMzM0TDMyLjk5OTggMTYuNTAwN0wzNy4xNjY1IDEyLjMzNEgyOC44MzMyWiIvPgoJCTxwYXRoIGQ9Ik0xNi4yMDk1IDIxLjYxMDRDMTUuNjg3MyAyMi4xMjk5IDE0Ljg0NDMgMjIuMTI5OSAxNC4zMjQ4IDIxLjYxMDRMNi45ODI5IDE0LjcyNDVDNi41NzI0IDE0LjMzOTQgNi4wODMxMyAxMy42MDk4IDYuMDQ3ODYgMTMuMDQ4MkM1Ljk1MzQ3IDExLjUyODggNi4wMjAwMiA4LjYxOTQ0IDYuMDY2MjEgNy4wNzY5NUM2LjA4MjgxIDYuNTE0NzcgNi41NTU0OCA2LjA0MzQ3IDcuMTE4MDQgNi4wMzA1NUM5LjA4ODYzIDUuOTg0NzMgMTMuMjYzOCA1LjkzNTc5IDEzLjY1MTggNi4zMjQyNUwyMS43MzY5IDEzLjYzOUMyMi4yNTYgMTQuMTU4NSAyMS43ODUxIDE1LjQ3MjQgMjEuMjYyIDE1Ljk5NDZMMTYuMjA5NSAyMS42MTA0Wk05Ljc3NTg1IDguMjY1QzkuMzM1NTEgNy44MjU2NiA4LjYyMzUxIDcuODI1NjYgOC4xODI4IDguMjY1QzcuNzQzNDYgOC43MDU3MSA3Ljc0MzQ2IDkuNDE3MzMgOC4xODI4IDkuODU2NjdDOC42MjM4MiAxMC4yOTY0IDkuMzM1ODIgMTAuMjk2NCA5Ljc3NTg1IDkuODU2NjdDMTAuMjE1NiA5LjQxNzMzIDEwLjIxNTYgOC43MDUzMyA5Ljc3NTg1IDguMjY1WiIvPgoJPC9nPgo8L3N2Zz4K);--jp-icon-terminal: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiA+CiAgICA8cmVjdCBjbGFzcz0ianAtdGVybWluYWwtaWNvbi1iYWNrZ3JvdW5kLWNvbG9yIGpwLWljb24tc2VsZWN0YWJsZSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyIDIpIiBmaWxsPSIjMzMzMzMzIi8+CiAgICA8cGF0aCBjbGFzcz0ianAtdGVybWluYWwtaWNvbi1jb2xvciBqcC1pY29uLXNlbGVjdGFibGUtaW52ZXJzZSIgZD0iTTUuMDU2NjQgOC43NjE3MkM1LjA1NjY0IDguNTk3NjYgNS4wMzEyNSA4LjQ1MzEyIDQuOTgwNDcgOC4zMjgxMkM0LjkzMzU5IDguMTk5MjIgNC44NTU0NyA4LjA4MjAzIDQuNzQ2MDkgNy45NzY1NkM0LjY0MDYyIDcuODcxMDkgNC41IDcuNzc1MzkgNC4zMjQyMiA3LjY4OTQ1QzQuMTUyMzQgNy41OTk2MSAzLjk0MzM2IDcuNTExNzIgMy42OTcyNyA3LjQyNTc4QzMuMzAyNzMgNy4yODUxNiAyLjk0MzM2IDcuMTM2NzIgMi42MTkxNCA2Ljk4MDQ3QzIuMjk0OTIgNi44MjQyMiAyLjAxNzU4IDYuNjQyNTggMS43ODcxMSA2LjQzNTU1QzEuNTYwNTUgNi4yMjg1MiAxLjM4NDc3IDUuOTg4MjggMS4yNTk3NyA1LjcxNDg0QzEuMTM0NzcgNS40Mzc1IDEuMDcyMjcgNS4xMDkzOCAxLjA3MjI3IDQuNzMwNDdDMS4wNzIyNyA0LjM5ODQ0IDEuMTI4OTEgNC4wOTU3IDEuMjQyMTkgMy44MjIyN0MxLjM1NTQ3IDMuNTQ0OTIgMS41MTU2MiAzLjMwNDY5IDEuNzIyNjYgMy4xMDE1NkMxLjkyOTY5IDIuODk4NDQgMi4xNzk2OSAyLjczNDM3IDIuNDcyNjYgMi42MDkzOEMyLjc2NTYyIDIuNDg0MzggMy4wOTE4IDIuNDA0MyAzLjQ1MTE3IDIuMzY5MTRWMS4xMDkzOEg0LjM4ODY3VjIuMzgwODZDNC43NDAyMyAyLjQyNzczIDUuMDU2NjQgMi41MjM0NCA1LjMzNzg5IDIuNjY3OTdDNS42MTkxNCAyLjgxMjUgNS44NTc0MiAzLjAwMTk1IDYuMDUyNzMgMy4yMzYzM0M2LjI1MTk1IDMuNDY2OCA2LjQwNDMgMy43NDAyMyA2LjUwOTc3IDQuMDU2NjRDNi42MTkxNCA0LjM2OTE0IDYuNjczODMgNC43MjA3IDYuNjczODMgNS4xMTEzM0g1LjA0NDkyQzUuMDQ0OTIgNC42Mzg2NyA0LjkzNzUgNC4yODEyNSA0LjcyMjY2IDQuMDM5MDZDNC41MDc4MSAzLjc5Mjk3IDQuMjE2OCAzLjY2OTkyIDMuODQ5NjEgMy42Njk5MkMzLjY1MDM5IDMuNjY5OTIgMy40NzY1NiAzLjY5NzI3IDMuMzI4MTIgMy43NTE5NUMzLjE4MzU5IDMuODAyNzMgMy4wNjQ0NSAzLjg3Njk1IDIuOTcwNyAzLjk3NDYxQzIuODc2OTUgNC4wNjgzNiAyLjgwNjY0IDQuMTc5NjkgMi43NTk3NyA0LjMwODU5QzIuNzE2OCA0LjQzNzUgMi42OTUzMSA0LjU3ODEyIDIuNjk1MzEgNC43MzA0N0MyLjY5NTMxIDQuODgyODEgMi43MTY4IDUuMDE5NTMgMi43NTk3NyA1LjE0MDYyQzIuODA2NjQgNS4yNTc4MSAyLjg4MjgxIDUuMzY3MTkgMi45ODgyOCA1LjQ2ODc1QzMuMDk3NjYgNS41NzAzMSAzLjI0MDIzIDUuNjY3OTcgMy40MTYwMiA1Ljc2MTcyQzMuNTkxOCA1Ljg1MTU2IDMuODEwNTUgNS45NDMzNiA0LjA3MjI3IDYuMDM3MTFDNC40NjY4IDYuMTg1NTUgNC44MjQyMiA2LjMzOTg0IDUuMTQ0NTMgNi41QzUuNDY0ODQgNi42NTYyNSA1LjczODI4IDYuODM5ODQgNS45NjQ4NCA3LjA1MDc4QzYuMTk1MzEgNy4yNTc4MSA2LjM3MTA5IDcuNSA2LjQ5MjE5IDcuNzc3MzRDNi42MTcxOSA4LjA1MDc4IDYuNjc5NjkgOC4zNzUgNi42Nzk2OSA4Ljc1QzYuNjc5NjkgOS4wOTM3NSA2LjYyMzA1IDkuNDA0MyA2LjUwOTc3IDkuNjgxNjRDNi4zOTY0OCA5Ljk1NTA4IDYuMjM0MzggMTAuMTkxNCA2LjAyMzQ0IDEwLjM5MDZDNS44MTI1IDEwLjU4OTggNS41NTg1OSAxMC43NSA1LjI2MTcyIDEwLjg3MTFDNC45NjQ4NCAxMC45ODgzIDQuNjMyODEgMTEuMDY0NSA0LjI2NTYyIDExLjA5OTZWMTIuMjQ4SDMuMzMzOThWMTEuMDk5NkMzLjAwMTk1IDExLjA2ODQgMi42Nzk2OSAxMC45OTYxIDIuMzY3MTkgMTAuODgyOEMyLjA1NDY5IDEwLjc2NTYgMS43NzczNCAxMC41OTc3IDEuNTM1MTYgMTAuMzc4OUMxLjI5Njg4IDEwLjE2MDIgMS4xMDU0NyA5Ljg4NDc3IDAuOTYwOTM4IDkuNTUyNzNDMC44MTY0MDYgOS4yMTY4IDAuNzQ0MTQxIDguODE0NDUgMC43NDQxNDEgOC4zNDU3SDIuMzc4OTFDMi4zNzg5MSA4LjYyNjk1IDIuNDE5OTIgOC44NjMyOCAyLjUwMTk1IDkuMDU0NjlDMi41ODM5OCA5LjI0MjE5IDIuNjg5NDUgOS4zOTI1OCAyLjgxODM2IDkuNTA1ODZDMi45NTExNyA5LjYxNTIzIDMuMTAxNTYgOS42OTMzNiAzLjI2OTUzIDkuNzQwMjNDMy40Mzc1IDkuNzg3MTEgMy42MDkzOCA5LjgxMDU1IDMuNzg1MTYgOS44MTA1NUM0LjIwMzEyIDkuODEwNTUgNC41MTk1MyA5LjcxMjg5IDQuNzM0MzggOS41MTc1OEM0Ljk0OTIyIDkuMzIyMjcgNS4wNTY2NCA5LjA3MDMxIDUuMDU2NjQgOC43NjE3MlpNMTMuNDE4IDEyLjI3MTVIOC4wNzQyMlYxMUgxMy40MThWMTIuMjcxNVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMuOTUyNjQgNikiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=);--jp-icon-text-editor: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8cGF0aCBjbGFzcz0ianAtdGV4dC1lZGl0b3ItaWNvbi1jb2xvciBqcC1pY29uLXNlbGVjdGFibGUiIGZpbGw9IiM2MTYxNjEiIGQ9Ik0xNSAxNUgzdjJoMTJ2LTJ6bTAtOEgzdjJoMTJWN3pNMyAxM2gxOHYtMkgzdjJ6bTAgOGgxOHYtMkgzdjJ6TTMgM3YyaDE4VjNIM3oiLz4KPC9zdmc+Cg==);--jp-icon-toc: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBjbGFzcz0ianAtaWNvbjMganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjNjE2MTYxIj4KICAgIDxwYXRoIGQ9Ik03LDVIMjFWN0g3VjVNNywxM1YxMUgyMVYxM0g3TTQsNC41QTEuNSwxLjUgMCAwLDEgNS41LDZBMS41LDEuNSAwIDAsMSA0LDcuNUExLjUsMS41IDAgMCwxIDIuNSw2QTEuNSwxLjUgMCAwLDEgNCw0LjVNNCwxMC41QTEuNSwxLjUgMCAwLDEgNS41LDEyQTEuNSwxLjUgMCAwLDEgNCwxMy41QTEuNSwxLjUgMCAwLDEgMi41LDEyQTEuNSwxLjUgMCAwLDEgNCwxMC41TTcsMTlWMTdIMjFWMTlIN000LDE2LjVBMS41LDEuNSAwIDAsMSA1LjUsMThBMS41LDEuNSAwIDAsMSA0LDE5LjVBMS41LDEuNSAwIDAsMSAyLjUsMThBMS41LDEuNSAwIDAsMSA0LDE2LjVaIiAvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-tree-view: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGNsYXNzPSJqcC1pY29uMyIgZmlsbD0iIzYxNjE2MSI+CiAgICAgICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPgogICAgICAgIDxwYXRoIGQ9Ik0yMiAxMVYzaC03djNIOVYzSDJ2OGg3VjhoMnYxMGg0djNoN3YtOGgtN3YzaC0yVjhoMnYzeiIvPgogICAgPC9nPgo8L3N2Zz4=);--jp-icon-trusted: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDI0IDI1Ij4KICAgIDxwYXRoIGNsYXNzPSJqcC1pY29uMiIgc3Ryb2tlPSIjMzMzMzMzIiBzdHJva2Utd2lkdGg9IjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIgMykiIGQ9Ik0xLjg2MDk0IDExLjQ0MDlDMC44MjY0NDggOC43NzAyNyAwLjg2Mzc3OSA2LjA1NzY0IDEuMjQ5MDcgNC4xOTkzMkMyLjQ4MjA2IDMuOTMzNDcgNC4wODA2OCAzLjQwMzQ3IDUuNjAxMDIgMi44NDQ5QzcuMjM1NDkgMi4yNDQ0IDguODU2NjYgMS41ODE1IDkuOTg3NiAxLjA5NTM5QzExLjA1OTcgMS41ODM0MSAxMi42MDk0IDIuMjQ0NCAxNC4yMTggMi44NDMzOUMxNS43NTAzIDMuNDEzOTQgMTcuMzk5NSAzLjk1MjU4IDE4Ljc1MzkgNC4yMTM4NUMxOS4xMzY0IDYuMDcxNzcgMTkuMTcwOSA4Ljc3NzIyIDE4LjEzOSAxMS40NDA5QzE3LjAzMDMgMTQuMzAzMiAxNC42NjY4IDE3LjE4NDQgOS45OTk5OSAxOC45MzU0QzUuMzMzMiAxNy4xODQ0IDIuOTY5NjggMTQuMzAzMiAxLjg2MDk0IDExLjQ0MDlaIi8+CiAgICA8cGF0aCBjbGFzcz0ianAtaWNvbjIiIGZpbGw9IiMzMzMzMzMiIHN0cm9rZT0iIzMzMzMzMyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCA5Ljg2NzE5KSIgZD0iTTIuODYwMTUgNC44NjUzNUwwLjcyNjU0OSAyLjk5OTU5TDAgMy42MzA0NUwyLjg2MDE1IDYuMTMxNTdMOCAwLjYzMDg3Mkw3LjI3ODU3IDBMMi44NjAxNSA0Ljg2NTM1WiIvPgo8L3N2Zz4K);--jp-icon-undo: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTEyLjUgOGMtMi42NSAwLTUuMDUuOTktNi45IDIuNkwyIDd2OWg5bC0zLjYyLTMuNjJjMS4zOS0xLjE2IDMuMTYtMS44OCA1LjEyLTEuODggMy41NCAwIDYuNTUgMi4zMSA3LjYgNS41bDIuMzctLjc4QzIxLjA4IDExLjAzIDE3LjE1IDggMTIuNSA4eiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-user: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGFzcz0ianAtaWNvbjMiIGZpbGw9IiM2MTYxNjEiPgogICAgPHBhdGggZD0iTTE2IDdhNCA0IDAgMTEtOCAwIDQgNCAwIDAxOCAwek0xMiAxNGE3IDcgMCAwMC03IDdoMTRhNyA3IDAgMDAtNy03eiIvPgogIDwvZz4KPC9zdmc+);--jp-icon-users: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDM2IDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPGcgY2xhc3M9ImpwLWljb24zIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjczMjcgMCAwIDEuNzMyNyAtMy42MjgyIC4wOTk1NzcpIiBmaWxsPSIjNjE2MTYxIj4KICA8cGF0aCB0cmFuc2Zvcm09Im1hdHJpeCgxLjUsMCwwLDEuNSwwLC02KSIgZD0ibTEyLjE4NiA3LjUwOThjLTEuMDUzNSAwLTEuOTc1NyAwLjU2NjUtMi40Nzg1IDEuNDEwMiAwLjc1MDYxIDAuMzEyNzcgMS4zOTc0IDAuODI2NDggMS44NzMgMS40NzI3aDMuNDg2M2MwLTEuNTkyLTEuMjg4OS0yLjg4MjgtMi44ODA5LTIuODgyOHoiLz4KICA8cGF0aCBkPSJtMjAuNDY1IDIuMzg5NWEyLjE4ODUgMi4xODg1IDAgMCAxLTIuMTg4NCAyLjE4ODUgMi4xODg1IDIuMTg4NSAwIDAgMS0yLjE4ODUtMi4xODg1IDIuMTg4NSAyLjE4ODUgMCAwIDEgMi4xODg1LTIuMTg4NSAyLjE4ODUgMi4xODg1IDAgMCAxIDIuMTg4NCAyLjE4ODV6Ii8+CiAgPHBhdGggdHJhbnNmb3JtPSJtYXRyaXgoMS41LDAsMCwxLjUsMCwtNikiIGQ9Im0zLjU4OTggOC40MjE5Yy0xLjExMjYgMC0yLjAxMzcgMC45MDExMS0yLjAxMzcgMi4wMTM3aDIuODE0NWMwLjI2Nzk3LTAuMzczMDkgMC41OTA3LTAuNzA0MzUgMC45NTg5OC0wLjk3ODUyLTAuMzQ0MzMtMC42MTY4OC0xLjAwMzEtMS4wMzUyLTEuNzU5OC0xLjAzNTJ6Ii8+CiAgPHBhdGggZD0ibTYuOTE1NCA0LjYyM2ExLjUyOTQgMS41Mjk0IDAgMCAxLTEuNTI5NCAxLjUyOTQgMS41Mjk0IDEuNTI5NCAwIDAgMS0xLjUyOTQtMS41Mjk0IDEuNTI5NCAxLjUyOTQgMCAwIDEgMS41Mjk0LTEuNTI5NCAxLjUyOTQgMS41Mjk0IDAgMCAxIDEuNTI5NCAxLjUyOTR6Ii8+CiAgPHBhdGggZD0ibTYuMTM1IDEzLjUzNWMwLTMuMjM5MiAyLjYyNTktNS44NjUgNS44NjUtNS44NjUgMy4yMzkyIDAgNS44NjUgMi42MjU5IDUuODY1IDUuODY1eiIvPgogIDxjaXJjbGUgY3g9IjEyIiBjeT0iMy43Njg1IiByPSIyLjk2ODUiLz4KIDwvZz4KPC9zdmc+Cg==);--jp-icon-vega: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8ZyBjbGFzcz0ianAtaWNvbjEganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjMjEyMTIxIj4KICAgIDxwYXRoIGQ9Ik0xMC42IDUuNGwyLjItMy4ySDIuMnY3LjNsNC02LjZ6Ii8+CiAgICA8cGF0aCBkPSJNMTUuOCAyLjJsLTQuNCA2LjZMNyA2LjNsLTQuOCA4djUuNWgxNy42VjIuMmgtNHptLTcgMTUuNEg1LjV2LTQuNGgzLjN2NC40em00LjQgMEg5LjhWOS44aDMuNHY3Ljh6bTQuNCAwaC0zLjRWNi41aDMuNHYxMS4xeiIvPgogIDwvZz4KPC9zdmc+Cg==);--jp-icon-yaml: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgdmlld0JveD0iMCAwIDIyIDIyIj4KICA8ZyBjbGFzcz0ianAtaWNvbi1jb250cmFzdDIganAtaWNvbi1zZWxlY3RhYmxlIiBmaWxsPSIjRDgxQjYwIj4KICAgIDxwYXRoIGQ9Ik03LjIgMTguNnYtNS40TDMgNS42aDMuM2wxLjQgMy4xYy4zLjkuNiAxLjYgMSAyLjUuMy0uOC42LTEuNiAxLTIuNWwxLjQtMy4xaDMuNGwtNC40IDcuNnY1LjVsLTIuOS0uMXoiLz4KICAgIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjE3LjYiIGN5PSIxNi41IiByPSIyLjEiLz4KICAgIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjE3LjYiIGN5PSIxMSIgcj0iMi4xIi8+CiAgPC9nPgo8L3N2Zz4K)}.jupyter-wrapper .jp-AddAboveIcon{background-image:var(--jp-icon-add-above)}.jupyter-wrapper .jp-AddBelowIcon{background-image:var(--jp-icon-add-below)}.jupyter-wrapper .jp-AddIcon{background-image:var(--jp-icon-add)}.jupyter-wrapper .jp-BellIcon{background-image:var(--jp-icon-bell)}.jupyter-wrapper .jp-BugDotIcon{background-image:var(--jp-icon-bug-dot)}.jupyter-wrapper .jp-BugIcon{background-image:var(--jp-icon-bug)}.jupyter-wrapper .jp-BuildIcon{background-image:var(--jp-icon-build)}.jupyter-wrapper .jp-CaretDownEmptyIcon{background-image:var(--jp-icon-caret-down-empty)}.jupyter-wrapper .jp-CaretDownEmptyThinIcon{background-image:var(--jp-icon-caret-down-empty-thin)}.jupyter-wrapper .jp-CaretDownIcon{background-image:var(--jp-icon-caret-down)}.jupyter-wrapper .jp-CaretLeftIcon{background-image:var(--jp-icon-caret-left)}.jupyter-wrapper .jp-CaretRightIcon{background-image:var(--jp-icon-caret-right)}.jupyter-wrapper .jp-CaretUpEmptyThinIcon{background-image:var(--jp-icon-caret-up-empty-thin)}.jupyter-wrapper .jp-CaretUpIcon{background-image:var(--jp-icon-caret-up)}.jupyter-wrapper .jp-CaseSensitiveIcon{background-image:var(--jp-icon-case-sensitive)}.jupyter-wrapper .jp-CheckIcon{background-image:var(--jp-icon-check)}.jupyter-wrapper .jp-CircleEmptyIcon{background-image:var(--jp-icon-circle-empty)}.jupyter-wrapper .jp-CircleIcon{background-image:var(--jp-icon-circle)}.jupyter-wrapper .jp-ClearIcon{background-image:var(--jp-icon-clear)}.jupyter-wrapper .jp-CloseIcon{background-image:var(--jp-icon-close)}.jupyter-wrapper .jp-CodeIcon{background-image:var(--jp-icon-code)}.jupyter-wrapper .jp-ConsoleIcon{background-image:var(--jp-icon-console)}.jupyter-wrapper .jp-CopyIcon{background-image:var(--jp-icon-copy)}.jupyter-wrapper .jp-CopyrightIcon{background-image:var(--jp-icon-copyright)}.jupyter-wrapper .jp-CutIcon{background-image:var(--jp-icon-cut)}.jupyter-wrapper .jp-DeleteIcon{background-image:var(--jp-icon-delete)}.jupyter-wrapper .jp-DownloadIcon{background-image:var(--jp-icon-download)}.jupyter-wrapper .jp-DuplicateIcon{background-image:var(--jp-icon-duplicate)}.jupyter-wrapper .jp-EditIcon{background-image:var(--jp-icon-edit)}.jupyter-wrapper .jp-EllipsesIcon{background-image:var(--jp-icon-ellipses)}.jupyter-wrapper .jp-ExtensionIcon{background-image:var(--jp-icon-extension)}.jupyter-wrapper .jp-FastForwardIcon{background-image:var(--jp-icon-fast-forward)}.jupyter-wrapper .jp-FileIcon{background-image:var(--jp-icon-file)}.jupyter-wrapper .jp-FileUploadIcon{background-image:var(--jp-icon-file-upload)}.jupyter-wrapper .jp-FilterListIcon{background-image:var(--jp-icon-filter-list)}.jupyter-wrapper .jp-FolderFavoriteIcon{background-image:var(--jp-icon-folder-favorite)}.jupyter-wrapper .jp-FolderIcon{background-image:var(--jp-icon-folder)}.jupyter-wrapper .jp-HomeIcon{background-image:var(--jp-icon-home)}.jupyter-wrapper .jp-Html5Icon{background-image:var(--jp-icon-html5)}.jupyter-wrapper .jp-ImageIcon{background-image:var(--jp-icon-image)}.jupyter-wrapper .jp-InspectorIcon{background-image:var(--jp-icon-inspector)}.jupyter-wrapper .jp-JsonIcon{background-image:var(--jp-icon-json)}.jupyter-wrapper .jp-JuliaIcon{background-image:var(--jp-icon-julia)}.jupyter-wrapper .jp-JupyterFaviconIcon{background-image:var(--jp-icon-jupyter-favicon)}.jupyter-wrapper .jp-JupyterIcon{background-image:var(--jp-icon-jupyter)}.jupyter-wrapper .jp-JupyterlabWordmarkIcon{background-image:var(--jp-icon-jupyterlab-wordmark)}.jupyter-wrapper .jp-KernelIcon{background-image:var(--jp-icon-kernel)}.jupyter-wrapper .jp-KeyboardIcon{background-image:var(--jp-icon-keyboard)}.jupyter-wrapper .jp-LaunchIcon{background-image:var(--jp-icon-launch)}.jupyter-wrapper .jp-LauncherIcon{background-image:var(--jp-icon-launcher)}.jupyter-wrapper .jp-LineFormIcon{background-image:var(--jp-icon-line-form)}.jupyter-wrapper .jp-LinkIcon{background-image:var(--jp-icon-link)}.jupyter-wrapper .jp-ListIcon{background-image:var(--jp-icon-list)}.jupyter-wrapper .jp-ListingsInfoIcon{background-image:var(--jp-icon-listings-info)}.jupyter-wrapper .jp-MarkdownIcon{background-image:var(--jp-icon-markdown)}.jupyter-wrapper .jp-MoveDownIcon{background-image:var(--jp-icon-move-down)}.jupyter-wrapper .jp-MoveUpIcon{background-image:var(--jp-icon-move-up)}.jupyter-wrapper .jp-NewFolderIcon{background-image:var(--jp-icon-new-folder)}.jupyter-wrapper .jp-NotTrustedIcon{background-image:var(--jp-icon-not-trusted)}.jupyter-wrapper .jp-NotebookIcon{background-image:var(--jp-icon-notebook)}.jupyter-wrapper .jp-NumberingIcon{background-image:var(--jp-icon-numbering)}.jupyter-wrapper .jp-OfflineBoltIcon{background-image:var(--jp-icon-offline-bolt)}.jupyter-wrapper .jp-PaletteIcon{background-image:var(--jp-icon-palette)}.jupyter-wrapper .jp-PasteIcon{background-image:var(--jp-icon-paste)}.jupyter-wrapper .jp-PdfIcon{background-image:var(--jp-icon-pdf)}.jupyter-wrapper .jp-PythonIcon{background-image:var(--jp-icon-python)}.jupyter-wrapper .jp-RKernelIcon{background-image:var(--jp-icon-r-kernel)}.jupyter-wrapper .jp-ReactIcon{background-image:var(--jp-icon-react)}.jupyter-wrapper .jp-RedoIcon{background-image:var(--jp-icon-redo)}.jupyter-wrapper .jp-RefreshIcon{background-image:var(--jp-icon-refresh)}.jupyter-wrapper .jp-RegexIcon{background-image:var(--jp-icon-regex)}.jupyter-wrapper .jp-RunIcon{background-image:var(--jp-icon-run)}.jupyter-wrapper .jp-RunningIcon{background-image:var(--jp-icon-running)}.jupyter-wrapper .jp-SaveIcon{background-image:var(--jp-icon-save)}.jupyter-wrapper .jp-SearchIcon{background-image:var(--jp-icon-search)}.jupyter-wrapper .jp-SettingsIcon{background-image:var(--jp-icon-settings)}.jupyter-wrapper .jp-ShareIcon{background-image:var(--jp-icon-share)}.jupyter-wrapper .jp-SpreadsheetIcon{background-image:var(--jp-icon-spreadsheet)}.jupyter-wrapper .jp-StopIcon{background-image:var(--jp-icon-stop)}.jupyter-wrapper .jp-TabIcon{background-image:var(--jp-icon-tab)}.jupyter-wrapper .jp-TableRowsIcon{background-image:var(--jp-icon-table-rows)}.jupyter-wrapper .jp-TagIcon{background-image:var(--jp-icon-tag)}.jupyter-wrapper .jp-TerminalIcon{background-image:var(--jp-icon-terminal)}.jupyter-wrapper .jp-TextEditorIcon{background-image:var(--jp-icon-text-editor)}.jupyter-wrapper .jp-TocIcon{background-image:var(--jp-icon-toc)}.jupyter-wrapper .jp-TreeViewIcon{background-image:var(--jp-icon-tree-view)}.jupyter-wrapper .jp-TrustedIcon{background-image:var(--jp-icon-trusted)}.jupyter-wrapper .jp-UndoIcon{background-image:var(--jp-icon-undo)}.jupyter-wrapper .jp-UserIcon{background-image:var(--jp-icon-user)}.jupyter-wrapper .jp-UsersIcon{background-image:var(--jp-icon-users)}.jupyter-wrapper .jp-VegaIcon{background-image:var(--jp-icon-vega)}.jupyter-wrapper .jp-YamlIcon{background-image:var(--jp-icon-yaml)}.jupyter-wrapper .jp-Icon,.jupyter-wrapper .jp-MaterialIcon{background-position:center;background-repeat:no-repeat;background-size:16px;min-width:16px;min-height:16px}.jupyter-wrapper .jp-Icon-cover{background-position:center;background-repeat:no-repeat;background-size:cover}.jupyter-wrapper .jp-Icon-16{background-size:16px;min-width:16px;min-height:16px}.jupyter-wrapper .jp-Icon-18{background-size:18px;min-width:18px;min-height:18px}.jupyter-wrapper .jp-Icon-20{background-size:20px;min-width:20px;min-height:20px}.jupyter-wrapper .lm-TabBar .lm-TabBar-addButton{align-items:center;display:flex;padding:4px 4px 5px;margin-right:1px;background-color:var(--jp-layout-color2)}.jupyter-wrapper .lm-TabBar .lm-TabBar-addButton:hover{background-color:var(--jp-layout-color1)}.jupyter-wrapper .lm-DockPanel-tabBar .lm-TabBar-tab{width:var(--jp-private-horizontal-tab-width)}.jupyter-wrapper .lm-DockPanel-tabBar .lm-TabBar-content{flex:unset}.jupyter-wrapper .lm-DockPanel-tabBar[data-orientation=horizontal]{flex:1 1 auto}.jupyter-wrapper .jp-icon0[fill]{fill:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon1[fill]{fill:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon2[fill]{fill:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon3[fill]{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon4[fill]{fill:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon0[stroke]{stroke:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon1[stroke]{stroke:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon2[stroke]{stroke:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon3[stroke]{stroke:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon4[stroke]{stroke:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon-accent0[fill]{fill:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-accent1[fill]{fill:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-accent2[fill]{fill:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-accent3[fill]{fill:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-accent4[fill]{fill:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-accent0[stroke]{stroke:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-accent1[stroke]{stroke:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-accent2[stroke]{stroke:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-accent3[stroke]{stroke:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-accent4[stroke]{stroke:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-none[fill]{fill:none}.jupyter-wrapper .jp-icon-none[stroke]{stroke:none}.jupyter-wrapper .jp-icon-brand0[fill]{fill:var(--jp-brand-color0)}.jupyter-wrapper .jp-icon-brand1[fill]{fill:var(--jp-brand-color1)}.jupyter-wrapper .jp-icon-brand2[fill]{fill:var(--jp-brand-color2)}.jupyter-wrapper .jp-icon-brand3[fill]{fill:var(--jp-brand-color3)}.jupyter-wrapper .jp-icon-brand4[fill]{fill:var(--jp-brand-color4)}.jupyter-wrapper .jp-icon-brand0[stroke]{stroke:var(--jp-brand-color0)}.jupyter-wrapper .jp-icon-brand1[stroke]{stroke:var(--jp-brand-color1)}.jupyter-wrapper .jp-icon-brand2[stroke]{stroke:var(--jp-brand-color2)}.jupyter-wrapper .jp-icon-brand3[stroke]{stroke:var(--jp-brand-color3)}.jupyter-wrapper .jp-icon-brand4[stroke]{stroke:var(--jp-brand-color4)}.jupyter-wrapper .jp-icon-warn0[fill]{fill:var(--jp-warn-color0)}.jupyter-wrapper .jp-icon-warn1[fill]{fill:var(--jp-warn-color1)}.jupyter-wrapper .jp-icon-warn2[fill]{fill:var(--jp-warn-color2)}.jupyter-wrapper .jp-icon-warn3[fill]{fill:var(--jp-warn-color3)}.jupyter-wrapper .jp-icon-warn0[stroke]{stroke:var(--jp-warn-color0)}.jupyter-wrapper .jp-icon-warn1[stroke]{stroke:var(--jp-warn-color1)}.jupyter-wrapper .jp-icon-warn2[stroke]{stroke:var(--jp-warn-color2)}.jupyter-wrapper .jp-icon-warn3[stroke]{stroke:var(--jp-warn-color3)}.jupyter-wrapper .jp-icon-contrast0[fill]{fill:var(--jp-icon-contrast-color0)}.jupyter-wrapper .jp-icon-contrast1[fill]{fill:var(--jp-icon-contrast-color1)}.jupyter-wrapper .jp-icon-contrast2[fill]{fill:var(--jp-icon-contrast-color2)}.jupyter-wrapper .jp-icon-contrast3[fill]{fill:var(--jp-icon-contrast-color3)}.jupyter-wrapper .jp-icon-contrast0[stroke]{stroke:var(--jp-icon-contrast-color0)}.jupyter-wrapper .jp-icon-contrast1[stroke]{stroke:var(--jp-icon-contrast-color1)}.jupyter-wrapper .jp-icon-contrast2[stroke]{stroke:var(--jp-icon-contrast-color2)}.jupyter-wrapper .jp-icon-contrast3[stroke]{stroke:var(--jp-icon-contrast-color3)}.jupyter-wrapper .jp-jupyter-icon-color[fill]{fill:var(--jp-jupyter-icon-color, var(--jp-warn-color0))}.jupyter-wrapper .jp-notebook-icon-color[fill]{fill:var(--jp-notebook-icon-color, var(--jp-warn-color0))}.jupyter-wrapper .jp-json-icon-color[fill]{fill:var(--jp-json-icon-color, var(--jp-warn-color1))}.jupyter-wrapper .jp-console-icon-color[fill]{fill:var(--jp-console-icon-color, white)}.jupyter-wrapper .jp-console-icon-background-color[fill]{fill:var(--jp-console-icon-background-color, var(--jp-brand-color1))}.jupyter-wrapper .jp-terminal-icon-color[fill]{fill:var(--jp-terminal-icon-color, var(--jp-layout-color2))}.jupyter-wrapper .jp-terminal-icon-background-color[fill]{fill:var(--jp-terminal-icon-background-color, var(--jp-inverse-layout2))}.jupyter-wrapper .jp-text-editor-icon-color[fill]{fill:var(--jp-text-editor-icon-color, var(--jp-inverse-layout3))}.jupyter-wrapper .jp-inspector-icon-color[fill]{fill:var(--jp-inspector-icon-color, var(--jp-inverse-layout3))}.jupyter-wrapper .jp-DirListing-item.jp-mod-selected .jp-icon-selectable[fill]{fill:#fff}.jupyter-wrapper .jp-DirListing-item.jp-mod-selected .jp-icon-selectable-inverse[fill]{fill:var(--jp-brand-color1)}.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-active .jp-icon-selectable[fill]{fill:#fff}.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-active .jp-icon-selectable-inverse[fill],.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-active .jp-icon-hover :hover .jp-icon-selectable[fill]{fill:var(--jp-brand-color1)}.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-active .jp-icon-hover :hover .jp-icon-selectable-inverse[fill]{fill:#fff}.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-dirty>.lm-TabBar-tabCloseIcon>:not(:hover)>.jp-icon3[fill]{fill:none}.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-dirty>.lm-TabBar-tabCloseIcon>:not(:hover)>.jp-icon-busy[fill]{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper #tab-manager .lm-TabBar-tab.jp-mod-dirty.jp-mod-active>.lm-TabBar-tabCloseIcon>:not(:hover)>.jp-icon-busy[fill]{fill:#fff}.jupyter-wrapper .lm-DockPanel-tabBar .lm-TabBar-tab.lm-mod-closable.jp-mod-dirty>.lm-TabBar-tabCloseIcon>:not(:hover)>.jp-icon3[fill]{fill:none}.jupyter-wrapper .lm-DockPanel-tabBar .lm-TabBar-tab.lm-mod-closable.jp-mod-dirty>.lm-TabBar-tabCloseIcon>:not(:hover)>.jp-icon-busy[fill]{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper #jp-main-statusbar .jp-mod-selected .jp-icon-selectable[fill]{fill:#fff}.jupyter-wrapper #jp-main-statusbar .jp-mod-selected .jp-icon-selectable-inverse[fill]{fill:var(--jp-brand-color1)}.jupyter-wrapper :root{--jp-warn-color0: var(--md-orange-700)}.jupyter-wrapper .jp-DragIcon{margin-right:4px}.jupyter-wrapper .jp-icon-alt .jp-icon0[fill]{fill:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-alt .jp-icon1[fill]{fill:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-alt .jp-icon2[fill]{fill:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-alt .jp-icon3[fill]{fill:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-alt .jp-icon4[fill]{fill:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-alt .jp-icon0[stroke]{stroke:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-alt .jp-icon1[stroke]{stroke:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-alt .jp-icon2[stroke]{stroke:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-alt .jp-icon3[stroke]{stroke:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-alt .jp-icon4[stroke]{stroke:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent0[fill]{fill:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent1[fill]{fill:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent2[fill]{fill:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent3[fill]{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent4[fill]{fill:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent0[stroke]{stroke:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent1[stroke]{stroke:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent2[stroke]{stroke:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent3[stroke]{stroke:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon-alt .jp-icon-accent4[stroke]{stroke:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon-hoverShow:not(:hover) .jp-icon-hoverShow-content{display:none!important}.jupyter-wrapper .jp-icon-hover :hover .jp-icon0-hover[fill]{fill:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon1-hover[fill]{fill:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon2-hover[fill]{fill:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon3-hover[fill]{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon4-hover[fill]{fill:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon0-hover[stroke]{stroke:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon1-hover[stroke]{stroke:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon2-hover[stroke]{stroke:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon3-hover[stroke]{stroke:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon4-hover[stroke]{stroke:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent0-hover[fill]{fill:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent1-hover[fill]{fill:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent2-hover[fill]{fill:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent3-hover[fill]{fill:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent4-hover[fill]{fill:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent0-hover[stroke]{stroke:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent1-hover[stroke]{stroke:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent2-hover[stroke]{stroke:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent3-hover[stroke]{stroke:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-accent4-hover[stroke]{stroke:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-none-hover[fill]{fill:none}.jupyter-wrapper .jp-icon-hover :hover .jp-icon-none-hover[stroke]{stroke:none}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon0-hover[fill]{fill:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon1-hover[fill]{fill:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon2-hover[fill]{fill:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon3-hover[fill]{fill:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon4-hover[fill]{fill:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon0-hover[stroke]{stroke:var(--jp-layout-color0)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon1-hover[stroke]{stroke:var(--jp-layout-color1)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon2-hover[stroke]{stroke:var(--jp-layout-color2)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon3-hover[stroke]{stroke:var(--jp-layout-color3)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon4-hover[stroke]{stroke:var(--jp-layout-color4)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent0-hover[fill]{fill:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent1-hover[fill]{fill:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent2-hover[fill]{fill:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent3-hover[fill]{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent4-hover[fill]{fill:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent0-hover[stroke]{stroke:var(--jp-inverse-layout-color0)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent1-hover[stroke]{stroke:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent2-hover[stroke]{stroke:var(--jp-inverse-layout-color2)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent3-hover[stroke]{stroke:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-icon-hover.jp-icon-alt :hover .jp-icon-accent4-hover[stroke]{stroke:var(--jp-inverse-layout-color4)}.jupyter-wrapper .jp-switch{display:flex;align-items:center;padding-left:4px;padding-right:4px;font-size:var(--jp-ui-font-size1);background-color:transparent;color:var(--jp-ui-font-color1);border:none;height:20px}.jupyter-wrapper .jp-switch:hover{background-color:var(--jp-layout-color2)}.jupyter-wrapper .jp-switch-label{margin-right:5px}.jupyter-wrapper .jp-switch-track{cursor:pointer;background-color:var(--jp-switch-color, var(--jp-border-color1));-webkit-transition:.4s;transition:.4s;border-radius:34px;height:16px;width:35px;position:relative}.jupyter-wrapper .jp-switch-track:before{content:\"\";position:absolute;height:10px;width:10px;margin:3px;left:0;background-color:var(--jp-ui-inverse-font-color1);-webkit-transition:.4s;transition:.4s;border-radius:50%}.jupyter-wrapper .jp-switch[aria-checked=true] .jp-switch-track{background-color:var(--jp-switch-true-position-color, var(--jp-warn-color0))}.jupyter-wrapper .jp-switch[aria-checked=true] .jp-switch-track:before{left:19px}.jupyter-wrapper html{box-sizing:unset}.jupyter-wrapper *,.jupyter-wrapper *:before,.jupyter-wrapper *:after{box-sizing:unset}.jupyter-wrapper body{color:unset;font-family:var(--jp-ui-font-family)}.jupyter-wrapper :focus{outline:unset;outline-offset:unset;-moz-outline-radius:unset}.jupyter-wrapper .jp-Button{border-radius:var(--jp-border-radius);padding:0 12px;font-size:var(--jp-ui-font-size1)}.jupyter-wrapper button.jp-Button.bp3-button.bp3-minimal:hover{background-color:var(--jp-layout-color2)}.jupyter-wrapper .jp-Button.minimal{color:unset!important}.jupyter-wrapper .jp-Button.jp-ToolbarButtonComponent{text-transform:none}.jupyter-wrapper .jp-InputGroup input{box-sizing:border-box;border-radius:0;background-color:transparent;color:var(--jp-ui-font-color0);box-shadow:inset 0 0 0 var(--jp-border-width) var(--jp-input-border-color)}.jupyter-wrapper .jp-InputGroup input:focus{box-shadow:inset 0 0 0 var(--jp-border-width) var(--jp-input-active-box-shadow-color),inset 0 0 0 3px var(--jp-input-active-box-shadow-color)}.jupyter-wrapper .jp-InputGroup input::placeholder,.jupyter-wrapper input::placeholder{color:var(--jp-ui-font-color3)}.jupyter-wrapper .jp-BPIcon{display:inline-block;vertical-align:middle;margin:auto}.jupyter-wrapper .bp3-icon.jp-BPIcon>svg:not([fill]){fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-InputGroupAction{padding:6px}.jupyter-wrapper .jp-HTMLSelect.jp-DefaultStyle select{background-color:initial;border:none;border-radius:0;box-shadow:none;color:var(--jp-ui-font-color0);display:block;font-size:var(--jp-ui-font-size1);height:24px;line-height:14px;padding:0 25px 0 10px;text-align:left;-moz-appearance:none;-webkit-appearance:none}.jupyter-wrapper .jp-HTMLSelect.jp-DefaultStyle select:hover,.jupyter-wrapper .jp-HTMLSelect.jp-DefaultStyle select>option{background-color:var(--jp-layout-color2);color:var(--jp-ui-font-color0)}.jupyter-wrapper select{box-sizing:border-box}.jupyter-wrapper .jp-Collapse{display:flex;flex-direction:column;align-items:stretch;border-top:1px solid var(--jp-border-color2);border-bottom:1px solid var(--jp-border-color2)}.jupyter-wrapper .jp-Collapse-header{padding:1px 12px;color:var(--jp-ui-font-color1);background-color:var(--jp-layout-color1);font-size:var(--jp-ui-font-size2)}.jupyter-wrapper .jp-Collapse-header:hover{background-color:var(--jp-layout-color2)}.jupyter-wrapper .jp-Collapse-contents{padding:0 12px;background-color:var(--jp-layout-color1);color:var(--jp-ui-font-color1);overflow:auto}.jupyter-wrapper :root{--jp-private-commandpalette-search-height: 28px}.jupyter-wrapper .lm-CommandPalette{padding-bottom:0;color:var(--jp-ui-font-color1);background:var(--jp-layout-color1);font-size:var(--jp-ui-font-size1)}.jupyter-wrapper .jp-ModalCommandPalette{position:absolute;z-index:10000;top:38px;left:30%;margin:0;padding:4px;width:40%;box-shadow:var(--jp-elevation-z4);border-radius:4px;background:var(--jp-layout-color0)}.jupyter-wrapper .jp-ModalCommandPalette .lm-CommandPalette{max-height:40vh}.jupyter-wrapper .jp-ModalCommandPalette .lm-CommandPalette .lm-close-icon:after{display:none}.jupyter-wrapper .jp-ModalCommandPalette .lm-CommandPalette .lm-CommandPalette-header{display:none}.jupyter-wrapper .jp-ModalCommandPalette .lm-CommandPalette .lm-CommandPalette-item{margin-left:4px;margin-right:4px}.jupyter-wrapper .jp-ModalCommandPalette .lm-CommandPalette .lm-CommandPalette-item.lm-mod-disabled{display:none}.jupyter-wrapper .lm-CommandPalette-search{padding:4px;background-color:var(--jp-layout-color1);z-index:2}.jupyter-wrapper .lm-CommandPalette-wrapper{overflow:overlay;padding:0 9px;background-color:var(--jp-input-active-background);height:30px;box-shadow:inset 0 0 0 var(--jp-border-width) var(--jp-input-border-color)}.jupyter-wrapper .lm-CommandPalette.lm-mod-focused .lm-CommandPalette-wrapper{box-shadow:inset 0 0 0 1px var(--jp-input-active-box-shadow-color),inset 0 0 0 3px var(--jp-input-active-box-shadow-color)}.jupyter-wrapper .jp-SearchIconGroup{color:#fff;background-color:var(--jp-brand-color1);position:absolute;top:4px;right:4px;padding:5px 5px 1px}.jupyter-wrapper .jp-SearchIconGroup svg{height:20px;width:20px}.jupyter-wrapper .jp-SearchIconGroup .jp-icon3[fill]{fill:var(--jp-layout-color0)}.jupyter-wrapper .lm-CommandPalette-input{background:transparent;width:calc(100% - 18px);float:left;border:none;outline:none;font-size:var(--jp-ui-font-size1);color:var(--jp-ui-font-color0);line-height:var(--jp-private-commandpalette-search-height)}.jupyter-wrapper .lm-CommandPalette-input::-webkit-input-placeholder,.jupyter-wrapper .lm-CommandPalette-input::-moz-placeholder,.jupyter-wrapper .lm-CommandPalette-input:-ms-input-placeholder{color:var(--jp-ui-font-color2);font-size:var(--jp-ui-font-size1)}.jupyter-wrapper .lm-CommandPalette-header:first-child{margin-top:0}.jupyter-wrapper .lm-CommandPalette-header{border-bottom:solid var(--jp-border-width) var(--jp-border-color2);color:var(--jp-ui-font-color1);cursor:pointer;display:flex;font-size:var(--jp-ui-font-size0);font-weight:600;letter-spacing:1px;margin-top:8px;padding:8px 0 8px 12px;text-transform:uppercase}.jupyter-wrapper .lm-CommandPalette-header.lm-mod-active{background:var(--jp-layout-color2)}.jupyter-wrapper .lm-CommandPalette-header>mark{background-color:transparent;font-weight:700;color:var(--jp-ui-font-color1)}.jupyter-wrapper .lm-CommandPalette-item{padding:4px 12px 4px 4px;color:var(--jp-ui-font-color1);font-size:var(--jp-ui-font-size1);font-weight:400;display:flex}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-disabled{color:var(--jp-ui-font-color2)}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-active{color:var(--jp-ui-inverse-font-color1);background:var(--jp-brand-color1)}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-active .jp-icon-selectable[fill]{fill:var(--jp-layout-color0)}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-active .lm-CommandPalette-itemLabel>mark{color:var(--jp-ui-inverse-font-color0)}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-active:hover:not(.lm-mod-disabled){color:var(--jp-ui-inverse-font-color1);background:var(--jp-brand-color1)}.jupyter-wrapper .lm-CommandPalette-item:hover:not(.lm-mod-active):not(.lm-mod-disabled){background:var(--jp-layout-color2)}.jupyter-wrapper .lm-CommandPalette-itemContent{overflow:hidden}.jupyter-wrapper .lm-CommandPalette-itemLabel>mark{color:var(--jp-ui-font-color0);background-color:transparent;font-weight:700}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-disabled mark{color:var(--jp-ui-font-color2)}.jupyter-wrapper .lm-CommandPalette-item .lm-CommandPalette-itemIcon{margin:0 4px 0 0;position:relative;width:16px;top:2px;flex:0 0 auto}.jupyter-wrapper .lm-CommandPalette-item.lm-mod-disabled .lm-CommandPalette-itemIcon{opacity:.6}.jupyter-wrapper .lm-CommandPalette-item .lm-CommandPalette-itemShortcut{flex:0 0 auto}.jupyter-wrapper .lm-CommandPalette-itemCaption{display:none}.jupyter-wrapper .lm-CommandPalette-content{background-color:var(--jp-layout-color1)}.jupyter-wrapper .lm-CommandPalette-content:empty:after{content:\"No results\";margin:20px auto auto;width:100px;display:block;font-size:var(--jp-ui-font-size2);font-family:var(--jp-ui-font-family);font-weight:lighter}.jupyter-wrapper .lm-CommandPalette-emptyMessage{text-align:center;margin-top:24px;line-height:1.32;padding:0 8px;color:var(--jp-content-font-color3)}.jupyter-wrapper .jp-Dialog{position:absolute;z-index:10000;display:flex;flex-direction:column;align-items:center;justify-content:center;top:0;left:0;margin:0;padding:0;width:100%;height:100%;background:var(--jp-dialog-background)}.jupyter-wrapper .jp-Dialog-content{display:flex;flex-direction:column;margin-left:auto;margin-right:auto;background:var(--jp-layout-color1);padding:24px 24px 12px;min-width:300px;min-height:150px;max-width:1000px;max-height:500px;box-sizing:border-box;box-shadow:var(--jp-elevation-z20);word-wrap:break-word;border-radius:var(--jp-border-radius);font-size:var(--jp-ui-font-size1);color:var(--jp-ui-font-color1);resize:both}.jupyter-wrapper .jp-Dialog-content.jp-Dialog-content-small{max-width:500px}.jupyter-wrapper .jp-Dialog-button{overflow:visible}.jupyter-wrapper button.jp-Dialog-button:focus{outline:1px solid var(--jp-brand-color1);outline-offset:4px;-moz-outline-radius:0px}.jupyter-wrapper button.jp-Dialog-button:focus::-moz-focus-inner{border:0}.jupyter-wrapper button.jp-Dialog-button.jp-mod-styled.jp-mod-accept:focus,.jupyter-wrapper button.jp-Dialog-button.jp-mod-styled.jp-mod-warn:focus,.jupyter-wrapper button.jp-Dialog-button.jp-mod-styled.jp-mod-reject:focus{outline-offset:4px;-moz-outline-radius:0px}.jupyter-wrapper button.jp-Dialog-button.jp-mod-styled.jp-mod-accept:focus{outline:1px solid var(--md-blue-700)}.jupyter-wrapper button.jp-Dialog-button.jp-mod-styled.jp-mod-warn:focus{outline:1px solid var(--md-red-600)}.jupyter-wrapper button.jp-Dialog-button.jp-mod-styled.jp-mod-reject:focus{outline:1px solid var(--md-grey-700)}.jupyter-wrapper button.jp-Dialog-close-button{padding:0;height:100%;min-width:unset;min-height:unset}.jupyter-wrapper .jp-Dialog-header{display:flex;justify-content:space-between;flex:0 0 auto;padding-bottom:12px;font-size:var(--jp-ui-font-size3);font-weight:400;color:var(--jp-ui-font-color0)}.jupyter-wrapper .jp-Dialog-body{display:flex;flex-direction:column;flex:1 1 auto;font-size:var(--jp-ui-font-size1);background:var(--jp-layout-color1);overflow:auto}.jupyter-wrapper .jp-Dialog-footer{display:flex;flex-direction:row;justify-content:flex-end;align-items:center;flex:0 0 auto;margin-left:-12px;margin-right:-12px;padding:12px}.jupyter-wrapper .jp-Dialog-checkbox{padding-right:5px}.jupyter-wrapper .jp-Dialog-checkbox>input:focus-visible{outline:1px solid var(--jp-input-active-border-color);outline-offset:1px}.jupyter-wrapper .jp-Dialog-spacer{flex:1 1 auto}.jupyter-wrapper .jp-Dialog-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.jupyter-wrapper .jp-Dialog-body>.jp-select-wrapper{width:100%}.jupyter-wrapper .jp-Dialog-body>button{padding:0 16px}.jupyter-wrapper .jp-Dialog-body>label{line-height:1.4;color:var(--jp-ui-font-color0)}.jupyter-wrapper .jp-Dialog-button.jp-mod-styled:not(:last-child){margin-right:12px}.jupyter-wrapper .jp-HoverBox{position:fixed}.jupyter-wrapper .jp-HoverBox.jp-mod-outofview{display:none}.jupyter-wrapper .jp-IFrame{width:100%;height:100%}.jupyter-wrapper .jp-IFrame>iframe{border:none}.jupyter-wrapper body.lm-mod-override-cursor .jp-IFrame{position:relative}.jupyter-wrapper body.lm-mod-override-cursor .jp-IFrame:before{content:\"\";position:absolute;top:0;left:0;right:0;bottom:0;background:transparent}.jupyter-wrapper .jp-Input-Boolean-Dialog{flex-direction:row-reverse;align-items:end;width:100%}.jupyter-wrapper .jp-Input-Boolean-Dialog>label{flex:1 1 auto}.jupyter-wrapper .jp-MainAreaWidget>:focus{outline:none}.jupyter-wrapper .jp-MainAreaWidget .jp-MainAreaWidget-error{padding:6px}.jupyter-wrapper .jp-MainAreaWidget .jp-MainAreaWidget-error>pre{width:auto;padding:10px;background:var(--jp-error-color3);border:var(--jp-border-width) solid var(--jp-error-color1);border-radius:var(--jp-border-radius);color:var(--jp-ui-font-color1);font-size:var(--jp-ui-font-size1);white-space:pre-wrap;word-wrap:break-word}.jupyter-wrapper .jp-MainAreaWidget{contain:strict}.jupyter-wrapper :root{--md-red-50: #ffebee;--md-red-100: #ffcdd2;--md-red-200: #ef9a9a;--md-red-300: #e57373;--md-red-400: #ef5350;--md-red-500: #f44336;--md-red-600: #e53935;--md-red-700: #d32f2f;--md-red-800: #c62828;--md-red-900: #b71c1c;--md-red-A100: #ff8a80;--md-red-A200: #ff5252;--md-red-A400: #ff1744;--md-red-A700: #d50000;--md-pink-50: #fce4ec;--md-pink-100: #f8bbd0;--md-pink-200: #f48fb1;--md-pink-300: #f06292;--md-pink-400: #ec407a;--md-pink-500: #e91e63;--md-pink-600: #d81b60;--md-pink-700: #c2185b;--md-pink-800: #ad1457;--md-pink-900: #880e4f;--md-pink-A100: #ff80ab;--md-pink-A200: #ff4081;--md-pink-A400: #f50057;--md-pink-A700: #c51162;--md-purple-50: #f3e5f5;--md-purple-100: #e1bee7;--md-purple-200: #ce93d8;--md-purple-300: #ba68c8;--md-purple-400: #ab47bc;--md-purple-500: #9c27b0;--md-purple-600: #8e24aa;--md-purple-700: #7b1fa2;--md-purple-800: #6a1b9a;--md-purple-900: #4a148c;--md-purple-A100: #ea80fc;--md-purple-A200: #e040fb;--md-purple-A400: #d500f9;--md-purple-A700: #aa00ff;--md-deep-purple-50: #ede7f6;--md-deep-purple-100: #d1c4e9;--md-deep-purple-200: #b39ddb;--md-deep-purple-300: #9575cd;--md-deep-purple-400: #7e57c2;--md-deep-purple-500: #673ab7;--md-deep-purple-600: #5e35b1;--md-deep-purple-700: #512da8;--md-deep-purple-800: #4527a0;--md-deep-purple-900: #311b92;--md-deep-purple-A100: #b388ff;--md-deep-purple-A200: #7c4dff;--md-deep-purple-A400: #651fff;--md-deep-purple-A700: #6200ea;--md-indigo-50: #e8eaf6;--md-indigo-100: #c5cae9;--md-indigo-200: #9fa8da;--md-indigo-300: #7986cb;--md-indigo-400: #5c6bc0;--md-indigo-500: #3f51b5;--md-indigo-600: #3949ab;--md-indigo-700: #303f9f;--md-indigo-800: #283593;--md-indigo-900: #1a237e;--md-indigo-A100: #8c9eff;--md-indigo-A200: #536dfe;--md-indigo-A400: #3d5afe;--md-indigo-A700: #304ffe;--md-blue-50: #e3f2fd;--md-blue-100: #bbdefb;--md-blue-200: #90caf9;--md-blue-300: #64b5f6;--md-blue-400: #42a5f5;--md-blue-500: #2196f3;--md-blue-600: #1e88e5;--md-blue-700: #1976d2;--md-blue-800: #1565c0;--md-blue-900: #0d47a1;--md-blue-A100: #82b1ff;--md-blue-A200: #448aff;--md-blue-A400: #2979ff;--md-blue-A700: #2962ff;--md-light-blue-50: #e1f5fe;--md-light-blue-100: #b3e5fc;--md-light-blue-200: #81d4fa;--md-light-blue-300: #4fc3f7;--md-light-blue-400: #29b6f6;--md-light-blue-500: #03a9f4;--md-light-blue-600: #039be5;--md-light-blue-700: #0288d1;--md-light-blue-800: #0277bd;--md-light-blue-900: #01579b;--md-light-blue-A100: #80d8ff;--md-light-blue-A200: #40c4ff;--md-light-blue-A400: #00b0ff;--md-light-blue-A700: #0091ea;--md-cyan-50: #e0f7fa;--md-cyan-100: #b2ebf2;--md-cyan-200: #80deea;--md-cyan-300: #4dd0e1;--md-cyan-400: #26c6da;--md-cyan-500: #00bcd4;--md-cyan-600: #00acc1;--md-cyan-700: #0097a7;--md-cyan-800: #00838f;--md-cyan-900: #006064;--md-cyan-A100: #84ffff;--md-cyan-A200: #18ffff;--md-cyan-A400: #00e5ff;--md-cyan-A700: #00b8d4;--md-teal-50: #e0f2f1;--md-teal-100: #b2dfdb;--md-teal-200: #80cbc4;--md-teal-300: #4db6ac;--md-teal-400: #26a69a;--md-teal-500: #009688;--md-teal-600: #00897b;--md-teal-700: #00796b;--md-teal-800: #00695c;--md-teal-900: #004d40;--md-teal-A100: #a7ffeb;--md-teal-A200: #64ffda;--md-teal-A400: #1de9b6;--md-teal-A700: #00bfa5;--md-green-50: #e8f5e9;--md-green-100: #c8e6c9;--md-green-200: #a5d6a7;--md-green-300: #81c784;--md-green-400: #66bb6a;--md-green-500: #4caf50;--md-green-600: #43a047;--md-green-700: #388e3c;--md-green-800: #2e7d32;--md-green-900: #1b5e20;--md-green-A100: #b9f6ca;--md-green-A200: #69f0ae;--md-green-A400: #00e676;--md-green-A700: #00c853;--md-light-green-50: #f1f8e9;--md-light-green-100: #dcedc8;--md-light-green-200: #c5e1a5;--md-light-green-300: #aed581;--md-light-green-400: #9ccc65;--md-light-green-500: #8bc34a;--md-light-green-600: #7cb342;--md-light-green-700: #689f38;--md-light-green-800: #558b2f;--md-light-green-900: #33691e;--md-light-green-A100: #ccff90;--md-light-green-A200: #b2ff59;--md-light-green-A400: #76ff03;--md-light-green-A700: #64dd17;--md-lime-50: #f9fbe7;--md-lime-100: #f0f4c3;--md-lime-200: #e6ee9c;--md-lime-300: #dce775;--md-lime-400: #d4e157;--md-lime-500: #cddc39;--md-lime-600: #c0ca33;--md-lime-700: #afb42b;--md-lime-800: #9e9d24;--md-lime-900: #827717;--md-lime-A100: #f4ff81;--md-lime-A200: #eeff41;--md-lime-A400: #c6ff00;--md-lime-A700: #aeea00;--md-yellow-50: #fffde7;--md-yellow-100: #fff9c4;--md-yellow-200: #fff59d;--md-yellow-300: #fff176;--md-yellow-400: #ffee58;--md-yellow-500: #ffeb3b;--md-yellow-600: #fdd835;--md-yellow-700: #fbc02d;--md-yellow-800: #f9a825;--md-yellow-900: #f57f17;--md-yellow-A100: #ffff8d;--md-yellow-A200: #ffff00;--md-yellow-A400: #ffea00;--md-yellow-A700: #ffd600;--md-amber-50: #fff8e1;--md-amber-100: #ffecb3;--md-amber-200: #ffe082;--md-amber-300: #ffd54f;--md-amber-400: #ffca28;--md-amber-500: #ffc107;--md-amber-600: #ffb300;--md-amber-700: #ffa000;--md-amber-800: #ff8f00;--md-amber-900: #ff6f00;--md-amber-A100: #ffe57f;--md-amber-A200: #ffd740;--md-amber-A400: #ffc400;--md-amber-A700: #ffab00;--md-orange-50: #fff3e0;--md-orange-100: #ffe0b2;--md-orange-200: #ffcc80;--md-orange-300: #ffb74d;--md-orange-400: #ffa726;--md-orange-500: #ff9800;--md-orange-600: #fb8c00;--md-orange-700: #f57c00;--md-orange-800: #ef6c00;--md-orange-900: #e65100;--md-orange-A100: #ffd180;--md-orange-A200: #ffab40;--md-orange-A400: #ff9100;--md-orange-A700: #ff6d00;--md-deep-orange-50: #fbe9e7;--md-deep-orange-100: #ffccbc;--md-deep-orange-200: #ffab91;--md-deep-orange-300: #ff8a65;--md-deep-orange-400: #ff7043;--md-deep-orange-500: #ff5722;--md-deep-orange-600: #f4511e;--md-deep-orange-700: #e64a19;--md-deep-orange-800: #d84315;--md-deep-orange-900: #bf360c;--md-deep-orange-A100: #ff9e80;--md-deep-orange-A200: #ff6e40;--md-deep-orange-A400: #ff3d00;--md-deep-orange-A700: #dd2c00;--md-brown-50: #efebe9;--md-brown-100: #d7ccc8;--md-brown-200: #bcaaa4;--md-brown-300: #a1887f;--md-brown-400: #8d6e63;--md-brown-500: #795548;--md-brown-600: #6d4c41;--md-brown-700: #5d4037;--md-brown-800: #4e342e;--md-brown-900: #3e2723;--md-grey-50: #fafafa;--md-grey-100: #f5f5f5;--md-grey-200: #eeeeee;--md-grey-300: #e0e0e0;--md-grey-400: #bdbdbd;--md-grey-500: #9e9e9e;--md-grey-600: #757575;--md-grey-700: #616161;--md-grey-800: #424242;--md-grey-900: #212121;--md-blue-grey-50: #eceff1;--md-blue-grey-100: #cfd8dc;--md-blue-grey-200: #b0bec5;--md-blue-grey-300: #90a4ae;--md-blue-grey-400: #78909c;--md-blue-grey-500: #607d8b;--md-blue-grey-600: #546e7a;--md-blue-grey-700: #455a64;--md-blue-grey-800: #37474f;--md-blue-grey-900: #263238}.jupyter-wrapper .jp-Spinner{position:absolute;display:flex;justify-content:center;align-items:center;z-index:10;left:0;top:0;width:100%;height:100%;background:var(--jp-layout-color0);outline:none}.jupyter-wrapper .jp-SpinnerContent{font-size:10px;margin:50px auto;text-indent:-9999em;width:3em;height:3em;border-radius:50%;background:var(--jp-brand-color3);background:linear-gradient(to right,#f37626 10%,rgba(255,255,255,0) 42%);position:relative;animation:load3 1s infinite linear,fadeIn 1s}.jupyter-wrapper .jp-SpinnerContent:before{width:50%;height:50%;background:#f37626;border-radius:100% 0 0;position:absolute;top:0;left:0;content:\"\"}.jupyter-wrapper .jp-SpinnerContent:after{background:var(--jp-layout-color0);width:75%;height:75%;border-radius:50%;content:\"\";margin:auto;position:absolute;top:0;left:0;bottom:0;right:0}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes load3{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.jupyter-wrapper button.jp-mod-styled{font-size:var(--jp-ui-font-size1);color:var(--jp-ui-font-color0);border:none;box-sizing:border-box;text-align:center;line-height:32px;height:32px;padding:0 12px;letter-spacing:.8px;outline:none;appearance:none;-webkit-appearance:none;-moz-appearance:none}.jupyter-wrapper input.jp-mod-styled{background:var(--jp-input-background);height:28px;box-sizing:border-box;border:var(--jp-border-width) solid var(--jp-border-color1);padding-left:7px;padding-right:7px;font-size:var(--jp-ui-font-size2);color:var(--jp-ui-font-color0);outline:none;appearance:none;-webkit-appearance:none;-moz-appearance:none}.jupyter-wrapper input[type=checkbox].jp-mod-styled{appearance:checkbox;-webkit-appearance:checkbox;-moz-appearance:checkbox;height:auto}.jupyter-wrapper input.jp-mod-styled:focus{border:var(--jp-border-width) solid var(--md-blue-500);box-shadow:inset 0 0 4px var(--md-blue-300)}.jupyter-wrapper .jp-FileDialog-Checkbox{margin-top:35px;display:flex;flex-direction:row;align-items:end;width:100%}.jupyter-wrapper .jp-FileDialog-Checkbox>label{flex:1 1 auto}.jupyter-wrapper .jp-select-wrapper{display:flex;position:relative;flex-direction:column;padding:1px;background-color:var(--jp-layout-color1);height:28px;box-sizing:border-box;margin-bottom:12px}.jupyter-wrapper .jp-select-wrapper.jp-mod-focused select.jp-mod-styled{border:var(--jp-border-width) solid var(--jp-input-active-border-color);box-shadow:var(--jp-input-box-shadow);background-color:var(--jp-input-active-background)}.jupyter-wrapper select.jp-mod-styled:hover{background-color:var(--jp-layout-color1);cursor:pointer;color:var(--jp-ui-font-color0);background-color:var(--jp-input-hover-background);box-shadow:inset 0 0 1px #00000080}.jupyter-wrapper select.jp-mod-styled{flex:1 1 auto;height:32px;width:100%;font-size:var(--jp-ui-font-size2);background:var(--jp-input-background);color:var(--jp-ui-font-color0);padding:0 25px 0 8px;border:var(--jp-border-width) solid var(--jp-input-border-color);border-radius:0;outline:none;appearance:none;-webkit-appearance:none;-moz-appearance:none}.jupyter-wrapper :root{--jp-private-toolbar-height: calc( 28px + var(--jp-border-width) )}.jupyter-wrapper .jp-Toolbar{color:var(--jp-ui-font-color1);flex:0 0 auto;display:flex;flex-direction:row;border-bottom:var(--jp-border-width) solid var(--jp-toolbar-border-color);box-shadow:var(--jp-toolbar-box-shadow);background:var(--jp-toolbar-background);min-height:var(--jp-toolbar-micro-height);padding:2px;z-index:8;overflow-x:hidden}.jupyter-wrapper .jp-Toolbar>.jp-Toolbar-item.jp-Toolbar-spacer{flex-grow:1;flex-shrink:1}.jupyter-wrapper .jp-Toolbar-item.jp-Toolbar-kernelStatus{display:inline-block;width:32px;background-repeat:no-repeat;background-position:center;background-size:16px}.jupyter-wrapper .jp-Toolbar>.jp-Toolbar-item{flex:0 0 auto;display:flex;padding-left:1px;padding-right:1px;font-size:var(--jp-ui-font-size1);line-height:var(--jp-private-toolbar-height);height:100%}.jupyter-wrapper div.jp-ToolbarButton{color:transparent;border:none;box-sizing:border-box;outline:none;appearance:none;-webkit-appearance:none;-moz-appearance:none;padding:0;margin:0}.jupyter-wrapper button.jp-ToolbarButtonComponent{background:var(--jp-layout-color1);border:none;box-sizing:border-box;outline:none;appearance:none;-webkit-appearance:none;-moz-appearance:none;padding:0 6px;margin:0;height:24px;border-radius:var(--jp-border-radius);display:flex;align-items:center;text-align:center;font-size:14px;min-width:unset;min-height:unset}.jupyter-wrapper button.jp-ToolbarButtonComponent:disabled{opacity:.4}.jupyter-wrapper button.jp-ToolbarButtonComponent span{padding:0;flex:0 0 auto}.jupyter-wrapper button.jp-ToolbarButtonComponent .jp-ToolbarButtonComponent-label{font-size:var(--jp-ui-font-size1);line-height:100%;padding-left:2px;color:var(--jp-ui-font-color1)}.jupyter-wrapper #jp-main-dock-panel[data-mode=single-document] .jp-MainAreaWidget>.jp-Toolbar.jp-Toolbar-micro{padding:0;min-height:0}.jupyter-wrapper #jp-main-dock-panel[data-mode=single-document] .jp-MainAreaWidget>.jp-Toolbar{border:none;box-shadow:none}.jupyter-wrapper body.p-mod-override-cursor *,.jupyter-wrapper body.lm-mod-override-cursor *{cursor:inherit!important}.jupyter-wrapper .jp-JSONEditor{display:flex;flex-direction:column;width:100%}.jupyter-wrapper .jp-JSONEditor-host{flex:1 1 auto;border:var(--jp-border-width) solid var(--jp-input-border-color);border-radius:0;background:var(--jp-layout-color0);min-height:50px;padding:1px}.jupyter-wrapper .jp-JSONEditor.jp-mod-error .jp-JSONEditor-host{border-color:red;outline-color:red}.jupyter-wrapper .jp-JSONEditor-header{display:flex;flex:1 0 auto;padding:0 0 0 12px}.jupyter-wrapper .jp-JSONEditor-header label{flex:0 0 auto}.jupyter-wrapper .jp-JSONEditor-commitButton{height:16px;width:16px;background-size:18px;background-repeat:no-repeat;background-position:center}.jupyter-wrapper .jp-JSONEditor-host.jp-mod-focused{background-color:var(--jp-input-active-background);border:1px solid var(--jp-input-active-border-color);box-shadow:var(--jp-input-box-shadow)}.jupyter-wrapper .jp-Editor.jp-mod-dropTarget{border:var(--jp-border-width) solid var(--jp-input-active-border-color);box-shadow:var(--jp-input-box-shadow)}.jupyter-wrapper .jp-Statusbar-ProgressCircle svg{display:block;margin:0 auto;width:16px;height:24px;align-self:normal}.jupyter-wrapper .jp-Statusbar-ProgressCircle path{fill:var(--jp-inverse-layout-color3)}.jupyter-wrapper .jp-Statusbar-ProgressBar-progress-bar{height:10px;width:100px;border:solid .25px var(--jp-brand-color2);border-radius:3px;overflow:hidden;align-self:center}.jupyter-wrapper .jp-Statusbar-ProgressBar-progress-bar>div{background-color:var(--jp-brand-color2);background-image:linear-gradient(-45deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);background-size:40px 40px;float:left;width:0%;height:100%;font-size:12px;line-height:14px;color:#fff;text-align:center;animation:jp-Statusbar-ExecutionTime-progress-bar 2s linear infinite}.jupyter-wrapper .jp-Statusbar-ProgressBar-progress-bar p{color:var(--jp-ui-font-color1);font-family:var(--jp-ui-font-family);font-size:var(--jp-ui-font-size1);line-height:10px;width:100px}@keyframes jp-Statusbar-ExecutionTime-progress-bar{0%{background-position:0 0}to{background-position:40px 40px}}.jupyter-wrapper .CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.jupyter-wrapper .CodeMirror-lines{padding:4px 0}.jupyter-wrapper .CodeMirror pre.CodeMirror-line,.jupyter-wrapper .CodeMirror pre.CodeMirror-line-like{padding:0 4px}.jupyter-wrapper .CodeMirror-scrollbar-filler,.jupyter-wrapper .CodeMirror-gutter-filler{background-color:#fff}.jupyter-wrapper .CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.jupyter-wrapper .CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.jupyter-wrapper .CodeMirror-guttermarker{color:#000}.jupyter-wrapper .CodeMirror-guttermarker-subtle{color:#999}.jupyter-wrapper .CodeMirror-cursor{border-left:1px solid black;border-right:none;width:0}.jupyter-wrapper .CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.jupyter-wrapper .cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.jupyter-wrapper .cm-fat-cursor div.CodeMirror-cursors{z-index:1}.jupyter-wrapper .cm-fat-cursor-mark{background-color:#14ff1480;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.jupyter-wrapper .cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.jupyter-wrapper .cm-tab{display:inline-block;text-decoration:inherit}.jupyter-wrapper .CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.jupyter-wrapper .CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.jupyter-wrapper .cm-s-default .cm-header{color:#00f}.jupyter-wrapper .cm-s-default .cm-quote{color:#090}.jupyter-wrapper .cm-negative{color:#d44}.jupyter-wrapper .cm-positive{color:#292}.jupyter-wrapper .cm-header,.jupyter-wrapper .cm-strong{font-weight:700}.jupyter-wrapper .cm-em{font-style:italic}.jupyter-wrapper .cm-link{text-decoration:underline}.jupyter-wrapper .cm-strikethrough{text-decoration:line-through}.jupyter-wrapper .cm-s-default .cm-keyword{color:#708}.jupyter-wrapper .cm-s-default .cm-atom{color:#219}.jupyter-wrapper .cm-s-default .cm-number{color:#164}.jupyter-wrapper .cm-s-default .cm-def{color:#00f}.jupyter-wrapper .cm-s-default .cm-variable-2{color:#05a}.jupyter-wrapper .cm-s-default .cm-variable-3,.jupyter-wrapper .cm-s-default .cm-type{color:#085}.jupyter-wrapper .cm-s-default .cm-comment{color:#a50}.jupyter-wrapper .cm-s-default .cm-string{color:#a11}.jupyter-wrapper .cm-s-default .cm-string-2{color:#f50}.jupyter-wrapper .cm-s-default .cm-meta,.jupyter-wrapper .cm-s-default .cm-qualifier{color:#555}.jupyter-wrapper .cm-s-default .cm-builtin{color:#30a}.jupyter-wrapper .cm-s-default .cm-bracket{color:#997}.jupyter-wrapper .cm-s-default .cm-tag{color:#170}.jupyter-wrapper .cm-s-default .cm-attribute{color:#00c}.jupyter-wrapper .cm-s-default .cm-hr{color:#999}.jupyter-wrapper .cm-s-default .cm-link{color:#00c}.jupyter-wrapper .cm-s-default .cm-error,.jupyter-wrapper .cm-invalidchar{color:red}.jupyter-wrapper .CodeMirror-composing{border-bottom:2px solid}.jupyter-wrapper div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}.jupyter-wrapper div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.jupyter-wrapper .CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.jupyter-wrapper .CodeMirror-activeline-background{background:#e8f2ff}.jupyter-wrapper .CodeMirror{position:relative;overflow:hidden;background:white}.jupyter-wrapper .CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:none;position:relative}.jupyter-wrapper .CodeMirror-sizer{position:relative;border-right:50px solid transparent}.jupyter-wrapper .CodeMirror-vscrollbar,.jupyter-wrapper .CodeMirror-hscrollbar,.jupyter-wrapper .CodeMirror-scrollbar-filler,.jupyter-wrapper .CodeMirror-gutter-filler{position:absolute;z-index:6;display:none;outline:none}.jupyter-wrapper .CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.jupyter-wrapper .CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.jupyter-wrapper .CodeMirror-scrollbar-filler{right:0;bottom:0}.jupyter-wrapper .CodeMirror-gutter-filler{left:0;bottom:0}.jupyter-wrapper .CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.jupyter-wrapper .CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.jupyter-wrapper .CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.jupyter-wrapper .CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.jupyter-wrapper .CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.jupyter-wrapper .CodeMirror-gutter-wrapper ::selection{background-color:transparent}.jupyter-wrapper .CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.jupyter-wrapper .CodeMirror-lines{cursor:text;min-height:1px}.jupyter-wrapper .CodeMirror pre.CodeMirror-line,.jupyter-wrapper .CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.jupyter-wrapper .CodeMirror-wrap pre.CodeMirror-line,.jupyter-wrapper .CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.jupyter-wrapper .CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.jupyter-wrapper .CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.jupyter-wrapper .CodeMirror-rtl pre{direction:rtl}.jupyter-wrapper .CodeMirror-code{outline:none}.jupyter-wrapper .CodeMirror-scroll,.jupyter-wrapper .CodeMirror-sizer,.jupyter-wrapper .CodeMirror-gutter,.jupyter-wrapper .CodeMirror-gutters,.jupyter-wrapper .CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.jupyter-wrapper .CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.jupyter-wrapper .CodeMirror-cursor{position:absolute;pointer-events:none}.jupyter-wrapper .CodeMirror-measure pre{position:static}.jupyter-wrapper div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.jupyter-wrapper div.CodeMirror-dragcursors,.jupyter-wrapper .CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.jupyter-wrapper .CodeMirror-selected{background:#d9d9d9}.jupyter-wrapper .CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.jupyter-wrapper .CodeMirror-crosshair{cursor:crosshair}.jupyter-wrapper .CodeMirror-line::selection,.jupyter-wrapper .CodeMirror-line>span::selection,.jupyter-wrapper .CodeMirror-line>span>span::selection{background:#d7d4f0}.jupyter-wrapper .CodeMirror-line::-moz-selection,.jupyter-wrapper .CodeMirror-line>span::-moz-selection,.jupyter-wrapper .CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.jupyter-wrapper .cm-searching{background-color:#ffa;background-color:#ff06}.jupyter-wrapper .cm-force-border{padding-right:.1px}@media print{.jupyter-wrapper .CodeMirror div.CodeMirror-cursors{visibility:hidden}}.jupyter-wrapper .cm-tab-wrap-hack:after{content:\"\"}.jupyter-wrapper span.CodeMirror-selectedtext{background:none}.jupyter-wrapper .CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit}.jupyter-wrapper .CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.jupyter-wrapper .CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.jupyter-wrapper .CodeMirror-dialog input{border:none;outline:none;background:transparent;width:20em;color:inherit;font-family:monospace}.jupyter-wrapper .CodeMirror-dialog button{font-size:70%}.jupyter-wrapper .CodeMirror-foldmarker{color:#00f;text-shadow:#b9f 1px 1px 2px,#b9f -1px -1px 2px,#b9f 1px -1px 2px,#b9f -1px 1px 2px;font-family:arial;line-height:.3;cursor:pointer}.jupyter-wrapper .CodeMirror-foldgutter{width:.7em}.jupyter-wrapper .CodeMirror-foldgutter-open,.jupyter-wrapper .CodeMirror-foldgutter-folded{cursor:pointer}.jupyter-wrapper .CodeMirror-foldgutter-open:after{content:\"\u25be\"}.jupyter-wrapper .CodeMirror-foldgutter-folded:after{content:\"\u25b8\"}.jupyter-wrapper .CodeMirror{line-height:var(--jp-code-line-height);font-size:var(--jp-code-font-size);font-family:var(--jp-code-font-family);border:0;border-radius:0;height:auto}.jupyter-wrapper .CodeMirror pre{padding:0 var(--jp-code-padding)}.jupyter-wrapper .CodeMirror.cm-fat-cursor .cm-overlay.cm-searching{opacity:.5}.jupyter-wrapper .jp-CodeMirrorEditor[data-type=inline] .CodeMirror-dialog{background-color:var(--jp-layout-color0);color:var(--jp-content-font-color1)}.jupyter-wrapper .CodeMirror-lines{padding:var(--jp-code-padding) 0}.jupyter-wrapper .CodeMirror-linenumber{padding:0 8px}.jupyter-wrapper .jp-CodeMirrorEditor{cursor:text}.jupyter-wrapper .jp-CodeMirrorEditor[data-type=inline] .CodeMirror-cursor{border-left:var(--jp-code-cursor-width0) solid var(--jp-editor-cursor-color)}@media screen and (min-width: 2138px) and (max-width: 4319px){.jupyter-wrapper .jp-CodeMirrorEditor[data-type=inline] .CodeMirror-cursor{border-left:var(--jp-code-cursor-width1) solid var(--jp-editor-cursor-color)}}@media screen and (min-width: 4320px){.jupyter-wrapper .jp-CodeMirrorEditor[data-type=inline] .CodeMirror-cursor{border-left:var(--jp-code-cursor-width2) solid var(--jp-editor-cursor-color)}}.jupyter-wrapper .CodeMirror.jp-mod-readOnly .CodeMirror-cursor{display:none}.jupyter-wrapper .CodeMirror-gutters{border-right:1px solid var(--jp-border-color2);background-color:var(--jp-layout-color0)}.jupyter-wrapper .jp-CollaboratorCursor{border-left:5px solid transparent;border-right:5px solid transparent;border-top:none;border-bottom:3px solid;background-clip:content-box;margin-left:-5px;margin-right:-5px}.jupyter-wrapper .CodeMirror-selectedtext.cm-searching{background-color:var(--jp-search-selected-match-background-color)!important;color:var(--jp-search-selected-match-color)!important}.jupyter-wrapper .cm-searching{background-color:var(--jp-search-unselected-match-background-color)!important;color:var(--jp-search-unselected-match-color)!important}.jupyter-wrapper .cm-trailingspace{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAsElEQVQIHQGlAFr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7+r3zKmT0/+pk9P/7+r3zAAAAAAAAAAABAAAAAAAAAAA6OPzM+/q9wAAAAAA6OPzMwAAAAAAAAAAAgAAAAAAAAAAGR8NiRQaCgAZIA0AGR8NiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQyoYJ/SY80UAAAAASUVORK5CYII=);background-position:center left;background-repeat:repeat-x}.jupyter-wrapper .CodeMirror-focused .CodeMirror-selected{background-color:var(--jp-editor-selected-focused-background)}.jupyter-wrapper .CodeMirror-selected{background-color:var(--jp-editor-selected-background)}.jupyter-wrapper .jp-CollaboratorCursor-hover{position:absolute;z-index:1;transform:translate(-50%);color:#fff;border-radius:3px;padding:1px 4px;text-align:center;font-size:var(--jp-ui-font-size1);white-space:nowrap}.jupyter-wrapper .jp-CodeMirror-ruler{border-left:1px dashed var(--jp-border-color2)}.jupyter-wrapper .CodeMirror.cm-s-jupyter{background:var(--jp-layout-color0);color:var(--jp-content-font-color1)}.jupyter-wrapper .jp-CodeConsole .CodeMirror.cm-s-jupyter,.jupyter-wrapper .jp-Notebook .CodeMirror.cm-s-jupyter{background:transparent}.jupyter-wrapper .cm-s-jupyter .CodeMirror-cursor{border-left:var(--jp-code-cursor-width0) solid var(--jp-editor-cursor-color)}.jupyter-wrapper .cm-s-jupyter span.cm-keyword{color:var(--jp-mirror-editor-keyword-color);font-weight:700}.jupyter-wrapper .cm-s-jupyter span.cm-atom{color:var(--jp-mirror-editor-atom-color)}.jupyter-wrapper .cm-s-jupyter span.cm-number{color:var(--jp-mirror-editor-number-color)}.jupyter-wrapper .cm-s-jupyter span.cm-def{color:var(--jp-mirror-editor-def-color)}.jupyter-wrapper .cm-s-jupyter span.cm-variable{color:var(--jp-mirror-editor-variable-color)}.jupyter-wrapper .cm-s-jupyter span.cm-variable-2{color:var(--jp-mirror-editor-variable-2-color)}.jupyter-wrapper .cm-s-jupyter span.cm-variable-3{color:var(--jp-mirror-editor-variable-3-color)}.jupyter-wrapper .cm-s-jupyter span.cm-punctuation{color:var(--jp-mirror-editor-punctuation-color)}.jupyter-wrapper .cm-s-jupyter span.cm-property{color:var(--jp-mirror-editor-property-color)}.jupyter-wrapper .cm-s-jupyter span.cm-operator{color:var(--jp-mirror-editor-operator-color);font-weight:700}.jupyter-wrapper .cm-s-jupyter span.cm-comment{color:var(--jp-mirror-editor-comment-color);font-style:italic}.jupyter-wrapper .cm-s-jupyter span.cm-string{color:var(--jp-mirror-editor-string-color)}.jupyter-wrapper .cm-s-jupyter span.cm-string-2{color:var(--jp-mirror-editor-string-2-color)}.jupyter-wrapper .cm-s-jupyter span.cm-meta{color:var(--jp-mirror-editor-meta-color)}.jupyter-wrapper .cm-s-jupyter span.cm-qualifier{color:var(--jp-mirror-editor-qualifier-color)}.jupyter-wrapper .cm-s-jupyter span.cm-builtin{color:var(--jp-mirror-editor-builtin-color)}.jupyter-wrapper .cm-s-jupyter span.cm-bracket{color:var(--jp-mirror-editor-bracket-color)}.jupyter-wrapper .cm-s-jupyter span.cm-tag{color:var(--jp-mirror-editor-tag-color)}.jupyter-wrapper .cm-s-jupyter span.cm-attribute{color:var(--jp-mirror-editor-attribute-color)}.jupyter-wrapper .cm-s-jupyter span.cm-header{color:var(--jp-mirror-editor-header-color)}.jupyter-wrapper .cm-s-jupyter span.cm-quote{color:var(--jp-mirror-editor-quote-color)}.jupyter-wrapper .cm-s-jupyter span.cm-link{color:var(--jp-mirror-editor-link-color)}.jupyter-wrapper .cm-s-jupyter span.cm-error{color:var(--jp-mirror-editor-error-color)}.jupyter-wrapper .cm-s-jupyter span.cm-hr{color:#999}.jupyter-wrapper .cm-s-jupyter span.cm-tab{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);background-position:right;background-repeat:no-repeat}.jupyter-wrapper .cm-s-jupyter .CodeMirror-activeline-background,.jupyter-wrapper .cm-s-jupyter .CodeMirror-gutter{background-color:var(--jp-layout-color2)}.jupyter-wrapper .jp-CodeMirrorEditor .remote-caret{position:relative;border-left:2px solid black;margin-left:-1px;margin-right:-1px;box-sizing:border-box}.jupyter-wrapper .jp-CodeMirrorEditor .remote-caret>div{white-space:nowrap;position:absolute;top:-1.15em;padding-bottom:.05em;left:-2px;font-size:.95em;background-color:#fa8100;font-family:var(--jp-ui-font-family);font-weight:700;line-height:normal;-webkit-user-select:none;user-select:none;color:#fff;padding-left:2px;padding-right:2px;z-index:3;transition:opacity .3s ease-in-out}.jupyter-wrapper .jp-CodeMirrorEditor .remote-caret.hide-name>div{transition-delay:.7s;opacity:0}.jupyter-wrapper .jp-CodeMirrorEditor .remote-caret:hover>div[style]{opacity:1;transition-delay:0s}.jupyter-wrapper :root{--jp-private-code-span-padding: calc( (var(--jp-code-line-height) - 1) * var(--jp-code-font-size) / 2 )}.jupyter-wrapper .jp-RenderedText{text-align:left;padding-left:var(--jp-code-padding);line-height:var(--jp-code-line-height);font-family:var(--jp-code-font-family)}.jupyter-wrapper .jp-RenderedText pre,.jupyter-wrapper .jp-RenderedJavaScript pre,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore pre{color:var(--jp-content-font-color1);font-size:var(--jp-code-font-size);border:none;margin:0;padding:0}.jupyter-wrapper .jp-RenderedText pre a:link{text-decoration:none;color:var(--jp-content-link-color)}.jupyter-wrapper .jp-RenderedText pre a:hover{text-decoration:underline;color:var(--jp-content-link-color)}.jupyter-wrapper .jp-RenderedText pre a:visited{text-decoration:none;color:var(--jp-content-link-color)}.jupyter-wrapper .jp-RenderedText pre .ansi-black-fg{color:#3e424d}.jupyter-wrapper .jp-RenderedText pre .ansi-red-fg{color:#e75c58}.jupyter-wrapper .jp-RenderedText pre .ansi-green-fg{color:#00a250}.jupyter-wrapper .jp-RenderedText pre .ansi-yellow-fg{color:#ddb62b}.jupyter-wrapper .jp-RenderedText pre .ansi-blue-fg{color:#208ffb}.jupyter-wrapper .jp-RenderedText pre .ansi-magenta-fg{color:#d160c4}.jupyter-wrapper .jp-RenderedText pre .ansi-cyan-fg{color:#60c6c8}.jupyter-wrapper .jp-RenderedText pre .ansi-white-fg{color:#c5c1b4}.jupyter-wrapper .jp-RenderedText pre .ansi-black-bg{background-color:#3e424d;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-red-bg{background-color:#e75c58;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-green-bg{background-color:#00a250;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-yellow-bg{background-color:#ddb62b;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-blue-bg{background-color:#208ffb;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-magenta-bg{background-color:#d160c4;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-cyan-bg{background-color:#60c6c8;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-white-bg{background-color:#c5c1b4;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-black-intense-fg{color:#282c36}.jupyter-wrapper .jp-RenderedText pre .ansi-red-intense-fg{color:#b22b31}.jupyter-wrapper .jp-RenderedText pre .ansi-green-intense-fg{color:#007427}.jupyter-wrapper .jp-RenderedText pre .ansi-yellow-intense-fg{color:#b27d12}.jupyter-wrapper .jp-RenderedText pre .ansi-blue-intense-fg{color:#0065ca}.jupyter-wrapper .jp-RenderedText pre .ansi-magenta-intense-fg{color:#a03196}.jupyter-wrapper .jp-RenderedText pre .ansi-cyan-intense-fg{color:#258f8f}.jupyter-wrapper .jp-RenderedText pre .ansi-white-intense-fg{color:#a1a6b2}.jupyter-wrapper .jp-RenderedText pre .ansi-black-intense-bg{background-color:#282c36;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-red-intense-bg{background-color:#b22b31;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-green-intense-bg{background-color:#007427;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-yellow-intense-bg{background-color:#b27d12;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-blue-intense-bg{background-color:#0065ca;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-magenta-intense-bg{background-color:#a03196;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-cyan-intense-bg{background-color:#258f8f;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-white-intense-bg{background-color:#a1a6b2;padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-default-inverse-fg{color:var(--jp-ui-inverse-font-color0)}.jupyter-wrapper .jp-RenderedText pre .ansi-default-inverse-bg{background-color:var(--jp-inverse-layout-color0);padding:var(--jp-private-code-span-padding) 0}.jupyter-wrapper .jp-RenderedText pre .ansi-bold{font-weight:700}.jupyter-wrapper .jp-RenderedText pre .ansi-underline{text-decoration:underline}.jupyter-wrapper .jp-RenderedText[data-mime-type=\"application/vnd.jupyter.stderr\"]{background:var(--jp-rendermime-error-background);padding-top:var(--jp-code-padding)}.jupyter-wrapper .jp-RenderedLatex{color:var(--jp-content-font-color1);font-size:var(--jp-content-font-size1);line-height:var(--jp-content-line-height)}.jupyter-wrapper .jp-OutputArea-output.jp-RenderedLatex{padding:var(--jp-code-padding);text-align:left}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore{color:var(--jp-content-font-color1);font-family:var(--jp-content-font-family);font-size:var(--jp-content-font-size1);line-height:var(--jp-content-line-height);padding-right:20px}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore em{font-style:italic}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore strong{font-weight:700}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore u{text-decoration:underline}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore a:link{text-decoration:none;color:var(--jp-content-link-color)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore a:hover{text-decoration:underline;color:var(--jp-content-link-color)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore a:visited{text-decoration:none;color:var(--jp-content-link-color)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h1,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h2,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h3,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h4,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h5,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h6{line-height:var(--jp-content-heading-line-height);font-weight:var(--jp-content-heading-font-weight);font-style:normal;margin:var(--jp-content-heading-margin-top) 0 var(--jp-content-heading-margin-bottom) 0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h1:first-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h2:first-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h3:first-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h4:first-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h5:first-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h6:first-child{margin-top:calc(.5 * var(--jp-content-heading-margin-top))}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h1:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h2:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h3:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h4:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h5:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h6:last-child{margin-bottom:calc(.5 * var(--jp-content-heading-margin-bottom))}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h1{font-size:var(--jp-content-font-size5)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h2{font-size:var(--jp-content-font-size4)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h3{font-size:var(--jp-content-font-size3)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h4{font-size:var(--jp-content-font-size2)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h5{font-size:var(--jp-content-font-size1)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore h6{font-size:var(--jp-content-font-size0)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul:not(.list-inline),.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol:not(.list-inline){padding-left:2em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul{list-style:disc}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul ul{list-style:square}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul ul ul{list-style:circle}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol{list-style:decimal}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol ol{list-style:upper-alpha}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol ol ol{list-style:lower-alpha}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol ol ol ol{list-style:lower-roman}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol ol ol ol ol{list-style:decimal}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul{margin-bottom:1em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul ul,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ul ol,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol ul,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore ol ol{margin-bottom:0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore hr{color:var(--jp-border-color2);background-color:var(--jp-border-color1);margin-top:1em;margin-bottom:1em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore>pre{margin:1.5em 2em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore pre,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore code{border:0;background-color:var(--jp-layout-color0);color:var(--jp-content-font-color1);font-family:var(--jp-code-font-family);font-size:inherit;line-height:var(--jp-code-line-height);padding:0;white-space:pre-wrap}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore :not(pre)>code{background-color:var(--jp-layout-color2);padding:1px 5px}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore table{border-collapse:collapse;border-spacing:0;border:none;color:var(--jp-ui-font-color1);font-size:var(--jp-ui-font-size1);table-layout:fixed;margin-left:auto;margin-right:auto}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore thead{border-bottom:var(--jp-border-width) solid var(--jp-border-color1);vertical-align:bottom}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore td,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore th,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore tr{vertical-align:middle;padding:.5em;line-height:normal;white-space:normal;max-width:none;border:none}.jupyter-wrapper .jp-RenderedMarkdown.jp-RenderedHTMLCommon-ignore td,.jupyter-wrapper .jp-RenderedMarkdown.jp-RenderedHTMLCommon-ignore th{max-width:none}.jupyter-wrapper :not(.jp-RenderedMarkdown).jp-RenderedHTMLCommon-ignore td,.jupyter-wrapper :not(.jp-RenderedMarkdown).jp-RenderedHTMLCommon-ignore th,.jupyter-wrapper :not(.jp-RenderedMarkdown).jp-RenderedHTMLCommon-ignore tr{text-align:right}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore th{font-weight:700}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore tbody tr:nth-child(odd){background:var(--jp-layout-color0)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore tbody tr:nth-child(2n){background:var(--jp-rendermime-table-row-background)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore tbody tr:hover{background:var(--jp-rendermime-table-row-hover-background)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore table{margin-bottom:1em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore p{text-align:left;margin:0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore p{margin-bottom:1em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore img{-moz-force-broken-image-icon:1}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore>img{display:block;margin-left:0;margin-right:0;margin-bottom:1em}.jupyter-wrapper [data-jp-theme-light=false] .jp-RenderedImage img.jp-needs-light-background,.jupyter-wrapper [data-jp-theme-light=true] .jp-RenderedImage img.jp-needs-dark-background{background-color:var(--jp-inverse-layout-color1)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore img,.jupyter-wrapper .jp-RenderedImage img,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore svg,.jupyter-wrapper .jp-RenderedSVG svg{max-width:100%;height:auto}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore img.jp-mod-unconfined,.jupyter-wrapper .jp-RenderedImage img.jp-mod-unconfined,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore svg.jp-mod-unconfined,.jupyter-wrapper .jp-RenderedSVG svg.jp-mod-unconfined{max-width:none}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert{padding:var(--jp-notebook-padding);border:var(--jp-border-width) solid transparent;border-radius:var(--jp-border-radius);margin-bottom:1em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-info{color:var(--jp-info-color0);background-color:var(--jp-info-color3);border-color:var(--jp-info-color2)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-info hr{border-color:var(--jp-info-color3)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-info>p:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-info>ul:last-child{margin-bottom:0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-warning{color:var(--jp-warn-color0);background-color:var(--jp-warn-color3);border-color:var(--jp-warn-color2)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-warning hr{border-color:var(--jp-warn-color3)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-warning>p:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-warning>ul:last-child{margin-bottom:0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-success{color:var(--jp-success-color0);background-color:var(--jp-success-color3);border-color:var(--jp-success-color2)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-success hr{border-color:var(--jp-success-color3)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-success>p:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-success>ul:last-child{margin-bottom:0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-danger{color:var(--jp-error-color0);background-color:var(--jp-error-color3);border-color:var(--jp-error-color2)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-danger hr{border-color:var(--jp-error-color3)}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-danger>p:last-child,.jupyter-wrapper .jp-RenderedHTMLCommon-ignore .alert-danger>ul:last-child{margin-bottom:0}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore blockquote{margin:1em 2em;padding:0 1em;border-left:5px solid var(--jp-border-color2)}.jupyter-wrapper a.jp-InternalAnchorLink{visibility:hidden;margin-left:8px;color:var(--md-blue-800)}.jupyter-wrapper h1:hover .jp-InternalAnchorLink,.jupyter-wrapper h2:hover .jp-InternalAnchorLink,.jupyter-wrapper h3:hover .jp-InternalAnchorLink,.jupyter-wrapper h4:hover .jp-InternalAnchorLink,.jupyter-wrapper h5:hover .jp-InternalAnchorLink,.jupyter-wrapper h6:hover .jp-InternalAnchorLink{visibility:visible}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore kbd{background-color:var(--jp-rendermime-table-row-background);border:1px solid var(--jp-border-color0);border-bottom-color:var(--jp-border-color2);border-radius:3px;box-shadow:inset 0 -1px #00000040;display:inline-block;font-size:var(--jp-ui-font-size0);line-height:1em;padding:.2em .5em}.jupyter-wrapper .jp-RenderedHTMLCommon-ignore>*:last-child{margin-bottom:.5em}.jupyter-wrapper .jp-MimeDocument{outline:none}.jupyter-wrapper :root{--jp-private-filebrowser-button-height: 28px;--jp-private-filebrowser-button-width: 48px}.jupyter-wrapper .jp-FileBrowser{display:flex;flex-direction:column;color:var(--jp-ui-font-color1);background:var(--jp-layout-color1);font-size:var(--jp-ui-font-size1)}.jupyter-wrapper .jp-FileBrowser-toolbar.jp-Toolbar{border-bottom:none;height:auto;margin:8px 12px 0;padding:0;box-shadow:none;justify-content:flex-start}.jupyter-wrapper .jp-BreadCrumbs{flex:0 0 auto;margin:8px 12px}.jupyter-wrapper .jp-BreadCrumbs-item{margin:0 2px;padding:0 2px;border-radius:var(--jp-border-radius);cursor:pointer}.jupyter-wrapper .jp-BreadCrumbs-item:hover{background-color:var(--jp-layout-color2)}.jupyter-wrapper .jp-BreadCrumbs-item:first-child{margin-left:0}.jupyter-wrapper .jp-BreadCrumbs-item.jp-mod-dropTarget{background-color:var(--jp-brand-color2);opacity:.7}.jupyter-wrapper .jp-FileBrowser-toolbar>.jp-Toolbar-item{flex:0 0 auto;padding-left:0;padding-right:2px}.jupyter-wrapper .jp-FileBrowser-toolbar>.jp-Toolbar-item .jp-ToolbarButtonComponent{width:40px}.jupyter-wrapper .jp-FileBrowser-toolbar .jp-ToolbarButtonComponent[data-command=\"filebrowser:create-main-launcher\"]{width:72px;background:var(--jp-brand-color1)}.jupyter-wrapper .jp-FileBrowser-toolbar .jp-ToolbarButtonComponent[data-command=\"filebrowser:create-main-launcher\"]:hover,.jupyter-wrapper .jp-FileBrowser-toolbar .jp-ToolbarButtonComponent[data-command=\"filebrowser:create-main-launcher\"]:focus-visible{background-color:var(--jp-brand-color0)!important}.jupyter-wrapper .jp-FileBrowser-toolbar .jp-ToolbarButtonComponent[data-command=\"filebrowser:create-main-launcher\"] .jp-icon3{fill:var(--jp-layout-color1)}.jupyter-wrapper .jp-FileDialog.jp-mod-conflict input{color:var(--jp-error-color1)}.jupyter-wrapper .jp-FileDialog .jp-new-name-title{margin-top:12px}.jupyter-wrapper .jp-LastModified-hidden{display:none}.jupyter-wrapper .jp-FileBrowser-filterBox{padding:0;flex:0 0 auto;margin:8px 12px 0}.jupyter-wrapper .jp-DirListing{flex:1 1 auto;display:flex;flex-direction:column;outline:0}.jupyter-wrapper .jp-DirListing:focus-visible{outline:1px solid var(--jp-brand-color1);outline-offset:-2px}.jupyter-wrapper .jp-DirListing-header{flex:0 0 auto;display:flex;flex-direction:row;overflow:hidden;border-top:var(--jp-border-width) solid var(--jp-border-color2);border-bottom:var(--jp-border-width) solid var(--jp-border-color1);box-shadow:var(--jp-toolbar-box-shadow);z-index:2}.jupyter-wrapper .jp-DirListing-headerItem{padding:4px 12px 2px;font-weight:500}.jupyter-wrapper .jp-DirListing-headerItem:hover{background:var(--jp-layout-color2)}.jupyter-wrapper .jp-DirListing-headerItem.jp-id-name{flex:1 0 84px}.jupyter-wrapper .jp-DirListing-headerItem.jp-id-modified{flex:0 0 112px;border-left:var(--jp-border-width) solid var(--jp-border-color2);text-align:right}.jupyter-wrapper .jp-id-narrow{display:none;flex:0 0 5px;padding:4px;border-left:var(--jp-border-width) solid var(--jp-border-color2);text-align:right;color:var(--jp-border-color2)}.jupyter-wrapper .jp-DirListing-narrow .jp-id-narrow{display:block}.jupyter-wrapper .jp-DirListing-narrow .jp-id-modified,.jupyter-wrapper .jp-DirListing-narrow .jp-DirListing-itemModified{display:none}.jupyter-wrapper .jp-DirListing-headerItem.jp-mod-selected{font-weight:600}.jupyter-wrapper .jp-DirListing-content{flex:1 1 auto;margin:0;padding:0;list-style-type:none;overflow:auto;background-color:var(--jp-layout-color1)}.jupyter-wrapper .jp-DirListing-content mark{color:var(--jp-ui-font-color0);background-color:transparent;font-weight:700}.jupyter-wrapper .jp-DirListing-content .jp-DirListing-item.jp-mod-selected mark{color:var(--jp-ui-inverse-font-color0)}.jupyter-wrapper .jp-DirListing.jp-mod-native-drop .jp-DirListing-content{outline:5px dashed rgba(128,128,128,.5);outline-offset:-10px;cursor:copy}.jupyter-wrapper .jp-DirListing-item{display:flex;flex-direction:row;padding:4px 12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper .jp-DirListing-item[data-is-dot]{opacity:75%}.jupyter-wrapper .jp-DirListing-item.jp-mod-selected{color:var(--jp-ui-inverse-font-color1);background:var(--jp-brand-color1)}.jupyter-wrapper .jp-DirListing-item.jp-mod-dropTarget{background:var(--jp-brand-color3)}.jupyter-wrapper .jp-DirListing-item:hover:not(.jp-mod-selected){background:var(--jp-layout-color2)}.jupyter-wrapper .jp-DirListing-itemIcon{flex:0 0 20px;margin-right:4px}.jupyter-wrapper .jp-DirListing-itemText{flex:1 0 64px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none}.jupyter-wrapper .jp-DirListing-itemModified{flex:0 0 125px;text-align:right}.jupyter-wrapper .jp-DirListing-editor{flex:1 0 64px;outline:none;border:none;color:var(--jp-ui-font-color1);background-color:var(--jp-layout-color1)}.jupyter-wrapper .jp-DirListing-item.jp-mod-running .jp-DirListing-itemIcon:before{color:var(--jp-success-color1);content:\"\u25cf\";font-size:8px;position:absolute;left:-8px}.jupyter-wrapper .jp-DirListing-item.jp-mod-running.jp-mod-selected .jp-DirListing-itemIcon:before{color:var(--jp-ui-inverse-font-color1)}.jupyter-wrapper .jp-DirListing-item.lm-mod-drag-image,.jupyter-wrapper .jp-DirListing-item.jp-mod-selected.lm-mod-drag-image{font-size:var(--jp-ui-font-size1);padding-left:4px;margin-left:4px;width:160px;background-color:var(--jp-ui-inverse-font-color2);box-shadow:var(--jp-elevation-z2);border-radius:0;color:var(--jp-ui-font-color1);transform:translate(-40%) translateY(-58%)}.jupyter-wrapper .jp-Document{min-width:120px;min-height:120px;outline:none}.jupyter-wrapper .jp-OutputArea{overflow-y:auto}.jupyter-wrapper .jp-OutputArea-child{display:flex;flex-direction:row}.jupyter-wrapper body[data-format=mobile] .jp-OutputArea-child{flex-direction:column}.jupyter-wrapper .jp-OutputPrompt{flex:0 0 var(--jp-cell-prompt-width);color:var(--jp-cell-outprompt-font-color);font-family:var(--jp-cell-prompt-font-family);padding:var(--jp-code-padding);letter-spacing:var(--jp-cell-prompt-letter-spacing);line-height:var(--jp-code-line-height);font-size:var(--jp-code-font-size);border:var(--jp-border-width) solid transparent;opacity:var(--jp-cell-prompt-opacity);text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper body[data-format=mobile] .jp-OutputPrompt{flex:0 0 auto;text-align:left}.jupyter-wrapper .jp-OutputArea-output{height:auto;overflow:auto;user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text}.jupyter-wrapper .jp-OutputArea-child .jp-OutputArea-output{flex-grow:1;flex-shrink:1}.jupyter-wrapper body[data-format=mobile] .jp-OutputArea-child .jp-OutputArea-output{margin-left:var(--jp-notebook-padding)}.jupyter-wrapper .jp-OutputArea-output.jp-mod-isolated{width:100%;display:block}.jupyter-wrapper body.lm-mod-override-cursor .jp-OutputArea-output.jp-mod-isolated{position:relative}.jupyter-wrapper body.lm-mod-override-cursor .jp-OutputArea-output.jp-mod-isolated:before{content:\"\";position:absolute;top:0;left:0;right:0;bottom:0;background:transparent}.jupyter-wrapper .jp-OutputArea-output pre{border:none;margin:0;padding:0;overflow-x:auto;overflow-y:auto;word-break:break-all;word-wrap:break-word;white-space:pre-wrap}.jupyter-wrapper .jp-OutputArea-output.jp-RenderedHTMLCommon-ignore table{margin-left:0;margin-right:0}.jupyter-wrapper .jp-OutputArea-output dl,.jupyter-wrapper .jp-OutputArea-output dt,.jupyter-wrapper .jp-OutputArea-output dd{display:block}.jupyter-wrapper .jp-OutputArea-output dl{width:100%;overflow:hidden;padding:0;margin:0}.jupyter-wrapper .jp-OutputArea-output dt{font-weight:700;float:left;width:20%;padding:0;margin:0}.jupyter-wrapper .jp-OutputArea-output dd{float:left;width:80%;padding:0;margin:0}.jupyter-wrapper .jp-TrimmedOutputs a{margin:10px;text-decoration:none;cursor:pointer}.jupyter-wrapper .jp-OutputArea .jp-OutputArea .jp-OutputArea-prompt{display:none}.jupyter-wrapper .jp-OutputArea-prompt:empty{padding:0;border:0}.jupyter-wrapper .jp-OutputArea-output.jp-OutputArea-executeResult{margin-left:0;flex:1 1 auto}.jupyter-wrapper .jp-OutputArea-executeResult .jp-RenderedText.jp-OutputArea-output{padding-top:var(--jp-code-padding);border-top:var(--jp-border-width) solid transparent}.jupyter-wrapper .jp-Stdin-prompt{color:var(--jp-content-font-color0);padding-right:var(--jp-code-padding);vertical-align:baseline;flex:0 0 auto}.jupyter-wrapper .jp-Stdin-input{font-family:var(--jp-code-font-family);font-size:inherit;color:inherit;background-color:inherit;width:42%;min-width:200px;vertical-align:baseline;padding:0 .25em;margin:0 .25em;flex:0 0 70%}.jupyter-wrapper .jp-Stdin-input::placeholder{opacity:0}.jupyter-wrapper .jp-Stdin-input:focus{box-shadow:none}.jupyter-wrapper .jp-Stdin-input:focus::placeholder{opacity:1}.jupyter-wrapper .jp-LinkedOutputView .jp-OutputArea{height:100%;display:block}.jupyter-wrapper .jp-LinkedOutputView .jp-OutputArea-output:only-child{height:100%}.jupyter-wrapper .jp-Collapser{flex:0 0 var(--jp-cell-collapser-width);padding:0;margin:0;border:none;outline:none;background:transparent;border-radius:var(--jp-border-radius);opacity:1}.jupyter-wrapper .jp-Collapser-child{display:block;width:100%;box-sizing:border-box;position:absolute;top:0;bottom:0}.jupyter-wrapper .jp-CellHeader,.jupyter-wrapper .jp-CellFooter{height:0px;width:100%;padding:0;margin:0;border:none;outline:none;background:transparent}.jupyter-wrapper .jp-InputArea{display:flex;flex-direction:row;overflow:hidden}.jupyter-wrapper body[data-format=mobile] .jp-InputArea{flex-direction:column}.jupyter-wrapper .jp-InputArea-editor{flex:1 1 auto;overflow:hidden}.jupyter-wrapper .jp-InputArea-editor{border:var(--jp-border-width) solid var(--jp-cell-editor-border-color);border-radius:0;background:var(--jp-cell-editor-background)}.jupyter-wrapper body[data-format=mobile] .jp-InputArea-editor{margin-left:var(--jp-notebook-padding)}.jupyter-wrapper .jp-InputPrompt{flex:0 0 var(--jp-cell-prompt-width);color:var(--jp-cell-inprompt-font-color);font-family:var(--jp-cell-prompt-font-family);padding:var(--jp-code-padding);letter-spacing:var(--jp-cell-prompt-letter-spacing);line-height:var(--jp-code-line-height);font-size:var(--jp-code-font-size);border:var(--jp-border-width) solid transparent;opacity:var(--jp-cell-prompt-opacity);text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jupyter-wrapper body[data-format=mobile] .jp-InputPrompt{flex:0 0 auto;text-align:left}.jupyter-wrapper .jp-Placeholder{display:flex;flex-direction:row;flex:1 1 auto}.jupyter-wrapper .jp-Placeholder-prompt{box-sizing:border-box}.jupyter-wrapper .jp-Placeholder-content{flex:1 1 auto;border:none;background:transparent;height:20px;box-sizing:border-box}.jupyter-wrapper .jp-Placeholder-content .jp-MoreHorizIcon{width:32px;height:16px;border:1px solid transparent;border-radius:var(--jp-border-radius)}.jupyter-wrapper .jp-Placeholder-content .jp-MoreHorizIcon:hover{border:1px solid var(--jp-border-color1);box-shadow:0 0 2px #00000040;background-color:var(--jp-layout-color0)}.jupyter-wrapper :root{--jp-private-cell-scrolling-output-offset: 5px}.jupyter-wrapper .jp-Cell{padding:var(--jp-cell-padding);margin:0;border:none;outline:none;background:transparent}.jupyter-wrapper .jp-Cell-inputWrapper,.jupyter-wrapper .jp-Cell-outputWrapper{display:flex;flex-direction:row;padding:0;margin:0;overflow:visible}.jupyter-wrapper .jp-Cell-inputArea,.jupyter-wrapper .jp-Cell-outputArea{flex:1 1 auto}.jupyter-wrapper .jp-Cell.jp-mod-noOutputs .jp-Cell-outputCollapser{border:none!important;background:transparent!important}.jupyter-wrapper .jp-Cell:not(.jp-mod-noOutputs) .jp-Cell-outputCollapser{min-height:var(--jp-cell-collapser-min-height)}.jupyter-wrapper .jp-Cell:not(.jp-mod-noOutputs) .jp-Cell-outputWrapper{margin-top:5px}.jupyter-wrapper .jp-CodeCell.jp-mod-outputsScrolled .jp-Cell-outputArea{overflow-y:auto;max-height:24em;margin-left:var(--jp-private-cell-scrolling-output-offset)}.jupyter-wrapper .jp-CodeCell.jp-mod-outputsScrolled .jp-Cell-outputArea:after{content:\" \";box-shadow:inset 0 0 6px 2px #0000004d;width:100%;height:100%;position:sticky;bottom:0;top:0;margin-top:-50%;float:left;display:block;pointer-events:none}.jupyter-wrapper .jp-CodeCell.jp-mod-outputsScrolled .jp-OutputArea-child{padding-top:6px}.jupyter-wrapper .jp-CodeCell.jp-mod-outputsScrolled .jp-OutputArea-prompt{flex:0 0 calc(var(--jp-cell-prompt-width) - var(--jp-private-cell-scrolling-output-offset))}.jupyter-wrapper .jp-MarkdownOutput{flex:1 1 auto;margin-top:0;margin-bottom:0;padding-left:var(--jp-code-padding)}.jupyter-wrapper .jp-MarkdownOutput.jp-RenderedHTMLCommon-ignore{overflow:auto}.jupyter-wrapper .jp-collapseHeadingButton{display:none;min-height:var(--jp-cell-collapser-min-height);font-size:var(--jp-code-font-size);position:absolute;right:0;top:0;bottom:0;background-color:transparent;background-size:25px;background-repeat:no-repeat;background-position-x:center;background-position-y:top;background-image:var(--jp-icon-caret-down);border:none;cursor:pointer}.jupyter-wrapper .jp-collapseHeadingButton:hover{background-color:var(--jp-layout-color2)}.jupyter-wrapper .jp-collapseHeadingButton.jp-mod-collapsed{background-image:var(--jp-icon-caret-right)}.jupyter-wrapper :is(.jp-MarkdownCell:hover,.jp-mod-active) .jp-collapseHeadingButton{display:flex}.jupyter-wrapper .jp-MarkdownCell .jp-InputPrompt{font-size:var(--jp-content-font-size1)}.jupyter-wrapper .jp-mod-rendered .jp-collapseHeadingButton[data-heading-level=\"1\"]{font-size:var(--jp-content-font-size5);background-position-y:calc(.3 * var(--jp-content-font-size5))}.jupyter-wrapper .jp-mod-rendered .jp-collapseHeadingButton[data-heading-level=\"2\"]{font-size:var(--jp-content-font-size4);background-position-y:calc(.3 * var(--jp-content-font-size4))}.jupyter-wrapper .jp-mod-rendered .jp-collapseHeadingButton[data-heading-level=\"3\"]{font-size:var(--jp-content-font-size3);background-position-y:calc(.3 * var(--jp-content-font-size3))}.jupyter-wrapper .jp-mod-rendered .jp-collapseHeadingButton[data-heading-level=\"4\"]{font-size:var(--jp-content-font-size2);background-position-y:calc(.3 * var(--jp-content-font-size2))}.jupyter-wrapper .jp-mod-rendered .jp-collapseHeadingButton[data-heading-level=\"5\"]{font-size:var(--jp-content-font-size1);background-position-y:top}.jupyter-wrapper .jp-mod-rendered .jp-collapseHeadingButton[data-heading-level=\"6\"]{font-size:var(--jp-content-font-size0);background-position-y:top}.jupyter-wrapper .jp-showHiddenCellsButton{margin-left:calc(var(--jp-cell-prompt-width) + 2 * var(--jp-code-padding));margin-top:var(--jp-code-padding);border:1px solid var(--jp-border-color2);background-color:var(--jp-border-color3)!important;color:var(--jp-content-font-color0)!important}.jupyter-wrapper .jp-showHiddenCellsButton:hover{background-color:var(--jp-border-color2)!important}.jupyter-wrapper :root{--jp-notebook-toolbar-padding: 2px 5px 2px 2px}.jupyter-wrapper .jp-NotebookPanel-toolbar{padding:var(--jp-notebook-toolbar-padding)}.jupyter-wrapper .jp-Toolbar-item.jp-Notebook-toolbarCellType .jp-select-wrapper.jp-mod-focused{border:none;box-shadow:none}.jupyter-wrapper .jp-Notebook-toolbarCellTypeDropdown select{height:24px;font-size:var(--jp-ui-font-size1);line-height:14px;border-radius:0;display:block}.jupyter-wrapper .jp-Notebook-toolbarCellTypeDropdown span{top:5px!important}.jupyter-wrapper .jp-Toolbar-responsive-popup{position:absolute;height:fit-content;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:flex-end;border-bottom:var(--jp-border-width) solid var(--jp-toolbar-border-color);box-shadow:var(--jp-toolbar-box-shadow);background:var(--jp-toolbar-background);min-height:var(--jp-toolbar-micro-height);padding:var(--jp-notebook-toolbar-padding);z-index:1;right:0;top:0}.jupyter-wrapper .jp-Toolbar>.jp-Toolbar-responsive-opener{margin-left:auto}.jupyter-wrapper .jp-Notebook-ExecutionIndicator{position:relative;display:inline-block;height:100%;z-index:9997}.jupyter-wrapper .jp-Notebook-ExecutionIndicator-tooltip{visibility:hidden;height:auto;width:max-content;width:-moz-max-content;background-color:var(--jp-layout-color2);color:var(--jp-ui-font-color1);text-align:justify;border-radius:6px;padding:0 5px;position:fixed;display:table}.jupyter-wrapper .jp-Notebook-ExecutionIndicator-tooltip.up{transform:translate(-50%) translateY(-100%) translateY(-32px)}.jupyter-wrapper .jp-Notebook-ExecutionIndicator-tooltip.down{transform:translate(calc(-100% + 16px)) translateY(5px)}.jupyter-wrapper .jp-Notebook-ExecutionIndicator-tooltip.hidden{display:none}.jupyter-wrapper .jp-Notebook-ExecutionIndicator:hover .jp-Notebook-ExecutionIndicator-tooltip{visibility:visible}.jupyter-wrapper .jp-Notebook-ExecutionIndicator span{font-size:var(--jp-ui-font-size1);font-family:var(--jp-ui-font-family);color:var(--jp-ui-font-color1);line-height:24px;display:block}.jupyter-wrapper .jp-Notebook-ExecutionIndicator-progress-bar{display:flex;justify-content:center;height:100%}.jupyter-wrapper :root{--jp-private-notebook-dragImage-width: 304px;--jp-private-notebook-dragImage-height: 36px;--jp-private-notebook-selected-color: var(--md-blue-400);--jp-private-notebook-active-color: var(--md-green-400)}.jupyter-wrapper .jp-NotebookPanel{display:block;height:100%}.jupyter-wrapper .jp-NotebookPanel.jp-Document{min-width:240px;min-height:120px}.jupyter-wrapper .jp-Notebook{padding:var(--jp-notebook-padding);outline:none;overflow:auto}.jupyter-wrapper .jp-Notebook.jp-mod-scrollPastEnd:after{display:block;content:\"\";min-height:var(--jp-notebook-scroll-padding)}.jupyter-wrapper .jp-MainAreaWidget-ContainStrict .jp-Notebook *{contain:strict}.jupyter-wrapper .jp-Notebook .jp-Cell{overflow:visible}.jupyter-wrapper .jp-Notebook .jp-Cell .jp-InputPrompt{cursor:move;float:left}.jupyter-wrapper .jp-Notebook .jp-Cell:not(.jp-mod-active) .jp-InputPrompt{opacity:var(--jp-cell-prompt-not-active-opacity);color:var(--jp-cell-prompt-not-active-font-color)}.jupyter-wrapper .jp-Notebook .jp-Cell:not(.jp-mod-active) .jp-OutputPrompt{opacity:var(--jp-cell-prompt-not-active-opacity);color:var(--jp-cell-prompt-not-active-font-color)}.jupyter-wrapper .jp-Notebook .jp-Cell.jp-mod-active .jp-Collapser{background:var(--jp-brand-color1)}.jupyter-wrapper .jp-Notebook .jp-Cell.jp-mod-dirty .jp-InputPrompt{color:var(--jp-warn-color1)}.jupyter-wrapper .jp-Notebook .jp-Cell.jp-mod-dirty .jp-InputPrompt:before{color:var(--jp-warn-color1);content:\"\u2022\"}.jupyter-wrapper .jp-Notebook .jp-Cell.jp-mod-active.jp-mod-dirty .jp-Collapser{background:var(--jp-warn-color1)}.jupyter-wrapper .jp-Notebook .jp-Cell .jp-Collapser:hover{box-shadow:var(--jp-elevation-z2);background:var(--jp-brand-color1);opacity:var(--jp-cell-collapser-not-active-hover-opacity)}.jupyter-wrapper .jp-Notebook .jp-Cell.jp-mod-active .jp-Collapser:hover{background:var(--jp-brand-color0);opacity:1}.jupyter-wrapper .jp-Notebook.jp-mod-commandMode .jp-Cell.jp-mod-selected{background:var(--jp-notebook-multiselected-color)}.jupyter-wrapper .jp-Notebook.jp-mod-commandMode .jp-Cell.jp-mod-active.jp-mod-selected:not(.jp-mod-multiSelected){background:transparent}.jupyter-wrapper .jp-Notebook.jp-mod-editMode .jp-Cell.jp-mod-active .jp-InputArea-editor{border:var(--jp-border-width) solid var(--jp-cell-editor-active-border-color);box-shadow:var(--jp-input-box-shadow);background-color:var(--jp-cell-editor-active-background)}.jupyter-wrapper .jp-Notebook-cell.jp-mod-dropSource{opacity:.5}.jupyter-wrapper .jp-Notebook-cell.jp-mod-dropTarget,.jupyter-wrapper .jp-Notebook.jp-mod-commandMode .jp-Notebook-cell.jp-mod-active.jp-mod-selected.jp-mod-dropTarget{border-top-color:var(--jp-private-notebook-selected-color);border-top-style:solid;border-top-width:2px}.jupyter-wrapper .jp-dragImage{display:block;flex-direction:row;width:var(--jp-private-notebook-dragImage-width);height:var(--jp-private-notebook-dragImage-height);border:var(--jp-border-width) solid var(--jp-cell-editor-border-color);background:var(--jp-cell-editor-background);overflow:visible}.jupyter-wrapper .jp-dragImage-singlePrompt{box-shadow:2px 2px 4px #0000001f}.jupyter-wrapper .jp-dragImage .jp-dragImage-content{flex:1 1 auto;z-index:2;font-size:var(--jp-code-font-size);font-family:var(--jp-code-font-family);line-height:var(--jp-code-line-height);padding:var(--jp-code-padding);border:var(--jp-border-width) solid var(--jp-cell-editor-border-color);background:var(--jp-cell-editor-background-color);color:var(--jp-content-font-color3);text-align:left;margin:4px 4px 4px 0}.jupyter-wrapper .jp-dragImage .jp-dragImage-prompt{flex:0 0 auto;min-width:36px;color:var(--jp-cell-inprompt-font-color);padding:var(--jp-code-padding);padding-left:12px;font-family:var(--jp-cell-prompt-font-family);letter-spacing:var(--jp-cell-prompt-letter-spacing);line-height:1.9;font-size:var(--jp-code-font-size);border:var(--jp-border-width) solid transparent}.jupyter-wrapper .jp-dragImage-multipleBack{z-index:-1;position:absolute;height:32px;width:300px;top:8px;left:8px;background:var(--jp-layout-color2);border:var(--jp-border-width) solid var(--jp-input-border-color);box-shadow:2px 2px 4px #0000001f}.jupyter-wrapper .jp-NotebookTools{display:block;min-width:var(--jp-sidebar-min-width);color:var(--jp-ui-font-color1);background:var(--jp-layout-color1);font-size:var(--jp-ui-font-size1);overflow:auto}.jupyter-wrapper .jp-NotebookTools-tool{padding:0 12px}.jupyter-wrapper .jp-ActiveCellTool{padding:12px;background-color:var(--jp-layout-color1);border-top:none!important}.jupyter-wrapper .jp-ActiveCellTool .jp-InputArea-prompt{flex:0 0 auto;padding-left:0}.jupyter-wrapper .jp-ActiveCellTool .jp-InputArea-editor{flex:1 1 auto;background:var(--jp-cell-editor-background);border-color:var(--jp-cell-editor-border-color)}.jupyter-wrapper .jp-ActiveCellTool .jp-InputArea-editor .CodeMirror{background:transparent}.jupyter-wrapper .jp-MetadataEditorTool{flex-direction:column;padding:12px 0}.jupyter-wrapper .jp-RankedPanel>:not(:first-child){margin-top:12px}.jupyter-wrapper .jp-KeySelector select.jp-mod-styled{font-size:var(--jp-ui-font-size1);color:var(--jp-ui-font-color0);border:var(--jp-border-width) solid var(--jp-border-color1)}.jupyter-wrapper .jp-KeySelector label,.jupyter-wrapper .jp-MetadataEditorTool label{line-height:1.4}.jupyter-wrapper .jp-NotebookTools .jp-select-wrapper{margin-top:4px;margin-bottom:0}.jupyter-wrapper .jp-NotebookTools .jp-Collapse{margin-top:16px}.jupyter-wrapper .jp-mod-presentationMode .jp-Notebook{--jp-content-font-size1: var(--jp-content-presentation-font-size1);--jp-code-font-size: var(--jp-code-presentation-font-size)}.jupyter-wrapper .jp-mod-presentationMode .jp-Notebook .jp-Cell .jp-InputPrompt,.jupyter-wrapper .jp-mod-presentationMode .jp-Notebook .jp-Cell .jp-OutputPrompt{flex:0 0 110px}.jupyter-wrapper :root{--jp-side-by-side-output-size: 1fr;--jp-side-by-side-resized-cell: var(--jp-side-by-side-output-size)}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-Notebook-cell{margin:3em 5%}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell{display:grid;grid-template-columns:minmax(0,1fr) min-content minmax(0,var(--jp-side-by-side-output-size));grid-template-rows:auto minmax(0,1fr) auto;grid-template-areas:\"header header header\" \"input handle output\" \"footer footer footer\"}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell.jp-mod-resizedCell{grid-template-columns:minmax(0,1fr) min-content minmax(0,var(--jp-side-by-side-resized-cell))}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell .jp-CellHeader{grid-area:header}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell .jp-Cell-inputWrapper{grid-area:input}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell .jp-Cell-outputWrapper{margin-top:0;grid-area:output}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell .jp-CellFooter{grid-area:footer}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell .jp-CellResizeHandle{grid-area:handle;-webkit-user-select:none;user-select:none;display:block;height:100%;cursor:ew-resize;padding:0 var(--jp-cell-padding)}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell .jp-CellResizeHandle:after{content:\"\";display:block;background:var(--jp-border-color2);height:100%;width:5px}.jupyter-wrapper .jp-mod-sideBySide.jp-Notebook .jp-CodeCell.jp-mod-resizedCell .jp-CellResizeHandle:after{background:var(--jp-border-color0)}.jupyter-wrapper .jp-CellResizeHandle{display:none}.jupyter-wrapper .jp-Cell-Placeholder{padding-left:55px}.jupyter-wrapper .jp-Cell-Placeholder-wrapper{background:#fff;border:1px solid;border-color:#e5e6e9 #dfe0e4 #d0d1d5;border-radius:4px;-webkit-border-radius:4px;margin:10px 15px}.jupyter-wrapper .jp-Cell-Placeholder-wrapper-inner{padding:15px;position:relative}.jupyter-wrapper .jp-Cell-Placeholder-wrapper-body{background-repeat:repeat;background-size:50% auto}.jupyter-wrapper .jp-Cell-Placeholder-wrapper-body div{background:#f6f7f8;background-image:-webkit-linear-gradient(left,#f6f7f8 0%,#edeef1 20%,#f6f7f8 40%,#f6f7f8 100%);background-repeat:no-repeat;background-size:800px 104px;height:104px;position:relative}.jupyter-wrapper .jp-Cell-Placeholder-wrapper-body div{position:absolute;right:15px;left:15px;top:15px}.jupyter-wrapper div.jp-Cell-Placeholder-h1{top:20px;height:20px;left:15px;width:150px}.jupyter-wrapper div.jp-Cell-Placeholder-h2{left:15px;top:50px;height:10px;width:100px}.jupyter-wrapper div.jp-Cell-Placeholder-content-1,.jupyter-wrapper div.jp-Cell-Placeholder-content-2,.jupyter-wrapper div.jp-Cell-Placeholder-content-3{left:15px;right:15px;height:10px}.jupyter-wrapper div.jp-Cell-Placeholder-content-1{top:100px}.jupyter-wrapper div.jp-Cell-Placeholder-content-2{top:120px}.jupyter-wrapper div.jp-Cell-Placeholder-content-3{top:140px}.jupyter-wrapper table.dataframe{table-layout:auto!important}.jupyter-wrapper .md-typeset__scrollwrap{margin:0}.jupyter-wrapper .jp-MarkdownOutput{padding:0}.jupyter-wrapper h1 .anchor-link,.jupyter-wrapper h2 .anchor-link,.jupyter-wrapper h3 .anchor-link,.jupyter-wrapper h4 .anchor-link,.jupyter-wrapper h5 .anchor-link,.jupyter-wrapper h6 .anchor-link{display:none;margin-left:.5rem;color:var(--md-default-fg-color--lighter)}.jupyter-wrapper h1 .anchor-link:hover,.jupyter-wrapper h2 .anchor-link:hover,.jupyter-wrapper h3 .anchor-link:hover,.jupyter-wrapper h4 .anchor-link:hover,.jupyter-wrapper h5 .anchor-link:hover,.jupyter-wrapper h6 .anchor-link:hover{text-decoration:none;color:var(--md-accent-fg-color)}.jupyter-wrapper h1:hover .anchor-link,.jupyter-wrapper h2:hover .anchor-link,.jupyter-wrapper h3:hover .anchor-link,.jupyter-wrapper h4:hover .anchor-link,.jupyter-wrapper h5:hover .anchor-link,.jupyter-wrapper h6:hover .anchor-link{display:inline-block}.jupyter-wrapper .jp-InputArea,.jupyter-wrapper .jp-Cell-inputArea,.jupyter-wrapper .jp-RenderedHTMLCommon{width:100%}.jupyter-wrapper .jp-Cell-inputWrapper .jp-InputPrompt{display:none}.jupyter-wrapper .jp-CodeCell .jp-Cell-inputWrapper .jp-InputPrompt{display:block}.jupyter-wrapper .jp-Cell .jp-InputPrompt{cursor:normal}.jupyter-wrapper .highlight pre{background-color:#f5f5f5;padding:10px;overflow:auto}.jupyter-wrapper .celltoolbar{border:none;background:#eee;border-radius:2px 2px 0 0;width:100%;height:29px;padding-right:4px;box-orient:horizontal;box-align:stretch;display:flex;flex-direction:row;align-items:stretch;box-pack:end;justify-content:flex-start;display:-webkit-flex}.jupyter-wrapper .celltoolbar .tags_button_container{display:flex}.jupyter-wrapper .celltoolbar .tags_button_container .tag-container{display:flex;flex-direction:row;flex-grow:1;overflow:hidden;position:relative}.jupyter-wrapper .celltoolbar .tags_button_container .tag-container .cell-tag{display:inline-flex;align-items:center;background-color:#fff;white-space:nowrap;margin:3px 4px;padding:0 4px;border-radius:1px;border:1px solid #ccc;box-shadow:none;width:inherit;font-size:11px;font-family:Roboto Mono,SFMono-Regular,Consolas,Menlo,monospace;height:17px}.jupyter-wrapper .jp-InputArea-editor{width:1px}.jupyter-wrapper .jp-InputPrompt,.jupyter-wrapper .jp-OutputPrompt{overflow:unset}.jupyter-wrapper .jp-RenderedText{font-size:var(--jp-code-font-size)}.jupyter-wrapper .highlight-ipynb{overflow:auto}.jupyter-wrapper .highlight-ipynb pre{margin:0;padding:5px 10px}.jupyter-wrapper table{width:max-content}.jupyter-wrapper table.dataframe{margin-left:auto;margin-right:auto;border:none;border-collapse:collapse;border-spacing:0;color:#000;font-size:12px;table-layout:fixed}.jupyter-wrapper table.dataframe thead{border-bottom:1px solid black;vertical-align:bottom}.jupyter-wrapper table.dataframe tr,.jupyter-wrapper table.dataframe th,.jupyter-wrapper table.dataframe td{text-align:right;vertical-align:middle;padding:.5em;line-height:normal;white-space:normal;max-width:none;border:none}.jupyter-wrapper table.dataframe th{font-weight:700}.jupyter-wrapper table.dataframe tbody tr:nth-child(odd){background:#f5f5f5}.jupyter-wrapper table.dataframe tbody tr:hover{background:rgba(66,165,245,.2)}.jupyter-wrapper *+table{margin-top:1em}.jupyter-wrapper .jp-InputArea-editor{position:relative}.jupyter-wrapper .zeroclipboard-container{position:absolute;top:-3px;right:0;z-index:1}.jupyter-wrapper .zeroclipboard-container clipboard-copy{-webkit-appearance:button;-moz-appearance:button;padding:7px 5px;font:11px system-ui,sans-serif;display:inline-block;cursor:default}.jupyter-wrapper .zeroclipboard-container clipboard-copy:hover{cursor:pointer}.jupyter-wrapper .zeroclipboard-container .clipboard-copy-icon{width:15px;padding:2px 0;color:#57606a;vertical-align:text-bottom}.jupyter-wrapper .clipboard-copy-txt{display:none}[data-md-color-scheme=slate] .highlight pre{background-color:#21222c;padding:10px;overflow:auto}[data-md-color-scheme=slate] .clipboard-copy-icon{color:#555!important}[data-md-color-scheme=slate] .celltoolbar{background:#333!important}[data-md-color-scheme=slate] .celltoolbar .tags_button_container .tag-container .cell-tag{background-color:transparent!important;border:1px solid #666!important}[data-md-color-scheme=slate] table.dataframe{color:#e9ebfc}[data-md-color-scheme=slate] table.dataframe thead{border-bottom:1px solid rgba(233,235,252,.12)}[data-md-color-scheme=slate] table.dataframe tbody tr:nth-child(odd){background:#222}[data-md-color-scheme=slate] table.dataframe tbody tr:hover{background:rgba(66,165,245,.2)}table{width:max-content} .jupyter-wrapper{--jp-shadow-base-lightness: 0;--jp-shadow-umbra-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .2 );--jp-shadow-penumbra-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .14 );--jp-shadow-ambient-color: rgba( var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), var(--jp-shadow-base-lightness), .12 );--jp-elevation-z0: none;--jp-elevation-z1: 0px 2px 1px -1px var(--jp-shadow-umbra-color), 0px 1px 1px 0px var(--jp-shadow-penumbra-color), 0px 1px 3px 0px var(--jp-shadow-ambient-color);--jp-elevation-z2: 0px 3px 1px -2px var(--jp-shadow-umbra-color), 0px 2px 2px 0px var(--jp-shadow-penumbra-color), 0px 1px 5px 0px var(--jp-shadow-ambient-color);--jp-elevation-z4: 0px 2px 4px -1px var(--jp-shadow-umbra-color), 0px 4px 5px 0px var(--jp-shadow-penumbra-color), 0px 1px 10px 0px var(--jp-shadow-ambient-color);--jp-elevation-z6: 0px 3px 5px -1px var(--jp-shadow-umbra-color), 0px 6px 10px 0px var(--jp-shadow-penumbra-color), 0px 1px 18px 0px var(--jp-shadow-ambient-color);--jp-elevation-z8: 0px 5px 5px -3px var(--jp-shadow-umbra-color), 0px 8px 10px 1px var(--jp-shadow-penumbra-color), 0px 3px 14px 2px var(--jp-shadow-ambient-color);--jp-elevation-z12: 0px 7px 8px -4px var(--jp-shadow-umbra-color), 0px 12px 17px 2px var(--jp-shadow-penumbra-color), 0px 5px 22px 4px var(--jp-shadow-ambient-color);--jp-elevation-z16: 0px 8px 10px -5px var(--jp-shadow-umbra-color), 0px 16px 24px 2px var(--jp-shadow-penumbra-color), 0px 6px 30px 5px var(--jp-shadow-ambient-color);--jp-elevation-z20: 0px 10px 13px -6px var(--jp-shadow-umbra-color), 0px 20px 31px 3px var(--jp-shadow-penumbra-color), 0px 8px 38px 7px var(--jp-shadow-ambient-color);--jp-elevation-z24: 0px 11px 15px -7px var(--jp-shadow-umbra-color), 0px 24px 38px 3px var(--jp-shadow-penumbra-color), 0px 9px 46px 8px var(--jp-shadow-ambient-color);--jp-border-width: 1px;--jp-border-color0: var(--md-grey-400);--jp-border-color1: var(--md-grey-400);--jp-border-color2: var(--md-grey-300);--jp-border-color3: var(--md-grey-200);--jp-inverse-border-color: var(--md-grey-600);--jp-border-radius: 2px;--jp-ui-font-scale-factor: 1.2;--jp-ui-font-size0: .83333em;--jp-ui-font-size1: 13px;--jp-ui-font-size2: 1.2em;--jp-ui-font-size3: 1.44em;--jp-ui-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--jp-ui-font-color0: rgba(0, 0, 0, 1);--jp-ui-font-color1: rgba(0, 0, 0, .87);--jp-ui-font-color2: rgba(0, 0, 0, .54);--jp-ui-font-color3: rgba(0, 0, 0, .38);--jp-ui-inverse-font-color0: rgba(255, 255, 255, 1);--jp-ui-inverse-font-color1: rgba(255, 255, 255, 1);--jp-ui-inverse-font-color2: rgba(255, 255, 255, .7);--jp-ui-inverse-font-color3: rgba(255, 255, 255, .5);--jp-content-line-height: 1.6;--jp-content-font-scale-factor: 1.2;--jp-content-font-size0: .83333em;--jp-content-font-size1: 14px;--jp-content-font-size2: 1.2em;--jp-content-font-size3: 1.44em;--jp-content-font-size4: 1.728em;--jp-content-font-size5: 2.0736em;--jp-content-presentation-font-size1: 17px;--jp-content-heading-line-height: 1;--jp-content-heading-margin-top: 1.2em;--jp-content-heading-margin-bottom: .8em;--jp-content-heading-font-weight: 500;--jp-content-font-color0: rgba(0, 0, 0, 1);--jp-content-font-color1: rgba(0, 0, 0, .87);--jp-content-font-color2: rgba(0, 0, 0, .54);--jp-content-font-color3: rgba(0, 0, 0, .38);--jp-content-link-color: var(--md-blue-700);--jp-content-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--jp-code-font-size: 13px;--jp-code-line-height: 1.3077;--jp-code-padding: 5px;--jp-code-font-family-default: Menlo, Consolas, \"DejaVu Sans Mono\", monospace;--jp-code-font-family: var(--jp-code-font-family-default);--jp-code-presentation-font-size: 16px;--jp-code-cursor-width0: 1.4px;--jp-code-cursor-width1: 2px;--jp-code-cursor-width2: 4px;--jp-layout-color0: white;--jp-layout-color1: white;--jp-layout-color2: var(--md-grey-200);--jp-layout-color3: var(--md-grey-400);--jp-layout-color4: var(--md-grey-600);--jp-inverse-layout-color0: #111111;--jp-inverse-layout-color1: var(--md-grey-900);--jp-inverse-layout-color2: var(--md-grey-800);--jp-inverse-layout-color3: var(--md-grey-700);--jp-inverse-layout-color4: var(--md-grey-600);--jp-brand-color0: var(--md-blue-900);--jp-brand-color1: var(--md-blue-700);--jp-brand-color2: var(--md-blue-300);--jp-brand-color3: var(--md-blue-100);--jp-brand-color4: var(--md-blue-50);--jp-accent-color0: var(--md-green-900);--jp-accent-color1: var(--md-green-700);--jp-accent-color2: var(--md-green-300);--jp-accent-color3: var(--md-green-100);--jp-warn-color0: var(--md-orange-900);--jp-warn-color1: var(--md-orange-700);--jp-warn-color2: var(--md-orange-300);--jp-warn-color3: var(--md-orange-100);--jp-error-color0: var(--md-red-900);--jp-error-color1: var(--md-red-700);--jp-error-color2: var(--md-red-300);--jp-error-color3: var(--md-red-100);--jp-success-color0: var(--md-green-900);--jp-success-color1: var(--md-green-700);--jp-success-color2: var(--md-green-300);--jp-success-color3: var(--md-green-100);--jp-info-color0: var(--md-cyan-900);--jp-info-color1: var(--md-cyan-700);--jp-info-color2: var(--md-cyan-300);--jp-info-color3: var(--md-cyan-100);--jp-cell-padding: 5px;--jp-cell-collapser-width: 8px;--jp-cell-collapser-min-height: 20px;--jp-cell-collapser-not-active-hover-opacity: .6;--jp-cell-editor-background: var(--md-grey-100);--jp-cell-editor-border-color: var(--md-grey-300);--jp-cell-editor-box-shadow: inset 0 0 2px var(--md-blue-300);--jp-cell-editor-active-background: var(--jp-layout-color0);--jp-cell-editor-active-border-color: var(--jp-brand-color1);--jp-cell-prompt-width: 64px;--jp-cell-prompt-font-family: var(--jp-code-font-family-default);--jp-cell-prompt-letter-spacing: 0px;--jp-cell-prompt-opacity: 1;--jp-cell-prompt-not-active-opacity: .5;--jp-cell-prompt-not-active-font-color: var(--md-grey-700);--jp-cell-inprompt-font-color: #307fc1;--jp-cell-outprompt-font-color: #bf5b3d;--jp-notebook-padding: 10px;--jp-notebook-select-background: var(--jp-layout-color1);--jp-notebook-multiselected-color: var(--md-blue-50);--jp-notebook-scroll-padding: calc( 100% - var(--jp-code-font-size) * var(--jp-code-line-height) - var(--jp-code-padding) - var(--jp-cell-padding) - 1px );--jp-rendermime-error-background: #fdd;--jp-rendermime-table-row-background: var(--md-grey-100);--jp-rendermime-table-row-hover-background: var(--md-light-blue-50);--jp-dialog-background: rgba(0, 0, 0, .25);--jp-console-padding: 10px;--jp-toolbar-border-color: var(--jp-border-color1);--jp-toolbar-micro-height: 8px;--jp-toolbar-background: var(--jp-layout-color1);--jp-toolbar-box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, .24);--jp-toolbar-header-margin: 4px 4px 0px 4px;--jp-toolbar-active-background: var(--md-grey-300);--jp-statusbar-height: 24px;--jp-input-box-shadow: inset 0 0 2px var(--md-blue-300);--jp-input-active-background: var(--jp-layout-color1);--jp-input-hover-background: var(--jp-layout-color1);--jp-input-background: var(--md-grey-100);--jp-input-border-color: var(--jp-inverse-border-color);--jp-input-active-border-color: var(--jp-brand-color1);--jp-input-active-box-shadow-color: rgba(19, 124, 189, .3);--jp-editor-selected-background: #d9d9d9;--jp-editor-selected-focused-background: #d7d4f0;--jp-editor-cursor-color: var(--jp-ui-font-color0);--jp-mirror-editor-keyword-color: #008000;--jp-mirror-editor-atom-color: #88f;--jp-mirror-editor-number-color: #080;--jp-mirror-editor-def-color: #00f;--jp-mirror-editor-variable-color: var(--md-grey-900);--jp-mirror-editor-variable-2-color: #05a;--jp-mirror-editor-variable-3-color: #085;--jp-mirror-editor-punctuation-color: #05a;--jp-mirror-editor-property-color: #05a;--jp-mirror-editor-operator-color: #aa22ff;--jp-mirror-editor-comment-color: #408080;--jp-mirror-editor-string-color: #ba2121;--jp-mirror-editor-string-2-color: #708;--jp-mirror-editor-meta-color: #aa22ff;--jp-mirror-editor-qualifier-color: #555;--jp-mirror-editor-builtin-color: #008000;--jp-mirror-editor-bracket-color: #997;--jp-mirror-editor-tag-color: #170;--jp-mirror-editor-attribute-color: #00c;--jp-mirror-editor-header-color: blue;--jp-mirror-editor-quote-color: #090;--jp-mirror-editor-link-color: #00c;--jp-mirror-editor-error-color: #f00;--jp-mirror-editor-hr-color: #999;--jp-collaborator-color1: #ffad8e;--jp-collaborator-color2: #dac83d;--jp-collaborator-color3: #72dd76;--jp-collaborator-color4: #00e4d0;--jp-collaborator-color5: #45d4ff;--jp-collaborator-color6: #e2b1ff;--jp-collaborator-color7: #ff9de6;--jp-vega-background: white;--jp-sidebar-min-width: 250px;--jp-search-toggle-off-opacity: .5;--jp-search-toggle-hover-opacity: .8;--jp-search-toggle-on-opacity: 1;--jp-search-selected-match-background-color: rgb(245, 200, 0);--jp-search-selected-match-color: black;--jp-search-unselected-match-background-color: var( --jp-inverse-layout-color0 );--jp-search-unselected-match-color: var(--jp-ui-inverse-font-color0);--jp-icon-contrast-color0: var(--md-purple-600);--jp-icon-contrast-color1: var(--md-green-600);--jp-icon-contrast-color2: var(--md-pink-600);--jp-icon-contrast-color3: var(--md-blue-600);--jp-jupyter-icon-color: #f37626;--jp-notebook-icon-color: #f37626;--jp-json-icon-color: var(--md-orange-700);--jp-console-icon-background-color: var(--md-blue-700);--jp-console-icon-color: white;--jp-terminal-icon-background-color: var(--md-grey-800);--jp-terminal-icon-color: var(--md-grey-200);--jp-text-editor-icon-color: var(--md-grey-700);--jp-inspector-icon-color: var(--md-grey-700);--jp-switch-color: var(--md-grey-400);--jp-switch-true-position-color: var(--md-orange-900)} init_mathjax = function() { if (window.MathJax) { // MathJax loaded MathJax.Hub.Config({ TeX: { equationNumbers: { autoNumber: \"AMS\", useLabelIds: true } }, tex2jax: { inlineMath: [ ['$','$'], [\"\\\\(\",\"\\\\)\"] ], displayMath: [ ['$$','$$'], [\"\\\\[\",\"\\\\]\"] ], processEscapes: true, processEnvironments: true }, displayAlign: 'center', CommonHTML: { linebreaks: { automatic: true } } }); MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]); } } init_mathjax(); document.addEventListener(\"DOMContentLoaded\", async () => { const diagrams = document.querySelectorAll(\".jp-Mermaid > pre.mermaid\"); // do not load mermaidjs if not needed if (!diagrams.length) { return; } const mermaid = (await import(\"https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.5.0/mermaid.esm.min.mjs\")).default; const parser = new DOMParser(); mermaid.initialize({ maxTextSize: 100000, startOnLoad: false, fontFamily: window .getComputedStyle(document.body) .getPropertyValue(\"--jp-ui-font-family\"), theme: document.querySelector(\"body[data-jp-theme-light='true']\") ? \"default\" : \"dark\", }); let _nextMermaidId = 0; function makeMermaidImage(svg) { const img = document.createElement(\"img\"); const doc = parser.parseFromString(svg, \"image/svg+xml\"); const svgEl = doc.querySelector(\"svg\"); const { maxWidth } = svgEl?.style || {}; const firstTitle = doc.querySelector(\"title\"); const firstDesc = doc.querySelector(\"desc\"); img.setAttribute(\"src\", `data:image/svg+xml,${encodeURIComponent(svg)}`); if (maxWidth) { img.width = parseInt(maxWidth); } if (firstTitle) { img.setAttribute(\"alt\", firstTitle.textContent); } if (firstDesc) { const caption = document.createElement(\"figcaption\"); caption.className = \"sr-only\"; caption.textContent = firstDesc.textContent; return [img, caption]; } return [img]; } async function makeMermaidError(text) { let errorMessage = \"\"; try { await mermaid.parse(text); } catch (err) { errorMessage = `${err}`; } const result = document.createElement(\"details\"); result.className = 'jp-RenderedMermaid-Details'; const summary = document.createElement(\"summary\"); summary.className = 'jp-RenderedMermaid-Summary'; const pre = document.createElement(\"pre\"); const code = document.createElement(\"code\"); code.innerText = text; pre.appendChild(code); summary.appendChild(pre); result.appendChild(summary); const warning = document.createElement(\"pre\"); warning.innerText = errorMessage; result.appendChild(warning); return [result]; } async function renderOneMarmaid(src) { const id = `jp-mermaid-${_nextMermaidId++}`; const parent = src.parentNode; let raw = src.textContent.trim(); const el = document.createElement(\"div\"); el.style.visibility = \"hidden\"; document.body.appendChild(el); let results = null; let output = null; try { const { svg } = await mermaid.render(id, raw, el); results = makeMermaidImage(svg); output = document.createElement(\"figure\"); results.map(output.appendChild, output); } catch (err) { parent.classList.add(\"jp-mod-warning\"); results = await makeMermaidError(raw); output = results[0]; } finally { el.remove(); } parent.classList.add(\"jp-RenderedMermaid\"); parent.appendChild(output); } void Promise.all([...diagrams].map(renderOneMarmaid)); }); .jp-Mermaid:not(.jp-RenderedMermaid) { display: none; } .jp-RenderedMermaid { overflow: auto; display: flex; } .jp-RenderedMermaid.jp-mod-warning { width: auto; padding: 0.5em; margin-top: 0.5em; border: var(--jp-border-width) solid var(--jp-warn-color2); border-radius: var(--jp-border-radius); color: var(--jp-ui-font-color1); font-size: var(--jp-ui-font-size1); white-space: pre-wrap; word-wrap: break-word; } .jp-RenderedMermaid figure { margin: 0; overflow: auto; max-width: 100%; } .jp-RenderedMermaid img { max-width: 100%; } .jp-RenderedMermaid-Details > pre { margin-top: 1em; } .jp-RenderedMermaid-Summary { color: var(--jp-warn-color2); } .jp-RenderedMermaid:not(.jp-mod-warning) pre { display: none; } .jp-RenderedMermaid-Summary > pre { display: inline-block; white-space: normal; } Expectile Regression \u00b6 Imports \u00b6 In [2]: Copied! from lightgbmlss.model import * from lightgbmlss.distributions.Expectile import * from lightgbmlss.datasets.data_loader import load_simulated_gaussian_data import plotnine from plotnine import * plotnine . options . figure_size = ( 20 , 10 ) from lightgbmlss.model import * from lightgbmlss.distributions.Expectile import * from lightgbmlss.datasets.data_loader import load_simulated_gaussian_data import plotnine from plotnine import * plotnine.options.figure_size = (20, 10) Data \u00b6 In [3]: Copied! # The data is a simulated Gaussian as follows, where x is the only true feature and all others are noise variables # loc = 10 # scale = 1 + 4*((0.3 < x) & (x < 0.5)) + 2*(x > 0.7) train , test = load_simulated_gaussian_data () X_train , y_train = train . filter ( regex = \"x\" ), train [ \"y\" ] . values X_test , y_test = test . filter ( regex = \"x\" ), test [ \"y\" ] . values dtrain = lgb . Dataset ( X_train , label = y_train ) # The data is a simulated Gaussian as follows, where x is the only true feature and all others are noise variables # loc = 10 # scale = 1 + 4*((0.3 < x) & (x < 0.5)) + 2*(x > 0.7) train, test = load_simulated_gaussian_data() X_train, y_train = train.filter(regex=\"x\"), train[\"y\"].values X_test, y_test = test.filter(regex=\"x\"), test[\"y\"].values dtrain = lgb.Dataset(X_train, label=y_train) Expectile Specification \u00b6 In [4]: Copied! lgblss = LightGBMLSS ( Expectile ( stabilization = \"None\" , # Options are \"None\", \"MAD\", \"L2\". expectiles = [ 0.05 , 0.95 ], # List of expectiles to be estimated, in increasing order. penalize_crossing = True # Whether to include a penalty term to discourage crossing of expectiles. ) ) lgblss = LightGBMLSS( Expectile(stabilization=\"None\", # Options are \"None\", \"MAD\", \"L2\". expectiles = [0.05, 0.95], # List of expectiles to be estimated, in increasing order. penalize_crossing = True # Whether to include a penalty term to discourage crossing of expectiles. ) ) Hyper-Parameter Optimization \u00b6 Any LightGBM hyperparameter can be tuned, where the structure of the parameter dictionary needs to be as follows: - Float/Int sample_type - {\"param_name\": [\"sample_type\", low, high, log]} - sample_type: str, Type of sampling, e.g., \"float\" or \"int\" - low: int, Lower endpoint of the range of suggested values - high: int, Upper endpoint of the range of suggested values - log: bool, Flag to sample the value from the log domain or not - Example: {\"eta\": \"float\", low=1e-5, high=1, log=True]} - Categorical sample_type - {\"param_name\": [\"sample_type\", [\"choice1\", \"choice2\", \"choice3\", \"...\"]]} - sample_type: str, Type of sampling, either \"categorical\" - choice1, choice2, choice3, ...: str, Possible choices for the parameter - Example: {\"boosting\": [\"categorical\", [\"gbdt\", \"dart\"]]} - For parameters without tunable choice (this is needed if tree_method = \"gpu_hist\" and gpu_id needs to be specified) - {\"param_name\": [\"none\", [value]]}, - param_name: str, Name of the parameter - value: int, Value of the parameter - Example: {\"gpu_id\": [\"none\", [0]]} In [5]: Copied! param_dict = { \"eta\" : [ \"float\" , { \"low\" : 1e-5 , \"high\" : 1 , \"log\" : True }], \"max_depth\" : [ \"int\" , { \"low\" : 1 , \"high\" : 10 , \"log\" : False }], \"num_leaves\" : [ \"int\" , { \"low\" : 255 , \"high\" : 255 , \"log\" : False }], # set to constant for this example \"min_data_in_leaf\" : [ \"int\" , { \"low\" : 20 , \"high\" : 20 , \"log\" : False }], # set to constant for this example \"min_gain_to_split\" : [ \"float\" , { \"low\" : 1e-8 , \"high\" : 40 , \"log\" : False }], \"min_sum_hessian_in_leaf\" : [ \"float\" , { \"low\" : 1e-8 , \"high\" : 500 , \"log\" : True }], \"subsample\" : [ \"float\" , { \"low\" : 0.2 , \"high\" : 1.0 , \"log\" : False }], \"feature_fraction\" : [ \"float\" , { \"low\" : 0.2 , \"high\" : 1.0 , \"log\" : False }], \"boosting\" : [ \"categorical\" , [ \"gbdt\" ]], } np . random . seed ( 123 ) opt_param = lgblss . hyper_opt ( param_dict , dtrain , num_boost_round = 100 , # Number of boosting iterations. nfold = 5 , # Number of cv-folds. early_stopping_rounds = 20 , # Number of early-stopping rounds max_minutes = 10 , # Time budget in minutes, i.e., stop study after the given number of minutes. n_trials = 30 , # The number of trials. If this argument is set to None, there is no limitation on the number of trials. silence = False , # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed = 123 , # Seed used to generate cv-folds. hp_seed = None # Seed for random number generator used in the Bayesian hyperparameter search. ) param_dict = { \"eta\": [\"float\", {\"low\": 1e-5, \"high\": 1, \"log\": True}], \"max_depth\": [\"int\", {\"low\": 1, \"high\": 10, \"log\": False}], \"num_leaves\": [\"int\", {\"low\": 255, \"high\": 255, \"log\": False}], # set to constant for this example \"min_data_in_leaf\": [\"int\", {\"low\": 20, \"high\": 20, \"log\": False}], # set to constant for this example \"min_gain_to_split\": [\"float\", {\"low\": 1e-8, \"high\": 40, \"log\": False}], \"min_sum_hessian_in_leaf\": [\"float\", {\"low\": 1e-8, \"high\": 500, \"log\": True}], \"subsample\": [\"float\", {\"low\": 0.2, \"high\": 1.0, \"log\": False}], \"feature_fraction\": [\"float\", {\"low\": 0.2, \"high\": 1.0, \"log\": False}], \"boosting\": [\"categorical\", [\"gbdt\"]], } np.random.seed(123) opt_param = lgblss.hyper_opt(param_dict, dtrain, num_boost_round=100, # Number of boosting iterations. nfold=5, # Number of cv-folds. early_stopping_rounds=20, # Number of early-stopping rounds max_minutes=10, # Time budget in minutes, i.e., stop study after the given number of minutes. n_trials=30, # The number of trials. If this argument is set to None, there is no limitation on the number of trials. silence=False, # Controls the verbosity of the trail, i.e., user can silence the outputs of the trail. seed=123, # Seed used to generate cv-folds. hp_seed=None # Seed for random number generator used in the Bayesian hyperparameter search. ) [I 2023-08-11 12:21:09,469] A new study created in memory with name: LightGBMLSS Hyper-Parameter Optimization 0%| | 0/30 [00:00, ?it/s] [I 2023-08-11 12:21:12,718] Trial 0 finished with value: 2455.671630859375 and parameters: {'eta': 4.999979903379203e-05, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 21.93993866573528, 'min_sum_hessian_in_leaf': 0.0003530133520827798, 'subsample': 0.4971819264686692, 'feature_fraction': 0.3707311537482785, 'boosting': 'gbdt'}. Best is trial 0 with value: 2455.671630859375. [I 2023-08-11 12:21:14,662] Trial 1 finished with value: 1905.1077880859375 and parameters: {'eta': 0.031600943671035775, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 25.283240746368264, 'min_sum_hessian_in_leaf': 49.082392515255734, 'subsample': 0.6788944834474666, 'feature_fraction': 0.9258044091945574, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:18,260] Trial 2 finished with value: 2163.520751953125 and parameters: {'eta': 0.005894981780547752, 'max_depth': 9, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 10.409790461895193, 'min_sum_hessian_in_leaf': 0.0008141832901711874, 'subsample': 0.4070793729617024, 'feature_fraction': 0.6846602442537073, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:20,956] Trial 3 finished with value: 2340.4150390625 and parameters: {'eta': 0.001961322558956042, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 12.389445813281812, 'min_sum_hessian_in_leaf': 0.0001665823267805825, 'subsample': 0.8122180498006835, 'feature_fraction': 0.5881597651097203, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:23,663] Trial 4 finished with value: 2455.928955078125 and parameters: {'eta': 4.876502677739385e-05, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 4.557086477842732, 'min_sum_hessian_in_leaf': 8.971172534030456e-08, 'subsample': 0.43772569367787945, 'feature_fraction': 0.33757411361894973, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:26,413] Trial 5 finished with value: 2020.169677734375 and parameters: {'eta': 0.015583832782804402, 'max_depth': 6, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 17.990075586462332, 'min_sum_hessian_in_leaf': 9.047858492815616e-06, 'subsample': 0.2661853234410493, 'feature_fraction': 0.43711054797968024, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:29,603] Trial 6 finished with value: 2453.094970703125 and parameters: {'eta': 3.192833281012269e-05, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 4.179852781197093, 'min_sum_hessian_in_leaf': 0.0013367062405656063, 'subsample': 0.760623390889942, 'feature_fraction': 0.9918521803651483, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:33,061] Trial 7 finished with value: 2390.234375 and parameters: {'eta': 0.001099408166117131, 'max_depth': 7, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 5.928259874226358, 'min_sum_hessian_in_leaf': 22.801819887756512, 'subsample': 0.8539904207951285, 'feature_fraction': 0.5227409182953131, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:36,013] Trial 8 finished with value: 1914.283935546875 and parameters: {'eta': 0.2493102080752807, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 9.13540771750102, 'min_sum_hessian_in_leaf': 0.34110612176978133, 'subsample': 0.5308020126325235, 'feature_fraction': 0.8641969342663409, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:36,677] Trial 9 finished with value: 1937.4613037109375 and parameters: {'eta': 0.9636709157054849, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 32.67317336133683, 'min_sum_hessian_in_leaf': 1.2744267371826801e-08, 'subsample': 0.9935887353691604, 'feature_fraction': 0.943953338852964, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:38,579] Trial 10 finished with value: 1946.8072509765625 and parameters: {'eta': 0.04879973495349672, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 39.06107022051272, 'min_sum_hessian_in_leaf': 106.46715749293509, 'subsample': 0.6595408126514654, 'feature_fraction': 0.7942683208717877, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:40,307] Trial 11 finished with value: 1942.6409912109375 and parameters: {'eta': 0.3475471835643695, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 23.567503144962224, 'min_sum_hessian_in_leaf': 0.4888567766798536, 'subsample': 0.598911344176229, 'feature_fraction': 0.8378628459298021, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:43,076] Trial 12 finished with value: 1970.949951171875 and parameters: {'eta': 0.1355810372876077, 'max_depth': 1, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 27.308295812909954, 'min_sum_hessian_in_leaf': 0.45165961145216943, 'subsample': 0.5607270559851195, 'feature_fraction': 0.8192697272856723, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:43,791] Trial 13 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:45,120] Trial 14 finished with value: 1917.481201171875 and parameters: {'eta': 0.1918179723427333, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 0.5575780726770159, 'min_sum_hessian_in_leaf': 0.38144740122258586, 'subsample': 0.5276921773301158, 'feature_fraction': 0.8851651910106035, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:46,648] Trial 15 finished with value: 1908.2398681640625 and parameters: {'eta': 0.06814525677756796, 'max_depth': 3, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 13.416527851881755, 'min_sum_hessian_in_leaf': 1.5533574937270136, 'subsample': 0.6891248913559522, 'feature_fraction': 0.9718069324964819, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:47,454] Trial 16 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:48,239] Trial 17 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:49,033] Trial 18 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:49,857] Trial 19 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:51,393] Trial 20 finished with value: 1911.4632568359375 and parameters: {'eta': 0.06986238192969108, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 29.160727721810346, 'min_sum_hessian_in_leaf': 0.016442500462406705, 'subsample': 0.6023777335914249, 'feature_fraction': 0.9155248389222844, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:52,873] Trial 21 finished with value: 1911.5198974609375 and parameters: {'eta': 0.07161895441713878, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 29.27696506397222, 'min_sum_hessian_in_leaf': 0.020946061323593588, 'subsample': 0.6163027333377462, 'feature_fraction': 0.9137504739618006, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:53,660] Trial 22 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:55,121] Trial 23 finished with value: 1908.989501953125 and parameters: {'eta': 0.08734639812497531, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 30.42886083118784, 'min_sum_hessian_in_leaf': 0.03828710084297306, 'subsample': 0.7131459665462002, 'feature_fraction': 0.7904449014182882, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:55,893] Trial 24 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:21:57,012] Trial 25 finished with value: 1917.7242431640625 and parameters: {'eta': 0.1615054287282905, 'max_depth': 5, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 20.364960331001463, 'min_sum_hessian_in_leaf': 3.329890678595058, 'subsample': 0.7972962597807455, 'feature_fraction': 0.8464533630717106, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:57,915] Trial 26 finished with value: 1909.692626953125 and parameters: {'eta': 0.5158300039154515, 'max_depth': 2, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 25.112985606000265, 'min_sum_hessian_in_leaf': 0.05243689522362543, 'subsample': 0.6973560330154309, 'feature_fraction': 0.7889207482360956, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:21:58,767] Trial 27 pruned. Trial was pruned at iteration 20. [I 2023-08-11 12:22:00,393] Trial 28 finished with value: 1921.2425537109375 and parameters: {'eta': 0.09255361005893879, 'max_depth': 4, 'num_leaves': 255, 'min_data_in_leaf': 20, 'min_gain_to_split': 14.802111899924473, 'min_sum_hessian_in_leaf': 1.7943497012679166, 'subsample': 0.692797127315583, 'feature_fraction': 0.6813967545026932, 'boosting': 'gbdt'}. Best is trial 1 with value: 1905.1077880859375. [I 2023-08-11 12:22:01,231] Trial 29 pruned. Trial was pruned at iteration 20. Hyper-Parameter Optimization successfully finished. Number of finished trials: 30 Best trial: Value: 1905.1077880859375 Params: eta: 0.031600943671035775 max_depth: 3 num_leaves: 255 min_data_in_leaf: 20 min_gain_to_split: 25.283240746368264 min_sum_hessian_in_leaf: 49.082392515255734 subsample: 0.6788944834474666 feature_fraction: 0.9258044091945574 boosting: gbdt opt_rounds: 55 Model Training \u00b6 In [6]: Copied! np . random . seed ( 123 ) opt_params = opt_param . copy () n_rounds = opt_params [ \"opt_rounds\" ] del opt_params [ \"opt_rounds\" ] # Train Model with optimized hyperparameters lgblss . train ( opt_params , dtrain , num_boost_round = n_rounds ) np.random.seed(123) opt_params = opt_param.copy() n_rounds = opt_params[\"opt_rounds\"] del opt_params[\"opt_rounds\"] # Train Model with optimized hyperparameters lgblss.train(opt_params, dtrain, num_boost_round=n_rounds ) Prediction \u00b6 In [7]: Copied! # Predicted expectiles pred_expectile = lgblss . predict ( X_test , pred_type = \"expectiles\" ) # Predicted expectiles pred_expectile = lgblss.predict(X_test, pred_type=\"expectiles\") In [8]: Copied! pred_expectile . head () pred_expectile.head() Out[8]: .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } expectile_0.05 expectile_0.95 0 6.695340 13.277894 1 6.615792 13.277894 2 8.519470 11.511595 3 4.557220 14.967069 4 6.615792 13.367647 SHAP Interpretability \u00b6 In [9]: Copied! # Partial Dependence Plot of how x acts on selected expectile lgblss . expectile_plot ( X_test , expectile = \"expectile_0.95\" , feature = \"x_true\" , plot_type = \"Partial_Dependence\" ) # Partial Dependence Plot of how x acts on selected expectile lgblss.expectile_plot(X_test, expectile=\"expectile_0.95\", feature=\"x_true\", plot_type=\"Partial_Dependence\") !function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,\"a\",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p=\"\",e(e.s=410)}([function(t,e,n){\"use strict\";function r(t,e,n,r,o,a,u,c){if(i(e),!t){var s;if(void 0===e)s=new Error(\"Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.\");else{var l=[n,r,o,a,u,c],f=0;s=new Error(e.replace(/%s/g,function(){return l[f++]})),s.name=\"Invariant Violation\"}throw s.framesToPop=1,s}}var i=function(t){};t.exports=r},function(t,e,n){\"use strict\";var r=n(8),i=r;t.exports=i},function(t,e,n){\"use strict\";function r(t){for(var e=arguments.length-1,n=\"Minified React error #\"+t+\"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=\"+t,r=0;r-1?void 0:a(\"96\",t),!s.plugins[n]){e.extractEvents?void 0:a(\"97\",t),s.plugins[n]=e;var r=e.eventTypes;for(var o in r)i(r[o],e,o)?void 0:a(\"98\",o,t)}}}function i(t,e,n){s.eventNameDispatchConfigs.hasOwnProperty(n)?a(\"99\",n):void 0,s.eventNameDispatchConfigs[n]=t;var r=t.phasedRegistrationNames;if(r){for(var i in r)if(r.hasOwnProperty(i)){var u=r[i];o(u,e,n)}return!0}return!!t.registrationName&&(o(t.registrationName,e,n),!0)}function o(t,e,n){s.registrationNameModules[t]?a(\"100\",t):void 0,s.registrationNameModules[t]=e,s.registrationNameDependencies[t]=e.eventTypes[n].dependencies}var a=n(2),u=(n(0),null),c={},s={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(t){ u?a(\"101\"):void 0,u=Array.prototype.slice.call(t),r()},injectEventPluginsByName:function(t){var e=!1;for(var n in t)if(t.hasOwnProperty(n)){var i=t[n];c.hasOwnProperty(n)&&c[n]===i||(c[n]?a(\"102\",n):void 0,c[n]=i,e=!0)}e&&r()},getPluginModuleForEvent:function(t){var e=t.dispatchConfig;if(e.registrationName)return s.registrationNameModules[e.registrationName]||null;if(void 0!==e.phasedRegistrationNames){var n=e.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var i=s.registrationNameModules[n[r]];if(i)return i}}return null},_resetEventPlugins:function(){u=null;for(var t in c)c.hasOwnProperty(t)&&delete c[t];s.plugins.length=0;var e=s.eventNameDispatchConfigs;for(var n in e)e.hasOwnProperty(n)&&delete e[n];var r=s.registrationNameModules;for(var i in r)r.hasOwnProperty(i)&&delete r[i]}};t.exports=s},function(t,e,n){\"use strict\";function r(t){var e=/[=:]/g,n={\"=\":\"=0\",\":\":\"=2\"},r=(\"\"+t).replace(e,function(t){return n[t]});return\"$\"+r}function i(t){var e=/(=0|=2)/g,n={\"=0\":\"=\",\"=2\":\":\"},r=\".\"===t[0]&&\"$\"===t[1]?t.substring(2):t.substring(1);return(\"\"+r).replace(e,function(t){return n[t]})}var o={escape:r,unescape:i};t.exports=o},function(t,e,n){\"use strict\";function r(t){null!=t.checkedLink&&null!=t.valueLink?u(\"87\"):void 0}function i(t){r(t),null!=t.value||null!=t.onChange?u(\"88\"):void 0}function o(t){r(t),null!=t.checked||null!=t.onChange?u(\"89\"):void 0}function a(t){if(t){var e=t.getName();if(e)return\" Check the render method of `\"+e+\"`.\"}return\"\"}var u=n(2),c=n(26),s=n(366),l=(n(0),n(1),{button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0}),f={value:function(t,e,n){return!t[e]||l[t.type]||t.onChange||t.readOnly||t.disabled?null:new Error(\"You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.\")},checked:function(t,e,n){return!t[e]||t.onChange||t.readOnly||t.disabled?null:new Error(\"You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.\")},onChange:c.PropTypes.func},p={},h={checkPropTypes:function(t,e,n){for(var r in f){if(f.hasOwnProperty(r))var i=f[r](e,r,t,\"prop\",null,s);if(i instanceof Error&&!(i.message in p)){p[i.message]=!0;a(n)}}},getValue:function(t){return t.valueLink?(i(t),t.valueLink.value):t.value},getChecked:function(t){return t.checkedLink?(o(t),t.checkedLink.value):t.checked},executeOnChange:function(t,e){return t.valueLink?(i(t),t.valueLink.requestChange(e.target.value)):t.checkedLink?(o(t),t.checkedLink.requestChange(e.target.checked)):t.onChange?t.onChange.call(void 0,e):void 0}};t.exports=h},function(t,e,n){\"use strict\";var r=n(2),i=(n(0),!1),o={replaceNodeWithMarkup:null,processChildrenUpdates:null,injection:{injectEnvironment:function(t){i?r(\"104\"):void 0,o.replaceNodeWithMarkup=t.replaceNodeWithMarkup,o.processChildrenUpdates=t.processChildrenUpdates,i=!0}}};t.exports=o},function(t,e,n){\"use strict\";function r(t,e,n){try{e(n)}catch(t){null===i&&(i=t)}}var i=null,o={invokeGuardedCallback:r,invokeGuardedCallbackWithCatch:r,rethrowCaughtError:function(){if(i){var t=i;throw i=null,t}}};t.exports=o},function(t,e,n){\"use strict\";function r(t){c.enqueueUpdate(t)}function i(t){var e=typeof t;if(\"object\"!==e)return e;var n=t.constructor&&t.constructor.name||e,r=Object.keys(t);return r.length>0&&r.length<20?n+\" (keys: \"+r.join(\", \")+\")\":n}function o(t,e){var n=u.get(t);if(!n){return null}return n}var a=n(2),u=(n(15),n(40)),c=(n(9),n(11)),s=(n(0),n(1),{isMounted:function(t){var e=u.get(t);return!!e&&!!e._renderedComponent},enqueueCallback:function(t,e,n){s.validateCallback(e,n);var i=o(t);return i?(i._pendingCallbacks?i._pendingCallbacks.push(e):i._pendingCallbacks=[e],void r(i)):null},enqueueCallbackInternal:function(t,e){t._pendingCallbacks?t._pendingCallbacks.push(e):t._pendingCallbacks=[e],r(t)},enqueueForceUpdate:function(t){var e=o(t,\"forceUpdate\");e&&(e._pendingForceUpdate=!0,r(e))},enqueueReplaceState:function(t,e){var n=o(t,\"replaceState\");n&&(n._pendingStateQueue=[e],n._pendingReplaceState=!0,r(n))},enqueueSetState:function(t,e){var n=o(t,\"setState\");if(n){var i=n._pendingStateQueue||(n._pendingStateQueue=[]);i.push(e),r(n)}},enqueueElementInternal:function(t,e,n){t._pendingElement=e,t._context=n,r(t)},validateCallback:function(t,e){t&&\"function\"!=typeof t?a(\"122\",e,i(t)):void 0}});t.exports=s},function(t,e,n){\"use strict\";var r={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(t){r.currentScrollLeft=t.x,r.currentScrollTop=t.y}};t.exports=r},function(t,e,n){\"use strict\";var r=function(t){return\"undefined\"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,n,r,i){MSApp.execUnsafeLocalFunction(function(){return t(e,n,r,i)})}:t};t.exports=r},function(t,e,n){\"use strict\";function r(t){var e,n=t.keyCode;return\"charCode\"in t?(e=t.charCode,0===e&&13===n&&(e=13)):e=n,e>=32||13===e?e:0}t.exports=r},function(t,e,n){\"use strict\";function r(t){var e=this,n=e.nativeEvent;if(n.getModifierState)return n.getModifierState(t);var r=o[t];return!!r&&!!n[r]}function i(t){return r}var o={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};t.exports=i},function(t,e,n){\"use strict\";function r(t){var e=t.target||t.srcElement||window;return e.correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}t.exports=r},function(t,e,n){\"use strict\";/** * Checks if an event is supported in the current execution environment. * * NOTE: This will not work correctly for non-generic events such as `change`, * `reset`, `load`, `error`, and `select`. * * Borrows from Modernizr. * * @param {string} eventNameSuffix Event name, e.g. \"click\". * @param {?boolean} capture Check if the capture phase is supported. * @return {boolean} True if the event is supported. * @internal * @license Modernizr 3.0.0pre (Custom Build) | MIT */ function r(t,e){if(!o.canUseDOM||e&&!(\"addEventListener\"in document))return!1;var n=\"on\"+t,r=n in document;if(!r){var a=document.createElement(\"div\");a.setAttribute(n,\"return;\"),r=\"function\"==typeof a[n]}return!r&&i&&\"wheel\"===t&&(r=document.implementation.hasFeature(\"Events.wheel\",\"3.0\")),r}var i,o=n(6);o.canUseDOM&&(i=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature(\"\",\"\")!==!0),t.exports=r},function(t,e,n){\"use strict\";function r(t,e){var n=null===t||t===!1,r=null===e||e===!1;if(n||r)return n===r;var i=typeof t,o=typeof e;return\"string\"===i||\"number\"===i?\"string\"===o||\"number\"===o:\"object\"===o&&t.type===e.type&&t.key===e.key}t.exports=r},function(t,e,n){\"use strict\";var r=(n(3),n(8)),i=(n(1),r);t.exports=i},function(t,e,n){\"use strict\";function r(t,e,n){this.props=t,this.context=e,this.refs=a,this.updater=n||o}var i=n(28),o=n(98),a=(n(176),n(38));n(0),n(1);r.prototype.isReactComponent={},r.prototype.setState=function(t,e){\"object\"!=typeof t&&\"function\"!=typeof t&&null!=t?i(\"85\"):void 0,this.updater.enqueueSetState(this,t),e&&this.updater.enqueueCallback(this,e,\"setState\")},r.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this),t&&this.updater.enqueueCallback(this,t,\"forceUpdate\")};t.exports=r},function(t,e,n){\"use strict\";function r(t,e){}var i=(n(1),{isMounted:function(t){return!1},enqueueCallback:function(t,e){},enqueueForceUpdate:function(t){r(t,\"forceUpdate\")},enqueueReplaceState:function(t,e){r(t,\"replaceState\")},enqueueSetState:function(t,e){r(t,\"setState\")}});t.exports=i},function(t,e){var n;n=function(){return this}();try{n=n||Function(\"return this\")()||(0,eval)(\"this\")}catch(t){\"object\"==typeof window&&(n=window)}t.exports=n},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,\"loaded\",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,\"id\",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,n){\"use strict\";n.d(e,\"b\",function(){return i}),n.d(e,\"a\",function(){return o});var r=Array.prototype,i=r.slice,o=r.map},function(t,e,n){\"use strict\";var r=n(18),i=n(103),o=n.i(i.a)(r.a),a=o.right;o.left;e.a=a},function(t,e,n){\"use strict\";function r(t){return function(e,r){return n.i(i.a)(t(e),r)}}var i=n(18);e.a=function(t){return 1===t.length&&(t=r(t)),{left:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r>>1;t(e[o],n)<0?r=o+1:i=o}return r},right:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r>>1;t(e[o],n)>0?i=o:r=o+1}return r}}}},function(t,e,n){\"use strict\";var r=n(111);e.a=function(t,e){var i=n.i(r.a)(t,e);return i?Math.sqrt(i):i}},function(t,e,n){\"use strict\";e.a=function(t,e){var n,r,i,o=-1,a=t.length;if(null==e){for(;++o=r){n=i=r;break}for(;++or&&(n=r),i 0&&(F+=L[j].effect),L[j].posyBottom=this.yscale(F),L[j].ind=j}catch(t){W=!0,V=t}finally{try{!B&&H.return&&H.return()}finally{if(W)throw V}}var q=F,Y=!0,K=!1,G=void 0;try{for(var $,X=this.currNegOrderedFeatures[Symbol.iterator]();!(Y=($=X.next()).done);Y=!0)j=$.value,L[j].negyTop=this.yscale(F),L[j].effect<0&&(F-=L[j].effect),L[j].negyBottom=this.yscale(F)}catch(t){K=!0,G=t}finally{try{!Y&&X.return&&X.return()}finally{if(K)throw G}}v[R].joinPoint=q,v[R].joinPointy=this.yscale(q)}var Z=(0,g.line)().x(function(t){return t[0]}).y(function(t){return t[1]}),Q=this.mainGroup.selectAll(\".force-bar-array-area-pos\").data(this.currUsedFeatures);Q.enter().append(\"path\").attr(\"class\",\"force-bar-array-area-pos\").merge(Q).attr(\"d\",function(t){var e=(0,y.map)((0,y.range)(P),function(e){return[v[e].xmapScaled,v[e].features[t].posyTop]}),n=(0,y.map)((0,y.rangeRight)(P),function(e){return[v[e].xmapScaled,v[e].features[t].posyBottom]});return Z(e.concat(n))}).attr(\"fill\",this.colors[0]),Q.exit().remove();var J=this.mainGroup.selectAll(\".force-bar-array-area-neg\").data(this.currUsedFeatures);J.enter().append(\"path\").attr(\"class\",\"force-bar-array-area-neg\").merge(J).attr(\"d\",function(t){var e=(0,y.map)((0,y.range)(P),function(e){return[v[e].xmapScaled,v[e].features[t].negyTop]}),n=(0,y.map)((0,y.rangeRight)(P),function(e){return[v[e].xmapScaled,v[e].features[t].negyBottom]});return Z(e.concat(n))}).attr(\"fill\",this.colors[1]),J.exit().remove();var tt=this.mainGroup.selectAll(\".force-bar-array-divider-pos\").data(this.currUsedFeatures);tt.enter().append(\"path\").attr(\"class\",\"force-bar-array-divider-pos\").merge(tt).attr(\"d\",function(t){var e=(0,y.map)((0,y.range)(P),function(e){return[v[e].xmapScaled,v[e].features[t].posyBottom]});return Z(e)}).attr(\"fill\",\"none\").attr(\"stroke-width\",1).attr(\"stroke\",function(){return t.colors[0].brighter(1.2)}),tt.exit().remove();var et=this.mainGroup.selectAll(\".force-bar-array-divider-neg\").data(this.currUsedFeatures);et.enter().append(\"path\").attr(\"class\",\"force-bar-array-divider-neg\").merge(et).attr(\"d\",function(t){var e=(0,y.map)((0,y.range)(P),function(e){return[v[e].xmapScaled,v[e].features[t].negyTop]});return Z(e)}).attr(\"fill\",\"none\").attr(\"stroke-width\",1).attr(\"stroke\",function(){return t.colors[1].brighter(1.5)}),et.exit().remove();for(var nt=function(t,e,n,r,i){var o=void 0,a=void 0;\"pos\"===i?(o=t[n].features[e].posyBottom,a=t[n].features[e].posyTop):(o=t[n].features[e].negyBottom,a=t[n].features[e].negyTop);for(var u=void 0,c=void 0,s=n+1;s<=r;++s)\"pos\"===i?(u=t[s].features[e].posyBottom,c=t[s].features[e].posyTop):(u=t[s].features[e].negyBottom,c=t[s].features[e].negyTop),u>o&&(o=u),c=it&&yt>=rt){for(;mt -1?void 0:a(\"96\",t),!s.plugins[n]){e.extractEvents?void 0:a(\"97\",t),s.plugins[n]=e;var r=e.eventTypes;for(var o in r)i(r[o],e,o)?void 0:a(\"98\",o,t)}}}function i(t,e,n){s.eventNameDispatchConfigs.hasOwnProperty(n)?a(\"99\",n):void 0,s.eventNameDispatchConfigs[n]=t;var r=t.phasedRegistrationNames;if(r){for(var i in r)if(r.hasOwnProperty(i)){var u=r[i];o(u,e,n)}return!0}return!!t.registrationName&&(o(t.registrationName,e,n),!0)}function o(t,e,n){s.registrationNameModules[t]?a(\"100\",t):void 0,s.registrationNameModules[t]=e,s.registrationNameDependencies[t]=e.eventTypes[n].dependencies}var a=n(2),u=(n(0),null),c={},s={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(t){ u?a(\"101\"):void 0,u=Array.prototype.slice.call(t),r()},injectEventPluginsByName:function(t){var e=!1;for(var n in t)if(t.hasOwnProperty(n)){var i=t[n];c.hasOwnProperty(n)&&c[n]===i||(c[n]?a(\"102\",n):void 0,c[n]=i,e=!0)}e&&r()},getPluginModuleForEvent:function(t){var e=t.dispatchConfig;if(e.registrationName)return s.registrationNameModules[e.registrationName]||null;if(void 0!==e.phasedRegistrationNames){var n=e.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var i=s.registrationNameModules[n[r]];if(i)return i}}return null},_resetEventPlugins:function(){u=null;for(var t in c)c.hasOwnProperty(t)&&delete c[t];s.plugins.length=0;var e=s.eventNameDispatchConfigs;for(var n in e)e.hasOwnProperty(n)&&delete e[n];var r=s.registrationNameModules;for(var i in r)r.hasOwnProperty(i)&&delete r[i]}};t.exports=s},function(t,e,n){\"use strict\";function r(t){var e=/[=:]/g,n={\"=\":\"=0\",\":\":\"=2\"},r=(\"\"+t).replace(e,function(t){return n[t]});return\"$\"+r}function i(t){var e=/(=0|=2)/g,n={\"=0\":\"=\",\"=2\":\":\"},r=\".\"===t[0]&&\"$\"===t[1]?t.substring(2):t.substring(1);return(\"\"+r).replace(e,function(t){return n[t]})}var o={escape:r,unescape:i};t.exports=o},function(t,e,n){\"use strict\";function r(t){null!=t.checkedLink&&null!=t.valueLink?u(\"87\"):void 0}function i(t){r(t),null!=t.value||null!=t.onChange?u(\"88\"):void 0}function o(t){r(t),null!=t.checked||null!=t.onChange?u(\"89\"):void 0}function a(t){if(t){var e=t.getName();if(e)return\" Check the render method of `\"+e+\"`.\"}return\"\"}var u=n(2),c=n(26),s=n(366),l=(n(0),n(1),{button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0}),f={value:function(t,e,n){return!t[e]||l[t.type]||t.onChange||t.readOnly||t.disabled?null:new Error(\"You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.\")},checked:function(t,e,n){return!t[e]||t.onChange||t.readOnly||t.disabled?null:new Error(\"You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.\")},onChange:c.PropTypes.func},p={},h={checkPropTypes:function(t,e,n){for(var r in f){if(f.hasOwnProperty(r))var i=f[r](e,r,t,\"prop\",null,s);if(i instanceof Error&&!(i.message in p)){p[i.message]=!0;a(n)}}},getValue:function(t){return t.valueLink?(i(t),t.valueLink.value):t.value},getChecked:function(t){return t.checkedLink?(o(t),t.checkedLink.value):t.checked},executeOnChange:function(t,e){return t.valueLink?(i(t),t.valueLink.requestChange(e.target.value)):t.checkedLink?(o(t),t.checkedLink.requestChange(e.target.checked)):t.onChange?t.onChange.call(void 0,e):void 0}};t.exports=h},function(t,e,n){\"use strict\";var r=n(2),i=(n(0),!1),o={replaceNodeWithMarkup:null,processChildrenUpdates:null,injection:{injectEnvironment:function(t){i?r(\"104\"):void 0,o.replaceNodeWithMarkup=t.replaceNodeWithMarkup,o.processChildrenUpdates=t.processChildrenUpdates,i=!0}}};t.exports=o},function(t,e,n){\"use strict\";function r(t,e,n){try{e(n)}catch(t){null===i&&(i=t)}}var i=null,o={invokeGuardedCallback:r,invokeGuardedCallbackWithCatch:r,rethrowCaughtError:function(){if(i){var t=i;throw i=null,t}}};t.exports=o},function(t,e,n){\"use strict\";function r(t){c.enqueueUpdate(t)}function i(t){var e=typeof t;if(\"object\"!==e)return e;var n=t.constructor&&t.constructor.name||e,r=Object.keys(t);return r.length>0&&r.length<20?n+\" (keys: \"+r.join(\", \")+\")\":n}function o(t,e){var n=u.get(t);if(!n){return null}return n}var a=n(2),u=(n(15),n(40)),c=(n(9),n(11)),s=(n(0),n(1),{isMounted:function(t){var e=u.get(t);return!!e&&!!e._renderedComponent},enqueueCallback:function(t,e,n){s.validateCallback(e,n);var i=o(t);return i?(i._pendingCallbacks?i._pendingCallbacks.push(e):i._pendingCallbacks=[e],void r(i)):null},enqueueCallbackInternal:function(t,e){t._pendingCallbacks?t._pendingCallbacks.push(e):t._pendingCallbacks=[e],r(t)},enqueueForceUpdate:function(t){var e=o(t,\"forceUpdate\");e&&(e._pendingForceUpdate=!0,r(e))},enqueueReplaceState:function(t,e){var n=o(t,\"replaceState\");n&&(n._pendingStateQueue=[e],n._pendingReplaceState=!0,r(n))},enqueueSetState:function(t,e){var n=o(t,\"setState\");if(n){var i=n._pendingStateQueue||(n._pendingStateQueue=[]);i.push(e),r(n)}},enqueueElementInternal:function(t,e,n){t._pendingElement=e,t._context=n,r(t)},validateCallback:function(t,e){t&&\"function\"!=typeof t?a(\"122\",e,i(t)):void 0}});t.exports=s},function(t,e,n){\"use strict\";var r={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(t){r.currentScrollLeft=t.x,r.currentScrollTop=t.y}};t.exports=r},function(t,e,n){\"use strict\";var r=function(t){return\"undefined\"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,n,r,i){MSApp.execUnsafeLocalFunction(function(){return t(e,n,r,i)})}:t};t.exports=r},function(t,e,n){\"use strict\";function r(t){var e,n=t.keyCode;return\"charCode\"in t?(e=t.charCode,0===e&&13===n&&(e=13)):e=n,e>=32||13===e?e:0}t.exports=r},function(t,e,n){\"use strict\";function r(t){var e=this,n=e.nativeEvent;if(n.getModifierState)return n.getModifierState(t);var r=o[t];return!!r&&!!n[r]}function i(t){return r}var o={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};t.exports=i},function(t,e,n){\"use strict\";function r(t){var e=t.target||t.srcElement||window;return e.correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}t.exports=r},function(t,e,n){\"use strict\";/** * Checks if an event is supported in the current execution environment. * * NOTE: This will not work correctly for non-generic events such as `change`, * `reset`, `load`, `error`, and `select`. * * Borrows from Modernizr. * * @param {string} eventNameSuffix Event name, e.g. \"click\". * @param {?boolean} capture Check if the capture phase is supported. * @return {boolean} True if the event is supported. * @internal * @license Modernizr 3.0.0pre (Custom Build) | MIT */ function r(t,e){if(!o.canUseDOM||e&&!(\"addEventListener\"in document))return!1;var n=\"on\"+t,r=n in document;if(!r){var a=document.createElement(\"div\");a.setAttribute(n,\"return;\"),r=\"function\"==typeof a[n]}return!r&&i&&\"wheel\"===t&&(r=document.implementation.hasFeature(\"Events.wheel\",\"3.0\")),r}var i,o=n(6);o.canUseDOM&&(i=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature(\"\",\"\")!==!0),t.exports=r},function(t,e,n){\"use strict\";function r(t,e){var n=null===t||t===!1,r=null===e||e===!1;if(n||r)return n===r;var i=typeof t,o=typeof e;return\"string\"===i||\"number\"===i?\"string\"===o||\"number\"===o:\"object\"===o&&t.type===e.type&&t.key===e.key}t.exports=r},function(t,e,n){\"use strict\";var r=(n(3),n(8)),i=(n(1),r);t.exports=i},function(t,e,n){\"use strict\";function r(t,e,n){this.props=t,this.context=e,this.refs=a,this.updater=n||o}var i=n(28),o=n(98),a=(n(176),n(38));n(0),n(1);r.prototype.isReactComponent={},r.prototype.setState=function(t,e){\"object\"!=typeof t&&\"function\"!=typeof t&&null!=t?i(\"85\"):void 0,this.updater.enqueueSetState(this,t),e&&this.updater.enqueueCallback(this,e,\"setState\")},r.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this),t&&this.updater.enqueueCallback(this,t,\"forceUpdate\")};t.exports=r},function(t,e,n){\"use strict\";function r(t,e){}var i=(n(1),{isMounted:function(t){return!1},enqueueCallback:function(t,e){},enqueueForceUpdate:function(t){r(t,\"forceUpdate\")},enqueueReplaceState:function(t,e){r(t,\"replaceState\")},enqueueSetState:function(t,e){r(t,\"setState\")}});t.exports=i},function(t,e){var n;n=function(){return this}();try{n=n||Function(\"return this\")()||(0,eval)(\"this\")}catch(t){\"object\"==typeof window&&(n=window)}t.exports=n},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,\"loaded\",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,\"id\",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,n){\"use strict\";n.d(e,\"b\",function(){return i}),n.d(e,\"a\",function(){return o});var r=Array.prototype,i=r.slice,o=r.map},function(t,e,n){\"use strict\";var r=n(18),i=n(103),o=n.i(i.a)(r.a),a=o.right;o.left;e.a=a},function(t,e,n){\"use strict\";function r(t){return function(e,r){return n.i(i.a)(t(e),r)}}var i=n(18);e.a=function(t){return 1===t.length&&(t=r(t)),{left:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r>>1;t(e[o],n)<0?r=o+1:i=o}return r},right:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r>>1;t(e[o],n)>0?i=o:r=o+1}return r}}}},function(t,e,n){\"use strict\";var r=n(111);e.a=function(t,e){var i=n.i(r.a)(t,e);return i?Math.sqrt(i):i}},function(t,e,n){\"use strict\";e.a=function(t,e){var n,r,i,o=-1,a=t.length;if(null==e){for(;++o=r){n=i=r;break}for(;++or&&(n=r),i=c)return-1;if(i=e.charCodeAt(a++),37===i){if(i=e.charAt(a++),o=Ut[i in et?e.charAt(a++):i],!o||(r=o(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}function u(t,e,n){var r=kt.exec(e.slice(n));return r?(t.p=Et[r[0].toLowerCase()],n+r[0].length):-1}function c(t,e,n){var r=Pt.exec(e.slice(n));return r?(t.w=Nt[r[0].toLowerCase()],n+r[0].length):-1}function tt(t,e,n){var r=Tt.exec(e.slice(n));return r?(t.w=St[r[0].toLowerCase()],n+r[0].length):-1}function nt(t,e,n){var r=It.exec(e.slice(n));return r?(t.m=Dt[r[0].toLowerCase()],n+r[0].length):-1}function rt(t,e,n){var r=At.exec(e.slice(n));return r?(t.m=Ot[r[0].toLowerCase()],n+r[0].length):-1}function it(t,e,n){return a(t,mt,e,n)}function ot(t,e,n){return a(t,yt,e,n)}function at(t,e,n){return a(t,_t,e,n)}function ut(t){return wt[t.getDay()]}function ct(t){return xt[t.getDay()]}function st(t){return Mt[t.getMonth()]}function lt(t){return Ct[t.getMonth()]}function ft(t){return bt[+(t.getHours()>=12)]}function pt(t){return wt[t.getUTCDay()]}function ht(t){return xt[t.getUTCDay()]}function dt(t){return Mt[t.getUTCMonth()]}function vt(t){return Ct[t.getUTCMonth()]}function gt(t){return bt[+(t.getUTCHours()>=12)]}var mt=t.dateTime,yt=t.date,_t=t.time,bt=t.periods,xt=t.days,wt=t.shortDays,Ct=t.months,Mt=t.shortMonths,kt=s(bt),Et=l(bt),Tt=s(xt),St=l(xt),Pt=s(wt),Nt=l(wt),At=s(Ct),Ot=l(Ct),It=s(Mt),Dt=l(Mt),Rt={a:ut,A:ct,b:st,B:lt,c:null,d:k,e:k,H:E,I:T,j:S,L:P,m:N,M:A,p:ft,S:O,U:I,w:D,W:R,x:null,X:null,y:L,Y:U,Z:F,\"%\":J},Lt={a:pt,A:ht,b:dt,B:vt,c:null,d:j,e:j,H:B,I:W,j:V,L:z,m:H,M:q,p:gt,S:Y,U:K,w:G,W:$,x:null,X:null,y:X,Y:Z,Z:Q,\"%\":J},Ut={a:c,A:tt,b:nt,B:rt,c:it,d:y,e:y,H:b,I:b,j:_,L:C,m:m,M:x,p:u,S:w,U:p,w:f,W:h,x:ot,X:at,y:v,Y:d,Z:g,\"%\":M};return Rt.x=e(yt,Rt),Rt.X=e(_t,Rt),Rt.c=e(mt,Rt),Lt.x=e(yt,Lt),Lt.X=e(_t,Lt),Lt.c=e(mt,Lt),{format:function(t){var n=e(t+=\"\",Rt);return n.toString=function(){return t},n},parse:function(t){var e=n(t+=\"\",r);return e.toString=function(){return t},e},utcFormat:function(t){var n=e(t+=\"\",Lt);return n.toString=function(){return t},n},utcParse:function(t){var e=n(t,i);return e.toString=function(){return t},e}}}function u(t,e,n){var r=t<0?\"-\":\"\",i=(r?-t:t)+\"\",o=i.length;return r+(o\",\"
\"],l=[3,\"
\"],f=[1,'\"],p={\"*\":[1,\"?\",\"
\"],legend:[1,\"\"],param:[1,\"\"],tr:[2,\"\",\"
\"],optgroup:c,option:c,caption:s,colgroup:s,tbody:s,tfoot:s,thead:s,td:l,th:l},h=[\"circle\",\"clipPath\",\"defs\",\"ellipse\",\"g\",\"image\",\"line\",\"linearGradient\",\"mask\",\"path\",\"pattern\",\"polygon\",\"polyline\",\"radialGradient\",\"rect\",\"stop\",\"text\",\"tspan\"];h.forEach(function(t){p[t]=f,u[t]=!0}),t.exports=r},function(t,e,n){\"use strict\";function r(t){return t===window?{x:window.pageXOffset||document.documentElement.scrollLeft,y:window.pageYOffset||document.documentElement.scrollTop}:{x:t.scrollLeft,y:t.scrollTop}}t.exports=r},function(t,e,n){\"use strict\";function r(t){return t.replace(i,\"-$1\").toLowerCase()}var i=/([A-Z])/g;t.exports=r},function(t,e,n){\"use strict\";function r(t){return i(t).replace(o,\"-ms-\")}var i=n(325),o=/^ms-/;t.exports=r},function(t,e,n){\"use strict\";function r(t){return!(!t||!(\"function\"==typeof Node?t instanceof Node:\"object\"==typeof t&&\"number\"==typeof t.nodeType&&\"string\"==typeof t.nodeName))}t.exports=r},function(t,e,n){\"use strict\";function r(t){return i(t)&&3==t.nodeType}var i=n(327);t.exports=r},function(t,e,n){\"use strict\";var r=function(t){var e;for(e in t)if(t.hasOwnProperty(e))return e;return null};t.exports=r},function(t,e,n){\"use strict\";function r(t){var e={};return function(n){return e.hasOwnProperty(n)||(e[n]=t.call(this,n)),e[n]}}t.exports=r},function(t,e,n){\"use strict\";var r={Properties:{\"aria-current\":0,\"aria-details\":0,\"aria-disabled\":0,\"aria-hidden\":0,\"aria-invalid\":0,\"aria-keyshortcuts\":0,\"aria-label\":0,\"aria-roledescription\":0,\"aria-autocomplete\":0,\"aria-checked\":0,\"aria-expanded\":0,\"aria-haspopup\":0,\"aria-level\":0,\"aria-modal\":0,\"aria-multiline\":0,\"aria-multiselectable\":0,\"aria-orientation\":0,\"aria-placeholder\":0,\"aria-pressed\":0,\"aria-readonly\":0,\"aria-required\":0,\"aria-selected\":0,\"aria-sort\":0,\"aria-valuemax\":0,\"aria-valuemin\":0,\"aria-valuenow\":0,\"aria-valuetext\":0,\"aria-atomic\":0,\"aria-busy\":0,\"aria-live\":0,\"aria-relevant\":0,\"aria-dropeffect\":0,\"aria-grabbed\":0,\"aria-activedescendant\":0,\"aria-colcount\":0,\"aria-colindex\":0,\"aria-colspan\":0,\"aria-controls\":0,\"aria-describedby\":0,\"aria-errormessage\":0,\"aria-flowto\":0,\"aria-labelledby\":0,\"aria-owns\":0,\"aria-posinset\":0,\"aria-rowcount\":0,\"aria-rowindex\":0,\"aria-rowspan\":0,\"aria-setsize\":0},DOMAttributeNames:{},DOMPropertyNames:{}};t.exports=r},function(t,e,n){\"use strict\";var r=n(4),i=n(151),o={focusDOMComponent:function(){i(r.getNodeFromInstance(this))}};t.exports=o},function(t,e,n){\"use strict\";function r(){var t=window.opera;return\"object\"==typeof t&&\"function\"==typeof t.version&&parseInt(t.version(),10)<=12}function i(t){return(t.ctrlKey||t.altKey||t.metaKey)&&!(t.ctrlKey&&t.altKey)}function o(t){switch(t){case\"topCompositionStart\":return E.compositionStart;case\"topCompositionEnd\":return E.compositionEnd;case\"topCompositionUpdate\":return E.compositionUpdate}}function a(t,e){return\"topKeyDown\"===t&&e.keyCode===_}function u(t,e){switch(t){case\"topKeyUp\":return y.indexOf(e.keyCode)!==-1;case\"topKeyDown\":return e.keyCode!==_;case\"topKeyPress\":case\"topMouseDown\":case\"topBlur\":return!0;default:return!1}}function c(t){var e=t.detail;return\"object\"==typeof e&&\"data\"in e?e.data:null}function s(t,e,n,r){var i,s;if(b?i=o(t):S?u(t,n)&&(i=E.compositionEnd):a(t,n)&&(i=E.compositionStart),!i)return null;C&&(S||i!==E.compositionStart?i===E.compositionEnd&&S&&(s=S.getData()):S=v.getPooled(r));var l=g.getPooled(i,e,n,r);if(s)l.data=s;else{var f=c(n);null!==f&&(l.data=f)}return h.accumulateTwoPhaseDispatches(l),l}function l(t,e){switch(t){case\"topCompositionEnd\":return c(e);case\"topKeyPress\":var n=e.which;return n!==M?null:(T=!0,k);case\"topTextInput\":var r=e.data;return r===k&&T?null:r;default:return null}}function f(t,e){if(S){if(\"topCompositionEnd\"===t||!b&&u(t,e)){var n=S.getData();return v.release(S),S=null,n}return null}switch(t){case\"topPaste\":return null;case\"topKeyPress\":return e.which&&!i(e)?String.fromCharCode(e.which):null;case\"topCompositionEnd\":return C?null:e.data;default:return null}}function p(t,e,n,r){var i;if(i=w?l(t,n):f(t,n),!i)return null;var o=m.getPooled(E.beforeInput,e,n,r);return o.data=i,h.accumulateTwoPhaseDispatches(o),o}var h=n(23),d=n(6),v=n(340),g=n(377),m=n(380),y=[9,13,27,32],_=229,b=d.canUseDOM&&\"CompositionEvent\"in window,x=null;d.canUseDOM&&\"documentMode\"in document&&(x=document.documentMode);var w=d.canUseDOM&&\"TextEvent\"in window&&!x&&!r(),C=d.canUseDOM&&(!b||x&&x>8&&x<=11),M=32,k=String.fromCharCode(M),E={beforeInput:{phasedRegistrationNames:{bubbled:\"onBeforeInput\",captured:\"onBeforeInputCapture\"},dependencies:[\"topCompositionEnd\",\"topKeyPress\",\"topTextInput\",\"topPaste\"]},compositionEnd:{phasedRegistrationNames:{bubbled:\"onCompositionEnd\",captured:\"onCompositionEndCapture\"},dependencies:[\"topBlur\",\"topCompositionEnd\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]},compositionStart:{phasedRegistrationNames:{bubbled:\"onCompositionStart\",captured:\"onCompositionStartCapture\"},dependencies:[\"topBlur\",\"topCompositionStart\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]},compositionUpdate:{phasedRegistrationNames:{bubbled:\"onCompositionUpdate\",captured:\"onCompositionUpdateCapture\"},dependencies:[\"topBlur\",\"topCompositionUpdate\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]}},T=!1,S=null,P={eventTypes:E,extractEvents:function(t,e,n,r){return[s(t,e,n,r),p(t,e,n,r)]}};t.exports=P},function(t,e,n){\"use strict\";var r=n(154),i=n(6),o=(n(9),n(319),n(386)),a=n(326),u=n(330),c=(n(1),u(function(t){return a(t)})),s=!1,l=\"cssFloat\";if(i.canUseDOM){var f=document.createElement(\"div\").style;try{f.font=\"\"}catch(t){s=!0}void 0===document.documentElement.style.cssFloat&&(l=\"styleFloat\")}var p={createMarkupForStyles:function(t,e){var n=\"\";for(var r in t)if(t.hasOwnProperty(r)){var i=t[r];null!=i&&(n+=c(r)+\":\",n+=o(r,i,e)+\";\")}return n||null},setValueForStyles:function(t,e,n){var i=t.style;for(var a in e)if(e.hasOwnProperty(a)){var u=o(a,e[a],n);if(\"float\"!==a&&\"cssFloat\"!==a||(a=l),u)i[a]=u;else{var c=s&&r.shorthandPropertyExpansions[a];if(c)for(var f in c)i[f]=\"\";else i[a]=\"\"}}}};t.exports=p},function(t,e,n){\"use strict\";function r(t){var e=t.nodeName&&t.nodeName.toLowerCase();return\"select\"===e||\"input\"===e&&\"file\"===t.type}function i(t){var e=C.getPooled(T.change,P,t,M(t));_.accumulateTwoPhaseDispatches(e),w.batchedUpdates(o,e)}function o(t){y.enqueueEvents(t),y.processEventQueue(!1)}function a(t,e){S=t,P=e,S.attachEvent(\"onchange\",i)}function u(){S&&(S.detachEvent(\"onchange\",i),S=null,P=null)}function c(t,e){if(\"topChange\"===t)return e}function s(t,e,n){\"topFocus\"===t?(u(),a(e,n)):\"topBlur\"===t&&u()}function l(t,e){S=t,P=e,N=t.value,A=Object.getOwnPropertyDescriptor(t.constructor.prototype,\"value\"),Object.defineProperty(S,\"value\",D),S.attachEvent?S.attachEvent(\"onpropertychange\",p):S.addEventListener(\"propertychange\",p,!1)}function f(){S&&(delete S.value,S.detachEvent?S.detachEvent(\"onpropertychange\",p):S.removeEventListener(\"propertychange\",p,!1),S=null,P=null,N=null,A=null)}function p(t){if(\"value\"===t.propertyName){var e=t.srcElement.value;e!==N&&(N=e,i(t))}}function h(t,e){if(\"topInput\"===t)return e}function d(t,e,n){\"topFocus\"===t?(f(),l(e,n)):\"topBlur\"===t&&f()}function v(t,e){if((\"topSelectionChange\"===t||\"topKeyUp\"===t||\"topKeyDown\"===t)&&S&&S.value!==N)return N=S.value,P}function g(t){return t.nodeName&&\"input\"===t.nodeName.toLowerCase()&&(\"checkbox\"===t.type||\"radio\"===t.type)}function m(t,e){if(\"topClick\"===t)return e}var y=n(22),_=n(23),b=n(6),x=n(4),w=n(11),C=n(14),M=n(93),k=n(94),E=n(170),T={change:{phasedRegistrationNames:{bubbled:\"onChange\",captured:\"onChangeCapture\"},dependencies:[\"topBlur\",\"topChange\",\"topClick\",\"topFocus\",\"topInput\",\"topKeyDown\",\"topKeyUp\",\"topSelectionChange\"]}},S=null,P=null,N=null,A=null,O=!1;b.canUseDOM&&(O=k(\"change\")&&(!document.documentMode||document.documentMode>8));var I=!1;b.canUseDOM&&(I=k(\"input\")&&(!document.documentMode||document.documentMode>11));var D={get:function(){return A.get.call(this)},set:function(t){N=\"\"+t,A.set.call(this,t)}},R={eventTypes:T,extractEvents:function(t,e,n,i){var o,a,u=e?x.getNodeFromInstance(e):window;if(r(u)?O?o=c:a=s:E(u)?I?o=h:(o=v,a=d):g(u)&&(o=m),o){var l=o(t,e);if(l){var f=C.getPooled(T.change,l,n,i);return f.type=\"change\",_.accumulateTwoPhaseDispatches(f),f}}a&&a(t,u,e)}};t.exports=R},function(t,e,n){\"use strict\";var r=n(2),i=n(20),o=n(6),a=n(322),u=n(8),c=(n(0),{dangerouslyReplaceNodeWithMarkup:function(t,e){if(o.canUseDOM?void 0:r(\"56\"),e?void 0:r(\"57\"),\"HTML\"===t.nodeName?r(\"58\"):void 0,\"string\"==typeof e){var n=a(e,u)[0];t.parentNode.replaceChild(n,t)}else i.replaceChildWithTree(t,e)}});t.exports=c},function(t,e,n){\"use strict\";var r=[\"ResponderEventPlugin\",\"SimpleEventPlugin\",\"TapEventPlugin\",\"EnterLeaveEventPlugin\",\"ChangeEventPlugin\",\"SelectEventPlugin\",\"BeforeInputEventPlugin\"];t.exports=r},function(t,e,n){\"use strict\";var r=n(23),i=n(4),o=n(52),a={mouseEnter:{registrationName:\"onMouseEnter\",dependencies:[\"topMouseOut\",\"topMouseOver\"]},mouseLeave:{registrationName:\"onMouseLeave\",dependencies:[\"topMouseOut\",\"topMouseOver\"]}},u={eventTypes:a,extractEvents:function(t,e,n,u){if(\"topMouseOver\"===t&&(n.relatedTarget||n.fromElement))return null; if(\"topMouseOut\"!==t&&\"topMouseOver\"!==t)return null;var c;if(u.window===u)c=u;else{var s=u.ownerDocument;c=s?s.defaultView||s.parentWindow:window}var l,f;if(\"topMouseOut\"===t){l=e;var p=n.relatedTarget||n.toElement;f=p?i.getClosestInstanceFromNode(p):null}else l=null,f=e;if(l===f)return null;var h=null==l?c:i.getNodeFromInstance(l),d=null==f?c:i.getNodeFromInstance(f),v=o.getPooled(a.mouseLeave,l,n,u);v.type=\"mouseleave\",v.target=h,v.relatedTarget=d;var g=o.getPooled(a.mouseEnter,f,n,u);return g.type=\"mouseenter\",g.target=d,g.relatedTarget=h,r.accumulateEnterLeaveDispatches(v,g,l,f),[v,g]}};t.exports=u},function(t,e,n){\"use strict\";var r={topAbort:null,topAnimationEnd:null,topAnimationIteration:null,topAnimationStart:null,topBlur:null,topCanPlay:null,topCanPlayThrough:null,topChange:null,topClick:null,topCompositionEnd:null,topCompositionStart:null,topCompositionUpdate:null,topContextMenu:null,topCopy:null,topCut:null,topDoubleClick:null,topDrag:null,topDragEnd:null,topDragEnter:null,topDragExit:null,topDragLeave:null,topDragOver:null,topDragStart:null,topDrop:null,topDurationChange:null,topEmptied:null,topEncrypted:null,topEnded:null,topError:null,topFocus:null,topInput:null,topInvalid:null,topKeyDown:null,topKeyPress:null,topKeyUp:null,topLoad:null,topLoadedData:null,topLoadedMetadata:null,topLoadStart:null,topMouseDown:null,topMouseMove:null,topMouseOut:null,topMouseOver:null,topMouseUp:null,topPaste:null,topPause:null,topPlay:null,topPlaying:null,topProgress:null,topRateChange:null,topReset:null,topScroll:null,topSeeked:null,topSeeking:null,topSelectionChange:null,topStalled:null,topSubmit:null,topSuspend:null,topTextInput:null,topTimeUpdate:null,topTouchCancel:null,topTouchEnd:null,topTouchMove:null,topTouchStart:null,topTransitionEnd:null,topVolumeChange:null,topWaiting:null,topWheel:null},i={topLevelTypes:r};t.exports=i},function(t,e,n){\"use strict\";function r(t){this._root=t,this._startText=this.getText(),this._fallbackText=null}var i=n(3),o=n(17),a=n(168);i(r.prototype,{destructor:function(){this._root=null,this._startText=null,this._fallbackText=null},getText:function(){return\"value\"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var t,e,n=this._startText,r=n.length,i=this.getText(),o=i.length;for(t=0;t=c)return-1;if(i=e.charCodeAt(a++),37===i){if(i=e.charAt(a++),o=Ut[i in et?e.charAt(a++):i],!o||(r=o(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}function u(t,e,n){var r=kt.exec(e.slice(n));return r?(t.p=Et[r[0].toLowerCase()],n+r[0].length):-1}function c(t,e,n){var r=Pt.exec(e.slice(n));return r?(t.w=Nt[r[0].toLowerCase()],n+r[0].length):-1}function tt(t,e,n){var r=Tt.exec(e.slice(n));return r?(t.w=St[r[0].toLowerCase()],n+r[0].length):-1}function nt(t,e,n){var r=It.exec(e.slice(n));return r?(t.m=Dt[r[0].toLowerCase()],n+r[0].length):-1}function rt(t,e,n){var r=At.exec(e.slice(n));return r?(t.m=Ot[r[0].toLowerCase()],n+r[0].length):-1}function it(t,e,n){return a(t,mt,e,n)}function ot(t,e,n){return a(t,yt,e,n)}function at(t,e,n){return a(t,_t,e,n)}function ut(t){return wt[t.getDay()]}function ct(t){return xt[t.getDay()]}function st(t){return Mt[t.getMonth()]}function lt(t){return Ct[t.getMonth()]}function ft(t){return bt[+(t.getHours()>=12)]}function pt(t){return wt[t.getUTCDay()]}function ht(t){return xt[t.getUTCDay()]}function dt(t){return Mt[t.getUTCMonth()]}function vt(t){return Ct[t.getUTCMonth()]}function gt(t){return bt[+(t.getUTCHours()>=12)]}var mt=t.dateTime,yt=t.date,_t=t.time,bt=t.periods,xt=t.days,wt=t.shortDays,Ct=t.months,Mt=t.shortMonths,kt=s(bt),Et=l(bt),Tt=s(xt),St=l(xt),Pt=s(wt),Nt=l(wt),At=s(Ct),Ot=l(Ct),It=s(Mt),Dt=l(Mt),Rt={a:ut,A:ct,b:st,B:lt,c:null,d:k,e:k,H:E,I:T,j:S,L:P,m:N,M:A,p:ft,S:O,U:I,w:D,W:R,x:null,X:null,y:L,Y:U,Z:F,\"%\":J},Lt={a:pt,A:ht,b:dt,B:vt,c:null,d:j,e:j,H:B,I:W,j:V,L:z,m:H,M:q,p:gt,S:Y,U:K,w:G,W:$,x:null,X:null,y:X,Y:Z,Z:Q,\"%\":J},Ut={a:c,A:tt,b:nt,B:rt,c:it,d:y,e:y,H:b,I:b,j:_,L:C,m:m,M:x,p:u,S:w,U:p,w:f,W:h,x:ot,X:at,y:v,Y:d,Z:g,\"%\":M};return Rt.x=e(yt,Rt),Rt.X=e(_t,Rt),Rt.c=e(mt,Rt),Lt.x=e(yt,Lt),Lt.X=e(_t,Lt),Lt.c=e(mt,Lt),{format:function(t){var n=e(t+=\"\",Rt);return n.toString=function(){return t},n},parse:function(t){var e=n(t+=\"\",r);return e.toString=function(){return t},e},utcFormat:function(t){var n=e(t+=\"\",Lt);return n.toString=function(){return t},n},utcParse:function(t){var e=n(t,i);return e.toString=function(){return t},e}}}function u(t,e,n){var r=t<0?\"-\":\"\",i=(r?-t:t)+\"\",o=i.length;return r+(o