Przekształcenie Boxa Coxa ma na celu przekształcić dane w taki sposób, aby ich rozkład był jak najbardziej zbliżony do rozkładu normalnego, czyli aby histogram wyglądał jak dzwon.
Ta technika ma swoje miejsce w inżynierii cech ponieważ nie wszystkie gatunki modeli predykcyjnych są odporne na skrzywione dane, dlatego warto użyć tego zabiegu podczas eksperymentowania. Zapewne nie zapewni spektakularnej poprawy choć na etapie „fine tuning” może spełnić swoje zadanie poprawiając naszą metrykę ewaluacyjną.
Zasada działania
Samo przekształcenie ma następującą formułę:
wyrażone kodem używając standardowej biblioteki Python
import math
def box_cox(x, lmbda):
if lmbda == 0:
return [math.log(v) for v in x]
return [(math.pow(v, lmbda) - 1) / lmbda for v in x]
lub używając pakietu numpy
import numpy as np
def box_cox(x: list, lmbda: float):
if lmbda == 0:
return np.log(x)
return (np.power(x, lmbda) - 1) / lmbda
Dane mam, ale jak dobrać lambdę?
Sprawa nie jest skomplikowana, potrzebujemy testu na „normalność”, porównać jego wyniki dla kilku lambda w zakresie (zwyczajowo) <-5, 5>
następnie wybrać tę której wynik testu jest najlepszy.
Gotowe rozwiązanie dostarcza nam pakiet scipy
Kiedy nie podamy drugiego argumentu (lambdy) dla funkcji boxcox, zostanie ona dobrana oraz zwrócona.
Jedyny problem który napotykamy korzystając z tej implementacji, to wymóg aby dane wejściowe były większe od zera. Wystarczy jednak przesunąć wartości o minimum zbioru danych kiedy wymaga tego sytuacja.
def shift_to_positive(x):
min_value = np.min(x)
if min_value > 0:
return x, 0
shift_value = np.abs(min_value) + 1
return x + shift_value, shift_value
Przykład dla populacji w poszczególnych państwach w 2007 roku.
Pełny kod znajdziesz w tym notatniku, tutaj skomentuję tylko wyniki.
Po lewej stronie widzimy rozkład dla naszych danych wejściowych. Co prawda wprawne oko zauważy że nałożenie logarytmu (środkowa kolumna) świetnie przybliży nasze dane do rozkładu normalnego jednak, najlepszy efekt osiągniemy wykorzystując tytułowe przekształcenie (prawa kolumna)
Zastosowanie w inżynierii cech
W takiej sytuacji na pewno chcemy zachować rozdział pomiędzy zbiorem treningowych i testowym, zatem lambdę chcemy dopasować tylko na zbiorze treningowym.
W pakiecie scikit-learn
powszechnie stosowanym interfejsem do tego zabiegu jest Transformer który posiada metody fit
oraz transform
. Pozwala zachować w kodzie oraz użyć go składową FeatureUnion oraz Pipeline
Poniższy blok kodu przedstawia jego implementację:
import numpy as np
from sklearn.base import (
TransformerMixin,
BaseEstimator
)
class BoxCoxTransformer(BaseEstimator, TransformerMixin):
fitted_lambda: float
def fit(self, x: np.array) -> 'BoxCoxTransformer':
_, self.fitted_lambda = stats.boxcox(x)
return self
def transform(self, x: np.array) -> np.array:
# Note that for x of length = 1 stats.boxcox will raise error
return stats.boxcox(x, self.fitted_lambda)
Zastosowanie dla naszego zbioru danych populacji w 2007 znajdziesz w tej sekcji notatnika a poniżej przedstawiam Ci rozkłady dla zbioru treningowego i testowego.
Jak widzimy, kształty się zgadzają, różnią się jedynie ilością wystąpień → cel osiągnięty.
Pełny notatnik notatnik znajdziesz na colab lub pobierz go sam:
Udostępnij ten wpis
Dobrnąłeś do końca. Jeśli ten artykuł był dla Ciebie wartościowy i chcesz otrzymywać informacje o kolejnych, to zapraszam Cię do zapisania się do listy mailingowej. Gwarantuję zero spamu.
Radek.