IlmHamroh
Data Science va sun'iy intellekt/Feature engineering2/10-dars21 daqiqa
Mundarija (22)

17.2-dars: Belgi yaratish

17-QISM — FEATURE ENGINEERING · 2-dars


1. Kirish va motivatsiya

Oldingi darsda belgining nima ekanini ko'rdik. Endi savol amaliy: xom ustunlardan qanday yangi belgi yaratiladi?

Javob bir nechta standart naqshga to'g'ri keladi: nisbat va farq, agregatsiya, bining (diskretlashtirish), polinomial va o'zaro ta'sir, matematik transformatsiya. Ularning har biri modelga o'z ko'rinishidagi ma'lumotni beradi.

Muhimi — bu naqshlarni ko'r-ko'rona qo'llamaslik. PolynomialFeatures(degree=3) 20 belgidan 1770 ta yaratadi, ularning aksariyati shovqin. Belgi yaratish maqsadli bo'lishi kerak: har yangi belgi ortida gipoteza turishi lozim.

Bu darsda: nisbat va farqlar, guruh bo'yicha agregatsiya (va undagi leakage), bining turlari, polinomial belgilar, matematik transformatsiyalar va yaratilgan belgilarni baholash.

Real vaziyat. Ko'chmas mulk narxini bashorat qilishda "maydon" va "xonalar soni" bor edi. Qo'shilgan bitta belgi — "bir xonaga to'g'ri keladigan maydon" — modelning MAE sini 8.4% ga yaxshiladi, chunki u kvartira turini (studiya, oilaviy, lyuks) bilvosita ifodalardi.

Bu darsda belgi yaratishni o'rganamiz.

Bu darsda:

  • Nisbat va farqlar
  • Guruh bo'yicha agregatsiya
  • Bining (diskretlashtirish)
  • Polinomial va o'zaro ta'sir
  • Matematik transformatsiyalar
  • Yaratilgan belgini baholash
  • Tuzoqlar
  • Amaliy: ko'chmas mulk

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


2. Nazariya — chuqur tushuntirish

2.1. Nisbat va farqlar

text
NISBAT: a / b       - eng samarali va eng arzon naqsh
  narx / maydon         -> kvadrat metr narxi
  qarz / daromad        -> yuk koeffitsiyenti
  kechikish / to'lovlar -> intizom
  joriy / o'rtacha      -> odatdan chetlanish

FARQ: a - b
  joriy_narx - oldingi_narx  -> o'zgarish
  sana2 - sana1              -> davomiylik

FOIZ O'ZGARISH: (a - b) / b

EHTIYOT: bo'luvchi nol bo'lishi mumkin
  a / (b + 1)  yoki  np.where(b > 0, a / b, 0)

Nisbatlar daraxtlar uchun ayniqsa qimmatli: daraxt a/b > 0.4 shartini to'g'ridan-to'g'ri ifodalay olmaydi (o'qlarga parallel chegaralar — 15.1), shuning uchun nisbatni tayyor belgi sifatida berish katta foyda beradi.

2.2. Guruh bo'yicha agregatsiya

python
# mijozning o'z odati
df["mijoz_ortacha"] = df.groupby("mijoz")["summa"].transform("mean")
df["odatdan_farq"] = df["summa"] / df["mijoz_ortacha"]

# hududning darajasi
df["hudud_medianasi"] = df.groupby("hudud")["narx"].transform("median")

# boshqa agregatlar: count, std, min, max, nunique, rank
df["mijoz_xaridlari"] = df.groupby("mijoz")["summa"].transform("count")
text
LEAKAGE XAVFI: agar agregatsiya MAQSAD bo'yicha bo'lsa (target encoding)
  -> qatorning O'ZI o'rtachaga hissa qo'shadi
  -> CV da optimistik natija
  YECHIM: out-of-fold agregatsiya yoki TargetEncoder 17.3-bob

Maqsadga bog'liq BO'LMAGAN agregatsiya (summa, soni) xavfsizroq,
lekin baribir Pipeline ichida bo'lgani yaxshi

"Odatdan farq" belgisi — agregatsiyaning eng foydali qo'llanilishi: u har kuzatuvni o'z kontekstiga nisbatan o'lchaydi va kontekstual anomaliyalarni ko'rinadigan qiladi 16.10-bob.

2.3. Bining (diskretlashtirish)

python
from sklearn.preprocessing import KBinsDiscretizer

pd.cut(df["yosh"], bins=[0, 25, 35, 50, 65, 100])          # qo'lda chegara
pd.qcut(df["daromad"], q=5, labels=False)                  # kvantil
KBinsDiscretizer(n_bins=5, encode="ordinal", strategy="quantile")
KBinsDiscretizer(n_bins=5, encode="onehot-dense", strategy="kmeans")
text
NEGA KERAK:
  + chiziqli modelga NOCHIZIQLILIK beradi
  + chetlanishlarni yumshatadi
  + domen chegaralarini ifodalaydi (yosh guruhlari)

NEGA EHTIYOT:
  - ma'lumot yo'qoladi (uzluksiz -> diskret)
  - DARAXTLARGA deyarli keraksiz (ular o'zi bo'ladi)
  - chegara tanlovi subyektiv

Bining daraxtlarga keraksiz: daraxt allaqachon optimal chegaralarni o'zi topadi, qo'lda bining esa uning imkoniyatini cheklaydi. Bu — chiziqli modellar uchun vosita.

2.4. Polinomial va o'zaro ta'sir

python
from sklearn.preprocessing import PolynomialFeatures

PolynomialFeatures(degree=2)                      # x1, x2, x1^2, x1x2, x2^2
PolynomialFeatures(degree=2, interaction_only=True)   # faqat x1x2
PolynomialFeatures(degree=2, include_bias=False)
text
BELGILAR SONI KESKIN OSHADI:
  p = 10, degree = 2  ->  65 belgi
  p = 20, degree = 2  ->  230 belgi
  p = 20, degree = 3  ->  1770 belgi

QOIDA:
  - faqat MUHIM belgilar to'plamida qo'llang (5-10 ta)
  - interaction_only=True ko'pincha yetarli
  - keyin regulyarizatsiya (Ridge/Lasso - 13.7, 13.8) yoki tanlash 17.7-bob
  - daraxtlarga odatda KERAKSIZ

Polinomial belgilarni butun ma'lumotga qo'llamang: avval eng muhim 5-10 belgini tanlang 15.11-bob, keyin ular orasida o'zaro ta'sirlarni yarating.

2.5. Matematik transformatsiyalar

text
log1p(x)    - qiyshiq taqsimotni simmetrik qiladi; x >= 0
sqrt(x)     - yumshoqroq variant
1/x         - teskari bog'liqlik (ehtiyot: x = 0)
x^2         - kuchayuvchi ta'sir
Box-Cox / Yeo-Johnson (PowerTransformer) - optimal daraja avtomatik
QuantileTransformer - taqsimotni normal yoki tekis qiladi

DAVRIY belgilar (soat, oy, burchak):
  sin(2*pi*x/T), cos(2*pi*x/T)
  -> 23:00 va 01:00 yaqin bo'ladi (aks holda uzoq)

Davriy kodlash (sin/cos) — soat va oy uchun majburiy: xom soat belgisida 23 va 0 maksimal uzoq, aslida esa ular qo'shni. Bu tuzatilmasa model vaqt naqshini to'g'ri o'rgana olmaydi.

2.6. Yaratilgan belgini baholash

text
1. GIPOTEZA: belgi nima uchun ishlashi kerak?
2. BAZAVIY natija bilan CV da taqqoslash
3. Yaxshilanish CV STD dan katta ekanini tekshirish
4. Permutation importance 15.11-bob bilan tasdiqlash
5. Leakage tekshiruvi 17.1-bob
6. Ishlab chiqarishda hisoblanishini tasdiqlash

BELGILAR GURUHI bo'lib qo'shiladi:
  "nisbatlar guruhi", "agregatsiya guruhi", "vaqt guruhi"
  -> qaysi guruh ishlayotganini ko'rish oson

Belgilarni guruh bo'lib qo'shish — samarali usul: 15 ta belgini bitta-bitta sinash 15 ta CV talab qiladi, guruh bo'lib esa 3 ta CV yetadi va natija tushunarliroq bo'ladi.

2.7. Tuzoqlar

Asosiy tuzoqlar: nolga bo'lishni tekshirmaslik; agregatsiyani butun ma'lumotda hisoblab, keyin CV qilish; daraxtlarga bining va polinomial qo'shish; PolynomialFeatures ni butun ma'lumotga qo'llash; davriy belgilarni xom holda qoldirish; gipotezasiz belgi yaratish; yaratilgan belgini CV siz qabul qilish; ishlab chiqarishda hisoblash mumkinligini tekshirmaslik.

2.8. Maqsadli yaratish

Nisbat va farqlar — eng arzon va eng samarali naqsh (ayniqsa daraxtlar uchun). Guruh bo'yicha agregatsiya har kuzatuvni o'z kontekstiga nisbatan o'lchaydi, lekin maqsadga bog'liq bo'lsa leakage beradi. Bining va polinomial belgilar chiziqli modellar uchun, daraxtlarga odatda keraksiz. Davriy belgilarni sin/cos bilan kodlang. Har belgi ortida gipoteza turishi va u CV std bilan tasdiqlanishi kerak. Keyingi dars — kategoriyali belgilarni kodlash.


3. Tez ma'lumotnoma

python
import numpy as np
import pandas as pd
from sklearn.preprocessing import KBinsDiscretizer, PolynomialFeatures, PowerTransformer

df["nisbat"] = df["a"] / df["b"].replace(0, np.nan)        # nolga ehtiyot
df["odatdan_farq"] = df["summa"] / df.groupby("mijoz")["summa"].transform("mean")
df["yosh_guruh"] = pd.qcut(df["yosh"], 5, labels=False)

# davriy
df["soat_sin"] = np.sin(2 * np.pi * df["soat"] / 24)
df["soat_cos"] = np.cos(2 * np.pi * df["soat"] / 24)

PolynomialFeatures(2, interaction_only=True, include_bias=False)
PowerTransformer(method="yeo-johnson")     # manfiy qiymatlar ham
QOIDA: gipoteza bilan yarat · guruh bo'lib qo'sh · CV std bilan tekshir ·
       daraxtga bining qo'shma

Belgi yaratish xulosasi

Nisbat/farq - eng samarali; daraxtlar uchun ayniqsa qimmatli
Agregatsiya - kontekst beradi; maqsad bo'yicha bo'lsa leakage
Bining va polinomial - chiziqli modellar uchun
Davriy belgilar: sin/cos bilan kodlang

4. Batafsil misollar

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

Misol 1 — Nisbatlar va farqlar

python
"""Eng samarali naqsh (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def yarat(seed: int = 9, n: int = 4000) -> pd.DataFrame:
    """Ko'chmas mulk: narx kvadrat metr va tur bo'yicha belgilanadi."""
    rng = np.random.default_rng(seed)
    xonalar = rng.integers(1, 6, n)
    maydon = xonalar * rng.uniform(18, 32, n) + rng.normal(0, 4, n)
    qavat = rng.integers(1, 17, n)
    jami_qavat = qavat + rng.integers(0, 9, n)
    yosh = rng.integers(0, 45, n)
    # HAQIQIY narx: kvadrat metr narxi * maydon
    kv_narx = (900.0 + 60.0 * (maydon / xonalar - 22)       # kengroq xona -> qimmat
               - 6.0 * yosh + 40.0 * (qavat / jami_qavat > 0.2)
               - 60.0 * (qavat == 1) - 40.0 * (qavat == jami_qavat))
    narx = np.clip(kv_narx, 300, None) * maydon * rng.lognormal(0, 0.08, n)
    return pd.DataFrame({"xonalar": xonalar, "maydon": maydon, "qavat": qavat,
                         "jami_qavat": jami_qavat, "yosh": yosh,
                         "narx": narx})


def main() -> None:
    df = yarat()
    y = df["narx"].to_numpy()
    cv = KFold(5, shuffle=True, random_state=0)
    xom = ["xonalar", "maydon", "qavat", "jami_qavat", "yosh"]

    modellar = {
        "Ridge": lambda: Pipeline([("sc", StandardScaler()),
                                   ("m", Ridge(alpha=1.0))]),
        "HistGB": lambda: HistGradientBoostingRegressor(learning_rate=0.1,
                                                        max_iter=200,
                                                        random_state=0),
    }

    print("=== 1. Xom belgilar ===")
    asos = {}
    print(f"  {'model':<9} {'CV R^2':>9} {'std':>8}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), df[xom], np.log(y), cv=cv,
                            scoring="r2")
        asos[nom] = b.mean()
        print(f"  {nom:<9} {b.mean():>9.4f} {b.std():>8.4f}")

    print("\n=== 2. Nisbat belgilari qo'shilgandan keyin ===")
    df["xona_maydoni"] = df["maydon"] / df["xonalar"]
    df["qavat_nisbati"] = df["qavat"] / df["jami_qavat"]
    nisbatlar = xom + ["xona_maydoni", "qavat_nisbati"]
    print(f"  {'model':<9} {'CV R^2':>9} {'std':>8} {'o_zgarish':>12}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), df[nisbatlar], np.log(y), cv=cv,
                            scoring="r2")
        print(f"  {nom:<9} {b.mean():>9.4f} {b.std():>8.4f} "
              f"{b.mean() - asos[nom]:>+12.4f}")

    print("\n=== 3. Chegara belgilari (domen bilimi) ===")
    df["birinchi_qavat"] = (df["qavat"] == 1).astype(int)
    df["oxirgi_qavat"] = (df["qavat"] == df["jami_qavat"]).astype(int)
    toliq = nisbatlar + ["birinchi_qavat", "oxirgi_qavat"]
    print(f"  {'model':<9} {'CV R^2':>9} {'std':>8} {'xomdan farq':>14}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), df[toliq], np.log(y), cv=cv,
                            scoring="r2")
        print(f"  {nom:<9} {b.mean():>9.4f} {b.std():>8.4f} "
              f"{b.mean() - asos[nom]:>+14.4f}")

    print("\n=== 4. Har guruhning hissasi ===")
    guruhlar = {
        "xom": xom,
        "xom + nisbatlar": nisbatlar,
        "xom + chegaralar": xom + ["birinchi_qavat", "oxirgi_qavat"],
        "hammasi": toliq,
    }
    print(f"  {'guruh':<20} {'Ridge':>9} {'HistGB':>9}")
    for nom, ustunlar in guruhlar.items():
        qator = []
        for _, yaratuvchi in modellar.items():
            b = cross_val_score(yaratuvchi(), df[ustunlar], np.log(y), cv=cv,
                                scoring="r2").mean()
            qator.append(b)
        print(f"  {nom:<20} {qator[0]:>9.4f} {qator[1]:>9.4f}")
    print("  ⭐ Nisbatlar - eng arzon va eng samarali naqsh")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Xom belgilar ===
  model        CV R^2      std
  Ridge        0.8580   0.0080
  HistGB       0.9833   0.0009

=== 2. Nisbat belgilari qo'shilgandan keyin ===
  model        CV R^2      std    o_zgarish
  Ridge        0.9455   0.0015      +0.0875
  HistGB       0.9846   0.0007      +0.0013

=== 3. Chegara belgilari (domen bilimi) ===
  model        CV R^2      std    xomdan farq
  Ridge        0.9461   0.0015        +0.0881
  HistGB       0.9846   0.0007        +0.0013

=== 4. Har guruhning hissasi ===
  guruh                    Ridge    HistGB
  xom                     0.8580    0.9833
  xom + nisbatlar         0.9455    0.9846
  xom + chegaralar        0.8584    0.9836
  hammasi                 0.9461    0.9846
  ⭐ Nisbatlar - eng arzon va eng samarali naqsh

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Agregatsiya va leakage

python
"""Kontekst beruvchi belgilar va ulardagi xavf (real pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split


def yarat(seed: int = 13, n: int = 8000) -> pd.DataFrame:
    """Tranzaksiyalar: firibgarlik mijoz odatiga nisbatan aniqlanadi."""
    rng = np.random.default_rng(seed)
    mijoz = rng.integers(0, 500, n)
    odat = rng.lognormal(11.8, 0.7, 500)
    summa = odat[mijoz] * rng.lognormal(0, 0.3, n)
    soat = rng.integers(0, 24, n)
    firibgar = rng.random(n) < 0.02
    summa[firibgar] = odat[mijoz[firibgar]] * rng.uniform(5, 12,
                                                          firibgar.sum())
    soat[firibgar] = rng.integers(1, 5, firibgar.sum())
    return pd.DataFrame({"mijoz": mijoz, "summa": summa, "soat": soat,
                         "firibgar": firibgar.astype(int)})


def main() -> None:
    df = yarat()
    y = df["firibgar"].to_numpy()
    cv = StratifiedKFold(5, shuffle=True, random_state=0)

    def model():
        return HistGradientBoostingClassifier(learning_rate=0.1, max_iter=200,
                                              random_state=0)

    print("=== 1. Xom belgilar ===")
    xom = ["summa", "soat"]
    b0 = cross_val_score(model(), df[xom], y, cv=cv, scoring="average_precision")
    print(f"  CV PR AUC: {b0.mean():.4f} (+-{b0.std():.4f})")
    print(f"  bazaviy (musbat ulush): {y.mean():.4f}")

    print("\n=== 2. Agregatsiya belgilari (maqsadga bog'liq EMAS) ===")
    df["mijoz_ortacha"] = df.groupby("mijoz")["summa"].transform("median")
    df["odatdan_farq"] = df["summa"] / df["mijoz_ortacha"]
    df["mijoz_soni"] = df.groupby("mijoz")["summa"].transform("count")
    df["mijoz_soat"] = df.groupby("mijoz")["soat"].transform("median")
    df["soat_farqi"] = (df["soat"] - df["mijoz_soat"]).abs()
    agregat = xom + ["odatdan_farq", "soat_farqi", "mijoz_soni"]
    b1 = cross_val_score(model(), df[agregat], y, cv=cv,
                         scoring="average_precision")
    print(f"  CV PR AUC: {b1.mean():.4f} (+-{b1.std():.4f})")
    print(f"  yaxshilanish: {b1.mean() - b0.mean():+.4f} "
          f"(std {b0.std():.4f})")

    print("\n=== 3. Maqsad bo'yicha agregatsiya (LEAKAGE) ===")
    # NOTO'G'RI: mijozning firibgarlik ulushi butun ma'lumotda
    df["mijoz_firibgarlik"] = df.groupby("mijoz")["firibgar"].transform("mean")
    leak = agregat + ["mijoz_firibgarlik"]
    b2 = cross_val_score(model(), df[leak], y, cv=cv,
                         scoring="average_precision")
    print(f"  CV PR AUC: {b2.mean():.4f} (+-{b2.std():.4f})")
    print(f"  'yaxshilanish': {b2.mean() - b1.mean():+.4f}")
    print("  SHUBHA: qatorning O'ZI o'rtachaga hissa qo'shgan")

    print("\n=== 4. Out-of-fold agregatsiya (to'g'ri usul) ===")
    from sklearn.model_selection import StratifiedKFold as SKF
    oof = np.zeros(len(df))
    for tr, te in SKF(5, shuffle=True, random_state=0).split(df, y):
        ulush = df.iloc[tr].groupby("mijoz")["firibgar"].mean()
        umumiy = y[tr].mean()
        oof[te] = df.iloc[te]["mijoz"].map(ulush).fillna(umumiy).to_numpy()
    df["mijoz_firibgarlik_oof"] = oof
    toza = agregat + ["mijoz_firibgarlik_oof"]
    b3 = cross_val_score(model(), df[toza], y, cv=cv,
                         scoring="average_precision")
    print(f"  CV PR AUC: {b3.mean():.4f} (+-{b3.std():.4f})")
    print(f"  {'variant':<28} {'PR AUC':>9}")
    for nom, v in [("xom", b0.mean()), ("agregatsiya", b1.mean()),
                   ("maqsad agregatsiyasi (leak)", b2.mean()),
                   ("out-of-fold agregatsiya", b3.mean())]:
        print(f"  {nom:<28} {v:>9.4f}")
    print("  ⭐ Maqsad bo'yicha agregatsiya out-of-fold bo'lishi kerak")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Xom belgilar ===
  CV PR AUC: 0.7727 (+-0.0544)
  bazaviy (musbat ulush): 0.0196

=== 2. Agregatsiya belgilari (maqsadga bog'liq EMAS) ===
  CV PR AUC: 1.0000 (+-0.0000)
  yaxshilanish: +0.2273 (std 0.0544)

=== 3. Maqsad bo'yicha agregatsiya (LEAKAGE) ===
  CV PR AUC: 1.0000 (+-0.0000)
  'yaxshilanish': +0.0000
  SHUBHA: qatorning O'ZI o'rtachaga hissa qo'shgan

=== 4. Out-of-fold agregatsiya (to'g'ri usul) ===
  CV PR AUC: 1.0000 (+-0.0000)
  variant                         PR AUC
  xom                             0.7727
  agregatsiya                     1.0000
  maqsad agregatsiyasi (leak)     1.0000
  out-of-fold agregatsiya         1.0000
  ⭐ Maqsad bo'yicha agregatsiya out-of-fold bo'lishi kerak

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — Bining va polinomial belgilar

python
"""Chiziqli modelga nochiziqlilik berish (real numpy/sklearn)."""

import numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import (KBinsDiscretizer, PolynomialFeatures,
                                   StandardScaler)
from sklearn.compose import ColumnTransformer


def yarat(seed: int = 4, n: int = 3000):
    """Maqsad: nochiziqli va o'zaro ta'sirli."""
    rng = np.random.default_rng(seed)
    yosh = rng.uniform(18, 70, n)
    daromad = rng.lognormal(13.5, 0.5, n)
    tajriba = rng.uniform(0, 40, n)
    # nochiziqli: o'rta yoshda maksimal
    kuch = (-0.002 * (yosh - 42) ** 2 + 0.4 * np.log(daromad)
            + 0.03 * tajriba * (yosh < 45) + rng.normal(0, 0.3, n))
    return np.column_stack([yosh, daromad, tajriba]), kuch


def main() -> None:
    X, y = yarat()
    cv = KFold(5, shuffle=True, random_state=0)

    print("=== 1. Bazaviy ===")
    modellar = {
        "Ridge": lambda: Pipeline([("sc", StandardScaler()),
                                   ("m", Ridge(alpha=1.0))]),
        "HistGB": lambda: HistGradientBoostingRegressor(learning_rate=0.1,
                                                        max_iter=200,
                                                        random_state=0),
    }
    asos = {}
    print(f"  {'model':<9} {'CV R^2':>9} {'std':>8}")
    for nom, yaratuvchi in modellar.items():
        b = cross_val_score(yaratuvchi(), X, y, cv=cv, scoring="r2")
        asos[nom] = b.mean()
        print(f"  {nom:<9} {b.mean():>9.4f} {b.std():>8.4f}")

    print("\n=== 2. Bining (yosh bo'yicha) ===")
    print(f"  {'savatlar':>9} {'strategiya':<12} {'Ridge R^2':>11} "
          f"{'HistGB R^2':>12}")
    for n_bins in [4, 8, 16]:
        for strat in ["quantile", "kmeans"]:
            tayyor = ColumnTransformer([
                ("bin", KBinsDiscretizer(n_bins=n_bins, encode="onehot-dense",
                                         strategy=strat,
                                         quantile_method="averaged_inverted_cdf"),
                 [0]),
                ("qolgan", StandardScaler(), [1, 2])])
            r = cross_val_score(Pipeline([("t", tayyor),
                                          ("m", Ridge(alpha=1.0))]),
                                X, y, cv=cv, scoring="r2").mean()
            g = cross_val_score(Pipeline([("t", tayyor),
                                          ("m", HistGradientBoostingRegressor(
                                              learning_rate=0.1, max_iter=200,
                                              random_state=0))]),
                                X, y, cv=cv, scoring="r2").mean()
            print(f"  {n_bins:>9} {strat:<12} {r:>11.4f} {g:>12.4f}")
    print(f"  (bazaviy: Ridge {asos['Ridge']:.4f}, "
          f"HistGB {asos['HistGB']:.4f})")

    print("\n=== 3. Polinomial belgilar ===")
    print(f"  {'daraja':>7} {'faqat o_zaro':>14} {'belgilar':>10} "
          f"{'Ridge R^2':>11}")
    for daraja in [1, 2, 3]:
        for faqat in [False, True]:
            if daraja == 1 and faqat:
                continue
            pf = PolynomialFeatures(daraja, interaction_only=faqat,
                                    include_bias=False)
            nechta = pf.fit_transform(X[:5]).shape[1]
            r = cross_val_score(Pipeline([("p", pf), ("sc", StandardScaler()),
                                          ("m", Ridge(alpha=1.0))]),
                                X, y, cv=cv, scoring="r2").mean()
            print(f"  {daraja:>7} {str(faqat):>14} {nechta:>10} {r:>11.4f}")

    print("\n=== 4. Belgilar soni portlashi ===")
    print(f"  {'p':>4} {'daraja 2':>10} {'daraja 3':>10} "
          f"{'daraja 2 (faqat o_zaro)':>26}")
    for p in [5, 10, 20, 50]:
        Xd = np.zeros((3, p))
        n2 = PolynomialFeatures(2, include_bias=False).fit_transform(Xd).shape[1]
        n3 = PolynomialFeatures(3, include_bias=False).fit_transform(Xd).shape[1]
        n2i = PolynomialFeatures(2, interaction_only=True,
                                 include_bias=False).fit_transform(Xd).shape[1]
        print(f"  {p:>4} {n2:>10} {n3:>10} {n2i:>26}")
    print("  ⭐ Bining va polinomial - chiziqli modellar uchun")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Bazaviy ===
  model        CV R^2      std
  Ridge        0.3976   0.0181
  HistGB       0.7856   0.0169

=== 2. Bining (yosh bo'yicha) ===
   savatlar strategiya     Ridge R^2   HistGB R^2
          4 quantile          0.6624       0.6703
          4 kmeans            0.6501       0.6528
          8 quantile          0.7349       0.7488
          8 kmeans            0.7247       0.7371
         16 quantile          0.7566       0.7754
         16 kmeans            0.7526       0.7699
  (bazaviy: Ridge 0.3976, HistGB 0.7856)

=== 3. Polinomial belgilar ===
   daraja   faqat o_zaro   belgilar   Ridge R^2
        1          False          3      0.3976
        2          False          9      0.7743
        2           True          6      0.4371
        3          False         19      0.7739
        3           True          7      0.4367

=== 4. Belgilar soni portlashi ===
     p   daraja 2   daraja 3    daraja 2 (faqat o_zaro)
     5         20         55                         15
    10         65        285                         55
    20        230       1770                        210
    50       1325      23425                       1275
  ⭐ Bining va polinomial - chiziqli modellar uchun

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

Misol 4 — Transformatsiyalar va davriy belgilar

python
"""log, PowerTransformer va sin/cos kodlash (real numpy/pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import (PowerTransformer, QuantileTransformer,
                                   StandardScaler)


def yarat(seed: int = 21, n: int = 5000) -> pd.DataFrame:
    """Tungi soatlarda va katta summalarda risk yuqori."""
    rng = np.random.default_rng(seed)
    summa = rng.lognormal(11.5, 1.1, n)              # juda qiyshiq
    soat = rng.integers(0, 24, n)
    oy = rng.integers(1, 13, n)
    # risk: tunda (22-04) va katta summada
    tun = (soat >= 22) | (soat <= 4)
    qish = (oy == 12) | (oy <= 2)
    kuch = -2.2 + 1.3 * tun + 0.55 * (np.log(summa) - 11.5) + 0.5 * qish
    y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
    return pd.DataFrame({"summa": summa, "soat": soat, "oy": oy, "risk": y})


def main() -> None:
    df = yarat()
    y = df["risk"].to_numpy()
    cv = StratifiedKFold(5, shuffle=True, random_state=0)

    print("=== 1. Taqsimotlar ===")
    print(f"  summa qiyshiqligi: {df['summa'].skew():.2f}")
    print(f"  log1p dan keyin:   {np.log1p(df['summa']).skew():.2f}")
    pt = PowerTransformer(method="yeo-johnson")
    print(f"  Yeo-Johnson dan keyin: "
          f"{pd.Series(pt.fit_transform(df[['summa']])[:, 0]).skew():.2f}")

    print("\n=== 2. Summa transformatsiyasi ===")
    variantlar = {
        "xom": df[["summa"]].to_numpy(),
        "log1p": np.log1p(df[["summa"]].to_numpy()),
        "sqrt": np.sqrt(df[["summa"]].to_numpy()),
        "Yeo-Johnson": PowerTransformer().fit_transform(df[["summa"]]),
        "Quantile(normal)": QuantileTransformer(
            output_distribution="normal", n_quantiles=500,
            random_state=0).fit_transform(df[["summa"]]),
    }
    print(f"  {'transformatsiya':<20} {'LogReg AUC':>12} {'HistGB AUC':>12}")
    for nom, Xa in variantlar.items():
        lr = cross_val_score(Pipeline([("sc", StandardScaler()),
                                       ("m", LogisticRegression(max_iter=2000))]),
                             Xa, y, cv=cv, scoring="roc_auc").mean()
        gb = cross_val_score(HistGradientBoostingClassifier(
            learning_rate=0.1, max_iter=150, random_state=0),
            Xa, y, cv=cv, scoring="roc_auc").mean()
        print(f"  {nom:<20} {lr:>12.4f} {gb:>12.4f}")
    print("  (daraxtlar uchun monoton transformatsiyalar deyarli farqsiz)")

    print("\n=== 3. Davriy kodlash ===")
    df["soat_sin"] = np.sin(2 * np.pi * df["soat"] / 24)
    df["soat_cos"] = np.cos(2 * np.pi * df["soat"] / 24)
    df["oy_sin"] = np.sin(2 * np.pi * df["oy"] / 12)
    df["oy_cos"] = np.cos(2 * np.pi * df["oy"] / 12)
    print(f"  23:00 va 01:00 orasidagi masofa:")
    x23 = np.array([np.sin(2 * np.pi * 23 / 24), np.cos(2 * np.pi * 23 / 24)])
    x01 = np.array([np.sin(2 * np.pi * 1 / 24), np.cos(2 * np.pi * 1 / 24)])
    x12 = np.array([np.sin(2 * np.pi * 12 / 24), np.cos(2 * np.pi * 12 / 24)])
    print(f"    xom soatda:      |23 - 1| = 22")
    print(f"    sin/cos da:      {np.linalg.norm(x23 - x01):.4f}")
    print(f"    23:00 va 12:00:  {np.linalg.norm(x23 - x12):.4f}")

    print("\n=== 4. Davriy belgilarning natijaga ta'siri ===")
    to_plamlar = {
        "xom (summa, soat, oy)": ["summa", "soat", "oy"],
        "log + xom vaqt": ["summa", "soat", "oy"],
        "log + sin/cos": ["summa", "soat_sin", "soat_cos", "oy_sin", "oy_cos"],
        "log + tun bayrog'i": ["summa", "soat", "oy"],
    }
    df["log_summa"] = np.log1p(df["summa"])
    df["tun"] = ((df["soat"] >= 22) | (df["soat"] <= 4)).astype(int)
    print(f"  {'belgilar to_plami':<24} {'LogReg':>9} {'HistGB':>9}")
    for nom, ustunlar in to_plamlar.items():
        u = list(ustunlar)
        if nom.startswith("log"):
            u = ["log_summa" if c == "summa" else c for c in u]
        if "tun bayrog" in nom:
            u = u + ["tun"]
        lr = cross_val_score(Pipeline([("sc", StandardScaler()),
                                       ("m", LogisticRegression(max_iter=2000))]),
                             df[u], y, cv=cv, scoring="roc_auc").mean()
        gb = cross_val_score(HistGradientBoostingClassifier(
            learning_rate=0.1, max_iter=150, random_state=0),
            df[u], y, cv=cv, scoring="roc_auc").mean()
        print(f"  {nom:<24} {lr:>9.4f} {gb:>9.4f}")
    print("  ⭐ Davriy belgilarni sin/cos bilan kodlang")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Taqsimotlar ===
  summa qiyshiqligi: 6.14
  log1p dan keyin:   0.02
  Yeo-Johnson dan keyin: -0.00

=== 2. Summa transformatsiyasi ===
  transformatsiya        LogReg AUC   HistGB AUC
  xom                        0.6425       0.6092
  log1p                      0.6425       0.6092
  sqrt                       0.6425       0.6092
  Yeo-Johnson                0.6425       0.6092
  Quantile(normal)           0.6425       0.6092
  (daraxtlar uchun monoton transformatsiyalar deyarli farqsiz)

=== 3. Davriy kodlash ===
  23:00 va 01:00 orasidagi masofa:
    xom soatda:      |23 - 1| = 22
    sin/cos da:      0.5176
    23:00 va 12:00:  1.9829

=== 4. Davriy belgilarning natijaga ta'siri ===
  belgilar to_plami           LogReg    HistGB
  xom (summa, soat, oy)       0.6343    0.6779
  log + xom vaqt              0.6526    0.6779
  log + sin/cos               0.7014    0.6789
  log + tun bayrog'i          0.7224    0.6782
  ⭐ Davriy belgilarni sin/cos bilan kodlang

Nima ko'rsatdi: 2.5-bo'lim.


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

Noto'g'ri fikr To'g'risi
"Daraxt nisbatni o'zi topadi" Qiyin (o'qlarga parallel)
"Agregatsiya xavfsiz" Maqsad bo'yicha bo'lsa leakage
"Bining har doim foydali" Daraxtlarga keraksiz
"PolynomialFeatures universal" Belgilar portlashi
"Soat oddiy sonli belgi" Davriy — sin/cos
"Ko'proq belgi — yaxshiroq" Shovqin va overfitting
"log daraxtlarga ham yordam beradi" Monoton — yo'q
"Belgini bitta-bitta sinash kerak" Guruh bo'lib samaraliroq

6. Keng tarqalgan xatolar va yechimlari

1. Nolga bo'lish

python
df["nisbat"] = df["a"] / df["b"]             # b = 0 bo'lsa inf     # ⚠️
df["nisbat"] = df["a"] / df["b"].replace(0, np.nan)                 # ✅

2. Maqsad bo'yicha agregatsiya

python
df["guruh_ulushi"] = df.groupby("g")["y"].transform("mean")         # ⚠️
# out-of-fold yoki TargetEncoder 17.3-bob                             # ✅

3. Daraxtga bining

python
KBinsDiscretizer(10).fit_transform(X)   # RandomForest uchun        # ⚠️
# daraxt chegaralarni o'zi topadi                                   # ✅

4. Butun ma'lumotga polinomial

python
PolynomialFeatures(2).fit_transform(X_50belgi)   # 1325 belgi       # ⚠️
# eng muhim 8 belgida interaction_only=True                         # ✅

5. Davriy belgini xom qoldirish

python
X["soat"] = df["soat"]      # 23 va 0 uzoq                          # ⚠️
X["soat_sin"], X["soat_cos"] = sin(2pi*h/24), cos(2pi*h/24)         # ✅

6. Gipotezasiz belgi yaratish

python
# barcha juftliklar uchun nisbat yaratish                           # ⚠️
# har belgi ortida gipoteza bo'lsin                                 # ✅

7. CV siz qabul qilish

python
df["yangi"] = ...   # "mantiqan foydali"                            # ⚠️
# CV da o'lchang va std bilan solishtiring                          # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 17.1-dars (o'tilgan): Belgi turlari
  • 17.3-dars: Kategoriyali kodlash
  • 17.5-dars: Sana belgilari
  • 13.5-dars (o'tilgan): Polinomial regressiya
  • 07-qism (o'tilgan): Guruhlash va agregatsiya

8. Eng yaxshi amaliyotlar

  1. Nisbatlardan boshlang.

  2. Gipoteza bilan yarating.

  3. Nolga bo'lishni tekshiring.

  4. Agregatsiyani out-of-fold qiling.

  5. Davriy belgilarni kodlang.

  6. Polinomialni cheklang.

  7. Guruh bo'lib qo'shing.

  8. CV std bilan tasdiqlang.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # eng samarali naqsh?
2.  # nega daraxtga nisbat kerak?
3.  # nolga bo'lishdan qanday saqlanish kerak?
4.  # agregatsiya nima beradi?
5.  # qaysi agregatsiya leakage beradi?
6.  # yechimi?
7.  # bining kimga foydali?
8.  # p=20, degree=2 da nechta belgi?
9.  # interaction_only nima qiladi?
10. # davriy belgi qanday kodlanadi?
11. # log daraxtlarga ta'sir qiladimi?
12. # belgilar qanday qo'shiladi?
Javoblar
  1. Nisbat (a/b)
  2. O'qlarga parallel chegaralar
  3. replace(0, nan) yoki b+1
  4. Kontekst
  5. Maqsad bo'yicha
  6. Out-of-fold
  7. Chiziqli modellarga
  8. 230
  9. Faqat o'zaro ta'sirlar
  10. sin/cos
  11. Yo'q (monoton)
  12. Guruh bo'lib

Vazifa 2: Xatolarni tuzating

python
1.  df["nisbat"] = df["a"] / df["b"]

2.  df["guruh_ulushi"] = df.groupby("g")["y"].transform("mean")

3.  KBinsDiscretizer(10).fit_transform(X)   # RandomForest

4.  PolynomialFeatures(2).fit_transform(X_50belgi)

5.  X["soat"] = df["soat"]   # 23 va 0
Javoblar
python
1.  df["nisbat"] = df["a"] / df["b"].replace(0, np.nan)

2.  # out-of-fold agregatsiya yoki TargetEncoder

3.  # daraxt chegaralarni o'zi topadi

4.  # eng muhim 8 belgida interaction_only=True

5.  X["soat_sin"], X["soat_cos"] = sin/cos kodlash

Vazifa 3: Nisbatlar

Modellang:

  1. Xom belgilar
  2. Nisbatlar
  3. Chegaralar
  4. Guruhlar

Vazifa 4: Agregatsiya

Modellang:

  1. Xom
  2. Agregatsiya
  3. Leakage
  4. Out-of-fold

Vazifa 5: Bining

Modellang:

  1. Bazaviy
  2. Bining
  3. Polinomial
  4. Portlash

Vazifa 6: Transformatsiya

Modellang:

  1. Taqsimotlar
  2. Summa
  3. Davriy kodlash
  4. Ta'sir

Vazifa 7: O'ylash

Avtomatik belgi generatsiyasi vositalari bor (featuretools, autofeat). Ular qo'lda muhandislikni almashtira oladimi?

Javob

Qisqa javob: ular kombinatorik qismni (nisbatlar, agregatsiyalar, o'zaro ta'sirlar) yaxshi bajaradi, lekin domen bilimini va leakage nazoratini almashtira olmaydi. Amalda ular qo'lda muhandislikka qo'shimcha, o'rin bosar emas.

1. Avtomatik vositalar nimani yaxshi qiladi

Vazifa Avtomatik vosita
Barcha juft nisbatlar Ha, tez
Guruh agregatsiyalari Ha (featuretools "deep feature synthesis")
Ko'p jadvalli bog'lanishlar Ha, kuchli tomoni
Belgi tanlash Qisman

2. Nima qo'lda qoladi

  1. Domen belgilari: "bayram kuni", "ish vaqti", "chegirma davri"
  2. Tashqi ma'lumot: ob-havo, valyuta, geografiya
  3. Leakage nazorati: vosita "qachon ma'lum bo'ladi?" savolini bilmaydi
  4. Talqin: MEAN(orders.SUM(items.price)) degan belgini tushuntirish qiyin
  5. Ishlab chiqarish: hisoblash mumkinmi va qancha turadi

3. Asosiy xavf: leakage

Avtomatik vosita barcha ustunlardan belgi yasaydi, shu jumladan:

  • Maqsaddan keyin to'ldiriladigan ustunlardan
  • Kelajakdagi hodisalarni o'z ichiga olgan agregatlardan

Natijada CV da ajoyib, ishlab chiqarishda yaroqsiz model chiqadi. Har avtomatik belgini ko'rib chiqish kerak — bu esa asosiy tejamkorlikni yo'qotadi.

4. Amaliy yondashuv

1. Domen belgilarini QO'LDA yarating (eng katta foyda)
2. Leakage xavfli ustunlarni ro'yxatdan CHIQARING
3. Avtomatik vositani qolgan ustunlarda ishga tushiring
4. Natijani feature selection bilan siqing (17.7)
5. Eng muhim avtomatik belgilarni ko'rib chiqing va nomlang

5. Xulosa

  1. Avtomatik vositalar kombinatorikani yaxshi bajaradi
  2. Domen bilimi va leakage nazorati qo'lda qoladi
  3. Ular qo'shimcha, o'rin bosar emas
  4. Natijani albatta ko'rib chiqing

Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.


Xulosa

Bu darsda belgi yaratishni o'rgandik.

Eng muhim uch fikr:

  1. Nisbat — eng samarali naqsh. narx/maydon, qarz/daromad, kechikish/to'lovlar — ular modelga bitta belgida kontekst beradi. Daraxtlar uchun bu ayniqsa qimmatli: o'qlarga parallel chegaralar tufayli daraxt a/b > 0.4 shartini to'g'ridan-to'g'ri ifodalay olmaydi. Nolga bo'lishni esa replace(0, np.nan) bilan hal qiling.

  2. Agregatsiya kontekst beradi, lekin leakage xavfi bor. df.groupby("mijoz")["summa"].transform("median") va undan hosil qilingan "odatdan farq" — kontekstual naqshlarni ko'rinadigan qiladi. Lekin agregatsiya maqsad bo'yicha bo'lsa (groupby("g")["y"].mean()), qatorning o'zi o'rtachaga hissa qo'shadi va CV optimistik chiqadi — out-of-fold hisoblang.

  3. Bining va polinomial — chiziqli modellar uchun. Daraxtlar chegaralarni o'zi topadi, shuning uchun qo'lda bining ularga keraksiz (va hatto zarar qiladi). PolynomialFeatures esa belgilar sonini portlatadi (20 belgi, daraja 2 → 230), shuning uchun uni faqat eng muhim 5-10 belgida interaction_only=True bilan qo'llang. Davriy belgilarni (soat, oy) esa sin/cos bilan kodlang — aks holda 23:00 va 01:00 model uchun maksimal uzoq bo'lib qoladi.

Keyingi darsda kategoriyali belgilarni kodlashni o'rganamiz: one-hot, ordinal, target encoding va yuqori kardinallik.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
17.2-dars: Belgi yaratish — IlmHamroh