IlmHamroh
Data Science va sun'iy intellekt/scikit learn toliq3/10-dars18 daqiqa
Mundarija (22)

19.3-dars: ColumnTransformer

19-QISM — SCIKIT-LEARN TO'LIQ · 3-dars


1. Kirish va motivatsiya

Real ma'lumot bir jinsli emas: sonli ustunlar masshtablashni, kategoriyali ustunlar kodlashni, matn ustunlari vektorizatsiyani, sanalar esa komponentlarga ajratishni talab qiladi. Pipeline esa butun matritsaga bitta transformer qo'llaydi.

ColumnTransformer aynan shu bo'shliqni to'ldiradi: u ustunlar guruhlariga turli transformerlarni parallel qo'llaydi va natijalarni birlashtiradi. Bu — real loyihadagi har qanday tayyorlash quvurining markazi.

Lekin u bir nechta nozik joyga ega: ustunlarni qanday tanlash (nom, indeks, tanlagich), tanlanmagan ustunlar bilan nima bo'ladi (remainder), chiqishdagi ustun nomlari qanday hosil bo'ladi, siyrak va zich matritsalar qanday birlashadi va ustunlar tartibi qanday o'zgaradi.

Bu darsda: qurilishi, ustun tanlash uch usuli, make_column_selector, remainder, get_feature_names_out, siyrak chiqish, ichma-ich quvurlar va verbose_feature_names_out.

Real vaziyat. Jamoa ColumnTransformer ni indekslar bilan qurdi: ("son", StandardScaler(), [0, 1, 2, 3]). Keyin ma'lumot manbaiga yangi ustun qo'shildi va u ikkinchi o'ringa tushdi. Kod xatosiz ishladi, lekin endi kategoriyali ustun masshtablanardi va sonli ustun kodlanardi. Xato ishlab chiqarishda uch hafta sezilmadi.

Bu darsda ColumnTransformer ni o'rganamiz.

Bu darsda:

  • Ustun tanlashning uch usuli
  • remainder
  • make_column_selector
  • get_feature_names_out
  • Siyrak va zich chiqish
  • Ichma-ich quvurlar
  • Tuzoqlar
  • Amaliy: tayyorlash markazi

ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14, sklearn 1.9, pandas 3).


2. Nazariya — chuqur tushuntirish

2.1. Qurilishi va ishlashi

python
ColumnTransformer([
    ("nom1", transformer1, ustunlar1),
    ("nom2", transformer2, ustunlar2),
], remainder="drop", verbose_feature_names_out=True)

ISHLASHI:
  har (transformer, ustunlar) juftligi ALOHIDA fit qilinadi
  natijalar GORIZONTAL birlashtiriladi (hstack)
  tartib: transformerlar ro'yxatdagi tartibda, keyin remainder

DIQQAT: chiqish ustunlari tartibi KIRISH tartibiga MOS EMAS
  u transformerlar ro'yxatiga bog'liq

Chiqish ustunlari tartibi transformerlar ro'yxatiga bog'liq, kirish DataFrame tartibiga emas.

2.2. Ustun tanlashning uch usuli

text
1. NOMLAR ro'yxati (ENG XAVFSIZ)
   ("son", StandardScaler(), ["yosh", "daromad"])
   + ustun joyi o'zgarsa ham ishlaydi
   + o'qishga tushunarli

2. INDEKSLAR ro'yxati (XAVFLI)
   ("son", StandardScaler(), [0, 1, 2])
   - yangi ustun qo'shilsa JIM buziladi
   - faqat numpy massivda oqlanadi

3. TANLAGICH (dinamik)
   ("son", StandardScaler(),
    make_column_selector(dtype_include=np.number))
   + yangi sonli ustun avtomatik qo'shiladi
   - qaysi ustun tushganini ko'rish qiyinroq

BITTA USTUN: ["yosh"] (ro'yxat) - "yosh" (satr) EMAS
  satr bersangiz transformer 1D oladi va ko'pchiligi xato beradi

Nomlar ro'yxati — sukut tanlov; indekslar faqat numpy massiv bilan ishlaganda oqlanadi.

2.3. remainder

text
remainder="drop" (SUKUT)
  ro'yxatda yo'q ustunlar TASHLANADI
  xavfli: yangi ustun jim yo'qoladi

remainder="passthrough"
  qolgan ustunlar O'ZGARTIRILMASDAN qo'shiladi
  xavfli: kategoriyali ustun modelga xom holda tushishi mumkin

remainder=transformer
  qolganlariga shu transformer qo'llanadi
  masalan remainder=SimpleImputer(strategy="median")

TAVSIYA: ustunlarni ANIQ sanang va remainder="drop" qoldiring,
         keyin get_feature_names_out bilan tekshiring

remainder="drop" sukut — ro'yxatga kirmagan ustun jim yo'qoladi, shuning uchun chiqish nomlarini har doim tekshiring.

2.4. make_column_selector

python
from sklearn.compose import make_column_selector as tanla

tanla(dtype_include=np.number)        # barcha sonli
tanla(dtype_include="str")            # matn (pandas 3 da 'str' dtype)
tanla(dtype_exclude=np.number)        # sonli bo'lmagan
tanla(pattern="^oy_")                 # nomi shablonga mos

DIQQAT (pandas 3):
  dtype_include=object  ->  Pandas4Warning beradi
  matn ustunlar uchun "str" yoki dtype_exclude=np.number ishlating

TANLAGICH CHAQIRILADI: tanlagich(df) -> ustun nomlari ro'yxati
  fit paytida bajariladi, ya'ni DINAMIK

pandas 3 da dtype_include=object ogohlantirish beradi — matn ustunlari uchun "str" yoki dtype_exclude=np.number ishlating.

2.5. get_feature_names_out va nomlar

text
verbose_feature_names_out=True (SUKUT)
  chiqish nomlari: "transformer_nomi__asl_nom"
  masalan: "son__yosh", "kat__hudud_shimol"
  + to'qnashuv bo'lmaydi
  - nomlar uzun

verbose_feature_names_out=False
  chiqish nomlari: "yosh", "hudud_shimol"
  + qisqa
  - ikki transformer bir xil nom bersa XATO

TEKSHIRISH:
  ct.get_feature_names_out()          # barcha nomlar
  ct.named_transformers_["kat"]       # fit qilingan transformer
  ct.output_indices_                  # qaysi ustunlar qayerdan

ct.output_indices_ har transformer chiqishdagi qaysi ustunlarni egallaganini ko'rsatadi — nosozlikni topishda juda foydali.

2.6. Siyrak va zich chiqish

text
sparse_threshold=0.3 (SUKUT)
  agar siyrak ustunlar ulushi > 0.3 bo'lsa -> SIYRAK chiqish

MUAMMO:
  HistGradientBoosting, ba'zi transformerlar siyrakni qabul qilmaydi
  set_output("pandas") siyrak bilan ishlamaydi

YECHIMLAR:
  OneHotEncoder(sparse_output=False)      # eng oddiy
  ColumnTransformer(..., sparse_threshold=0)   # majburan zich
  yoki modelga siyrakni qabul qiladiganini tanlang (LogisticRegression)

XOTIRA: 10 000 darajali kategoriya -> zich matritsa GB larni oladi

Matn yoki ko'p darajali kategoriya bo'lsa siyrakni saqlang; zichga aylantirish xotirani portlatishi mumkin.

2.7. Tuzoqlar

Asosiy tuzoqlar: indeks bilan ustun tanlash; bitta ustunni satr sifatida berish; remainder ni unutish; chiqish nomlarini tekshirmaslik; pandas 3 da dtype_include=object; siyrak chiqishda set_output("pandas"); bir ustunni ikki transformerga berish (u ikki marta chiqadi); ColumnTransformer ichida fit_transform ni qo'lda chaqirish.

2.8. Tayyorlashning markazi

ColumnTransformer — har qanday jiddiy tayyorlash quvurining markazi. Ustunlarni nom bilan tanlang, remainder ni ongli qo'ying va get_feature_names_out() hamda output_indices_ bilan natijani tekshiring. Har bir ustun guruhi o'z ichki Pipeline iga ega bo'lishi mumkin — shunda imputatsiya, kodlash va masshtablash ham leakage dan himoyalangan bo'ladi.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

son_quvur = Pipeline([("imp", SimpleImputer(strategy="median")),
                      ("sc", StandardScaler())])
kat_quvur = Pipeline([("imp", SimpleImputer(strategy="most_frequent")),
                      ("oh", OneHotEncoder(handle_unknown="ignore",
                                           sparse_output=False))])

ct = ColumnTransformer([
    ("son", son_quvur, make_column_selector(dtype_include=np.number)),
    ("kat", kat_quvur, make_column_selector(dtype_include="str")),
], remainder="drop", verbose_feature_names_out=True)

ct.fit(df)
ct.get_feature_names_out() · ct.output_indices_ · ct.named_transformers_["kat"]
QOIDA: nom bilan tanla · remainder ni ongli qo'y ·
       nomlarni tekshir · siyrakni ongli boshqar

ColumnTransformer xulosasi

Ustun guruhlariga TURLI transformer
Tanlash: nomlar (xavfsiz) / indeks (xavfli) / tanlagich (dinamik)
remainder: drop (sukut) / passthrough / transformer
Nomlar: transformer__asl_nom
output_indices_: qaysi ustun qayerdan
sparse_threshold: siyrak yoki zich

4. Batafsil misollar

Misollar real numpy/pandas/sklearn bilan (Python 3.14).

Misol 1 — Uch usulda ustun tanlash

python
"""Nom, indeks va tanlagich (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def yarat(n: int = 200, seed: int = 0, yangi_ustun: bool = False):
    rng = np.random.default_rng(seed)
    ustunlar = {}
    if yangi_ustun:
        ustunlar["mijoz_id"] = rng.integers(1000, 9999, n).astype(float)
    ustunlar.update({
        "yosh": rng.integers(18, 70, n).astype(float),
        "daromad": rng.lognormal(10, 0.5, n),
        "hudud": rng.choice(["shimol", "janub"], n),
    })
    return pd.DataFrame(ustunlar)


def main() -> None:
    df = yarat()
    print("=== 1. Ma'lumot ===")
    print(f"  ustunlar: {list(df.columns)}")
    print(f"  turlari: {[str(t) for t in df.dtypes]}")

    print("\n=== 2. Uch usul bir xil natija beradi ===")
    variantlar = {
        "nomlar": ColumnTransformer([
            ("son", StandardScaler(), ["yosh", "daromad"]),
            ("kat", OneHotEncoder(sparse_output=False), ["hudud"])]),
        "indekslar": ColumnTransformer([
            ("son", StandardScaler(), [0, 1]),
            ("kat", OneHotEncoder(sparse_output=False), [2])]),
        "tanlagich": ColumnTransformer([
            ("son", StandardScaler(),
             make_column_selector(dtype_include=np.number)),
            ("kat", OneHotEncoder(sparse_output=False),
             make_column_selector(dtype_include="str"))]),
    }
    print(f"  {'usul':<12} {'shakl':>10} {'nomlar':<44}")
    for nom, ct in variantlar.items():
        chiqish = ct.fit_transform(df)
        print(f"  {nom:<12} {str(chiqish.shape):>10} "
              f"{str(ct.get_feature_names_out().tolist()):<44}")

    print("\n=== 3. Yangi ustun qo'shilganda ===")
    df2 = yarat(yangi_ustun=True)
    print(f"  yangi ustunlar: {list(df2.columns)}")
    print(f"  {'usul':<12} {'shakl':>10} {'sonli qadamga tushgan ustunlar'}")
    for nom, ct in variantlar.items():
        yangi_ct = ColumnTransformer(ct.transformers)
        chiqish = yangi_ct.fit_transform(df2)
        nomlar = [n for n in yangi_ct.get_feature_names_out()
                  if n.startswith("son__")]
        print(f"  {nom:<12} {str(chiqish.shape):>10} {nomlar}")

    print("\n=== 4. Nima bo'ldi ===")
    print("  nomlar:    o'sha ikkita ustun - TO'G'RI")
    print("  indekslar: [0, 1] endi mijoz_id va yosh - JIM XATO")
    print("  tanlagich: uchala sonli ustun - yangi ustun avtomatik")
    print("  ⭐ Indeks bilan tanlash ustun qo'shilganda jim buziladi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  ustunlar: ['yosh', 'daromad', 'hudud']
  turlari: ['float64', 'float64', 'str']

=== 2. Uch usul bir xil natija beradi ===
  usul              shakl nomlar
  nomlar         (200, 4) ['son__yosh', 'son__daromad', 'kat__hudud_janub', 'kat__hudud_shimol']
  indekslar      (200, 4) ['son__yosh', 'son__daromad', 'kat__hudud_janub', 'kat__hudud_shimol']
  tanlagich      (200, 4) ['son__yosh', 'son__daromad', 'kat__hudud_janub', 'kat__hudud_shimol']

=== 3. Yangi ustun qo'shilganda ===
  yangi ustunlar: ['mijoz_id', 'yosh', 'daromad', 'hudud']
  usul              shakl sonli qadamga tushgan ustunlar
  nomlar         (200, 4) ['son__yosh', 'son__daromad']
  indekslar    (200, 202) ['son__mijoz_id', 'son__yosh']
  tanlagich      (200, 5) ['son__mijoz_id', 'son__yosh', 'son__daromad']

=== 4. Nima bo'ldi ===
  nomlar:    o'sha ikkita ustun - TO'G'RI
  indekslar: [0, 1] endi mijoz_id va yosh - JIM XATO
  tanlagich: uchala sonli ustun - yangi ustun avtomatik
  ⭐ Indeks bilan tanlash ustun qo'shilganda jim buziladi

Nima ko'rsatdi: 2.2-bo'lim.

Misol 2 — remainder va yo'qolgan ustunlar

python
"""Ro'yxatga kirmagan ustunlar taqdiri (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def main() -> None:
    rng = np.random.default_rng(0)
    n = 300
    df = pd.DataFrame({
        "yosh": rng.integers(18, 70, n).astype(float),
        "daromad": rng.lognormal(10, 0.5, n),
        "ball": rng.normal(600, 80, n),            # ro'yxatga KIRMAYDI
        "hudud": rng.choice(["shimol", "janub"], n),
        "tarif": rng.choice(["oddiy", "premium"], n),   # KIRMAYDI
    })

    asos = [("son", StandardScaler(), ["yosh", "daromad"]),
            ("kat", OneHotEncoder(sparse_output=False), ["hudud"])]

    print("=== 1. remainder='drop' (sukut) ===")
    ct = ColumnTransformer(asos).fit(df)
    print(f"  kirish ustunlari: {len(df.columns)}")
    print(f"  chiqish ustunlari: {len(ct.get_feature_names_out())}")
    print(f"  nomlar: {ct.get_feature_names_out().tolist()}")
    yoqolgan = set(df.columns) - {"yosh", "daromad", "hudud"}
    print(f"  JIM YO'QOLGAN: {sorted(yoqolgan)}")

    print("\n=== 2. remainder='passthrough' ===")
    ct2 = ColumnTransformer(asos, remainder="passthrough").fit(df)
    print(f"  chiqish ustunlari: {len(ct2.get_feature_names_out())}")
    print(f"  nomlar: {ct2.get_feature_names_out().tolist()}")
    chiqish = ct2.transform(df)
    print(f"  chiqish dtype: {chiqish.dtype}")
    print("  DIQQAT: 'tarif' matn holida qoldi -> model xato beradi")

    print("\n=== 3. remainder=transformer ===")
    ct3 = ColumnTransformer(
        [("son", StandardScaler(), ["yosh", "daromad", "ball"]),
         ("kat", OneHotEncoder(sparse_output=False), ["hudud", "tarif"])],
        remainder="drop").fit(df)
    print(f"  barcha ustun sanaldi: {len(ct3.get_feature_names_out())} ta")
    print(f"  nomlar: {ct3.get_feature_names_out().tolist()}")

    print("\n=== 4. output_indices_ bilan tekshirish ===")
    print(f"  {'transformer':<14} {'chiqish ustunlari':>20}")
    for nom, kesim in ct3.output_indices_.items():
        if kesim.stop > kesim.start:
            print(f"  {nom:<14} {f'{kesim.start}..{kesim.stop - 1}':>20}")
    print(f"  named_transformers_ kalitlari: "
          f"{list(ct3.named_transformers_)}")
    oh = ct3.named_transformers_["kat"]
    print(f"  OneHotEncoder ko'rgan darajalar: "
          f"{[c.tolist() for c in oh.categories_]}")
    print("  ⭐ remainder='drop' sukut - ustunni jim yo'qotadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. remainder='drop' (sukut) ===
  kirish ustunlari: 5
  chiqish ustunlari: 4
  nomlar: ['son__yosh', 'son__daromad', 'kat__hudud_janub', 'kat__hudud_shimol']
  JIM YO'QOLGAN: ['ball', 'tarif']

=== 2. remainder='passthrough' ===
  chiqish ustunlari: 6
  nomlar: ['son__yosh', 'son__daromad', 'kat__hudud_janub', 'kat__hudud_shimol', 'remainder__ball', 'remainder__tarif']
  chiqish dtype: object
  DIQQAT: 'tarif' matn holida qoldi -> model xato beradi

=== 3. remainder=transformer ===
  barcha ustun sanaldi: 7 ta
  nomlar: ['son__yosh', 'son__daromad', 'son__ball', 'kat__hudud_janub', 'kat__hudud_shimol', 'kat__tarif_oddiy', 'kat__tarif_premium']

=== 4. output_indices_ bilan tekshirish ===
  transformer       chiqish ustunlari
  son                            0..2
  kat                            3..6
  named_transformers_ kalitlari: ['son', 'kat']
  OneHotEncoder ko'rgan darajalar: [['janub', 'shimol'], ['oddiy', 'premium']]
  ⭐ remainder='drop' sukut - ustunni jim yo'qotadi

Nima ko'rsatdi: 2.3, 2.5-bo'limlar.

Misol 3 — To'liq tayyorlash markazi

python
"""Har guruhga o'z quvuri (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def yarat(n: int = 800, seed: int = 0) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    df = pd.DataFrame({
        "yosh": rng.integers(18, 70, n).astype(float),
        "daromad": rng.lognormal(10, 0.5, n),
        "ball": rng.normal(600, 80, n),
        "hudud": rng.choice(["shimol", "janub", "markaz"], n),
        "tarif": rng.choice(["oddiy", "premium", "biznes"], n),
    })
    # yo'qolgan qiymatlar
    for ustun, ulush in [("daromad", 0.12), ("ball", 0.07), ("tarif", 0.09)]:
        idx = rng.choice(n, int(n * ulush), replace=False)
        df.loc[idx, ustun] = None
    return df


def main() -> None:
    df = yarat()
    kuch = (0.02 * df["yosh"].fillna(40)
            + 0.004 * df["ball"].fillna(600)
            + 1.2 * (df["tarif"].fillna("oddiy") == "biznes") - 3.4)
    rng = np.random.default_rng(1)
    y = (rng.random(len(df)) < 1 / (1 + np.exp(-kuch))).astype(int)

    print("=== 1. Ma'lumot ===")
    print(f"  {len(df)} qator, {len(df.columns)} ustun")
    print(f"  {'ustun':<10} {'dtype':<10} {'yo_qolgan':>10}")
    for ustun in df.columns:
        print(f"  {ustun:<10} {str(df[ustun].dtype):<10} "
              f"{df[ustun].isna().sum():>10}")

    print("\n=== 2. Har guruhga o'z quvuri ===")
    son_quvur = Pipeline([("imp", SimpleImputer(strategy="median")),
                          ("sc", StandardScaler())])
    kat_quvur = Pipeline([("imp", SimpleImputer(strategy="most_frequent")),
                          ("oh", OneHotEncoder(handle_unknown="ignore",
                                               sparse_output=False))])
    tayyor = ColumnTransformer([
        ("son", son_quvur, make_column_selector(dtype_include=np.number)),
        ("kat", kat_quvur, make_column_selector(dtype_include="str")),
    ])
    tayyor.fit(df)
    print(f"  sonli ustunlar: "
          f"{make_column_selector(dtype_include=np.number)(df)}")
    print(f"  matn ustunlar: "
          f"{make_column_selector(dtype_include='str')(df)}")
    print(f"  chiqish: {tayyor.transform(df).shape}")

    print("\n=== 3. Chiqish nomlari ===")
    nomlar = tayyor.get_feature_names_out()
    print(f"  jami {len(nomlar)} ta:")
    for i in range(0, len(nomlar), 4):
        print(f"    {nomlar[i:i + 4].tolist()}")
    print(f"  output_indices_:")
    for nom, kesim in tayyor.output_indices_.items():
        if kesim.stop > kesim.start:
            print(f"    {nom}: {kesim.start}..{kesim.stop - 1}")

    print("\n=== 4. Ichki qadamlarga murojaat ===")
    son_imp = tayyor.named_transformers_["son"].named_steps["imp"]
    print(f"  median qiymatlar: "
          f"{np.round(son_imp.statistics_, 2).tolist()}")
    kat_oh = tayyor.named_transformers_["kat"].named_steps["oh"]
    print(f"  kategoriyalar: {[c.tolist() for c in kat_oh.categories_]}")

    print("\n=== 5. Modelga ulash ===")
    quvur = Pipeline([("t", tayyor),
                      ("m", LogisticRegression(max_iter=3000))])
    cv = StratifiedKFold(5, shuffle=True, random_state=0)
    b = cross_val_score(quvur, df, y, cv=cv, scoring="roc_auc")
    print(f"  CV ROC AUC: {b.mean():.4f} (+-{b.std():.4f})")
    quvur.fit(df, y)
    koef = quvur["m"].coef_[0]
    tartib = np.argsort(-np.abs(koef))[:5]
    print(f"  {'eng kuchli belgi':<22} {'koeffitsiyent':>14}")
    for i in tartib:
        print(f"  {nomlar[i]:<22} {koef[i]:>+14.4f}")
    print("  ⭐ Har guruh o'z quvuriga ega - imputatsiya ham himoyalangan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  800 qator, 5 ustun
  ustun      dtype       yo_qolgan
  yosh       float64             0
  daromad    float64            96
  ball       float64            56
  hudud      str                 0
  tarif      str                72

=== 2. Har guruhga o'z quvuri ===
  sonli ustunlar: ['yosh', 'daromad', 'ball']
  matn ustunlar: ['hudud', 'tarif']
  chiqish: (800, 9)

=== 3. Chiqish nomlari ===
  jami 9 ta:
    ['son__yosh', 'son__daromad', 'son__ball', 'kat__hudud_janub']
    ['kat__hudud_markaz', 'kat__hudud_shimol', 'kat__tarif_biznes', 'kat__tarif_oddiy']
    ['kat__tarif_premium']
  output_indices_:
    son: 0..2
    kat: 3..8

=== 4. Ichki qadamlarga murojaat ===
  median qiymatlar: [45.0, 21354.65, 598.27]
  kategoriyalar: [['janub', 'markaz', 'shimol'], ['biznes', 'oddiy', 'premium']]

=== 5. Modelga ulash ===
  CV ROC AUC: 0.6815 (+-0.0306)
  eng kuchli belgi        koeffitsiyent
  kat__tarif_biznes             +0.8884
  kat__tarif_oddiy              -0.5078
  kat__tarif_premium            -0.3795
  son__ball                     +0.2708
  son__yosh                     +0.2680
  ⭐ Har guruh o'z quvuriga ega - imputatsiya ham himoyalangan

Nima ko'rsatdi: 2.1, 2.4, 2.5-bo'limlar.

Misol 4 — Siyrak chiqish va xotira

python
"""sparse_threshold va xotira (real numpy/pandas/scipy/sklearn)."""

import numpy as np
import pandas as pd
from scipy import sparse
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler


def yarat(n: int = 4000, darajalar: int = 400, seed: int = 0):
    rng = np.random.default_rng(seed)
    kod = rng.integers(0, darajalar, n)
    df = pd.DataFrame({
        "yosh": rng.integers(18, 70, n).astype(float),
        "daromad": rng.lognormal(10, 0.5, n),
        "mahsulot": [f"m{k}" for k in kod],
    })
    kuch = -1.2 + 0.02 * df["yosh"] + 1.1 * np.sin(kod * 0.7)
    y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
    return df, y


def hajm_mb(matritsa) -> float:
    if sparse.issparse(matritsa):
        baytlar = (matritsa.data.nbytes + matritsa.indices.nbytes
                   + matritsa.indptr.nbytes)
    else:
        baytlar = matritsa.nbytes
    return baytlar / 1024 / 1024


def main() -> None:
    df, y = yarat()
    print("=== 1. Ma'lumot ===")
    print(f"  {len(df)} qator, mahsulot darajalari: "
          f"{df['mahsulot'].nunique()}")

    print("\n=== 2. sparse_threshold ta'siri ===")
    print(f"  {'sparse_threshold':>17} {'chiqish turi':<16} "
          f"{'shakl':>14} {'hajm (MB)':>11}")
    for chegara in [0.0, 0.3, 1.0]:
        ct = ColumnTransformer([
            ("son", StandardScaler(), ["yosh", "daromad"]),
            ("kat", OneHotEncoder(handle_unknown="ignore"), ["mahsulot"])],
            sparse_threshold=chegara)
        chiqish = ct.fit_transform(df)
        turi = "siyrak" if sparse.issparse(chiqish) else "zich"
        print(f"  {chegara:>17} {turi:<16} {str(chiqish.shape):>14} "
              f"{hajm_mb(chiqish):>11.2f}")

    print("\n=== 3. Siyraklik darajasi ===")
    ct = ColumnTransformer([
        ("son", StandardScaler(), ["yosh", "daromad"]),
        ("kat", OneHotEncoder(handle_unknown="ignore"), ["mahsulot"])],
        sparse_threshold=0.3)
    siyrak = ct.fit_transform(df)
    toldirilgan = siyrak.nnz / (siyrak.shape[0] * siyrak.shape[1])
    print(f"  nolmas elementlar ulushi: {toldirilgan:.4%}")
    print(f"  siyrak hajm: {hajm_mb(siyrak):.2f} MB")
    print(f"  zich hajm: "
          f"{siyrak.shape[0] * siyrak.shape[1] * 8 / 1024 / 1024:.2f} MB")
    print(f"  tejash: "
          f"{siyrak.shape[0] * siyrak.shape[1] * 8 / 1024 / 1024 / hajm_mb(siyrak):.0f}x")

    print("\n=== 4. Model siyrakni qabul qiladimi ===")
    cv = StratifiedKFold(4, shuffle=True, random_state=0)
    variantlar = {
        "siyrak + LogisticRegression": ColumnTransformer([
            ("son", StandardScaler(), ["yosh", "daromad"]),
            ("kat", OneHotEncoder(handle_unknown="ignore"), ["mahsulot"])],
            sparse_threshold=0.3),
        "zich + LogisticRegression": ColumnTransformer([
            ("son", StandardScaler(), ["yosh", "daromad"]),
            ("kat", OneHotEncoder(handle_unknown="ignore",
                                  sparse_output=False), ["mahsulot"])],
            sparse_threshold=0),
    }
    print(f"  {'variant':<30} {'CV AUC':>9}")
    for nom, tayyor in variantlar.items():
        quvur = Pipeline([("t", tayyor),
                          ("m", LogisticRegression(max_iter=1000))])
        b = cross_val_score(quvur, df, y, cv=cv, scoring="roc_auc")
        print(f"  {nom:<30} {b.mean():>9.4f}")
    print("  natija bir xil, lekin xotira sezilarli farq qiladi")
    print("  ⭐ Ko'p darajali kategoriyada siyrakni saqlang")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  4000 qator, mahsulot darajalari: 400

=== 2. sparse_threshold ta'siri ===
   sparse_threshold chiqish turi              shakl   hajm (MB)
                0.0 zich                (4000, 402)       12.27
                0.3 siyrak              (4000, 402)        0.15
                1.0 siyrak              (4000, 402)        0.15

=== 3. Siyraklik darajasi ===
  nolmas elementlar ulushi: 0.7463%
  siyrak hajm: 0.15 MB
  zich hajm: 12.27 MB
  tejash: 80x

=== 4. Model siyrakni qabul qiladimi ===
  variant                           CV AUC
  siyrak + LogisticRegression       0.6605
  zich + LogisticRegression         0.6605
  natija bir xil, lekin xotira sezilarli farq qiladi
  ⭐ Ko'p darajali kategoriyada siyrakni saqlang

Nima ko'rsatdi: 2.6-bo'lim.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Indeks bilan tanlash qulay" Ustun qo'shilsa jim buziladi
"Bitta ustun uchun satr yetarli" Ro'yxat kerak: ["yosh"]
"remainder sukut bo'yicha saqlaydi" "drop" — tashlaydi
"Chiqish tartibi kirish tartibi bilan bir xil" Transformerlar tartibida
"object dtype tanlagichi ishlaydi" pandas 3 da ogohlantirish
"Siyrakni har doim zichga aylantirish kerak" Xotira portlashi mumkin
"Nomlarni tekshirish shart emas" Asosiy diagnostika
"Bir ustunni ikki transformerga berish mumkin" Ikki marta chiqadi

6. Keng tarqalgan xatolar va yechimlari

1. Indeks bilan tanlash

python
("son", StandardScaler(), [0, 1, 2])                             # ⚠️
("son", StandardScaler(), ["yosh", "daromad", "ball"])           # ✅

2. Bitta ustun satr sifatida

python
("kat", OneHotEncoder(), "hudud")        # 1D -> xato              # ⚠️
("kat", OneHotEncoder(), ["hudud"])                                # ✅

3. remainder ni unutish

python
ColumnTransformer([...])       # qolgan ustunlar yo'qoladi         # ⚠️
# barcha ustunni sanang yoki remainder ni ongli tanlang            # ✅

4. pandas 3 da object

python
make_column_selector(dtype_include=object)      # ogohlantirish     # ⚠️
make_column_selector(dtype_include="str")                           # ✅

5. Siyrakda set_output("pandas")

python
OneHotEncoder(); ct.set_output(transform="pandas")    # xato        # ⚠️
OneHotEncoder(sparse_output=False)                                  # ✅

6. Nomlarni tekshirmaslik

python
quvur.fit(df, y)   # nima bo'lganini bilmaymiz                      # ⚠️
print(tayyor.get_feature_names_out(), tayyor.output_indices_)       # ✅

7. Imputatsiyani tashqarida qilish

python
df = df.fillna(df.median()); ct.fit(df)     # leakage               # ⚠️
Pipeline([("imp", SimpleImputer()), ("sc", StandardScaler())])      # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 17.9-dars (o'tilgan): Pipeline va leakage
  • 19.2-dars (o'tilgan): Pipeline chuqur
  • 19.4-dars: O'z transformeringiz
  • 19.9-dars: Tezlik va xotira
  • 19.10-dars: To'liq loyiha

8. Eng yaxshi amaliyotlar

  1. Ustunlarni nom bilan tanlang.

  2. Bitta ustun ham ro'yxatda.

  3. remainder ni ongli qo'ying.

  4. get_feature_names_out() ni tekshiring.

  5. output_indices_ bilan joylashuvni ko'ring.

  6. Har guruhga o'z Pipeline i.

  7. Siyraklikni ongli boshqaring.

  8. pandas 3 da "str" tanlagichi.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # ColumnTransformer nima qiladi?
2.  # ustun tanlashning uch usuli?
3.  # eng xavfsizi qaysi?
4.  # bitta ustun qanday beriladi?
5.  # remainder sukut qiymati?
6.  # chiqish tartibi nimaga bog'liq?
7.  # nomlar qanday hosil bo'ladi?
8.  # output_indices_ nima?
9.  # sparse_threshold nima qiladi?
10. # pandas 3 da matn tanlagichi?
11. # named_transformers_ nima?
12. # bir ustun ikki transformerda bo'lsa?
Javoblar
  1. Ustun guruhlariga turli transformer
  2. Nomlar, indekslar, tanlagich
  3. Nomlar
  4. Ro'yxatda: ["yosh"]
  5. "drop"
  6. Transformerlar ro'yxatiga
  7. transformer__asl_nom
  8. Qaysi chiqish ustuni qaysi transformerdan
  9. Siyrak/zich chiqishni tanlaydi
  10. dtype_include="str"
  11. Fit qilingan transformerlar lug'ati
  12. Ikki marta chiqadi

Vazifa 2: Xatolarni tuzating

python
1.  ("son", StandardScaler(), [0, 1, 2])

2.  ("kat", OneHotEncoder(), "hudud")

3.  make_column_selector(dtype_include=object)

4.  OneHotEncoder(); ct.set_output(transform="pandas")

5.  df = df.fillna(df.median()); ct.fit(df)
Javoblar
python
1.  ("son", StandardScaler(), ["yosh", "daromad", "ball"])

2.  ("kat", OneHotEncoder(), ["hudud"])

3.  make_column_selector(dtype_include="str")

4.  OneHotEncoder(sparse_output=False)

5.  Pipeline([("imp", SimpleImputer()), ("sc", StandardScaler())])

Vazifa 3: Tanlash

Modellang:

  1. Ma'lumot
  2. Uch usul
  3. Yangi ustun
  4. Xulosa

Vazifa 4: remainder

Modellang:

  1. drop
  2. passthrough
  3. To'liq sanash
  4. output_indices_

Vazifa 5: Markaz

Modellang:

  1. Ma'lumot
  2. Guruh quvurlari
  3. Nomlar
  4. Ichki qadamlar

Vazifa 6: Siyraklik

Modellang:

  1. Ma'lumot
  2. sparse_threshold
  3. Siyraklik
  4. Model

Vazifa 7: O'ylash

ColumnTransformer ni make_column_selector(dtype_include=np.number) bilan qurdingiz. Ma'lumotda mijoz_id ustuni bor va u ham sonli. Model CV da 0.99 beradi. Nima bo'lgan va qanday tuzatasiz?

Javob

Qisqa javob: mijoz_id identifikator, lekin tanlagich uni oddiy sonli belgi deb qabul qilgan. Model mijozlarni yodlab olgan — bu leakage yoki (guruh tuzilmasi bo'lsa) guruh leakage i.

1. Nima uchun sodir bo'ldi

make_column_selector(dtype_include=np.number) dtype ga qaraydi, ma'noga emas. Identifikatorlar, kodlar, telefon raqamlari, pochta indekslari — hammasi sonli dtype ga ega bo'lishi mumkin.

2. Qanday aniqlash

python
# 1. tanlagich nimani tanlaganini ChOP ETING
print(make_column_selector(dtype_include=np.number)(df))

# 2. har belgining YAKKA CV bahosi
for ustun in sonli_ustunlar:
    b = cross_val_score(model, df[[ustun]], y, cv=cv, scoring="roc_auc")
    if b.mean() > 0.85:
        print("SHUBHALI:", ustun, round(b.mean(), 4))

# 3. noyob qiymatlar ulushi
print((df[sonli].nunique() / len(df)).sort_values(ascending=False))

nunique / n nisbati 1 ga yaqin bo'lgan sonli ustun — deyarli har doim identifikator.

3. Uchta yechim

a) Aniq ro'yxat (eng ishonchli)

python
SONLI = ["yosh", "daromad", "ball"]
KATEGORIYA = ["hudud", "tarif"]
ct = ColumnTransformer([("son", son_quvur, SONLI),
                        ("kat", kat_quvur, KATEGORIYA)])

b) Tanlagichdan keyin chiqarib tashlash

python
def sonli_tanla(df):
    ustunlar = make_column_selector(dtype_include=np.number)(df)
    return [u for u in ustunlar if u not in TASHLANADIGANLAR]

c) Identifikatorni X ga umuman kiritmaslik

python
guruh = df["mijoz_id"].to_numpy()      # GroupKFold uchun
X = df.drop(columns=["mijoz_id"])      # modelga kirmaydi

4. Qo'shimcha: guruh tuzilmasi

Agar bir mijozning bir necha qatori bo'lsa, mijoz_id ni olib tashlash yetarli emas — GroupKFold(groups=mijoz_id) ham kerak (18.2-dars), aks holda boshqa belgilar orqali ham leakage qoladi.

5. Umumiy qoida

Dinamik tanlagichlar qulay, lekin ular ma'noni bilmaydi. Ishlab chiqarish quvurida aniq ro'yxat afzal: u hujjat vazifasini ham bajaradi va yangi ustun jim qo'shilib qolmaydi.

6. Xulosa

  1. Tanlagich natijasini chop eting
  2. Yakka CV bahosi bilan shubhalilarni toping
  3. Ishlab chiqarishda aniq ro'yxat
  4. Identifikatorni X dan chiqarib, groups ga bering

Nimani mustahkamlaydi: 2.2, 2.4-bo'limlar.


Xulosa

Bu darsda ColumnTransformer ni o'rgandik.

Eng muhim uch fikr:

  1. Ustunlarni nom bilan tanlang. Indekslar ([0, 1, 2]) ma'lumot manbaiga yangi ustun qo'shilganda jim buziladi va xato oylar davomida sezilmasligi mumkin. Dinamik tanlagichlar (make_column_selector) qulay, lekin ular dtype ga qaraydi, ma'noga emas — shuning uchun identifikatorlar ham sonli belgi sifatida tushib qolishi mumkin.

  2. remainder sukut bo'yicha "drop". Ro'yxatga kirmagan har qanday ustun jim yo'qoladi. Shuning uchun fit dan keyin get_feature_names_out() va output_indices_ ni chop eting: bu kutilgan ustunlar borligini va ular qayerga tushganini bir qarashda ko'rsatadi.

  3. Har ustun guruhiga o'z Pipeline i. Imputatsiya, kodlash va masshtablash ColumnTransformer ichidagi Pipeline da bo'lsa, ular ham leakage dan himoyalangan bo'ladi. Siyraklikni esa ongli boshqaring: ko'p darajali kategoriyada sparse_output=False xotirani portlatishi mumkin.

Keyingi darsda o'z transformeringizni yozamiz: BaseEstimator va TransformerMixin, get_feature_names_out, check_estimator bilan tekshirish va keng tarqalgan xatolar.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
19.3-dars: ColumnTransformer — IlmHamroh