julienmalard/Tikon

View on GitHub
tikon/ecs/dists/utils.py

Summary

Maintainability
A
2 hrs
Test Coverage
from math import pi
from typing import TypedDict, Union, Optional

import numpy as np
import scipy.stats as estad
from scipy.stats._distn_infrastructure import rv_continuous, rv_continuous_frozen

from tikon.tipos import Tipo_Valor_Numérico

Líms_Con_None = tuple[Union[Tipo_Valor_Numérico, None], Union[Tipo_Valor_Numérico, None]]
Líms_Numéricas = tuple[Tipo_Valor_Numérico, Tipo_Valor_Numérico]


class DicEspecDistScipy(TypedDict):
    scipy: rv_continuous
    paráms: list[str]
    límites: Líms_Con_None


# Un diccionario de las distribuciones y de sus objetos de SciPy correspondientes.
dists: dict[str, DicEspecDistScipy] = {
    'Alpha': {'scipy': estad.alpha,
              'paráms': ['a', 'loc', 'scale'],
              'límites': (0, None)
              },
    'Anglit': {'scipy': estad.anglit,
               'paráms': ['loc', 'scale'],
               'límites': (-pi / 4, pi / 4)
               },
    'Arcsen': {'scipy': estad.arcsine,
               'paráms': ['loc', 'scale'],
               'límites': (0, 1)
               },
    'Beta': {'scipy': estad.beta,
             'paráms': ['a', 'b', 'loc', 'scale'],
             'límites': (0, 1)
             },
    'Bradford': {'scipy': estad.bradford,
                 'paráms': ['c', 'loc', 'scale'],
                 'límites': (0, 1)
                 },
    'Burr': {'scipy': estad.burr,
             'paráms': ['c', 'd', 'loc', 'scale'],
             'límites': (0, None)
             },
    'Cauchy': {'scipy': estad.cauchy,
               'paráms': ['loc', 'scale'],
               'límites': (None, None)
               },
    'Chi': {'scipy': estad.chi,
            'paráms': ['df', 'loc', 'scale'],
            'límites': (0, None)
            },
    'Chi2': {'scipy': estad.chi2,
             'paráms': ['df', 'loc', 'scale'],
             'límites': (0, None)
             },
    'DobleGamma': {'scipy': estad.dgamma,
                   'paráms': ['a', 'loc', 'scale'],
                   'límites': (None, None)
                   },
    'DobleWeibull': {'scipy': estad.dweibull,
                     'paráms': ['c', 'loc', 'scale'],
                     'límites': (None, None)
                     },
    'Exponencial': {'scipy': estad.expon,
                    'paráms': ['loc', 'scale'],
                    'límites': (0, None)
                    },
    'NormalExponencial': {'scipy': estad.exponnorm,
                          'paráms': ['K', 'loc', 'scale'],
                          'límites': (None, None)
                          },
    'F': {'scipy': estad.f,
          'paráms': ['dfn', 'dfd', 'loc', 'scale'],
          'límites': (0, None)
          },
    'BirnbaumSaunders': {'scipy': estad.fatiguelife,
                         'paráms': ['c', 'loc', 'scale'],
                         'límites': (0, None)
                         },
    'Fisk': {'scipy': estad.fisk,
             'paráms': ['c', 'loc', 'scale'],
             'límites': (0, None)
             },
    'CauchyDoblada': {'scipy': estad.foldcauchy,
                      'paráms': ['c', 'loc', 'scale'],
                      'límites': (0, None)
                      },
    'NormalDoblada': {'scipy': estad.foldnorm,
                      'paráms': ['c', 'loc', 'scale'],
                      'límites': (0, None)
                      },
    'LogísticaGeneral': {'scipy': estad.genlogistic,
                         'paráms': ['c', 'loc', 'scale'],
                         'límites': (None, None)
                         },
    'NormalGeneral': {'scipy': estad.gennorm,
                      'paráms': ['beta', 'loc', 'scale'],
                      'límites': (None, None)
                      },
    'ParetoGeneral': {'scipy': estad.genpareto,
                      'paráms': ['c', 'loc', 'scale'],
                      'límites': (0, None)
                      },
    'ExtremaGeneral': {'scipy': estad.genextreme,
                       'paráms': ['c', 'loc', 'scale'],
                       'límites': (None, None)
                       },
    'Gamma': {'scipy': estad.gamma,
              'paráms': ['a', 'loc', 'scale'],
              'límites': (0, None)
              },
    'GammaGeneral': {'scipy': estad.gengamma,
                     'paráms': ['a', 'c', 'loc', 'scale'],
                     'límites': (0, None)
                     },
    'Gilbrat': {'scipy': estad.gilbrat,
                'paráms': ['loc', 'scale'],
                'límites': (0, None)
                },
    'GumbelDerecho': {'scipy': estad.gumbel_r,
                      'paráms': ['loc', 'scale'],
                      'límites': (None, None)
                      },
    'GumbelIzquierda': {'scipy': estad.gumbel_l,
                        'paráms': ['loc', 'scale'],
                        'límites': (None, None)
                        },
    'MitadCauchy': {'scipy': estad.halfcauchy,
                    'paráms': ['loc', 'scale'],
                    'límites': (0, None)
                    },
    'MitadLogística': {'scipy': estad.halflogistic,
                       'paráms': ['loc', 'scale'],
                       'límites': (0, None)
                       },
    'MitadNormal': {'scipy': estad.halfnorm,
                    'paráms': ['loc', 'scale'],
                    'límites': (0, None)
                    },
    'HyperSecante': {'scipy': estad.hypsecant,
                     'paráms': ['loc', 'scale'],
                     'límites': (None, None)
                     },
    'GammaInversa': {'scipy': estad.invgamma,
                     'paráms': ['a', 'loc', 'scale'],
                     'límites': (0, None)
                     },
    'GaussInversa': {'scipy': estad.invgauss,
                     'paráms': ['mu', 'loc', 'scale'],
                     'límites': (0, None)
                     },
    'WeibullInversa': {'scipy': estad.invweibull,
                       'paráms': ['c', 'loc', 'scale'],
                       'límites': (0, None)
                       },
    'Laplace': {'scipy': estad.laplace,
                'paráms': ['loc', 'scale'],
                'límites': (None, None)
                },
    'Levy': {'scipy': estad.levy,
             'paráms': ['loc', 'scale'],
             'límites': (0, None)
             },
    'Logística': {'scipy': estad.logistic,
                  'paráms': ['loc', 'scale'],
                  'límites': (None, None)
                  },
    'LogGamma': {'scipy': estad.loggamma,
                 'paráms': ['c', 'loc', 'scale'],
                 'límites': (None, None)
                 },
    'LogLaplace': {'scipy': estad.loglaplace,
                   'paráms': ['c', 'loc', 'scale'],
                   'límites': (0, None)
                   },
    'LogNormal': {'scipy': estad.lognorm,
                  'paráms': ['s', 'loc', 'scale'],
                  'límites': (0, None)
                  },
    'Lomax': {'scipy': estad.lomax,
              'paráms': ['c', 'loc', 'scale'],
              'límites': (0, None)
              },
    'Maxwell': {'scipy': estad.maxwell,
                'paráms': ['loc', 'scale'],
                'límites': (0, None)
                },
    'Nakagami': {'scipy': estad.nakagami,
                 'paráms': ['nu', 'loc', 'scale'],
                 'límites': (0, None)
                 },
    'Chi2NoCentral': {'scipy': estad.ncx2,
                      'paráms': ['df', 'nc', 'loc', 'scale'],
                      'límites': (0, None)
                      },
    'Normal': {'scipy': estad.norm,
               'paráms': ['loc', 'scale'],
               'límites': (None, None)
               },
    'Pareto': {'scipy': estad.pareto,
               'paráms': ['b', 'loc', 'scale'],
               'límites': (1, None)
               },
    'Pearson3': {'scipy': estad.pearson3,
                 'paráms': ['skew', 'loc', 'scale'],
                 'límites': (None, None)
                 },
    'Potencial': {'scipy': estad.powerlaw,
                  'paráms': ['a', 'loc', 'scale'],
                  'límites': (0, 1)
                  },
    'Rayleigh': {'scipy': estad.rayleigh,
                 'paráms': ['loc', 'scale'],
                 'límites': (0, None)
                 },
    'Rice': {'scipy': estad.rice,
             'paráms': ['b', 'loc', 'scale'],
             'límites': (0, None)
             },
    'NormalSesgada': {'scipy': estad.skewnorm,
                      'paráms': ['a', 'loc', 'scale'],
                      'límites': (None, None)
                      },
    'T': {'scipy': estad.t,
          'paráms': ['df', 'loc', 'scale'],
          'límites': (None, None)
          },
    'Triang': {'scipy': estad.triang,
               'paráms': ['c', 'loc', 'scale'],
               'límites': (0, 1),  # El límite es ('a', 'b')
               },
    'TukeyLambda': {'scipy': estad.tukeylambda,
                    'paráms': ['lam', 'loc', 'scale'],
                    'límites': (None, None)
                    },
    'Uniforme': {'scipy': estad.uniform,
                 'paráms': ['loc', 'scale'],
                 'límites': (0, 1),
                 },
    'Wald': {'scipy': estad.wald,
             'paráms': ['loc', 'scale'],
             'límites': (0, None)
             },
    'Weibull': {'scipy': estad.weibull_min,
                'paráms': ['c', 'loc', 'scale'],
                'límites': (0, None)
                },
    'WeibullMáximo': {'scipy': estad.weibull_max,
                      'paráms': ['c', 'loc', 'scale'],
                      'límites': (None, 0)
                      },
    'CauchyEnvuelto': {'scipy': estad.wrapcauchy,
                       'paráms': ['c', 'loc', 'scale'],
                       'límites': (0, 2 * pi)
                       }
}


def _valid_nombre(nombre: str) -> str:
    try:
        return next(nmbr for nmbr in dists if nmbr.lower() == nombre.lower())
    except StopIteration:
        raise ValueError(
            'No hay distribución llamada "{nm}". Debe ser una de:\n'
            '\t{ops}'.format(nm=nombre, ops=', '.join(dists))
        )


def _obt_dic_dist(nombre: str) -> DicEspecDistScipy:
    nombre = _valid_nombre(nombre)
    return dists[nombre]


def clase_scipy(nombre: str) -> rv_continuous:
    return _obt_dic_dist(nombre)['scipy']


def líms_dist(dist: Union[str, rv_continuous_frozen]) -> Líms_Numéricas:
    if isinstance(dist, str):
        return proc_líms(_obt_dic_dist(dist)['límites'])
    else:
        nombre = obt_nombre(dist)
        líms = proc_líms(_obt_dic_dist(nombre)['límites'])
        ubic, escala = obt_prms_obj_scipy(dist)[1:]
        return líms[0] + ubic, (líms[1] - líms[0]) * escala + ((líms[0] + ubic) if líms[0] > -np.inf else 0)


def obt_prms_obj_scipy(dist: rv_continuous_frozen) -> tuple[tuple, Tipo_Valor_Numérico, Tipo_Valor_Numérico]:
    return dist.dist._parse_args(*dist.args, **dist.kwds)


def prms_dist(nombre)->list[str]:
    return _obt_dic_dist(nombre)['paráms']


def obt_scipy(nombre: str, paráms: Union[dict[str, Tipo_Valor_Numérico], tuple[tuple, Tipo_Valor_Numérico, Tipo_Valor_Numérico]]):
    cls_dist = clase_scipy(nombre)

    if isinstance(paráms, dict):
        return cls_dist(**paráms)
    else:
        return cls_dist(*paráms[0], loc=paráms[1], scale=paráms[2])


def obt_nombre(dist_sp: rv_continuous_frozen)-> str:
    return next(nmb for nmb in dists if dists[nmb]['scipy'].name == dist_sp.dist.name)


def proc_líms(líms: Optional[Líms_Con_None]) -> Líms_Numéricas:
    inf = np.inf

    if líms is None:
        return -inf, inf
    return -inf if líms[0] is None else líms[0], inf if líms[1] is None else líms[1]