Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Kategoriyalarni kodlash
- 2.2. Ko'p darajali kategoriyalar
- 2.3. Sana va vaqt belgilari
- 2.4. Siklik kodlash
- 2.5. Agregat belgilar
- 2.6. Tartib va amaliyot
- 2.7. Tuzoqlar
- 2.8. Belgilar — modeldan muhimroq
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Kategoriya: raqam va one-hot
- Misol 2 — Siklik kodlash
- Misol 3 — Ko'p darajali kategoriya
- Misol 4 — Sana belgilari va agregatlar
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
13.9-dars: Kategoriyalar va belgi muhandisligi
13-QISM — REGRESSIYA · 9-dars
1. Kirish va motivatsiya
Real ma'lumotda belgilar kamdan-kam "tayyor son" bo'ladi: hudud, tarif, kun, oy, mahsulot kodi — bularning hammasi kategoriya, va ularni modelga qanday kiritish natijaga modelni almashtirishdan ko'ra ko'proq ta'sir qiladi. Chiziqli modelda bu ayniqsa muhim: u o'zaro ta'sir va nochiziqlikni o'zi topmaydi 13.5-bob.
Bu darsda: one-hot kodlash va tayanch daraja, ko'p darajali kategoriyalar bilan nima qilish (min_frequency, target encoding), sana/vaqt belgilari va siklik kodlash, agregat belgilar, belgi muhandisligining tartibi va leakagedan qochish 12.9-bob.
Real vaziyat. Taksi xizmati safar narxini bashorat qilmoqchi. Birinchi model: masofa, davomiylik, hudud kodi (1..48 raqam sifatida) — MAE 3 200 so'm. Hudud one-hot qilindi: 2 400. Soat "siklik" (sin/cos) kodlandi va "ish kuni" belgisi qo'shildi: 1 950. Masofa × tirbandlik o'zaro ta'siri: 1 780. Model o'zgarmadi — faqat belgilar o'zgardi.
Bu darsda belgi muhandisligini o'rganamiz.
Bu darsda:
- Kategoriyalarni kodlash
- Ko'p darajali kategoriyalar
- Sana va vaqt belgilari
- Siklik kodlash
- Agregat belgilar
- Tartib va leakage
- Tuzoqlar
- Amaliy: taksi narxi
ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14).
2. Nazariya — chuqur tushuntirish
2.1. Kategoriyalarni kodlash
XATO: hudud → 1, 2, 3, 4 (modelga "4 > 1" va "3 - 2 = 1" deb ko'rinadi)
TO'G'RI:
one-hot (drop_first) — nominal kategoriya uchun standart 13.3-bob
ordinal — TARTIBLI kategoriya uchun (kam, o'rta, ko'p)
target/mean encoding — juda ko'p daraja bo'lsa (CV ichida! — 12.9)
hashing — juda ko'p daraja va oqim ma'lumot
OneHotEncoder(handle_unknown="ignore", drop="first", min_frequency=...)Kategoriyani raqam sifatida berish — eng keng tarqalgan xato: model unga sun'iy tartib va masofa yuklaydi. Chiziqli modelda bu koeffitsiyentni ma'nosiz qiladi. Tartibli kategoriyalarda (kam < o'rta < ko'p) ordinal kodlash to'g'ri, lekin qadamlar teng deb qabul qilinishini eslang.
2.2. Ko'p darajali kategoriyalar
Muammo: 3 000 mahsulot kodi → 3 000 ustun (siyrak, overfitting, sekin)
Yechimlar:
1. min_frequency — kam uchraydiganlarni "boshqa" ga birlashtirish
2. Guruhlash — domen bilimi bilan (mahsulot → kategoriya)
3. Target encoding — daraja o'rtachasi bilan almashtirish (silliqlash bilan, CV ichida)
4. Frequency encoding — uchrash chastotasi
5. Embedding — neyron tarmoqlarda (25-qism)Ko'p darajali kategoriya (high cardinality) — amaliyotdagi tipik muammo. Eng xavfsiz birinchi qadam — min_frequency bilan kam uchraydiganlarni birlashtirish; keyin domen bo'yicha guruhlash. Target encoding kuchli, lekin leakage manbai: u faqat pipeline/CV ichida va silliqlash bilan qilinadi 12.9-bob.
2.3. Sana va vaqt belgilari
Sanadan chiqariladigan belgilar:
yil, oy, kun, hafta kuni, yil kuni, hafta raqami
ish kuni / dam olish, bayram
oy boshi/oxiri, chorak
"boshlanishdan o'tgan kunlar" (trend uchun)
pandas: df["sana"].dt.dayofweek, .dt.month, .dt.is_month_end
Diqqat: vaqt qatorida bu belgilar LEAKAGE manbai bo'lishi mumkin (12.3)Sana — eng boy belgi manbai: bitta ustundan o'nlab foydali belgi chiqadi. Lekin tanlov domendan kelib chiqishi kerak: chakana savdoda hafta kuni va bayramlar, energiya iste'molida soat va harorat, moliyada chorak oxiri muhim.
2.4. Siklik kodlash
Muammo: soat 23 va soat 0 — qo'shni, lekin son sifatida eng uzoq
oy 12 va oy 1 ham shunday
Yechim:
sin_soat = sin(2pi · soat / 24)
cos_soat = cos(2pi · soat / 24)
→ doira bo'ylab joylashadi, 23 va 0 yaqin bo'ladi
Muqobil: soatni KATEGORIYA sifatida (24 ustun) yoki splayn (13.5) Siklik kodlash — vaqt belgilarida standart usul: ikki ustun (sin, cos) davriy tuzilmani saqlaydi. Lekin u silliq shaklni taxmin qiladi; agar naqsh keskin bo'lsa (masalan, ish vaqti 9:00 da keskin boshlansa), kategoriya yoki splayn yaxshiroq ishlaydi.
2.5. Agregat belgilar
Guruh bo'yicha statistika: mijozning o'rtacha cheki, hududning median narxi,
oxirgi 7 kundagi o'rtacha talab
Kuchli, lekin LEAKAGE xavfi yuqori:
· agregat maqsadni o'z ichiga olmasligi kerak 12.9-bob
· vaqt kesimidan OLDINGI ma'lumotdan hisoblanishi kerak 12.2-bob
· CV ichida qayta hisoblanishi kerak
Xavfsiz variant: agregatni O'QUV qismida hisoblab, test'ga xarita sifatida qo'llashAgregat belgilar ko'pincha eng katta foyda beradi (ayniqsa, tranzaksion ma'lumotda), lekin ular leakagening eng ko'p uchraydigan manbai. Oltin qoida: agregat faqat o'tmish ma'lumotidan va faqat o'quv qismidan hisoblanadi (12.2, 12.9).
2.6. Tartib va amaliyot
1. Domen savoli: qanday belgi mantiqan foydali?
2. Oddiy belgilardan boshlash (baza — 12.6)
3. Har qo'shimchani CV bilan tekshirish 12.3-bob
4. Hammasi PIPELINE ichida 12.9-bob
5. Belgi sonini nazorat qilish (n >> p — 13.2)
6. Hujjatlashtirish: har belgi manbasi va ma'nosi
Regressiyada tipik to'plam:
sonli (transformatsiya bilan) + one-hot kategoriya + sana + tanlangan o'zaro ta'sirlarBelgi muhandisligi — iterativ jarayon: bir nechta belgi qo'shib, CV bilan tekshirib, foydasizlarini olib tashlash. Har qadamda leakage savolini bering: "bu belgi bashorat paytida mavjudmi?" 12.2-bob. Va har doim pipeline ichida 12.9-bob — aks holda CV natijasi optimistik bo'ladi.
2.7. Tuzoqlar
Asosiy tuzoqlar: kategoriyani raqam sifatida berish; handle_unknown ni qo'ymaslik; dummy tuzog'i (barcha darajalar + kesma); ko'p darajali kategoriyani to'g'ridan-to'g'ri one-hot qilish; target encoding ni CV dan tashqarida (leakage — 12.9); sana belgilarini kelajakdan olish; agregatni butun ma'lumotdan hisoblash; siklik naqshni chiziqli qoldirish; belgi sonini nazoratsiz oshirish 13.2-bob.
2.8. Belgilar — modeldan muhimroq
Chiziqli modelda natijani ko'pincha belgilar belgilaydi: kategoriyalar one-hot (raqam sifatida emas), ko'p darajalilar min_frequency yoki guruhlash bilan, sana dan hafta kuni/oy/bayram belgilari, davriy qiymatlar uchun siklik (sin/cos) kodlash, tranzaksiyalardan agregatlar. Ikki qoida o'zgarmaydi: hammasi pipeline ichida va har belgi uchun "bashorat paytida mavjudmi?" savoli (12.2, 12.9). Keyingi dars — logistik regressiya.
3. Tez ma'lumotnoma
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer, OneHotEncoder, StandardScaler
OneHotEncoder(handle_unknown="ignore", drop="first", min_frequency=30)
# sana belgilari
d = df["sana"].dt
df["hafta_kuni"], df["oy"], df["ish_kuni"] = d.dayofweek, d.month, (d.dayofweek < 5)
# siklik
df["sin_soat"] = np.sin(2 * np.pi * df["soat"] / 24)
df["cos_soat"] = np.cos(2 * np.pi * df["soat"] / 24)
# agregat (faqat o'quvda hisoblanadi!)
xarita = tr.groupby("hudud")["narx"].median()
df["hudud_median"] = df["hudud"].map(xarita).fillna(tr["narx"].median())
QOIDA: kategoriya ≠ raqam · min_frequency · siklik kodlash · agregat o'tmishdan · pipelineBelgilar xulosasi
One-hot (drop_first, handle_unknown) · ordinal faqat tartibli uchun
Ko'p daraja: min_frequency → guruhlash → target encoding (CV ichida)
Sana: hafta kuni, oy, bayram, trend · Siklik: sin/cos
Agregat: o'tmishdan va o'quvdan · Hammasi pipeline ichida4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14). Misollar bitta taksi ma'lumotidan foydalanadi.
Misol 1 — Kategoriya: raqam va one-hot
"""Kategoriyani raqam sifatida berish nega xato (real pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def yarat(seed: int = 5, n: int = 6000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
hudud = rng.integers(0, 12, n) # 12 hudud
hudud_qosh = np.array([0, 5200, -1800, 900, 3100, -2400,
400, 6100, -900, 2200, 1500, -3000])
masofa = rng.gamma(3, 2.2, n).clip(0.5, 40)
tirband = rng.beta(2, 3, n)
soat = rng.integers(0, 24, n)
narx = (4000 + 2100 * masofa + 9000 * tirband + hudud_qosh[hudud]
+ rng.normal(0, 1500, n))
return pd.DataFrame({"hudud": hudud, "masofa": masofa, "tirband": tirband,
"soat": soat, "narx": narx})
def main() -> None:
df = yarat()
y = df["narx"]
cv = KFold(5, shuffle=True, random_state=0)
def mae(X, model) -> float:
return -cross_val_score(model, X, y, cv=cv,
scoring="neg_mean_absolute_error").mean()
print("=== 1. Hudud — raqam sifatida ===")
X1 = df[["masofa", "tirband", "hudud"]]
print(f" CV MAE = {mae(X1, Ridge(alpha=1.0)):.1f} so'm")
m = Ridge(alpha=1.0).fit(X1, y)
print(f" hudud koeffitsiyenti = {m.coef_[2]:.1f} "
f"(modelga 'hudud 11 — hudud 1 dan 10 barobar ko'p' deb ko'rinadi)")
print("\n=== 2. Hudud — one-hot ===")
tayyor = ColumnTransformer([
("son", StandardScaler(), ["masofa", "tirband"]),
("kat", OneHotEncoder(handle_unknown="ignore", drop="first"), ["hudud"]),
])
X2 = df[["masofa", "tirband", "hudud"]]
quvur = Pipeline([("t", tayyor), ("m", Ridge(alpha=1.0))])
print(f" CV MAE = {mae(X2, quvur):.1f} so'm")
print("\n=== 3. Hududsiz model (taqqoslash uchun) ===")
X0 = df[["masofa", "tirband"]]
print(f" CV MAE = {mae(X0, Ridge(alpha=1.0)):.1f} so'm")
print("\n=== 4. Hudud qo'shimchalari ===")
quvur.fit(X2, y)
nomlar = quvur.named_steps["t"].get_feature_names_out()
w = quvur.named_steps["m"].coef_
for nom, v in list(zip(nomlar, w))[2:7]:
print(f" {nom:<16}: {v:+8.1f}")
print(" (hudud 0 — tayanch; haqiqiy qo'shimchalar: 5200, -1800, 900, 3100, -2400)")
print(" ⭐ Kategoriya raqam emas")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Hudud — raqam sifatida ===
CV MAE = 2541.5 so'm
hudud koeffitsiyenti = -131.1 (modelga 'hudud 11 — hudud 1 dan 10 barobar ko'p' deb ko'rinadi)
=== 2. Hudud — one-hot ===
CV MAE = 1191.7 so'm
=== 3. Hududsiz model (taqqoslash uchun) ===
CV MAE = 2543.2 so'm
=== 4. Hudud qo'shimchalari ===
kat__hudud_1 : +5214.5
kat__hudud_2 : -1741.7
kat__hudud_3 : +1030.1
kat__hudud_4 : +3118.6
kat__hudud_5 : -2363.3
(hudud 0 — tayanch; haqiqiy qo'shimchalar: 5200, -1800, 900, 3100, -2400)
⭐ Kategoriya raqam emasNima ko'rsatdi: 2.1-bo'lim.
Misol 2 — Siklik kodlash
"""Soat va oy kabi davriy belgilar (real numpy/pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def yarat(seed: int = 9, n: int = 6000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
soat = rng.integers(0, 24, n)
masofa = rng.gamma(3, 2.2, n).clip(0.5, 40)
# tungi tarif: 23:00-05:00 oralig'ida qimmat (0 va 23 qo'shni!)
tungi = np.sin(2 * np.pi * (soat + 6) / 24)
narx = 4000 + 2100 * masofa + 2600 * tungi + rng.normal(0, 1200, n)
return pd.DataFrame({"soat": soat, "masofa": masofa, "narx": narx})
def main() -> None:
df = yarat()
y = df["narx"]
cv = KFold(5, shuffle=True, random_state=0)
def mae(X, model=None) -> float:
model = model or Ridge(alpha=1.0)
return -cross_val_score(model, X, y, cv=cv,
scoring="neg_mean_absolute_error").mean()
print("=== 1. Soat — oddiy son ===")
print(f" CV MAE = {mae(df[['masofa', 'soat']]):.1f} so'm")
print(f" corr(soat, narx) = {df['soat'].corr(df['narx']):.3f} "
f"(chiziqli bog'liqlik deyarli yo'q)")
print("\n=== 2. Siklik kodlash (sin/cos) ===")
X2 = df[["masofa"]].copy()
X2["sin"] = np.sin(2 * np.pi * df["soat"] / 24)
X2["cos"] = np.cos(2 * np.pi * df["soat"] / 24)
print(f" CV MAE = {mae(X2):.1f} so'm (2 ta qo'shimcha ustun)")
print("\n=== 3. Soat — kategoriya (24 ustun) ===")
tayyor = ColumnTransformer([
("son", StandardScaler(), ["masofa"]),
("kat", OneHotEncoder(handle_unknown="ignore", drop="first"), ["soat"]),
])
quvur = Pipeline([("t", tayyor), ("m", Ridge(alpha=1.0))])
print(f" CV MAE = {mae(df[['masofa', 'soat']], quvur):.1f} so'm "
f"(23 ta qo'shimcha ustun)")
print("\n=== 4. 23 va 0 soat yaqinmi ===")
for s in [0, 1, 22, 23]:
print(f" soat {s:>2}: sin = {np.sin(2 * np.pi * s / 24):+.3f}, "
f"cos = {np.cos(2 * np.pi * s / 24):+.3f}")
masofa_son = abs(23 - 0)
d_sin = np.hypot(np.sin(2 * np.pi * 23 / 24) - np.sin(0),
np.cos(2 * np.pi * 23 / 24) - np.cos(0))
print(f" son sifatida 23 va 0 orasidagi masofa: {masofa_son}")
print(f" siklik kodlashda: {d_sin:.3f} (1 va 0 orasidagiga teng)")
print(" ⭐ Davriy belgilar sin/cos bilan kodlanadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Soat — oddiy son ===
CV MAE = 1816.7 so'm
corr(soat, narx) = -0.022 (chiziqli bog'liqlik deyarli yo'q)
=== 2. Siklik kodlash (sin/cos) ===
CV MAE = 958.3 so'm (2 ta qo'shimcha ustun)
=== 3. Soat — kategoriya (24 ustun) ===
CV MAE = 964.5 so'm (23 ta qo'shimcha ustun)
=== 4. 23 va 0 soat yaqinmi ===
soat 0: sin = +0.000, cos = +1.000
soat 1: sin = +0.259, cos = +0.966
soat 22: sin = -0.500, cos = +0.866
soat 23: sin = -0.259, cos = +0.966
son sifatida 23 va 0 orasidagi masofa: 23
siklik kodlashda: 0.261 (1 va 0 orasidagiga teng)
⭐ Davriy belgilar sin/cos bilan kodlanadiNima ko'rsatdi: 2.4-bo'lim.
Misol 3 — Ko'p darajali kategoriya
"""min_frequency va target encoding (real pandas/sklearn)."""
import warnings
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
class TargetEncoder(BaseEstimator, TransformerMixin):
"""Kategoriyani maqsad o'rtachasi bilan (silliqlash bilan) almashtiradi."""
def __init__(self, silliq: float = 20.0):
self.silliq = silliq
def fit(self, X, y):
y = np.asarray(y, dtype=float)
k = np.asarray(X).ravel()
d = pd.DataFrame({"k": k, "y": y}).groupby("k")["y"]
n, o = d.count(), d.mean()
self.umumiy_ = float(y.mean())
self.xarita_ = ((n * o + self.silliq * self.umumiy_)
/ (n + self.silliq)).to_dict()
return self
def transform(self, X):
k = pd.Series(np.asarray(X).ravel())
return k.map(self.xarita_).fillna(self.umumiy_).to_numpy().reshape(-1, 1)
def yarat(seed: int = 12, n: int = 8000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
# 400 ta marshrut kodi, chastotasi juda notekis
ogirlik = rng.pareto(1.2, 400) + 1
kod = rng.choice(400, n, p=ogirlik / ogirlik.sum())
kod_qosh = rng.normal(0, 2200, 400)
masofa = rng.gamma(3, 2.2, n).clip(0.5, 40)
narx = 4000 + 2100 * masofa + kod_qosh[kod] + rng.normal(0, 1400, n)
return pd.DataFrame({"kod": kod.astype(str), "masofa": masofa, "narx": narx})
def main() -> None:
df = yarat()
y = df["narx"]
X = df[["masofa", "kod"]]
cv = KFold(5, shuffle=True, random_state=0)
def mae(model) -> float:
# CV bo'laklarida ko'rilmagan kodlar uchun ogohlantirish chiqadi —
# handle_unknown="ignore" ularni nol ustun sifatida kodlaydi
with warnings.catch_warnings():
warnings.simplefilter("ignore")
return -cross_val_score(model, X, y, cv=cv,
scoring="neg_mean_absolute_error").mean()
print("=== 1. Ma'lumot ===")
chastota = df["kod"].value_counts()
print(f" {df['kod'].nunique()} ta marshrut kodi, {len(df)} qator")
print(f" eng ko'p uchraydigani: {chastota.iloc[0]} marta, "
f"mediana: {chastota.median():.0f}, bir marta uchraydigan: "
f"{(chastota == 1).sum()} ta")
print("\n=== 2. Kodsiz baza ===")
baza = Pipeline([
("t", ColumnTransformer([("son", StandardScaler(), ["masofa"])],
remainder="drop")),
("m", Ridge(alpha=1.0)),
])
print(f" CV MAE = {mae(baza):.1f} so'm")
print("\n=== 3. To'liq one-hot va min_frequency ===")
for mf in [None, 5, 20, 100]:
tayyor = ColumnTransformer([
("son", StandardScaler(), ["masofa"]),
("kat", OneHotEncoder(handle_unknown="ignore", drop="first",
min_frequency=mf), ["kod"]),
])
quvur = Pipeline([("t", tayyor), ("m", Ridge(alpha=1.0))])
quvur.fit(X, y)
ustun = len(quvur.named_steps["t"].get_feature_names_out())
nom = "yo'q" if mf is None else str(mf)
print(f" min_frequency {nom:>4}: {ustun:>4} ustun, CV MAE {mae(quvur):7.1f}")
print("\n=== 4. Target encoding (pipeline ichida) ===")
te_natija = {}
for silliq in [1, 10, 50]:
tayyor = ColumnTransformer([
("son", StandardScaler(), ["masofa"]),
("te", TargetEncoder(silliq=silliq), ["kod"]),
])
quvur = Pipeline([("t", tayyor), ("m", Ridge(alpha=1.0))])
te_natija[silliq] = mae(quvur)
print(f" silliqlash {silliq:>3}: 2 ustun, CV MAE {te_natija[silliq]:7.1f}")
baza_mae = mae(baza)
eng_te = min(te_natija.values())
tayyor = ColumnTransformer([
("son", StandardScaler(), ["masofa"]),
("kat", OneHotEncoder(handle_unknown="ignore", drop="first"), ["kod"]),
])
toliq = mae(Pipeline([("t", tayyor), ("m", Ridge(alpha=1.0))]))
print(f"\n bazadan yaxshilanish: to'liq one-hot (396 ustun) "
f"{(baza_mae - toliq) / baza_mae:.0%}, "
f"target encoding (2 ustun) {(baza_mae - eng_te) / baza_mae:.0%}")
print(" ⭐ Ma'lumot ko'p bo'lsa to'liq one-hot aniqroq;")
print(" target encoding foydaning katta qismini 2 ustunda beradi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot ===
396 ta marshrut kodi, 8000 qator
eng ko'p uchraydigani: 2368 marta, mediana: 7, bir marta uchraydigan: 12 ta
=== 2. Kodsiz baza ===
CV MAE = 1760.6 so'm
=== 3. To'liq one-hot va min_frequency ===
min_frequency yo'q: 396 ustun, CV MAE 1173.6
min_frequency 5: 289 ustun, CV MAE 1244.8
min_frequency 20: 58 ustun, CV MAE 1448.2
min_frequency 100: 8 ustun, CV MAE 1656.6
=== 4. Target encoding (pipeline ichida) ===
silliqlash 1: 2 ustun, CV MAE 1537.2
silliqlash 10: 2 ustun, CV MAE 1458.3
silliqlash 50: 2 ustun, CV MAE 1456.8
bazadan yaxshilanish: to'liq one-hot (396 ustun) 33%, target encoding (2 ustun) 17%
⭐ Ma'lumot ko'p bo'lsa to'liq one-hot aniqroq;
target encoding foydaning katta qismini 2 ustunda beradiNima ko'rsatdi: 2.2-bo'lim.
Misol 4 — Sana belgilari va agregatlar
"""Sanadan belgilar va leakage'siz agregat (real pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def yarat(seed: int = 15, n: int = 9000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
sana = pd.Timestamp("2024-01-01") + pd.to_timedelta(rng.integers(0, 365, n), "D")
soat = rng.integers(0, 24, n)
hudud = rng.choice(["markaz", "shimol", "janub", "chekka"], n,
p=[0.4, 0.25, 0.2, 0.15])
masofa = rng.gamma(3, 2.2, n).clip(0.5, 40)
hafta_kuni = sana.dayofweek
dam = (hafta_kuni >= 5).astype(float)
tungi = np.sin(2 * np.pi * (soat + 6) / 24)
hudud_qosh = pd.Series(hudud).map({"markaz": 3000.0, "shimol": 500.0,
"janub": -400.0, "chekka": -2200.0}).to_numpy()
narx = (4000 + 2100 * masofa + 2600 * tungi + 1800 * dam + hudud_qosh
+ rng.normal(0, 1300, n))
return pd.DataFrame({"sana": sana, "soat": soat, "hudud": hudud,
"masofa": masofa, "narx": narx})
def belgilar(df: pd.DataFrame, xarita: dict | None = None,
umumiy: float = 0.0) -> pd.DataFrame:
X = pd.DataFrame(index=df.index)
X["masofa"] = df["masofa"]
X["sin_soat"] = np.sin(2 * np.pi * df["soat"] / 24)
X["cos_soat"] = np.cos(2 * np.pi * df["soat"] / 24)
X["hafta_kuni"] = df["sana"].dt.dayofweek
X["dam_olish"] = (df["sana"].dt.dayofweek >= 5).astype(float)
X["oy"] = df["sana"].dt.month
X["hudud"] = df["hudud"]
if xarita is not None:
X["hudud_median"] = df["hudud"].map(xarita).fillna(umumiy)
return X
def main() -> None:
df = yarat()
chegara = pd.Timestamp("2024-10-01")
tr, te = df[df["sana"] < chegara], df[df["sana"] >= chegara]
print("=== 1. Vaqt bo'yicha ajratish ===")
print(f" o'quv {len(tr)} qator, test {len(te)} qator")
def quvur(X: pd.DataFrame) -> Pipeline:
son = [c for c in X.columns if c != "hudud"]
qadamlar = [("son", StandardScaler(), son)]
if "hudud" in X.columns:
qadamlar.append(("kat", OneHotEncoder(handle_unknown="ignore",
drop="first"), ["hudud"]))
return Pipeline([("t", ColumnTransformer(qadamlar)),
("m", Ridge(alpha=1.0))])
print("\n=== 2. Belgilar to'plamlari ===")
natija = {}
toplamlar = {
"faqat masofa": ["masofa"],
"+ hudud": ["masofa", "hudud"],
"+ siklik soat": ["masofa", "hudud", "sin_soat", "cos_soat"],
"+ sana belgilari": ["masofa", "hudud", "sin_soat", "cos_soat",
"hafta_kuni", "dam_olish", "oy"],
}
Xtr_full, Xte_full = belgilar(tr), belgilar(te)
for nom, ustunlar in toplamlar.items():
Xtr, Xte = Xtr_full[ustunlar], Xte_full[ustunlar]
m = quvur(Xtr).fit(Xtr, tr["narx"])
natija[nom] = mean_absolute_error(te["narx"], m.predict(Xte))
print(f" {nom:<18}: test MAE {natija[nom]:7.1f} so'm")
print("\n=== 3. Agregat belgi (faqat o'quvdan hisoblanadi) ===")
xarita = tr.groupby("hudud")["narx"].median().to_dict()
umumiy = float(tr["narx"].median())
Xtr2 = belgilar(tr, xarita, umumiy)
Xte2 = belgilar(te, xarita, umumiy)
m = quvur(Xtr2).fit(Xtr2, tr["narx"])
print(f" + hudud median narxi: test MAE "
f"{mean_absolute_error(te['narx'], m.predict(Xte2)):7.1f} so'm")
print(f" xarita: {({k: round(v) for k, v in xarita.items()})}")
print("\n=== 4. Leakage tekshiruvi ===")
xato_xarita = df.groupby("hudud")["narx"].median().to_dict() # BUTUN ma'lumot
farq = {k: round(xato_xarita[k] - xarita[k]) for k in xarita}
print(f" butun ma'lumotdan hisoblangan xarita farqi: {farq}")
print(" agregat o'quvdan hisoblanishi kerak — aks holda test ma'lumoti sizadi")
print(" ⭐ Belgi muhandisligi natijani modeldan ko'ra ko'proq o'zgartiradi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Vaqt bo'yicha ajratish ===
o'quv 6768 qator, test 2232 qator
=== 2. Belgilar to'plamlari ===
faqat masofa : test MAE 2465.4 so'm
+ hudud : test MAE 1993.5 so'm
+ siklik soat : test MAE 1227.9 so'm
+ sana belgilari : test MAE 1052.0 so'm
=== 3. Agregat belgi (faqat o'quvdan hisoblanadi) ===
+ hudud median narxi: test MAE 1052.1 so'm
xarita: {'chekka': 14565, 'janub': 16415, 'markaz': 20292, 'shimol': 17740}
=== 4. Leakage tekshiruvi ===
butun ma'lumotdan hisoblangan xarita farqi: {'chekka': 384, 'janub': -74, 'markaz': -29, 'shimol': -79}
agregat o'quvdan hisoblanishi kerak — aks holda test ma'lumoti sizadi
⭐ Belgi muhandisligi natijani modeldan ko'ra ko'proq o'zgartiradiNima ko'rsatdi: 2.3, 2.5-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Kategoriyani raqam qilish mumkin" | Sun'iy tartib paydo bo'ladi |
| "One-hot har doim yaxshi" | Ko'p darajada muammo |
| "Target encoding oddiy" | CV ichida bo'lishi shart |
| "Soat — oddiy son" | Davriy (sin/cos) |
| "Agregat belgi zararsiz" | Leakage manbai |
| "Ko'p belgi — yaxshi" | n >> p |
| "Sana bitta belgi" | O'nlab belgi manbai |
| "Belgilar model tanlaganidan keyin" | Avval belgilar |
6. Keng tarqalgan xatolar va yechimlari
1. Kategoriya raqam sifatida
X["hudud"] = df["hudud"].astype(int) # ⚠️
OneHotEncoder(handle_unknown="ignore", drop="first") # ✅2. handle_unknown yo'q
OneHotEncoder(drop="first") # test'da yangi daraja # ⚠️
OneHotEncoder(drop="first", handle_unknown="infrequent_if_exist") # ✅3. Ko'p darajani to'g'ridan-to'g'ri one-hot
pd.get_dummies(df["mahsulot"]) # 3000 ustun # ⚠️
OneHotEncoder(min_frequency=30) # yoki target encoding # ✅4. Target encoding CV dan tashqarida
df["kod_te"] = df.groupby("kod")["narx"].transform("mean") # ⚠️
Pipeline([("te", TargetEncoder()), ("m", Ridge())]) # ✅5. Siklik belgini chiziqli qoldirish
X["soat"] = df["soat"] # ⚠️
X["sin"], X["cos"] = np.sin(2 * np.pi * s / 24), np.cos(...) # ✅6. Agregatni butun ma'lumotdan
xarita = df.groupby("hudud")["narx"].median() # ⚠️
xarita = tr.groupby("hudud")["narx"].median() # ✅7. Kelajakdagi sana belgilari
X["oy_oxiri_savdo"] = ... # kesimdan keyin ma'lum # ⚠️
# faqat kesimgacha mavjud belgilar 12.2-bob # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 12.9-dars (o'tilgan): Pipeline va leakage
- 13.3-dars (o'tilgan): Kategoriya koeffitsiyentlari
- 13.5-dars (o'tilgan): O'zaro ta'sirlar
- 13.12-dars: To'liq loyiha
- Feature engineering qismi: Kengaytirilgan usullar
8. Eng yaxshi amaliyotlar
Kategoriyani hech qachon raqam qilib bermang.
handle_unknown ni har doim qo'ying.
Ko'p darajada min_frequency dan boshlang.
Target encoding faqat pipeline ichida.
Davriy belgilarni siklik kodlang.
Agregatni o'quvdan hisoblang.
Har belgini CV bilan tekshiring.
Belgilarni hujjatlashtiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # kategoriyani raqam qilish nega xato?
2. # one-hot da drop_first nima uchun?
3. # handle_unknown nima qiladi?
4. # min_frequency nima?
5. # target encoding qayerda bajariladi?
6. # silliqlash nima uchun?
7. # sanadan qanday belgilar?
8. # siklik kodlash formulasi?
9. # nega siklik kerak?
10. # agregat belgi qayerdan hisoblanadi?
11. # ordinal kodlash qachon?
12. # belgi qo'shishni qanday tekshirish?Javoblar
- Sun'iy tartib va masofa
- Dummy tuzog'i
- Yangi darajada xato bermaydi
- Kam uchraydiganlarni birlashtiradi
- Pipeline/CV ichida
- Kam kuzatuvli darajalar uchun
- Hafta kuni, oy, bayram, trend
- sin(2pix/T), cos(2pix/T)
- 23 va 0 qo'shni
- O'quv qismidan, o'tmishdan
- Tartibli kategoriyada
- CV bilan
Vazifa 2: Xatolarni tuzating
1. X["hudud"] = df["hudud"].astype(int)
2. pd.get_dummies(df["mahsulot"]) # 5000 daraja
3. df["kod_te"] = df.groupby("kod")["narx"].transform("mean")
4. X["oy"] = df["sana"].dt.month # faqat shu
5. xarita = df.groupby("hudud")["narx"].mean() # butun ma'lumotJavoblar
1. OneHotEncoder(handle_unknown="ignore", drop="first")
2. OneHotEncoder(min_frequency=30, handle_unknown="infrequent_if_exist")
3. Pipeline([("te", TargetEncoder()), ("m", Ridge())])
4. # sin/cos oy, hafta kuni, bayram ham qo'shing
5. xarita = tr.groupby("hudud")["narx"].mean()Vazifa 3: Kodlash
Modellang:
- Raqam va one-hot
- CV taqqoslash
- Koeffitsiyentlar
- Xulosa
Vazifa 4: Siklik
Modellang:
- Davriy naqsh
- Uch yondashuv
- Masofalar
- Tanlov
Vazifa 5: Ko'p daraja
Modellang:
- 500 daraja
- min_frequency
- Target encoding
- Taqqoslash
Vazifa 6: Sana va agregat
Modellang:
- Sana belgilari
- Agregat
- Leakage tekshiruvi
- Yakuniy to'plam
Vazifa 7: O'ylash
"Belgi muhandisligi o'ladi — chuqur o'rganish belgilarni o'zi topadi" degan fikr keng tarqalgan. Bu jadval ma'lumotiga ham tegishlimi?
Javob
Qisqa javob: rasm, matn va audioda bu deyarli to'g'ri — tarmoqlar xom ma'lumotdan ierarxik belgilarni o'zi o'rganadi. Jadval ma'lumotida esa hali emas: domen belgilari (agregatlar, sana, o'zaro ta'sirlar) hali ham eng katta foyda manbai.
1. Nega jadvalda boshqacha
| Sabab | Izoh |
|---|---|
| Tuzilma yo'q | Rasmda piksel qo'shnilari bor, jadvalda ustun tartibi ma'nosiz |
| Ma'lumot kam | Tarmoq uchun yetarli emas |
| Domen bilimi tashqarida | "Oxirgi 7 kun o'rtachasi" ma'lumotda yo'q — uni siz yaratasiz |
| Gradient boosting kuchli | Jadvalda hali ham lider |
2. Nima o'zgarmoqda
- Avtomatik belgi yaratish vositalari (featuretools va o'xshashlari)
- Jadval uchun transformer arxitekturalari (ba'zi hollarda raqobatbardosh)
- Target/embedding kodlash neyron tarmoqlarda
3. Nima o'zgarmaydi
- Agregatlarning vaqt kesimi (leakage nazorati) — 12.2
- Domen mantiqi (nima o'lchanadi va qachon mavjud)
- Belgi hujjatlari va barqarorligi
4. Amaliy tavsiya
- Jadvalda: oddiy belgilar + boosting dan boshlang
- Domen belgilariga vaqt sarflang — eng yuqori qaytim
- Avtomatik vositalarni qo'shimcha sifatida ishlating
- Har belgini leakage bo'yicha tekshiring
5. Xulosa
- Tuzilmali ma'lumotda avtomatik belgi ishlaydi
- Jadvalda domen belgilari yetakchi
- Leakage nazorati avtomatlashmaydi
- Belgi muhandisligi yaqin kelajakda saqlanadi
Nimani mustahkamlaydi: 2.5, 2.6-bo'limlar.
Xulosa
Bu darsda kategoriyalar va belgi muhandisligini o'rgandik.
Eng muhim uch fikr:
Kategoriya — raqam emas. Hududni
1..12sifatida berish modelga sun'iy tartib va masofa yuklaydi. To'g'ri yo'l: nominal kategoriya uchun one-hot (drop_first,handle_unknown), tartibli uchun ordinal. Ko'p darajali kategoriyada (yuzlab/minglab) avval min_frequency bilan kam uchraydiganlarni birlashtiring, keyin domen bo'yicha guruhlang; target encoding kuchli, lekin faqat pipeline/CV ichida va silliqlash bilan 12.9-bob.Vaqt belgilari — eng boy manba. Bitta sanadan hafta kuni, oy, bayram, dam olish, trend belgilari chiqadi. Davriy qiymatlar (soat, oy, hafta kuni) uchun siklik kodlash (
sin,cos) kerak — aks holda 23 va 0 soat modelga "eng uzoq" bo'lib ko'rinadi.Agregatlar — eng katta foyda va eng katta xavf. Guruh statistikalari (hudud medianasi, mijozning o'rtacha cheki, oxirgi 7 kun o'rtachasi) kuchli belgilar, lekin ular leakagening asosiy manbai: faqat o'tmish ma'lumotidan va faqat o'quv qismidan hisoblanishi kerak 12.2-bob. Umumiy qoida: hamma narsa pipeline ichida, har belgi uchun "bashorat paytida mavjudmi?" savoli.
Keyingi darsda logistik regressiyani o'rganamiz: chiziqli modelni klassifikatsiyaga o'tkazish, sigmoid, log-loss va koeffitsiyentlarni odds ratio sifatida talqin qilish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!