# Source code for limix.qc._boxcox

from __future__ import division

from brent_search import brent

[docs]def boxcox(x):
r"""Box-Cox transformation for normality conformance.

It applies the power transformation

.. math::

f(x) = \begin{cases}
\frac{x^{\lambda} - 1}{\lambda}, & \text{if } \lambda > 0; \\
\log(x), & \text{if } \lambda = 0.
\end{cases}

to the provided data, hopefully making it more normal distribution-like.
The λ parameter is fit by maximum likelihood estimation.

Parameters
----------
X : array_like
Data to be transformed.

Returns
-------
boxcox : ndarray
Box-Cox transformed data.

Examples
--------
.. plot::

>>> import limix
>>> import numpy as np
>>> import scipy.stats as stats
...
>>> np.random.seed(0)
...
>>> x = stats.loggamma.rvs(0.1, size=100)
>>> y = limix.qc.boxcox(x)
...
>>> plt = limix.plot.get_pyplot()
...
>>> _, (ax1, ax2) = plt.subplots(2, 1)
>>> _ = stats.probplot(x, dist=stats.norm, plot=ax1)
>>> _ = stats.probplot(y, dist=stats.norm, plot=ax2)
>>> plt.tight_layout()
"""
import numpy as np

if isinstance(x, da.Array):
return _boxcox(da, x)
return _boxcox(np, x)

def _boxcox(lib, x):
from numpy_sugar import epsilon
from scipy.stats import boxcox_llf
from scipy.special import boxcox as bc

x = lib.asarray(x).astype(float)

m = x.min()
if m <= 0:
m = max(lib.abs(m), epsilon.small)
x = x + m + m / 2

lmb = brent(lambda lmb: -boxcox_llf(lmb, x), -5, +5)[0]
return bc(x, lmb)