Scikit-learn Pipeline 2026: Täydellinen opas ColumnTransformer-työkaluun
Rakenna scikit-learn Pipeline ja ColumnTransformer askel askeleelta: yhdistä esikäsittely ja malli yhdeksi olioksi, säädä hyperparametrit GridSearchCV:llä ja vie koko työnkulku tuotantoon ilman tietovuotoa.
Scikit-learn Pipeline on työkalu, joka ketjuttaa esikäsittelyvaiheet ja mallin yhdeksi kutsuttavaksi olioksi, ja ColumnTransformer laajentaa tämän eri sarakkeille eri esikäsittelyillä (tyypillisesti numeerisille sarakkeille skaalauksen ja kategorisille one-hot-koodauksen). Yhdessä ne muodostavat tuotantovalmiin työnkulun, joka estää tietovuodon (data leakage) ristiinvalidoinnissa, sarjallistuu yhdeksi tiedostoksi ja tekee hyperparametrien haun ristiinvalidoinnilla oikeaoppiseksi ilman käsityötä. Tämä opas näyttää, miten rakennat, säädät ja viet tuotantoon Pipeline- ja ColumnTransformer-työnkulun scikit-learnissa 1.7+ vuonna 2026. Olen käyttänyt tätä samaa reseptiä pariin viimeisimpään asiakasprojektiini, ja pari pientä nyanssia on säästänyt monta tuntia debuggausta.
Pipeline ketjuttaa muuntimet ja lopullisen estimaattorin niin, että fit/predict kutsuu jokaisen vaiheen oikeassa järjestyksessä ja välttää tietovuodon.
ColumnTransformer antaa käyttää eri esikäsittelyä eri sarakkeille (esimerkiksi StandardScaler numeerisille ja OneHotEncoder kategorisille sarakkeille).
Scikit-learn 1.7 (julkaistu 5.6.2025) tuo from_cv_results()-metodin ROC-käyrille sekä paremmat virheilmoitukset ja tuen Pandas 2.2:lle sekä NumPy 2.0:lle.
Käyttämällä make_column_selector-apuria vältät sarakenimien kovakoodaamisen ja saat pipelinen, joka mukautuu skeeman muutoksiin.
GridSearchCV tunnistaa pipeline-parametrit kaksoiskaksoispisteellä (esim. preprocessor__num__imputer__strategy), mikä mahdollistaa esikäsittelyn ja mallin yhtäaikaisen säädön.
Tallenna koko pipeline tuotantoon joblib.dump-funktiolla, jolloin yksi tiedosto sisältää sekä esikäsittelyn että mallin.
Mikä on scikit-learn Pipeline?
Scikit-learn Pipeline on luokka, joka ottaa listan (nimi, muunnin)-pareja ja käsittelee ne peräkkäin yhtenä estimaattorina. Jokainen välivaihe on muunnin (transformer), jolla on fit- ja transform-metodit. Viimeinen vaihe on tyypillisesti estimaattori (esimerkiksi LogisticRegression tai RandomForestClassifier), jolla on fit ja predict.
Kun kutsut pipeline.fit(X_train, y_train), scikit-learn sovittaa jokaisen muuntimen vuorotellen edellisen tulokseen ja opettaa lopullisen mallin lopputulokseen. Kutsu pipeline.predict(X_test) ajaa testidatan samojen muuntimien läpi ilman uudelleensovitusta ja tuottaa ennusteen. Tämä sopimus on syvempi kuin miltä näyttää. Ristiinvalidoinnissa pipeline sovitetaan erikseen jokaiselle koulutus-fold-jaolle, jolloin skaalaimen keskiarvo ja hajonta lasketaan pelkästä koulutusosuudesta, eikä validointi-fold koskaan vuoda mukaan.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
pipe = Pipeline([
("scaler", StandardScaler()),
("clf", LogisticRegression(max_iter=1000)),
])
pipe.fit(X_train, y_train) # StandardScaler oppii keskiarvon/hajonnan, sitten LR opettaa mallin
pred = pipe.predict(X_test) # StandardScaler transformoi, sitten LR ennustaa
Pipeline muuttaa siis useiden vaiheiden manuaalisen ketjun yhdeksi olioksi, jota voi tallentaa, ristiinvalidoida ja siirtää tuotantoon ilman järjestysvirheitä. Vastaava ajattelu koskee myös DataFrame-tason työnkulkuja, joita käsittelin Pandas 3.0 -oppaassa uusiin ominaisuuksiin ja siirtymään: mitä enemmän vaiheita paketoit yhdeksi kokonaisuudeksi, sitä vähemmän virheitä syntyy kokoonpanovaiheessa.
Miksi ColumnTransformer on välttämätön esikäsittelyssä?
Todellinen data on harvoin homogeenistä. Tyypillinen taulukkoaineisto sisältää numeerisia sarakkeita (ikä, tulot, kesto), kategorisia sarakkeita (maa, tuoteryhmä, sopimustyyppi), ja mahdollisesti päivämääriä tai vapaata tekstiä. Yksittäinen skaalain ei toimi kategorisille sarakkeille, eikä one-hot-koodain sovi numeerisille. ColumnTransformer ratkaisee tämän ohjaamalla eri sarakejoukot eri muuntimille rinnakkain, ja liimaa lopputulokset yhdeksi matriisiksi.
Ilman ColumnTransformeria joudut jakamaan DataFramen kahteen osaan, sovittamaan skaalaimen ja koodaimen erikseen, yhdistämään tulokset takaisin oikeassa järjestyksessä ja varmistamaan ristiinvalidoinnissa, ettei kumpikaan sovitu validointidataan. Rehellisesti sanottuna, olen nähnyt juuri tästä syntyvän tuotantobugeja useammin kuin muistan. ColumnTransformer puolestaan hyväksyy listan (nimi, muunnin, sarakkeet)-kolmikoita, jotka määrittelevät, mitä tehdään mihinkin. Parametri remainder="passthrough" päästää nimeämättömät sarakkeet läpi muuttumattomina; remainder="drop" (oletus) pudottaa ne pois. Vuoden 2026 työnkuluissa suositan usein verbose_feature_names_out=False-asetusta, jotta lopulliset sarakenimet pysyvät luettavina, kun poimit ne get_feature_names_out()-metodilla.
Pipeline vs. ColumnTransformer: milloin kumpaakin?
Näitä kahta luokkaa käytetään yhdessä, mutta ne ratkaisevat eri ongelmia. Pipeline on peräkkäinen: vaihe 1 syöttää vaihe 2:lle, joka syöttää vaihe 3:lle. ColumnTransformer taas on rinnakkainen: sarakejoukko A ohjataan muuntimelle A, sarakejoukko B muuntimelle B, ja tulokset kootaan vierekkäin. Karkea nyrkkisääntö: jos vaiheet toimivat samalle datalle peräkkäin, käytä Pipeline. Jos vaiheet toimivat eri sarakkeille rinnakkain, käytä ColumnTransformer. Käytännössä ColumnTransformer on lähes aina yksi Pipelinen vaihe.
Ominaisuus
Pipeline
ColumnTransformer
Suoritustapa
Peräkkäinen (vaihe → vaihe)
Rinnakkainen (sarakejoukko → muunnin)
Tyypillinen käyttö
Esikäsittely + malli
Eri esikäsittely eri sarakkeille
Viimeinen vaihe
Voi olla estimaattori (predict)
Aina muunnin (ei predict)
Parametripolku
step__param
transformer__step__param
Sarakkeiden valinta
Ei valintaa, koko matriisi
Lista, indeksit tai make_column_selector
Rinnakkaisajo
Ei
Kyllä (n_jobs-parametri)
Käytännössä lopullinen työnkulku näyttää siis tältä: uloin Pipeline, jonka ensimmäinen vaihe on ColumnTransformer ja viimeinen vaihe on luokittelija tai regressori. Näin koko ketju on yksi olio, jota voi ristiinvalidoida, säätää ja tallentaa levylle.
Käytännön esimerkki: asiakaspoistuman ennustaminen
Rakennetaan täydellinen työnkulku telekommunikaatioyrityksen asiakaspoistuman (churn) ennustamiseen. Aineistossa on numeerisia sarakkeita (kuukausimaksu, sopimuksen kesto), kategorisia sarakkeita (sopimustyyppi, maksutapa) ja puuttuvia arvoja. Käytämme scikit-learn 1.7:ää ja Pandas 2.2:ta, jotka toimivat yhdessä ilman erillistä konversiota.
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import train_test_split, cross_val_score
# 1) Lataa data
df = pd.read_csv("telco_churn.csv")
y = (df.pop("Churn") == "Yes").astype(int)
X = df
# 2) Erota sarakkeet tyypin mukaan
numeric_cols = ["tenure", "MonthlyCharges", "TotalCharges"]
categorical_cols = ["Contract", "PaymentMethod", "InternetService"]
# 3) Määrittele esikäsittely per sarakejoukko
numeric_pipe = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
])
categorical_pipe = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])
preprocessor = ColumnTransformer(
transformers=[
("num", numeric_pipe, numeric_cols),
("cat", categorical_pipe, categorical_cols),
],
remainder="drop",
verbose_feature_names_out=False,
)
# 4) Yhdistä esikäsittely ja malli
model = Pipeline([
("preprocessor", preprocessor),
("classifier", HistGradientBoostingClassifier(random_state=42)),
])
# 5) Sovita ja arvioi ristiinvalidoinnilla
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42
)
scores = cross_val_score(model, X_train, y_train, cv=5, scoring="roc_auc")
print(f"Ristiinvalidoinnin ROC-AUC: {scores.mean():.3f} ± {scores.std():.3f}")
model.fit(X_train, y_train)
print(f"Testin ROC-AUC: {model.score(X_test, y_test):.3f}")
Huomaa, että SimpleImputer laskee mediaanin vain koulutusosuudesta jokaisessa fold-jaossa. Jos olisit imputoinut ennen jakoa, testijoukon mediaani vuotaisi koulutukseen. Juuri sitä Pipeline estää oletuksena. Historiallisesti tätä kutsutaan yleiseksi sudenkuopaksi, jonka scikit-learnin virallinen dokumentaatio nostaa esiin ensimmäisenä varoituksena.
Kehittyneet ominaisuudet: make_column_selector ja FunctionTransformer
Kovakoodattu sarakelistaus on hauras: jos aineistoon lisätään uusi kategorinen kenttä, unohdat päivittää listan ja malli hiljaa tiputtaa sen. make_column_selector ratkaisee tämän valitsemalla sarakkeet dtype-tyypin tai säännöllisen lausekkeen perusteella dynaamisesti. Näin pipeline mukautuu automaattisesti skeeman muutoksiin.
FunctionTransformer puolestaan käärii tavallisen Python-funktion muuntimeksi, joka istuu pipeliniin. Käyttökohteita ovat logaritmimuunnokset, päivämäärän purkaminen komponentteihin ja domain-spesifit laskennat.
import numpy as np
from sklearn.preprocessing import FunctionTransformer
log_transformer = FunctionTransformer(np.log1p, feature_names_out="one-to-one")
skewed_pipe = Pipeline([
("log", log_transformer),
("scaler", StandardScaler()),
])
Yhdistämällä make_column_selector, FunctionTransformer ja perusmuuntimet voit rakentaa hyvin ilmaisuvoimaisia esikäsittelyketjuja ilman että kirjoitat yhtään omaa muunninluokkaa. Kun tarvitset lisäksi taulukkotasoisen suorituksen ripeyttä, Polars-oppaani Rust-ytimestä näyttää, miten esilaskennat kannattaa siirtää DataFrame-kerrokseen ennen scikit-learnin pipelinea.
Hyperparametrien säätö GridSearchCV:llä
Pipelinen todellinen voima paljastuu ristiinvalidoidussa hyperparametrien haussa. GridSearchCV ristiinvalidoi koko pipelinen (mukaan lukien esikäsittelyn) jokaiselle parametrikombinaatiolle. Parametriavaimet käyttävät kaksoisalaviivaa (__) tasojen erottimena: step__param pipelinelle, transformer__step__param ColumnTransformer-sisäisille askelille.
Tämä haku sovittaa koko pipelinen (imputoinnin, skaalauksen, koodauksen ja mallin) jokaiselle 5 × 2 × 3 × 3 × 2 = 180 parametrikombinaation ristiinvalidointi-foldille. Vuoden 2026 versioissa suosittelen HalvingGridSearchCV-vaihtoehtoa isoille hakuavaruuksille: se karsii huonoimmat kandidaatit iteratiivisesti ja säästää usein 60–80 % laskenta-ajasta ilman merkittävää tarkkuushävikkiä.
Miten estää tietovuoto sklearn Pipelinessa?
Tietovuoto (data leakage) tapahtuu, kun informaatiota testi- tai validointijoukosta pääsee koulutukseen. Yleisimpiä syitä ovat: (1) esikäsittelijän sovittaminen koko dataan ennen jakoa, (2) kohde-enkoodauksen (target encoding) laskeminen käyttäen koko datajoukkoa, ja (3) piirteen skaalaaminen kertaalleen ja sen käyttö kaikissa fold-jaoissa. Pipeline poistaa nämä oletuksena, kunhan et itse riko sopimusta.
Kolme sääntöä, joita noudatan kaikissa projekteissa:
Älä koskaan kutsu fit_transform ennen jakoa. Jaa data ensin train_test_split-funktiolla, ja anna pipelinen hoitaa loput. Kaikki oppiminen tapahtuu pipeline.fit(X_train, y_train)-kutsussa.
Käytä cross_val_score-, cross_validate- tai GridSearchCV-työkaluja suoraan pipelinelle, äläkä koskaan esikäsittele erikseen ennen ristiinvalidointia. Nämä työkalut sovittavat pipelinen erikseen jokaisessa fold-jaossa.
Kohde-enkoodaus tehdään TargetEncoder-luokalla (scikit-learn 1.3+), joka laskee kategorian keskiarvon kohdemuuttujasta pelkän koulutusosuuden perusteella. Vältä custom-koodia, joka laskee keskiarvon koko datasta.
Pipelinen tallennus ja tuotantoon vienti
Yksi pipelinen suurimmista käytännön eduista on sarjallistuminen. Koska koko esikäsittely + malli on yksi Python-olio, sen voi tallentaa yhdellä komennolla ja ladata takaisin missä tahansa Python-ympäristössä, jossa on sama scikit-learn-versio.
import joblib
# Tallenna
joblib.dump(search.best_estimator_, "churn_model.joblib", compress=3)
# Lataa toisessa prosessissa
model = joblib.load("churn_model.joblib")
prediction = model.predict(new_customer_dataframe)
Tuotannossa pipeline saa syötteekseen tuoreen DataFramen (samoilla sarakenimillä ja -tyypeillä kuin koulutuksessa), ajaa saman imputoinnin, skaalauksen ja koodauksen, ja tuottaa ennusteen. Jos syötteessä on uusi kategoria, jota koulutuksessa ei nähty, OneHotEncoder(handle_unknown="ignore")-asetus estää kaatumisen: tuntematon arvo koodataan pelkiksi nolliksi.
Versiohallinta on kriittistä. Muistan yhden tuotantopäivityksen, jossa scikit-learn-versio nousi minor-numeron verran ja OneHotEncoderin sarakejärjestys muuttui hienoisesti. Tallenna aina metatietoihin scikit-learnin, Pandasin ja NumPyn versionumerot. Vuoden 2026 työnkuluissa suosittelen MLflown mallirekisteriä tai vastaavaa työkalua, joka pakkaa mallin, riippuvuudet ja skeeman yhdeksi artefaktiksi. Nopeita analyyttisiä ennusteita varten voi myös harkita DuckDB:tä data-analyysin oppaassani yhdistettynä sarjallistettuun pipelineen: tällöin ennusteita voi ajaa SQL-kyselyistä ilman erillistä palvelinta.
Pipelinen debuggaus ja välitulosten tarkastelu
Kun pipeline kaatuu tai antaa oudon tuloksen, ensimmäinen refleksi on usein purkaa se erillisiksi vaiheiksi. Se on tarpeetonta. Scikit-learn tarjoaa suoran pääsyn väliolioihin named_steps- ja named_transformers_-attribuuttien kautta. Näin saat tarkasteltua sovitetun skaalaimen keskiarvot, koodaimen kategoriat tai luokittelijan piirretärkeydet ilman, että joudut ajamaan pipelinea uudelleen paloissa.
Scikit-learn 1.7 (ks. virallinen release notes -sivu) paransi Jupyterin HTML-esitystä niin, että kaikki parametrit näkyvät suoraan, ja oletusarvoista poikkeavat korostetaan. Kirjoita solussa vain model ja saat interaktiivisen puun, jota voi klikata auki. Tämä on paras tapa nähdä yhdellä silmäyksellä, mitä pipelinesi tekee, erityisesti kun se on GridSearchCV:n valitsema best_estimator_, jonka rakennetta et välttämättä muista ulkoa.
Jos haluat nähdä, miltä data näyttää ennen luokittelijaa, katkaise pipeline yhdeltä vaiheelta model[:-1]-viipaloinnilla. Tämä palauttaa uuden pipelinen ilman viimeistä askelta, ja voit ajaa datan sen läpi transform-kutsulla.
preprocessed = model[:-1].transform(X_test.head())
import pandas as pd
pd.DataFrame(preprocessed, columns=model[:-1].get_feature_names_out()).head()
Suorituskykyvinkit isoille aineistoille
Kun aineiston koko kasvaa miljooniin riveihin, muutama pieni asetusmuutos ratkaisee suorituskyvyn. Ensinnäkin, OneHotEncoder(sparse_output=True) tuottaa harvan matriisin, joka säästää muistia dramaattisesti, kun kategoriasarakkeissa on kymmeniä tai satoja arvoja. HistGradientBoostingClassifier, LogisticRegression ja monet muut mallit hyväksyvät sen suoraan. Toiseksi, ColumnTransformer(n_jobs=-1) rinnakkaistaa haarojen sovittamisen, mikä nopeuttaa kokonaisajoa, kun eri sarakejoukoille on kalliita muunnoksia.
Kolmanneksi, memory-parametri välimuistittaa muuntimien tulokset levylle. Kun GridSearchCV kokeilee samaa esikäsittelyä eri malliparametreilla, välimuisti estää sen uudelleensovittamisen. Tässä on yksi niistä pikkujutuista, jotka voivat leikata koko haun keston puoleen isoissa aineistoissa.
from tempfile import mkdtemp
cache_dir = mkdtemp()
model = Pipeline(
steps=[("preprocessor", preprocessor), ("classifier", clf)],
memory=cache_dir,
)
Kategoristen sarakkeiden käsittelyssä OneHotEncoder(min_frequency=10) yhdistää kaikki harvinaiset kategoriat (alle 10 esiintymää) yhdeksi "infrequent"-luokaksi. Tämä pienentää piirreavaruutta, vähentää ylisovittumista ja parantaa yleistyskykyä ilman että joudut tekemään ryhmittelyä käsin. Vaihtoehtoisesti TargetEncoder (scikit-learn 1.3+) korvaa kategorian sen ehdollisella kohdemuuttujan keskiarvolla, mikä on tehokas korkean kardinaliteetin kentille kuten postinumero tai tuote-ID.
Usein kysytyt kysymykset
Mikä on ero Pipelinen ja ColumnTransformerin välillä?
Pipeline suorittaa vaiheet peräkkäin samalle datalle, kun taas ColumnTransformer soveltaa eri muuntimia eri sarakejoukkoihin rinnakkain. Käytännössä ColumnTransformer on lähes aina yksi vaihe uloimman Pipelinen sisällä, ei sen korvike.
Miten hyperparametrit säädetään Pipelinessa?
Käytä GridSearchCV- tai RandomizedSearchCV-luokkia ja viittaa parametreihin step__param-syntaksilla. Sisäkkäisiin askeliin viitataan pidemmällä polulla: preprocessor__num__scaler__with_mean. Hakutyökalu ristiinvalidoi koko pipelinen jokaiselle kombinaatiolle.
Miksi Pipeline estää tietovuodon?
Koska ristiinvalidointi sovittaa pipelinen erikseen jokaisen fold-jaon koulutusosuuteen, muuntimet (esim. skaalaimen keskiarvo tai imputoinnin mediaani) oppivat pelkästä koulutusdatasta. Validointi-fold ajetaan opitun muuntimen läpi ilman uudelleensovitusta, joten sen tilastot eivät vuoda koulutukseen.
Voiko Pipelinea käyttää tekstidatan kanssa?
Kyllä. Käytä TfidfVectorizer- tai CountVectorizer-muunninta yhtenä ColumnTransformern haarana ja aja sen läpi kunkin tekstisarakkeen sisältö. Numeeriset ja kategoriset piirteet menevät omilla haaroillaan, ja lopputulokset yhdistetään yhdeksi harvaksi matriisiksi luokittelijalle.
Miten Pipeline tallennetaan ja ladataan uudelleen?
Käytä joblib.dump(pipeline, "model.joblib") tallentamiseen ja joblib.load("model.joblib") lataamiseen. Tallenna aina scikit-learnin, Pandasin ja NumPyn versionumerot metatietoihin, sillä sarjallistus ei ole taaksepäin yhteensopiva versioiden välillä.