"""Created on Aug 03 21:35:28 2024"""
from typing import Dict
import numpy as np
from .backend import BaseDistribution
from .backend.errorHandling import NegativeAmplitudeError, NegativeScaleError
from .utilities_d import skew_normal_cdf_, skew_normal_pdf_, skew_normal_log_pdf_
from .. import md_scipy_like, LOG, OneDArray, TWO_PI, TWO_BY_PI, SQRT_TWO_BY_PI
[docs]
class SkewNormalDistribution(BaseDistribution):
r"""
Class for SkewNormal distribution.
:param amplitude: The amplitude of the PDF. Defaults to 1.0. Ignored if **normalize** is ``True``.
:type amplitude: float, optional
:param shape: The mean parameter, :math:`\mu`. Defaults to 0.0.
:type shape: float, optional
:param scale: The scale parameter, for scaling. Defaults to 1.0,
:type scale: float, optional
:param location: The location parameter, for shifting. Defaults to 0.0.
:type location: float, optional
:param normalize: If ``True``, the distribution is normalized so that the total area under the PDF equals 1. Defaults to ``False``.
:type normalize: bool, optional
:raise NegativeAmplitudeError: If the provided value of amplitude is negative.
:raise NegativeStandardDeviationError: If the provided value of standard deviation is negative.
Examples
--------
Importing libraries:
.. literalinclude:: ../../../examples/basic/skewnormal.py
:language: python
:linenos:
:lineno-start: 3
:lines: 3-7
Generating a standard Skew Normal(:math:`\xi=1, \mu = 0, \sigma = 1`) distribution with ``pyMultiFit`` and ``scipy``:
.. literalinclude:: ../../../examples/basic/skewnormal.py
:language: python
:linenos:
:lineno-start: 9
:lines: 9-12
Plotting **PDF** and **CDF**:
.. literalinclude:: ../../../examples/basic/skewnormal.py
:language: python
:linenos:
:lineno-start: 14
:lines: 14-29
.. image:: ../../../images/skew_norm_example1.png
:alt: SkewNormal(1, 0, 1)
:align: center
Generating a translated Skew Normal(:math:`\xi=3, \mu=-3, \sigma=3`) distribution:
.. literalinclude:: ../../../examples/basic/skewnormal.py
:language: python
:lineno-start: 32
:lines: 32
Plotting **PDF** and **CDF**:
.. literalinclude:: ../../../examples/basic/skewnormal.py
:language: python
:lineno-start: 34
:lines: 34-49
.. image:: ../../../images/skew_norm_example2.png
:alt: Skew Normal(3, -3, 3)
:align: center
"""
def __init__(
self,
amplitude: float = 1.0,
shape: float = 1.0,
location: float = 0.0,
scale: float = 1.0,
normalize: bool = False,
):
if not normalize and amplitude < 0.0:
raise NegativeAmplitudeError()
if scale <= 0.0:
raise NegativeScaleError()
self.amplitude = 1 if normalize else amplitude
self.shape = shape
self.location = location
self.scale = scale
self.norm = normalize
[docs]
@classmethod
@md_scipy_like("1.0.7")
def scipy_like(cls, a: float, loc: float = 0.0, scale: float = 1.0) -> "SkewNormalDistribution":
"""
Instantiate SkewNormalDistribution with scipy parametrization.
Parameters
----------
a : float
The skewness parameter.
loc : float, optional
The location parameter. Defaults to 0.0.
scale : float, optional
The scale parameter. Defaults to 1.0.
Returns
-------
SkewNormalDistribution
An instance of normalized SkewNormalDistribution.
"""
return cls(shape=a, location=loc, scale=scale, normalize=True)
[docs]
@classmethod
def from_scipy_params(cls, a: float, loc: float = 0.0, scale: float = 1.0) -> "SkewNormalDistribution":
"""
Instantiate SkewNormalDistribution with scipy parametrization.
Parameters
----------
a : float
The skewness parameter.
loc : float, optional
The location parameter. Defaults to 0.0.
scale : float, optional
The scale parameter. Defaults to 1.0.
Returns
-------
SkewNormalDistribution
An instance of normalized SkewNormalDistribution.
"""
return cls(shape=a, location=loc, scale=scale, normalize=True)
[docs]
def pdf(self, x: OneDArray) -> OneDArray:
return skew_normal_pdf_(
x, amplitude=self.amplitude, shape=self.shape, loc=self.location, scale=self.scale, normalize=self.norm
)
[docs]
def logpdf(self, x: OneDArray) -> OneDArray:
return skew_normal_log_pdf_(
x, amplitude=self.amplitude, shape=self.shape, loc=self.location, scale=self.scale, normalize=self.norm
)
[docs]
def cdf(self, x: OneDArray) -> OneDArray:
return skew_normal_cdf_(
x, amplitude=self.amplitude, shape=self.shape, loc=self.location, scale=self.scale, normalize=self.norm
)
[docs]
def logcdf(self, x: OneDArray) -> OneDArray:
return LOG(self.cdf(x))
[docs]
def stats(self) -> Dict[str, float]:
alpha, omega, epsilon = self.shape, self.scale, self.location
delta = alpha / np.sqrt(1 + alpha**2)
sqrt_2_pi_delta = SQRT_TWO_BY_PI * delta
def _m0(alpha_):
term2 = (1 - np.pi / 4) * sqrt_2_pi_delta**3 / (1 - TWO_BY_PI * delta**2)
term3 = (TWO_PI / abs(alpha_)) * np.exp(-TWO_PI / abs(alpha_)) * np.sign(alpha_)
return sqrt_2_pi_delta - term2 - term3
# Calculating mean, mode, variance, and std
mean_ = epsilon + omega * sqrt_2_pi_delta
mode_ = epsilon + omega * _m0(alpha)
variance_ = omega**2 * (1 - (2 * delta**2 / np.pi))
std_ = np.sqrt(variance_)
return {"mean": mean_, "mode": mode_, "variance": variance_, "std": std_}