From 04a6865b76da1e7ed4c5b3944560ca4906b3dc75 Mon Sep 17 00:00:00 2001 From: RUPESH-KUMAR01 <118011558+RUPESH-KUMAR01@users.noreply.github.com> Date: Fri, 17 Jan 2025 20:40:08 +0530 Subject: [PATCH 1/4] [ENH] Erlang Distribution --- docs/source/api_reference/distributions.rst | 1 + skpro/distributions/__init__.py | 2 + skpro/distributions/erlang.py | 87 +++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 skpro/distributions/erlang.py diff --git a/docs/source/api_reference/distributions.rst b/docs/source/api_reference/distributions.rst index 3cdf53f53..cafbee45e 100644 --- a/docs/source/api_reference/distributions.rst +++ b/docs/source/api_reference/distributions.rst @@ -55,6 +55,7 @@ Continuous support - non-negative reals Beta ChiSquared Exponential + Erlang Fisk Gamma HalfCauchy diff --git a/skpro/distributions/__init__.py b/skpro/distributions/__init__.py index 9abf8b094..fef2e6449 100644 --- a/skpro/distributions/__init__.py +++ b/skpro/distributions/__init__.py @@ -10,6 +10,7 @@ "ChiSquared", "Delta", "Empirical", + "Erlang", "Exponential", "Fisk", "Gamma", @@ -46,6 +47,7 @@ from skpro.distributions.compose import IID from skpro.distributions.delta import Delta from skpro.distributions.empirical import Empirical +from skpro.distributions.erlang import Erlang from skpro.distributions.exponential import Exponential from skpro.distributions.fisk import Fisk from skpro.distributions.gamma import Gamma diff --git a/skpro/distributions/erlang.py b/skpro/distributions/erlang.py new file mode 100644 index 000000000..bfc50e0a8 --- /dev/null +++ b/skpro/distributions/erlang.py @@ -0,0 +1,87 @@ +# copyright: skpro developers, BSD-3-Clause License (see LICENSE file) +"""Erlang probability distribution.""" + +__author__ = ["RUPESH-KUMAR01"] + +import pandas as pd +from scipy.stats import erlang, rv_continuous + +from skpro.distributions.adapters.scipy import _ScipyAdapter + +class Erlang(_ScipyAdapter): + r"""Erlang Distribution. + + Most methods wrap ``scipy.stats.erlang``. + + The Erlang Distribution is parameterized by shape :math:`k` + and rate :math:`\lambda`, such that the pdf is + + .. math:: f(x) = \frac{x^{k-1}\exp\left(-\lambda x\right) \lambda^{k}}{(k-1)!} + + Parameters + ---------- + shape : int or array of int (1D or 2D) + Represents the shape parameter. + rate : float or array of float (1D or 2D) + Represents the rate parameter, which is also the inverse of the scale parameter. + index : pd.Index, optional, default = RangeIndex + columns : pd.Index, optional, default = RangeIndex + + Examples + -------- + >>> from skpro.distributions.erlang import Erlang + + >>> d = Erlang(rate=[[1, 1], [2, 3], [4, 5]], shape=2) + """ + + _tags = { + "capabilities:approx": ["energy", "pdfnorm"], + "capabilities:exact": ["mean", "var", "pdf", "log_pdf", "cdf", "ppf"], + "distr:measuretype": "continuous", + "distr:paramtype": "parametric", + "broadcast_init": "on", + } + + def __init__(self,rate, shape, index=None, columns=None): + if rate <= 0: + raise ValueError("Rate must be greater than 0.") + if not isinstance(shape, int): + raise ValueError("shape must be a positive integer.") + if shape <= 0: + raise ValueError("shape must be a positive integer.") + self.rate = rate + self.shape = shape + + super().__init__(index=index, columns=columns) + + def _get_scipy_object(self) -> rv_continuous: + return erlang + + def _get_scipy_param(self): + rate = self._bc_params["rate"] + shape = self._bc_params["shape"] + + return [],{"scale":1/rate,"a":shape} + + @classmethod + def get_test_params(cls, parameter_set="default"): + """Return testing parameter settings for the estimator.""" + # Array case examples + params1 = { + "rate": 2.0, + "shape": 3, + "index": pd.Index([0, 1, 2]), + "columns": pd.Index(["x", "y"]), + } + # Scalar case examples + params2 = { + "rate": 0.8, + "shape": 2 + } + + params3 = { + "rate": 3.0, + "shape": 1 + } + + return [params1, params2, params3] From ca20c4a4ff63d4dc44d1ea134d8eadeb611251b4 Mon Sep 17 00:00:00 2001 From: RUPESH-KUMAR01 <118011558+RUPESH-KUMAR01@users.noreply.github.com> Date: Sun, 19 Jan 2025 11:31:28 +0530 Subject: [PATCH 2/4] "Minor Changes" --- skpro/distributions/erlang.py | 39 +++++++++++++++-------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/skpro/distributions/erlang.py b/skpro/distributions/erlang.py index bfc50e0a8..2b0922412 100644 --- a/skpro/distributions/erlang.py +++ b/skpro/distributions/erlang.py @@ -8,24 +8,25 @@ from skpro.distributions.adapters.scipy import _ScipyAdapter + class Erlang(_ScipyAdapter): r"""Erlang Distribution. Most methods wrap ``scipy.stats.erlang``. - The Erlang Distribution is parameterized by shape :math:`k` + The Erlang Distribution is parameterized by shape :math:`k` and rate :math:`\lambda`, such that the pdf is .. math:: f(x) = \frac{x^{k-1}\exp\left(-\lambda x\right) \lambda^{k}}{(k-1)!} Parameters ---------- - shape : int or array of int (1D or 2D) + shape : int or array of int (1D or 2D) Represents the shape parameter. - rate : float or array of float (1D or 2D) + rate : float or array of float (1D or 2D) Represents the rate parameter, which is also the inverse of the scale parameter. index : pd.Index, optional, default = RangeIndex - columns : pd.Index, optional, default = RangeIndex + columns : pd.Index, optional, default = RangeIndex Examples -------- @@ -33,7 +34,7 @@ class Erlang(_ScipyAdapter): >>> d = Erlang(rate=[[1, 1], [2, 3], [4, 5]], shape=2) """ - + _tags = { "capabilities:approx": ["energy", "pdfnorm"], "capabilities:exact": ["mean", "var", "pdf", "log_pdf", "cdf", "ppf"], @@ -42,7 +43,7 @@ class Erlang(_ScipyAdapter): "broadcast_init": "on", } - def __init__(self,rate, shape, index=None, columns=None): + def __init__(self, rate, shape, index=None, columns=None): if rate <= 0: raise ValueError("Rate must be greater than 0.") if not isinstance(shape, int): @@ -51,37 +52,31 @@ def __init__(self,rate, shape, index=None, columns=None): raise ValueError("shape must be a positive integer.") self.rate = rate self.shape = shape - + super().__init__(index=index, columns=columns) def _get_scipy_object(self) -> rv_continuous: return erlang - + def _get_scipy_param(self): rate = self._bc_params["rate"] shape = self._bc_params["shape"] - return [],{"scale":1/rate,"a":shape} - + return [], {"scale": 1 / rate, "a": shape} + @classmethod def get_test_params(cls, parameter_set="default"): """Return testing parameter settings for the estimator.""" # Array case examples params1 = { "rate": 2.0, - "shape": 3, - "index": pd.Index([0, 1, 2]), - "columns": pd.Index(["x", "y"]), + "shape": 3, + "index": pd.Index([0, 1, 2]), + "columns": pd.Index(["x", "y"]), } # Scalar case examples - params2 = { - "rate": 0.8, - "shape": 2 - } - - params3 = { - "rate": 3.0, - "shape": 1 - } + params2 = {"rate": 0.8, "shape": 2} + + params3 = {"rate": 3.0, "shape": 1} return [params1, params2, params3] From 7363f7f5491aae5bcdc7afb9191b9313ad533cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 24 Jan 2025 23:53:04 +0100 Subject: [PATCH 3/4] Update erlang.py --- skpro/distributions/erlang.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/skpro/distributions/erlang.py b/skpro/distributions/erlang.py index 2b0922412..672c7f85a 100644 --- a/skpro/distributions/erlang.py +++ b/skpro/distributions/erlang.py @@ -4,7 +4,7 @@ __author__ = ["RUPESH-KUMAR01"] import pandas as pd -from scipy.stats import erlang, rv_continuous +from scipy.stats import erlang from skpro.distributions.adapters.scipy import _ScipyAdapter @@ -21,10 +21,10 @@ class Erlang(_ScipyAdapter): Parameters ---------- - shape : int or array of int (1D or 2D) - Represents the shape parameter. rate : float or array of float (1D or 2D) Represents the rate parameter, which is also the inverse of the scale parameter. + k : int or array of int (1D or 2D) + Represents the shape parameter. index : pd.Index, optional, default = RangeIndex columns : pd.Index, optional, default = RangeIndex @@ -43,26 +43,26 @@ class Erlang(_ScipyAdapter): "broadcast_init": "on", } - def __init__(self, rate, shape, index=None, columns=None): + def __init__(self, rate, k, index=None, columns=None): if rate <= 0: raise ValueError("Rate must be greater than 0.") - if not isinstance(shape, int): + if not isinstance(k, int): raise ValueError("shape must be a positive integer.") - if shape <= 0: + if k <= 0: raise ValueError("shape must be a positive integer.") self.rate = rate - self.shape = shape + self.k = k super().__init__(index=index, columns=columns) - def _get_scipy_object(self) -> rv_continuous: + def _get_scipy_object(self): return erlang def _get_scipy_param(self): rate = self._bc_params["rate"] - shape = self._bc_params["shape"] + k = self._bc_params["k"] - return [], {"scale": 1 / rate, "a": shape} + return [], {"scale": 1 / rate, "a": k} @classmethod def get_test_params(cls, parameter_set="default"): @@ -70,13 +70,13 @@ def get_test_params(cls, parameter_set="default"): # Array case examples params1 = { "rate": 2.0, - "shape": 3, + "k": 3, "index": pd.Index([0, 1, 2]), "columns": pd.Index(["x", "y"]), } # Scalar case examples - params2 = {"rate": 0.8, "shape": 2} + params2 = {"rate": 0.8, "k": 2} - params3 = {"rate": 3.0, "shape": 1} + params3 = {"rate": 3.0, "k": 1} return [params1, params2, params3] From ea6eef3251033cbad79c79ac95b2d86b2b98386c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Sat, 25 Jan 2025 00:22:37 +0100 Subject: [PATCH 4/4] Update erlang.py --- skpro/distributions/erlang.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/skpro/distributions/erlang.py b/skpro/distributions/erlang.py index 672c7f85a..9cda0ee22 100644 --- a/skpro/distributions/erlang.py +++ b/skpro/distributions/erlang.py @@ -23,7 +23,7 @@ class Erlang(_ScipyAdapter): ---------- rate : float or array of float (1D or 2D) Represents the rate parameter, which is also the inverse of the scale parameter. - k : int or array of int (1D or 2D) + k : int or array of int (1D or 2D), optional, default = 1 Represents the shape parameter. index : pd.Index, optional, default = RangeIndex columns : pd.Index, optional, default = RangeIndex @@ -43,11 +43,9 @@ class Erlang(_ScipyAdapter): "broadcast_init": "on", } - def __init__(self, rate, k, index=None, columns=None): + def __init__(self, rate, k=1, index=None, columns=None): if rate <= 0: raise ValueError("Rate must be greater than 0.") - if not isinstance(k, int): - raise ValueError("shape must be a positive integer.") if k <= 0: raise ValueError("shape must be a positive integer.") self.rate = rate