FunctionBoxCoxTransform
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description Methods to compute the Box-Cox Transformer.
library(title='FunctionBoxCoxTransform')

// reference:
//      https://www.statology.org/box-cox-transformation-excel/
//      https://www.geeksforgeeks.org/box-cox-transformation-using-python/
//      https://github.com/business-science/timetk/blob/master/R/vec-box_cox.R
//      https://frost-lee.github.io/box-cox-linear-model/
//      https://otexts.com/fpp2/transformations.html
//      

// @function Regular transform.
// @param sample float array, sample data values.
// @param lambda float, scaling factor.
// @returns float array.
export regular(float[] sample, float lambda) => //{
    int _size = array.size(id=sample)
    //
    switch
        (_size < 1) => runtime.error('FunctionBoxCoxTransformer -> regular(): "sample" has the wrong size.')
    //
    float[] _X = array.copy(id=sample)
    for _i = 0 to _size - 1
        _xi = array.get(id=_X, index=_i)
        if lambda == 0
            array.set(id=_X, index=_i, value=math.log(_xi))
        else
            array.set(id=_X, index=_i, value=math.sign(_xi) * (math.pow(math.abs(_xi), lambda) - 1) / lambda)
    _X
//{ usage:
//{ remarks:
//      lambda: 1:no rescale, >1:scale up and keep structure, <1:scale down and aproximate to log curve
//}}}

// @function Regular transform.
// @param sample float array, sample data values.
// @param lambda float, scaling factor.
// @returns float array.
export inverse (float[] sample, float lambda) => //{
    int _size = array.size(id=sample)
    //
    switch
        (_size < 1) => runtime.error('FunctionBoxCoxTransformer -> inverse(): "sample" has the wrong size.')
    //
    float[] _X = array.copy(id=sample)
    for _i = 0 to _size - 1 by 1
        _xi = array.get(id=_X, index=_i)
        if lambda == 0
            array.set(id=_X, index=_i, value=math.exp(_xi))
        else
            _xe = lambda * _xi + 1.0
            array.set(id=_X, index=_i, value=math.sign(_xe) * math.pow(math.abs(_xe), 1.0 / lambda))
    _X
//{ usage:
//{ remarks:
//}}}


lambda = input(0.5225)

var sample_data = array.from(4.0, 5.0, 2.0, 5.0, 7.0, 2.0, 2.0, 4.0, 6.0, 3.0, 4.0, 3.0, 8.0, 6.0, 1, 2, 4, 7, 8, 3, 5, 9)
Y = regular(sample_data, lambda)
iY = inverse(Y, lambda)
var label la = label.new(bar_index, 0.0, '')
label.set_xy(la, bar_index, 0.0)
label.set_text(la, 'sample: ' + str.tostring(sample_data) + '\ninverse: ' + str.tostring(iY) + '\nregular: ' + str.tostring(Y))
plot(array.get(Y, bar_index % array.size(Y)))
plot(array.get(iY, bar_index % array.size(Y)))

