Zielona koperta z kartą z napisem Big Data na jasnym tle
Źródło: Pexels | Autor: alleksana
Rate this post

Nawigacja po artykule:

Na czym polega standaryzacja i skalowanie danych w R

Standaryzacja vs skalowanie – podstawowe definicje

Standaryzacja i skalowanie to dwie techniki przekształcania danych liczbowych, które mają ułatwić pracę wielu algorytmom uczenia maszynowego oraz uprościć interpretację współczynników w modelach statystycznych. Choć w potocznym języku te pojęcia bywają mylone, w analizie danych oznaczają różne operacje matematyczne.

Standaryzacja (często nazywana też standardization, z-score scaling) polega na przekształceniu zmiennej tak, by miała średnią równą 0 i odchylenie standardowe równe 1. Każda obserwacja jest wyrażona w „liczbie odchyleń standardowych od średniej”. Formalnie:

z = (x − μ) / σ,
gdzie μ to średnia, a σ to odchylenie standardowe w próbce (lub populacji, zależnie od kontekstu).

Skalowanie (często: min-max scaling, normalization w sensie przeskalowania do [0, 1]) polega natomiast na przeniesieniu wartości zmiennej do zadanego przedziału, zwykle [0, 1] lub [−1, 1]. Najprostsza postać:

x_scaled = (x − min) / (max − min).

Obie techniki zmieniają rozkład liczbowy, ale w różny sposób. Standaryzacja „centruje” i „skaluje” przez odchylenie standardowe, a min-max scaling rozciąga dane tak, by dopasować je do konkretnego zakresu. To, które podejście wybrać, zależy od modelu i celu analizy.

Dlaczego w ogóle zmieniać skalę zmiennych

Jeśli w zbiorze danych są zmienne o bardzo różnych jednostkach i zakresach (np. dochód w tysiącach, wiek w latach, liczba dzieci w sztukach), wiele algorytmów będzie w praktyce „bardziej wrażliwych” na zmienne o większych liczbach. Nie chodzi o merytoryczne znaczenie zmiennej, lecz o czystą geometrię:

  • odległości euklidesowe będą zdominowane przez cechy o większym zakresie,
  • gradienty w optymalizacji numerycznej będą bardziej strome wzdłuż osi o dużej wariancji,
  • penalizacje typu L1/L2 w regresji karanej (LASSO, Ridge) będą działały inaczej na zmienne o różnych skalach.

Z drugiej strony, nie każde przekształcenie jest korzystne. W części modeli brak skalowania nie wpływa na dopasowanie, a może jedynie utrudnić interpretację. Czasem skalowanie może wręcz utrudnić komunikację wyników z osobami nietechnicznymi, bo współczynniki przestają być „w jednostkach naturalnych”.

Podstawowe funkcje do skalowania w R

W R istnieje kilka dróg do standaryzacji i skalowania, od bazowych funkcji po rozbudowane narzędzia z tidyverse i caret. Najprostszy punkt wyjścia to funkcja scale() z base R:


# przykładowe dane
set.seed(123)
x <- rnorm(10, mean = 100, sd = 15)

# standaryzacja: domyślnie center = TRUE, scale = TRUE
x_std <- scale(x)

mean(x_std)    # ~0
sd(x_std)      # ~1

Ta sama funkcja obsługuje też samo centrowanie (odejmowanie średniej) oraz skalowanie przez odchylenie standardowe bez centrowania:


x_centered <- scale(x, center = TRUE, scale = FALSE)
x_scaled_sd <- scale(x, center = FALSE, scale = TRUE)

Do min-max scalingu używa się zwykle prostych operacji arytmetycznych lub gotowych funkcji z pakietów, np. caret, recipes, scales. W praktyce warto dbać o to, by te same parametry skalowania (średnie, odchylenia, minima, maksima) były konsekwentnie stosowane do zbioru treningowego i testowego, o czym szerzej w dalszej części tekstu.

Modele czułe i odporne na skalę – które wymagają standaryzacji

Modele oparte na odległościach

Algorytmy, które dosłownie liczą odległości pomiędzy punktami w przestrzeni cech, są szczególnie wrażliwe na to, jak zmienne są przeskalowane. W tej grupie znajdują się między innymi:

  • k najbliższych sąsiadów (kNN),
  • klasteryzacja k-średnich (k-means),
  • metody hierarchiczne z metryką euklidesową,
  • algorytmy oparte na jądrach RBF (np. SVM z kernel RBF).

Jeśli jedna zmienna ma zakres 0–10 000, a inna 0–10, to przy odległości euklidesowej:

d = √∑ (x_i − y_i)²

różnice w zmiennej „dużej” będą niemal całkowicie dominować wynik d. Klastrowanie będzie więc grupować obserwacje przede wszystkim według tej jednej cechy, a pozostałe zostaną zignorowane niezależnie od ich sensu merytorycznego.

W takich sytuacjach standaryzacja lub skalowanie do [0, 1] jest praktycznie obowiązkowe. Pozwala to traktować wszystkie cechy „równo” z perspektywy geometrii przestrzeni cech. Dopiero wtedy można ocenić, które zmienne faktycznie wnoszą najwięcej informacji.

Modele regularizowane (LASSO, Ridge, Elastic Net)

Regresja z karą L1 (LASSO) lub L2 (Ridge) minimalizuje funkcję celu zawierającą zarówno błąd dopasowania, jak i karę za duże współczynniki. Dla regresji liniowej ma to postać:

  • LASSO: RSS + λ ∑ |β_j|,
  • Ridge: RSS + λ ∑ β_j².

Jeśli zmienne są na bardzo różnych skalach, ta sama wartość współczynnika β oznacza inną zmianę w przestrzeni odpowiedzi (y). Algorytm regularizacji będzie wtedy nierówno „ścinał” współczynniki – jedne zmienne będą karane bardziej niż inne tylko z powodu skali, a nie faktycznej istotności.

Dlatego dla LASSO, Ridge i Elastic Net standaryzacja jest standardem praktycznym. W wielu implementacjach (np. w pakiecie glmnet w R) zmienne są domyślnie standaryzowane przed dopasowaniem modelu, a współczynniki są obliczane w tej przekształconej przestrzeni. Po treningu można je przeliczyć z powrotem na skalę oryginalną, jeśli jest taka potrzeba interpretacyjna.

Metody redukcji wymiaru

Metody takie jak PCA (Analiza składowych głównych) czy FA (Analiza czynnikowa) opierają się na strukturze kowariancji bądź korelacji pomiędzy zmiennymi. Bez standaryzacji zmienne o większej wariancji będą dominować pierwsze składowe główne, nawet jeśli nie są najbardziej informacyjne z punktu widzenia celu analizy.

Typowy dylemat: czy PCA na macierzy kowariancji, czy na macierzy korelacji? PCA na macierzy korelacji oznacza de facto wcześniejszą standaryzację każdej zmiennej (średnia 0, odchylenie 1). To podejście jest najczęściej stosowane w sytuacji, gdy:

  • zmienne wyjściowe są w różnych jednostkach (np. wzrost, waga, dochód),
  • nie ma powodu, by zmienne o większej wariancji traktować jako „ważniejsze”.

W R wykonuje się to zwykle przez:


pca_res <- prcomp(df, scale. = TRUE)

Argument scale. = TRUE oznacza standaryzację każdej kolumny przed wyliczeniem PCA. Bez tego zmienne w dużej skali mogłyby zdominować pierwsze składowe.

Biała koperta z napisem Big Data na czerwonym tle
Źródło: Pexels | Autor: alleksana

Kiedy standaryzacja jest zbędna – modele odporne na skalę

Modele liniowe bez regularizacji

Regresja liniowa (funkcja lm() w R) jest pod względem predykcji odporna na liniowe przeskalowanie zmiennych wejściowych. Jeśli przemnożymy jedną ze zmiennych przez stałą, model dopasuje po prostu inny współczynnik β tak, by równanie regresji opisywało tę samą zależność. Dokładniej:

  • jeśli x’ = c · x,
  • to nowy współczynnik β’ = β / c,
  • przewidywane wartości y pozostają takie same.

Oznacza to, że standaryzacja nie poprawi jakości dopasowania ani błędu predykcji w sensie MSE, ale zmieni interpretację współczynników. Dla wielu analityków to właśnie interpretacja jest kluczowa: łatwiej porównać istotność zmiennych, gdy każda jest wyrażona w tych samych jednostkach (np. „jedno odchylenie standardowe”).

Ryzyko pojawia się dopiero, gdy wprowadzamy:

  • regularizację (L1/L2),
  • zabiegi numeryczne typu wczesne zatrzymanie, ograniczenia na normy współczynników,
  • silną współliniowość, która prowadzi do niestabilnych estymatorów i problemów numerycznych.

W takich przypadkach brak standaryzacji może utrudniać zbieżność algorytmu optymalizacji lub zwiększać wariancję estymatorów.

Drzewa decyzyjne i lasy losowe

Modele oparte na drzewach decyzyjnych (CART, C4.5 i ich implementacje w R: rpart, tree, randomForest, xgboost w trybie tree-based) są w dużej mierze nieczułe na skalę zmiennych wejściowych. Drzewo szuka punktów podziału typu:

  • x_j < t

i ocenia jakość podziału na podstawie miary nieczystości (Gini, entropia, wariancja). Przemnożenie zmiennej przez stałą lub przesunięcie o stałą zmienia wartości progów, ale nie zmienia kolejności obserwacji ani ich przydziału do liści, jeśli próg jest dopasowany na nowo.

W konsekwencji:

  • drzewa decyzyjne,
  • lasy losowe,
  • gradient boosting oparty na drzewach

nie wymagają standaryzacji wejść dla poprawnego działania. Można ją oczywiście zastosować dla spójności pipeline’u (np. gdy używa się kilku różnych modeli), ale nie jest to konieczne warunkowanie poprawności algorytmu.

Modele rozkładowe i GLM

Modele z rodziny uogólnionych modeli liniowych (GLM), takie jak:

  • regresja logistyczna (funkcja glm() z family = binomial),
  • regresja Poissona,
  • regresja Gamma

zachowują się podobnie jak zwykła regresja liniowa. Skalowanie zmiennych objaśniających nie zmienia jakości predykcji ani wartości funkcji straty, o ile zmienimy jednocześnie interpretację współczynników.

Standaryzacja w GLM ma sens głównie z trzech powodów:

  • porównywalność efektów różnych zmiennych (efekt „jednego odchylenia standardowego”),
  • stabilność numeryczna przy zmiennych o ogromnych zakresach,
  • współpraca z regularizacją, np. logistyczne LASSO.

W prostych modelach, gdzie istotne jest raportowanie efektu „na jednostkę zmiennej” w naturalnych jednostkach (np. wzrost o 1 rok, wzrost o 1 tys. zł), standaryzacja może utrudnić komunikację wyniku. W takim przypadku często łączy się dwa podejścia: model do interpretacji na danych w oryginalnej skali i model „techniczny” na danych standaryzowanych, np. do selekcji zmiennych lub diagnostyki.

Standaryzacja w R – praktyczne techniki i pułapki

Użycie base::scale() – najprostsza droga

Funkcja scale() z base R jest najczęściej pierwszym wyborem przy standaryzacji. Działa zarówno na wektorach, jak i na macierzach oraz ramkach danych (po wewnętrznej konwersji do macierzy). Najważniejsze argumenty:

  • center = TRUE/FALSE – czy odejmować średnią,
  • scale = TRUE/FALSE – czy dzielić przez odchylenie standardowe.

Przykładowe zastosowanie do ramki danych:


df <- data.frame(
  x1 = rnorm(100, 10, 2),
  x2 = rnorm(100, 100, 15),
  x3 = rnorm(100, 0, 1)
)

df_scaled <- as.data.frame(scale(df))
summary(df_scaled)

Warto zwrócić uwagę, że scale() zwraca obiekt klasy „matrix” z atrybutami "scaled:center" i "scaled:scale", które przechowują użyte średnie i odchylenia. Można je wykorzystać do przeskalowania nowych danych w dokładnie ten sam sposób:

Ręczne wykorzystanie parametrów z scale() na nowych danych

Przy budowie modeli predykcyjnych kluczowa jest spójność przekształceń między zbiorem treningowym a produkcyjnym. W przypadku scale() oznacza to użycie tych samych średnich i odchyleń standardowych. Bez tego standaryzacja „rozjedzie się” i model będzie widział inne rozkłady cech niż podczas uczenia.

Atrybuty "scaled:center" i "scaled:scale" można wyciągnąć i wykorzystać ponownie:


X_train <- as.matrix(df)      # dane treningowe
X_train_sc <- scale(X_train)

train_center <- attr(X_train_sc, "scaled:center")
train_scale  <- attr(X_train_sc, "scaled:scale")

# nowe dane
X_new <- as.matrix(df_new)

# konsekwentna standaryzacja
X_new_sc <- scale(X_new, center = train_center, scale = train_scale)

W tym ujęciu:

  • średnie i odchylenia są liczone wyłącznie na zbiorze treningowym,
  • nowe obserwacje są przekształcane przy użyciu tych samych liczb, bez „podejrzenia” rozkładu nowych danych.

To rozdzielenie ma znaczenie zwłaszcza przy walidacji krzyżowej i w uczeniu na strumieniach danych. Pojawia się więc pytanie: jak pilnować, by standaryzacja nie przeciekała pomiędzy foldami lub zestawami trening/test?

Standaryzacja a wyciek informacji (data leakage)

Data leakage w kontekście skalowania to sytuacja, w której parametry standaryzacji (średnie, odchylenia) zostały obliczone na danych, które nie powinny być widoczne na etapie treningu danego modelu lub folda. Faktem jest, że takie „podejrzenie” może zawyżać wyniki walidacji.

Klasyczny przykład:

  1. łączenie danych,
  2. globalna standaryzacja na całym zbiorze,
  3. losowy podział na zbiór treningowy i testowy,
  4. uczenie modelu i ocena na teście.

W takim scenariuszu test „wie” już, jakie są globalne średnie i odchylenia – częściowo odzwierciedla to przyszłą wiedzę, której w rzeczywistym scenariuszu nie byłoby. Efekt jest subtelny, ale szczególnie przy małych próbach może zafałszować ocenę modelu.

Bezpieczniejsza procedura:

  • najpierw podział na trening/test,
  • liczenie parametrów standaryzacji tylko na treningu,
  • zastosowanie ich do treningu i testu,
  • przy walidacji krzyżowej – obliczanie parametrów osobno w każdym foldzie treningowym.

W R da się to zautomatyzować przez użycie pakietów, które budują cały pipeline przekształceń, zamiast ręcznego wywoływania scale() w różnych miejscach.

Standaryzacja z recipes (pakiet tidymodels)

Pakiet recipes w ekosystemie tidymodels porządkuje przekształcenia danych, w tym skalowanie. Różnica względem gołego scale() jest taka, że parametry są wyliczane wyłącznie w kroku prep() na danych treningowych, a następnie używane w bake() na nowych zbiorach.


library(recipes)

rec <- recipe(y ~ ., data = train_df) |>
  step_center(all_predictors()) |>
  step_scale(all_predictors())

rec_prep <- prep(rec, training = train_df)

train_prepared <- bake(rec_prep, new_data = train_df)
test_prepared  <- bake(rec_prep, new_data = test_df)

Tutaj:

  • step_center() i step_scale() zachowują wyliczone średnie i odchylenia w obiekcie recepty,
  • te same wartości są konsekwentnie stosowane do zbioru testowego i wszelkich dalszych danych produkcyjnych.

Ten sposób jest odporny na typowe pomyłki: nie trzeba ręcznie pilnować kolejności działań ani kopiować parametrów. W praktyce, w większych projektach z wieloma modelami, taka „recepta” jest jedynym miejscem zarządzania skalą zmiennych.

Skalowanie z caret – preProcess

Starszy, ale nadal używany ekosystem caret obejmuje moduł preProcess, który łączy najczęstsze przekształcenia: centrowanie, skalowanie, PCA, imputację. Podczas dopasowywania modelu przez train() operacje te są integrowane z walidacją krzyżową.


library(caret)

preproc <- preProcess(train_df[, -which(names(train_df) == "y")],
                      method = c("center", "scale"))

train_sc <- predict(preproc, train_df)
test_sc  <- predict(preproc, test_df)

Podobnie jak w recipes:

  • parametry (średnie, odchylenia) są liczone tylko na danych przekazanych do preProcess(),
  • funkcja predict() stosuje je do nowych obserwacji.

Przy użyciu train() parametr preProcess = c("center","scale") włącza ten mechanizm w sposób automatyczny, łącznie z przeliczaniem w każdym foldzie CV.

Skalowanie a wartości odstające – robocze obejścia

Klasyczna standaryzacja (z użyciem średniej i odchylenia) bywa bardzo czuła na outliery. Pojedyncza obserwacja o wielkiej wartości może znacząco powiększyć odchylenie standardowe, spłaszczając jednocześnie skale pozostałych przypadków. W segmentacji klientów albo detekcji anomalii może to przekłamywać wyniki.

Stosuje się wtedy podejścia bardziej odporne, oparte na medianie i rozstępie międzykwartylowym (IQR). W R można to zakodować własnoręcznie lub użyć odpowiedniego kroku w recipes:


robust_scale <- function(x) {
  med <- median(x, na.rm = TRUE)
  iqr <- IQR(x, na.rm = TRUE)
  (x - med) / iqr
}

df_robust <- as.data.frame(lapply(df, robust_scale))

W recipes podobną funkcję pełni step_YeoJohnson() lub step_BoxCox() (transformacje korygujące skośność), ale do stricte „odpornej” standaryzacji trzeba na ogół napisać własny krok lub użyć zewnętrznych funkcji. Pojawia się tu pytanie: co jest ważniejsze – pełna odporność na outliery czy zachowanie liniowej interpretacji współczynników?

Skalowanie do [0, 1] i innych przedziałów

Alternatywą dla klasycznej standaryzacji jest skalowanie min–max, które sprowadza każdą zmienną do zadanej skali (często [0, 1]). Ma to znaczenie tam, gdzie algorytm technicznie zakłada określony zakres wejścia (np. niektóre sieci neuronowe, modele probabilistyczne z warunkiem na wsparcie).


minmax_scale <- function(x) {
  rng <- range(x, na.rm = TRUE)
  (x - rng[1]) / (rng[2] - rng[1])
}

df_mm <- as.data.frame(lapply(df, minmax_scale))

Wadą skalowania min–max jest jeszcze większa wrażliwość na wartości odstające niż w przypadku scale(). Ekstremalne obserwacje ustalają granice, a cały środek rozkładu zostaje ściśnięty.

W pakiecie caret odpowiada za to metoda "range", a w recipesstep_range(), która dodatkowo pozwala wybrać dowolny zakres docelowy:


rec <- recipe(y ~ ., data = train_df) |>
  step_range(all_predictors(), min = -1, max = 1)

Standaryzacja a dane kategoryczne i mieszane

Skalowanie ma sens wyłącznie dla zmiennych liczbowych. Próba wykonania scale() na ramce z faktorami kończy się albo automatyczną konwersją na kody liczbowe, albo błędem – w obu przypadkach efekt może być niezgodny z intencją analityka.

Bezpieczny schemat pracy:

  • oddzielnie przetwarzać zmienne numeryczne i kategoryczne,
  • dla kategorii stosować kodowanie (one-hot, efektowe, target encoding),
  • dopiero potem, w razie potrzeby, skalować kolumny powstałe z kodowania.

W recipes łatwo to zapisać selektorami:


rec <- recipe(y ~ ., data = train_df) |>
  step_dummy(all_nominal_predictors()) |>
  step_center(all_numeric_predictors()) |>
  step_scale(all_numeric_predictors())

Tutaj kolejność ma znaczenie: najpierw kodowanie, potem standaryzacja. Gdyby odwrócić kroki, step_center() spróbowałby odjąć średnią od faktorów, co kończy się błędem lub niezamierzoną konwersją typów.

Różne strategie skalowania dla różnych bloków zmiennych

Zdarzają się sytuacje, w których ten sam zbiór zawiera różne „bloki” cech, np.:

  • klasyczne zmienne demograficzne (wiek, dochód),
  • setki cech wynikających z tekstu (TF–IDF),
  • miary odległości geograficznej.

Równe traktowanie ich przez jedno globalne scale() nie zawsze jest uzasadnione. Przykładowo, wektor TF–IDF jest już częściowo znormalizowany, a dodatkowe dzielenie przez odchylenie może nie wnieść nic oprócz komplikacji numerycznych.

W takim układzie lepiej definiować różne procedury dla różnych podzbiorów kolumn, np.:


demo_vars <- c("wiek", "dochód")
geo_vars  <- c("dist_center", "dist_airport")
tfidf_vars <- grep("^tfidf_", names(df), value = TRUE)

df_scaled <- df
df_scaled[demo_vars] <- scale(df[demo_vars])
df_scaled[geo_vars]  <- scale(df[geo_vars])
# tfidf_vars pozostają bez zmian

Takie podejście wymaga ręcznego zarządzania listami kolumn, ale pozwala zachować intencję: części danych nie dotyka się wcale, inne są skalowane klasycznie, a jeszcze inne – w sposób odporny czy logarytmiczny.

Standaryzacja w sieciach neuronowych i modelach głębokich

W wielu bibliotekach deep learning (Keras, torch, TensorFlow) zaleca się, by wejścia były przeskalowane do zakresu zbliżonego do [−1, 1] lub [0, 1], a zmienne o rząd wielkości różniących się skalach często utrudniają uczenie. W R, korzystając z interfejsu keras lub torch, stosuje się zwykle jedną z dwóch praktyk:

  • centrowanie + skalowanie odchyleniem (jak w scale()),
  • skalowanie min–max do [0, 1] lub [−1, 1].

Dalej obowiązuje ta sama zasada: parametry liczymy na treningu, stosujemy na walidacji i teście. Na poziomie kodu różni się to niewiele od wcześniej pokazanych przykładów, ale ma większe skutki numeryczne – nasycanie się funkcji aktywacji (sigmoida, tanh) jest znacznie rzadsze, gdy wejścia nie mają ekstremalnych wartości.

Standaryzacja a interpretacja współczynników i ważności cech

Dla modeli liniowych bez regularizacji pytanie brzmi: co jest priorytetem – surowa interpretacja w jednostkach dziedziny, czy porównywalność efektów między zmiennymi? Standaryzacja sprowadza jednostkę efektu do „jednego odchylenia standardowego zmiennej”, co pozwala porównywać współczynniki β między cechami niezależnie od jednostek.

W praktyce:

  • modele raportowane klientom czy decydentom buduje się często na danych nieskalowanych (łatwiejsza komunikacja: „wzrost o 1 rok”, „wzrost o 1000 zł”),
  • do selekcji cech, wykrywania współliniowości czy szybkiego porównania efektów korzysta się z wersji standaryzowanej.

Dla modeli nieliniowych (np. lasy losowe) standaryzacja nie zmienia struktury podziałów, ale wpływa na miary ważności cech tam, gdzie bazują na wielkości spadku błędu czy zysku informacji przeskalowanego względem rozkładu zmiennej. Dlatego porównując „feature importance” między różnymi modelami w jednym projekcie, często wygodniej jest przyjąć jeden, spójny schemat przekształceń.

Specyficzne przypadki: wskaźniki, logarytmy, transformacje Box–Cox

Nie każdą zmienną warto od razu standaryzować. Część cech ma już naturalną skalę ograniczoną (proporcje, udział procentowy), inne są mocno skośne i bardziej przydatne stają się po transformacji logarytmicznej lub Box–Cox. Dopiero na tak przekształconych wartościach ewentualna standaryzacja ma sens.

Przykład z logarytmowaniem dochodu przed skalowaniem:

Łączenie logarytmowania i standaryzacji w praktyce

Transformacja logarytmiczna redukuje skośność rozkładu i ścina „długi ogon”, a dopiero potem można dorywczo korzystać z klasycznego scale(). W wielu modelach liniowych przekłada się to na bardziej stabilne współczynniki i mniejszą wrażliwość na pojedyncze, bardzo wysokie obserwacje.


df$log_income <- log1p(df$income)   # log(1 + x), bezpieczniejszy dla zera
df$log_income_std <- scale(df$log_income)[, 1]

m1 <- lm(y ~ income, data = df)
m2 <- lm(y ~ log_income_std, data = df)

Pierwszy model operuje bezpośrednio na dochodzie, drugi – na zlogarytmowanej i wystandaryzowanej wersji. Z punktu widzenia interpretacji, m2 przestawia się na „procentowe” zmiany dochodu zmierzone w odchyleniach standardowych. Diagnozując różnice między m1 i m2, można sprawdzić, czy relacja z y jest bliższa logarytmicznej niż liniowej.

W schemacie opartym na recipes taki ciąg przekształceń zapisuje się jako sekwencję kroków:


rec <- recipe(y ~ income + age, data = train_df) |>
  step_log(income, offset = 1) |>
  step_center(all_numeric_predictors()) |>
  step_scale(all_numeric_predictors())

Pojawia się pytanie kontrolne: co jest źródłem ewentualnej poprawy – samo logarytmowanie, czy fakt standaryzacji? Sprawdzenie wersji z i bez step_center()/step_scale() pozwala to szybko oddzielić.

Standaryzacja a metryka odległości w uczeniu nienadzorowanym

W klasteryzacji i metodach typu k-NN metryka odległości staje się kluczowa. Gdy cechy mają różne rzędy wielkości, zmienna o największej skali dominuje odległość euklidesową. Standaryzacja sprowadza wszystkie wymiary do porównywalnych jednostek, co wprost wpływa na kształt klastrów.

Przykład hierarchicznej klasteryzacji z i bez skalowania:


dist_raw  <- dist(df[, c("age", "income")])
hc_raw    <- hclust(dist_raw, method = "ward.D2")

df_scaled <- scale(df[, c("age", "income")])
dist_sc   <- dist(df_scaled)
hc_sc     <- hclust(dist_sc, method = "ward.D2")

Dendrogramy hc_raw i hc_sc mogą się diametralnie różnić, jeśli dochód ma dużo większy zakres niż wiek. Co wiemy z samej definicji? Po standaryzacji obie cechy wnoszą podobny wkład do odległości euklidesowej, o ile ich rozkłady nie są skrajnie skośne.

W cuście rowerów miejskich czy segmentacji klientów kartowych podobne rozjazdy zdarzają się regularnie: jedna zmienna powiązana z częstotliwością użycia lub łączną kwotą „przytłacza” pozostałe. W R można temu przeciwdziałać wprost, stosując skalowanie przed klasteryzacją:


library(cluster)

df_num <- df[, c("rides_per_month", "distance_mean", "age")]
df_num_sc <- scale(df_num)

pam_fit <- pam(df_num_sc, k = 4)   # k-medoids na danych wystandaryzowanych

W uczeniu nienadzorowanym często testuje się alternatywnie skalowanie odporne na outliery (np. z użyciem mediany i IQR) oraz klasyczne scale(). Różnice w wynikach klasteryzacji wskazują, które obserwacje „ciągną” podziały.

Standaryzacja w PCA i innych metodach redukcji wymiaru

W klasycznej analizie głównych składowych (PCA) kluczowy wybór dotyczy macierzy, z której liczone są wartości własne: kowariancji czy korelacji. Standaryzacja sprowadza się tu do użycia macierzy korelacji (każda zmienna wcześniej zcentrowana i przeskalowana odchyleniem standardowym).

W R domyślne ustawienie prcomp() sprzyja takiemu podejściu:


pca1 <- prcomp(df_num, center = TRUE, scale. = TRUE)   # PCA na macierzy korelacji
pca2 <- prcomp(df_num, center = TRUE, scale. = FALSE)  # PCA na macierzy kowariancji

Jeżeli zmienne mierzone są w nieporównywalnych jednostkach (cm, zł, liczba sztuk), brak scale. = TRUE

Sytuacja jest inna, gdy wszystkie cechy mają tę samą jednostkę i podobny sens (np. 100 pomiarów tego samego typu). Wtedy rezygnacja ze standaryzacji umożliwia, by PCA zachowała informację o absolutnej skali zmienności.

W pakiecie recipes analogiczny zestaw kroków wygląda tak:


rec_pca <- recipe(~ ., data = df_num) |>
  step_center(all_predictors()) |>
  step_scale(all_predictors()) |>
  step_pca(all_predictors(), num_comp = 5)

Kolejno: centrowanie, skalowanie, redukcja wymiaru. Zmiana kolejności tutaj nie ma większego sensu – PCA z definicji zakłada wcześniejsze centrowanie, a opcjonalnie także standaryzację.

Skalowanie w modelach k-NN i SVM: wpływ na margines i sąsiedztwo

Dla k-NN i SVM standaryzacja nie jest kosmetycznym zabiegiem, ale elementem, który zmienia trajektorię uczenia. W k-NN odległość wyznacza sąsiadów, w SVM – margines i położenie hiperpowierzchni rozdzielającej.

W przypadku k-NN na surowych danych zmienna o największej skali w praktyce „przejmuje” kontrolę nad odległością euklidesową. Kod porównujący oba warianty wygląda zwykle tak:


library(class)

x_train <- as.matrix(train_df[, predictors])
x_test  <- as.matrix(test_df[, predictors])

# Bez skalowania
pred_raw <- knn(train = x_train,
                test  = x_test,
                cl    = train_df$y,
                k     = 11)

# Ze skalowaniem
x_train_sc <- scale(x_train)
x_test_sc  <- scale(x_test, center = attr(x_train_sc, "scaled:center"),
                             scale  = attr(x_train_sc, "scaled:scale"))

pred_sc <- knn(train = x_train_sc,
               test  = x_test_sc,
               cl    = train_df$y,
               k     = 11)

W SVM z jądrem RBF parametr gamma kontroluje „zasięg” jądra. Jeżeli cechy są nieskalowane, jego optymalna wartość staje się trudna do uchwycenia w siatce strojenia, a sam margines może być silnie zdominowany przez pojedynczy wymiar.


library(e1071)

svm_fit <- svm(y ~ ., data = train_df,
               kernel = "radial",
               cost   = 1,
               gamma  = 0.1,
               scale  = TRUE)  # włącza wewnętrzne skalowanie predyktorów

Parametr scale = TRUEscale(), ale nie daje pełnej kontroli nad mechanizmem, dlatego w bardziej rozbudowanych pipeline’ach częściej stosuje się zewnętrzną standaryzację (np. w caret albo tidymodels).

Jak empirycznie sprawdzić, czy skalowanie „się opłaca”

Zamiast zakładać, że standaryzacja zawsze pomaga lub szkodzi, można ją potraktować jak zwykłą decyzję modelarską i poddać walidacji. Dwie ścieżki są szczególnie przydatne:

  • porównanie metryk modeli z i bez skalowania (RMSE, AUC, log-loss),
  • porównanie stabilności modeli w krzyżowej walidacji (wariancja wyniku, wrażliwość na zmianę foldów).

W caret różne warianty preProcess traktuje się jako osobne modele do porównania:


ctrl <- trainControl(method = "repeatedcv", number = 5, repeats = 3)

set.seed(123)
m_raw <- train(y ~ ., data = train_df,
               method = "glmnet",
               trControl = ctrl)

set.seed(123)
m_scaled <- train(y ~ ., data = train_df,
                  method = "glmnet",
                  preProcess = c("center", "scale"),
                  trControl = ctrl)

resamples(list(raw = m_raw, scaled = m_scaled)) |>
  summary()

Wyniki pozwalają oddzielić przekonania od danych: jeśli model skalowany wygrywa minimalnie, a różnice mieszczą się w przedziale niepewności, decyzja zależy już bardziej od wymagań interpretacyjnych niż od surowej jakości predykcji.

Standaryzacja w workflow z pakietem tidymodels

Ekosystem tidymodels mocno zachęca, by wszystkie transformacje – w tym skalowanie – zapisywać jawnie w recipe. To ułatwia replikację, ocenę na danych testowych i wykorzystanie modelu w produkcji.

Przykład kompletnego workflow z regresją liniową z regularyzacją:


library(tidymodels)

set.seed(123)
split <- initial_split(df, prop = 0.8, strata = y)
train_df <- training(split)
test_df  <- testing(split)

rec <- recipe(y ~ ., data = train_df) |>
  step_nzv(all_predictors()) |>                 # usunięcie cech prawie stałych
  step_dummy(all_nominal_predictors()) |>       # kodowanie kategorii
  step_center(all_numeric_predictors()) |>      # centrowanie
  step_scale(all_numeric_predictors())          # skalowanie

mod <- linear_reg(penalty = tune(), mixture = 1) |>  # LASSO
  set_engine("glmnet")

wf <- workflow() |>
  add_model(mod) |>
  add_recipe(rec)

folds <- vfold_cv(train_df, v = 5)

grid <- tibble(penalty = 10^seq(-4, 1, length.out = 20))

tuned <- tune_grid(wf, resamples = folds, grid = grid)

best <- select_best(tuned, "rmse")

final_wf <- finalize_workflow(wf, best)
final_fit <- fit(final_wf, data = train_df)

metrics <- final_wf |>
  last_fit(split) |>
  collect_metrics()

Standaryzacja w tym układzie nie jest dodatkiem, lecz integralną częścią przepisu. Wszystkie etapy – od krzyżowej walidacji po końcową ocenę – korzystają z tych samych parametrów centrowania i skalowania, liczonych wyłącznie na odpowiednich fragmentach danych treningowych.

Skalowanie w kontekście driftu danych i wdrożenia produkcyjnego

Po przeniesieniu modelu na produkcję jedna rzecz pozostaje stała: parametry skalowania obliczone na danych treningowych. Strumień nowych danych może stopniowo zmieniać rozkład cech (drift), ale model nadal stosuje stare średnie i odchylenia. To fakt techniczny, który czasem wymaga odświeżenia transformacji.

Minimalny zestaw praktyk obejmuje:

  • utrwalenie parametrów skalowania (np. zapis w pliku RDS, bazie, konfiguracji),
  • monitorowanie rozkładów cech wejściowych na produkcji i porównywanie ich z rozkładami treningowymi,
  • okresowe przeliczanie modeli wraz z nowymi parametrami standaryzacji, gdy rozjazd staje się znaczący.

W R parametry można zapisać choćby w prostej liście:


scaler <- list(
  center = attr(x_train_sc, "scaled:center"),
  scale  = attr(x_train_sc, "scaled:scale")
)

saveRDS(scaler, "scaler.rds")

# W środowisku produkcyjnym:
scaler <- readRDS("scaler.rds")

scale_new <- function(x, scaler) {
  sweep(sweep(x, 2, scaler$center, "-"), 2, scaler$scale, "/")
}

x_new_sc <- scale_new(x_new, scaler)

Jeżeli w codziennych logach widać, że nowe obserwacje coraz częściej wykraczają daleko poza zakresy znane z treningu, skalowanie zaczyna wypychać je poza typowy przedział wartości. Czego jeszcze nie wiadomo? Bez dodatkowych testów nie da się od razu ocenić, czy to już czas na rekalibrację modelu, czy dopiero sygnał do baczniejszej obserwacji.

Standaryzacja w eksperymentach porównawczych modeli

Porównując kilka algorytmów na jednym zbiorze danych, analitycy często wahają się, czy skalować wejścia wspólnie, czy dopasowywać transformacje do każdego modelu osobno. Każda z tych strategii ma konsekwencje.

  • Wspólne skalowanie – jeden schemat transformacji dla wszystkich modeli; łatwiej wtedy porównać wyniki i interpretować różnice jako efekt algorytmu, a nie preprocessing’u.
  • Skalowanie „pod model” – osobne przepisy dla algorytmów wymagających standaryzacji (SVM, k-NN, regresja z regularyzacją) i brak przekształceń dla drzew lub lasów; odzwierciedla to naturalne środowisko każdego modelu, ale utrudnia ścisłe porównanie.

W R taką macierzową organizację eksperymentu można zaimplementować przy pomocy list workflow:

Najczęściej zadawane pytania (FAQ)

Na czym polega standaryzacja danych w R i kiedy jej używać?

Standaryzacja polega na takim przekształceniu zmiennej, aby miała średnią równą 0 i odchylenie standardowe równe 1. Każdą obserwację można wtedy odczytać jako „liczbę odchyleń standardowych od średniej”. W R najprościej zrobić to funkcją scale(x) z domyślnymi argumentami center = TRUE, scale = TRUE.

Stosuje się ją głównie wtedy, gdy:

  • zmienne są w różnych jednostkach i zakresach,
  • algorytmy bazują na odległościach (kNN, k-means, SVM z RBF),
  • wykorzystywane są metody z regularizacją (LASSO, Ridge, Elastic Net),
  • robimy PCA lub analizę czynnikową na macierzy korelacji.

Bez standaryzacji takie modele mogą „faworyzować” zmienne o największej wariancji lub zakresie, niezależnie od ich znaczenia merytorycznego.

Jaka jest różnica między standaryzacją a skalowaniem (min-max) w R?

Standaryzacja przekształca zmienną tak, by miała średnią 0 i odchylenie standardowe 1: z = (x − μ) / σ. Skupia się na tym, jak daleko obserwacja leży od średniej w jednostkach odchylenia standardowego. Skaluje więc względem rozrzutu danych.

Min-max scaling (często nazywany normalizacją do [0,1]) przenosi wartości do zadanego przedziału, najczęściej [0, 1]: x_scaled = (x − min) / (max − min). Utrzymuje kształt rozkładu, ale „rozciąga” go do konkretnego zakresu. Wybór zależy od modelu: dla PCA i regresji regularizowanej typowa jest standaryzacja, dla sieci neuronowych czy metod wrażliwych na zakres – bywa wygodny min-max.

Czy zawsze trzeba standaryzować dane przed regresją liniową w R?

Dla klasycznej regresji liniowej bez regularizacji (np. lm()) przewidywania nie zależą od liniowego przeskalowania zmiennych wejściowych. Jeśli jedna zmienna zostanie przemnożona przez stałą, współczynnik regresji się odpowiednio przeskaluje, ale dopasowanie (MSE, R²) pozostanie takie samo.

Standaryzacja bywa jednak użyteczna z innych powodów:

  • ułatwia porównywanie „siły” zmiennych – wszystkie mierzone są w jednostkach odchyleń standardowych,
  • poprawia kondycję numeryczną przy silnej współliniowości czy dużych różnicach skali,
  • przy przejściu na modele z karą (LASSO, Ridge) jest w praktyce wymagana.
  • Jeśli najważniejsza jest interpretacja w „naturalnych” jednostkach (np. zł, lata), da się pracować bez standaryzacji, ale analityk musi świadomie przyjąć, że współczynniki są wtedy nieporównywalne między zmiennymi.

Jak poprawnie skalować dane treningowe i testowe w R?

Kluczowy fakt: parametry skalowania (średnia, odchylenie standardowe, minimum, maksimum) należy wyliczyć wyłącznie na zbiorze treningowym, a następnie zastosować te same wartości do przekształcenia zbioru testowego. Dzięki temu unikamy „wycieku informacji” ze zbioru testowego do procesu treningu.

Przykładowy schemat:

  • oblicz średnią i odchylenie na train,
  • przekształć train za pomocą tych parametrów,
  • tym samym przekształceniem (te same μ, σ lub min, max) objij test.
  • W praktyce ułatwiają to pakiety takie jak caret (preProcess) czy recipes (step_center(), step_scale(), step_range()), które „zapamiętują” parametry przekształceń na etapie dopasowania.

Które modele w R są szczególnie wrażliwe na skalę zmiennych?

Najbardziej reagują na skalę te algorytmy, które:

  • używają metryk odległości (kNN, k-means, hierarchiczne klastrów z odległością euklidesową, SVM z jądrem RBF),
  • stosują kary na wielkość współczynników (LASSO, Ridge, Elastic Net),
  • opierają się na strukturze kowariancji/korelacji (PCA, analiza czynnikowa).
  • Jeżeli w takim modelu jedna zmienna ma zakres 0–10 000, a inna 0–10, pierwsza praktycznie zdominuje obliczane odległości lub wariancje, nawet jeśli wcale nie jest ważniejsza w sensie merytorycznym.

Z drugiej strony, modele drzewiaste (drzewa decyzyjne, lasy losowe, gradient boosting w wersjach drzewiastych) są względnie odporne na liniowe przeskalowanie cech – działają głównie przez porównywanie wartości i podziały, a nie przez odległości euklidesowe czy normy wektorów.

Czy min-max scaling jest lepszy od standaryzacji przy uczeniu maszynowym w R?

Nie ma jednej odpowiedzi „lepszy/gorszy” – narzędzie dobiera się do zadania. Min-max scaling bywa korzystny, gdy:

  • model wymaga ograniczonego zakresu wejść (np. niektóre sieci neuronowe),
  • chcemy zachować kształt rozkładu, ale ujednolicić zakres,
  • algorytm silnie reaguje na absolutne wielkości cech.
  • Standaryzacja lepiej sprawdza się, gdy liczy się relacja do średniej i odchylenia standardowego (PCA, metody regularizowane, analizy porównujące „siłę” predyktorów).

Praktycznym kompromisem jest test: co wiemy? Typ algorytmu i jego wymagania. Czego nie wiemy? Jak skalowanie wpłynie na wynik na konkretnych danych. Często warto więc przygotować prosty eksperyment w R – porównać skuteczność modelu przy różnych przekształceniach i wybrać to, które faktycznie poprawia metryki na zbiorze walidacyjnym.

Czy standaryzacja może utrudnić interpretację wyników modelu?

Tak, jeśli odbiorcą raportu są osoby przyzwyczajone do „naturalnych” jednostek. W zstandaryzowanym modelu współczynnik mówi o zmianie odpowiedzi przy zmianie o jedno odchylenie standardowe zmiennej, a nie „o 1 rok” czy „o 100 zł”. Dla części użytkowników to mniej intuicyjne.

Z drugiej strony, standaryzacja pozwala bezpośrednio porównywać współczynniki między zmiennymi – większa wartość bezwzględna oznacza silniejszy związek z odpowiedzią w jednakowych jednostkach. W praktyce część analityków dopasowuje model na danych standaryzowanych, a do prezentacji przelicza efekty (np. różnice przewidywań) z powrotem w jednostkach oryginalnych, łącząc wygodę estymacji z czytelnością komunikacji.

Najważniejsze punkty

  • Standaryzacja i skalowanie to różne operacje: standaryzacja ustawia średnią na 0 i odchylenie standardowe na 1, a min-max scaling przekształca dane do zadanego przedziału (najczęściej [0, 1] lub [−1, 1]).
  • Zmiana skali jest kluczowa, gdy zmienne mają różne jednostki i zakresy – bez tego odległości euklidesowe, gradienty oraz kary L1/L2 są zdominowane przez zmienne o największym rozrzucie wartości.
  • Algorytmy oparte na odległościach (kNN, k-means, metody hierarchiczne, SVM z jądrem RBF) praktycznie wymagają standaryzacji lub min-max scalingu, inaczej jedna zmienna o dużym zakresie może „ustawiać” całe wyniki.
  • W modelach regularizowanych (LASSO, Ridge, Elastic Net) standaryzacja jest standardem, bo bez niej kara L1/L2 działa nierówno na zmienne i faworyzuje te o mniejszej skali, niezależnie od ich znaczenia merytorycznego.
  • Metody redukcji wymiaru, takie jak PCA czy analiza czynnikowa, bazują na kowariancji/korelacjach, więc bez standaryzacji pierwsze składowe są zdominowane przez zmienne o największej wariancji, a niekoniecznie najbardziej informacyjne.
  • Funkcja scale() w R umożliwia szybkie centrowanie i standaryzację, a do min-max scalingu używa się prostych operacji arytmetycznych lub funkcji z pakietów (caret, recipes, scales); w praktyce trzeba pilnować, by te same parametry przekształceń stosować do zbioru treningowego i testowego.
  • Bibliografia

  • The Elements of Statistical Learning: Data Mining, Inference, and Prediction. Springer (2009) – Standaryzacja, regularizacja (LASSO, Ridge), wpływ skali na modele
  • An Introduction to Statistical Learning with Applications in R. Springer (2013) – Praktyczne omówienie skalowania, PCA i modeli wrażliwych na skalę
  • R for Data Science. O’Reilly Media (2017) – Workflow analizy danych w R, transformacje zmiennych, recipes
  • Statistical Learning with Sparsity: The Lasso and Generalizations. Chapman and Hall/CRC (2015) – Teoria LASSO, regularizacja, rola standaryzacji zmiennych
  • glmnet for Regularized Generalized Linear Models. Journal of Statistical Software (2010) – Opis pakietu glmnet w R, domyślna standaryzacja predyktorów