tra-analysis/data analysis/analysis/trueskill/backends.py

136 lines
3.7 KiB
Python

# -*- coding: utf-8 -*-
"""
trueskill.backends
~~~~~~~~~~~~~~~~~~
Provides mathematical statistics backend chooser.
:copyright: (c) 2012-2016 by Heungsub Lee.
:license: BSD, see LICENSE for more details.
"""
from __future__ import absolute_import
import math
from six.moves import range
__all__ = ['available_backends', 'choose_backend', 'cdf', 'pdf', 'ppf']
def _gen_erfcinv(erfc, math=math):
"""Generates the inverse function of erfc by the given erfc function and
math module.
"""
def erfcinv(y):
"""The inverse function of erfc."""
if y >= 2:
return -100.
elif y <= 0:
return 100.
zero_point = y < 1
if not zero_point:
y = 2 - y
t = math.sqrt(-2 * math.log(y / 2.))
x = -0.70711 * \
((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t)
for i in range(2):
err = erfc(x) - y
x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err)
return x if zero_point else -x
return erfcinv
def _gen_ppf(erfc, math=math):
"""ppf is the inverse function of cdf. This function generates cdf by the
given erfc and math module.
"""
erfcinv = _gen_erfcinv(erfc, math)
def ppf(x, mu=0, sigma=1):
"""The inverse function of cdf."""
return mu - sigma * math.sqrt(2) * erfcinv(2 * x)
return ppf
def erfc(x):
"""Complementary error function (via `http://bit.ly/zOLqbc`_)"""
z = abs(x)
t = 1. / (1. + z / 2.)
r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * (
0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * (
0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * (
-0.82215223 + t * 0.17087277
)))
)))
)))
return 2. - r if x < 0 else r
def cdf(x, mu=0, sigma=1):
"""Cumulative distribution function"""
return 0.5 * erfc(-(x - mu) / (sigma * math.sqrt(2)))
def pdf(x, mu=0, sigma=1):
"""Probability density function"""
return (1 / math.sqrt(2 * math.pi) * abs(sigma) *
math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2)))
ppf = _gen_ppf(erfc)
def choose_backend(backend):
"""Returns a tuple containing cdf, pdf, ppf from the chosen backend.
>>> cdf, pdf, ppf = choose_backend(None)
>>> cdf(-10)
7.619853263532764e-24
>>> cdf, pdf, ppf = choose_backend('mpmath')
>>> cdf(-10)
mpf('7.6198530241605255e-24')
.. versionadded:: 0.3
"""
if backend is None: # fallback
return cdf, pdf, ppf
elif backend == 'mpmath':
try:
import mpmath
except ImportError:
raise ImportError('Install "mpmath" to use this backend')
return mpmath.ncdf, mpmath.npdf, _gen_ppf(mpmath.erfc, math=mpmath)
elif backend == 'scipy':
try:
from scipy.stats import norm
except ImportError:
raise ImportError('Install "scipy" to use this backend')
return norm.cdf, norm.pdf, norm.ppf
raise ValueError('%r backend is not defined' % backend)
def available_backends():
"""Detects list of available backends. All of defined backends are
``None`` -- internal implementation, "mpmath", "scipy".
You can check if the backend is available in the current environment with
this function::
if 'mpmath' in available_backends():
# mpmath can be used in the current environment
setup(backend='mpmath')
.. versionadded:: 0.3
"""
backends = [None]
for backend in ['mpmath', 'scipy']:
try:
__import__(backend)
except ImportError:
continue
backends.append(backend)
return backends