Skip to content

Commit

Permalink
Merge pull request #7 from msamsami/improve-dist-mixins
Browse files Browse the repository at this point in the history
Improve distribution mixins
  • Loading branch information
msamsami authored May 10, 2023
2 parents a42124e + f09674d commit ae476fb
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WNB: General and weighted naive Bayes classifiers

![](https://img.shields.io/badge/version-v0.1.6-green)
![](https://img.shields.io/badge/version-v0.1.7-green)
![](https://img.shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9-blue)

<p>
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='wnb',
version='0.1.6',
version='0.1.7',
description='Python library for the implementations of general and weighted naive Bayes (WNB) classifiers.',
keywords=['python', 'bayes', 'naivebayes', 'classifier', 'probabilistic'],
author='Mehdi Samsami',
Expand Down
2 changes: 1 addition & 1 deletion wnb/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.1.6"
__version__ = "0.1.7"
__author__ = "Mehdi Samsami"

__all__ = [
Expand Down
94 changes: 68 additions & 26 deletions wnb/_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from abc import ABCMeta
import inspect
from functools import wraps
from numbers import Number

import numpy as np

Expand All @@ -26,25 +28,82 @@ def wrapper(*args):
return decorator


class ContinuousDistMixin(metaclass=ABCMeta):
class DistMixin(metaclass=ABCMeta):
"""
Mixin class for all continuous probability distributions in wnb.
Mixin class for probability distributions in wnb.
"""

name = None

def __init__(self, **kwargs):
"""Initializes an instance of the probability distribution with given parameters.
@classmethod
def from_data(cls, data):
"""Creates an instance of the class from given data. Distribution parameters will be estimated from the data.
Returns:
self: An instance of the class.
"""
pass

@classmethod
def from_data(cls, data):
"""Creates an instance of the class from given data. Distribution parameters will be estimated from the data.
def _get_param_names(cls):
"""Gets parameter names for the distribution instance.
"""
init = getattr(cls.__init__, "deprecated_original", cls.__init__)
if init is object.__init__:
return []

init_signature = inspect.signature(init)
parameters = [
p
for p in init_signature.parameters.values()
if p.name != "self" and p.kind != p.VAR_KEYWORD
]

for p in parameters:
if p.kind == p.VAR_POSITIONAL:
raise RuntimeError(
"wnb estimators should always "
"specify their parameters in the signature "
"of their __init__ (no varargs). "
"%s with constructor %s doesn't "
"follow this convention." % (cls, init_signature)
)

return sorted([p.name for p in parameters])

def get_params(self) -> dict:
"""Gets parameters for this distribution instance.
Returns:
self: An instance of the class.
dict: Parameter names mapped to their values.
"""
out = dict()
for key in self._get_param_names():
value = getattr(self, key)
out[key] = value
return out

def __repr__(self) -> str:
return "".join([
"<",
self.__class__.__name__,
"(",
", ".join(
[f"{k}={v:.4f}" if isinstance(v, Number) else f"{k}={v}" for k, v in self.get_params().items()]
),
")>"
])


class ContinuousDistMixin(DistMixin, metaclass=ABCMeta):
"""
Mixin class for all continuous probability distributions in wnb.
"""

def __init__(self, **kwargs):
"""Initializes an instance of the continuous probability distribution with given parameters.
"""
pass

Expand All @@ -63,32 +122,18 @@ def pdf(self, x: float) -> float:
def __call__(self, x: float) -> float:
return self.pdf(x)

def __repr__(self) -> str:
pass


class DiscreteDistMixin(metaclass=ABCMeta):
class DiscreteDistMixin(DistMixin, metaclass=ABCMeta):
"""
Mixin class for all discrete probability distributions in wnb.
"""

name = None

def __init__(self, **kwargs):
"""Initializes an instance of the probability distribution with given parameters.
"""Initializes an instance of the discrete probability distribution with given parameters.
"""
pass

@classmethod
def from_data(cls, data):
"""Creates an instance of the class from given data. Distribution parameters will be estimated from the data.
Returns:
self: An instance of the class.
"""
pass

def pmf(self, x: float) -> float:
"""Returns the value of probability mass function (PMF) at x.
Expand All @@ -103,6 +148,3 @@ def pmf(self, x: float) -> float:
@vectorize(signature="(),()->()")
def __call__(self, x: float) -> float:
return self.pmf(x)

def __repr__(self) -> str:
pass
33 changes: 0 additions & 33 deletions wnb/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ def from_data(cls, data: np.ndarray):
def pdf(self, x: float) -> float:
return (1.0 / np.sqrt(2 * np.pi * self.sigma**2)) * np.exp(-0.5 * (((x - self.mu) / self.sigma)**2))

def __repr__(self) -> str:
return f"<NormalDist(mu={self.mu:.4f}, sigma={self.sigma:.4f})>"


class LognormalDist(ContinuousDistMixin):
name = D.LOGNORMAL
Expand All @@ -59,9 +56,6 @@ def from_data(cls, data: np.ndarray):
def pdf(self, x: float) -> float:
return (1.0 / (x * np.sigma * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((np.log(x - self.mu) / self.sigma)**2))

def __repr__(self) -> str:
return f"<LognormalDist(mu={self.mu:.4f}, sigma={self.sigma:.4f})>"


class ExponentialDist(ContinuousDistMixin):
name = D.EXPONENTIAL
Expand All @@ -77,9 +71,6 @@ def from_data(cls, data: np.ndarray):
def pdf(self, x: float) -> float:
return self.rate * np.exp(-self.rate * x) if x >= 0 else 0.0

def __repr__(self) -> str:
return f"<ExponentialDist(rate={self.rate:.4f})>"


class UniformDist(ContinuousDistMixin):
name = D.UNIFORM
Expand All @@ -96,9 +87,6 @@ def from_data(cls, data):
def pdf(self, x: float) -> float:
return 1 / (self.b - self.a) if self.a <= x <= self.b else 0.0

def __repr__(self) -> str:
return f"<UniformDist(a={self.a:.4f}, b={self.b:.4f})>"


class ParetoDist(ContinuousDistMixin):
name = D.PARETO
Expand All @@ -116,9 +104,6 @@ def from_data(cls, data):
def pdf(self, x: float) -> float:
return (self.alpha * self.x_m**self.alpha) / x**(self.alpha + 1) if x >= self.x_m else 0.0

def __repr__(self) -> str:
return f"<ParetoDist(x_m={self.x_m:.4f}, alpha={self.alpha:.4f})>"


class GammaDist(ContinuousDistMixin):
name = D.GAMMA
Expand All @@ -139,9 +124,6 @@ def from_data(cls, data):
def pdf(self, x: float) -> float:
return (x ** (self.k-1) * np.exp(-x / self.theta)) / (gamma(self.k) * self.theta ** self.k)

def __repr__(self) -> str:
return f"<GammaDist(k={self.k:.4f}, theta={self.theta:.4f})>"


class BernoulliDist(DiscreteDistMixin):
name = D.BERNOULLI
Expand All @@ -160,9 +142,6 @@ def from_data(cls, data):
def pmf(self, x: int) -> float:
return 0.0 if x not in [0, 1] else self.p if x == 1 else 1 - self.p

def __repr__(self) -> str:
return f"<BernoulliDist(p={self.p:.4f})>"


class CategoricalDist(DiscreteDistMixin):
name = D.CATEGORICAL
Expand All @@ -179,9 +158,6 @@ def from_data(cls, data):
def pmf(self, x: Any) -> float:
return self.prob.get(x)

def __repr__(self) -> str:
return f"<CategoricalDist(prob={self.prob})>"


class MultinomialDist(DiscreteDistMixin):
name = D.MULTINOMIAL
Expand All @@ -206,9 +182,6 @@ def pmf(self, x: Sequence[int]) -> float:
return np.math.factorial(self.n) * np.product([p**v for v, p in self.prob.items()]) / \
np.product([np.math.factorial(v) for v in self.prob.keys()])

def __repr__(self) -> str:
return f"<MultinomialDist(n={self.n}, prob={self.prob})>"


class GeometricDist(DiscreteDistMixin):
name = D.GEOMETRIC
Expand All @@ -227,9 +200,6 @@ def from_data(cls, data):
def pmf(self, x: int) -> float:
return self.p * (1 - self.p)**(x-1) if x >= 1 else 0.0

def __repr__(self) -> str:
return f"<GeometricDist(p={self.p:.4f})>"


class PoissonDist(DiscreteDistMixin):
name = D.POISSON
Expand All @@ -245,9 +215,6 @@ def from_data(cls, data):
def pmf(self, x: int) -> float:
return (np.exp(-self.rate) * self.rate**x) / np.math.factorial(x)

def __repr__(self) -> str:
return f"<PoissonDist(rate={self.rate:.4f})>"


AllDistributions = {
eval(cls).name: eval(cls)
Expand Down

0 comments on commit ae476fb

Please sign in to comment.