Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Nisbat va farqlar
- 2.2. Guruh bo'yicha agregatsiya
- 2.3. Bining (diskretlashtirish)
- 2.4. Polinomial va o'zaro ta'sir
- 2.5. Matematik transformatsiyalar
- 2.6. Yaratilgan belgini baholash
- 2.7. Tuzoqlar
- 2.8. Maqsadli yaratish
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Nisbatlar va farqlar
- Misol 2 — Agregatsiya va leakage
- Misol 3 — Bining va polinomial belgilar
- Misol 4 — Transformatsiyalar va davriy belgilar
- 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
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
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
# 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")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)
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")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 subyektivBining 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
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)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 KERAKSIZPolinomial 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
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
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 osonBelgilarni 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
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'shmaBelgi 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 kodlang4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14).
Misol 1 — Nisbatlar va farqlar
"""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:
=== 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 naqshNima ko'rsatdi: 2.1-bo'lim.
Misol 2 — Agregatsiya va leakage
"""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:
=== 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 kerakNima ko'rsatdi: 2.2-bo'lim.
Misol 3 — Bining va polinomial belgilar
"""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:
=== 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 uchunNima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 4 — Transformatsiyalar va davriy belgilar
"""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:
=== 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 kodlangNima 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
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
df["guruh_ulushi"] = df.groupby("g")["y"].transform("mean") # ⚠️
# out-of-fold yoki TargetEncoder 17.3-bob # ✅3. Daraxtga bining
KBinsDiscretizer(10).fit_transform(X) # RandomForest uchun # ⚠️
# daraxt chegaralarni o'zi topadi # ✅4. Butun ma'lumotga polinomial
PolynomialFeatures(2).fit_transform(X_50belgi) # 1325 belgi # ⚠️
# eng muhim 8 belgida interaction_only=True # ✅5. Davriy belgini xom qoldirish
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
# barcha juftliklar uchun nisbat yaratish # ⚠️
# har belgi ortida gipoteza bo'lsin # ✅7. CV siz qabul qilish
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
Nisbatlardan boshlang.
Gipoteza bilan yarating.
Nolga bo'lishni tekshiring.
Agregatsiyani out-of-fold qiling.
Davriy belgilarni kodlang.
Polinomialni cheklang.
Guruh bo'lib qo'shing.
CV std bilan tasdiqlang.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
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
- Nisbat (a/b)
- O'qlarga parallel chegaralar
- replace(0, nan) yoki b+1
- Kontekst
- Maqsad bo'yicha
- Out-of-fold
- Chiziqli modellarga
- 230
- Faqat o'zaro ta'sirlar
- sin/cos
- Yo'q (monoton)
- Guruh bo'lib
Vazifa 2: Xatolarni tuzating
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 0Javoblar
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 kodlashVazifa 3: Nisbatlar
Modellang:
- Xom belgilar
- Nisbatlar
- Chegaralar
- Guruhlar
Vazifa 4: Agregatsiya
Modellang:
- Xom
- Agregatsiya
- Leakage
- Out-of-fold
Vazifa 5: Bining
Modellang:
- Bazaviy
- Bining
- Polinomial
- Portlash
Vazifa 6: Transformatsiya
Modellang:
- Taqsimotlar
- Summa
- Davriy kodlash
- 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
- Domen belgilari: "bayram kuni", "ish vaqti", "chegirma davri"
- Tashqi ma'lumot: ob-havo, valyuta, geografiya
- Leakage nazorati: vosita "qachon ma'lum bo'ladi?" savolini bilmaydi
- Talqin:
MEAN(orders.SUM(items.price))degan belgini tushuntirish qiyin - 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 nomlang5. Xulosa
- Avtomatik vositalar kombinatorikani yaxshi bajaradi
- Domen bilimi va leakage nazorati qo'lda qoladi
- Ular qo'shimcha, o'rin bosar emas
- Natijani albatta ko'rib chiqing
Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.
Xulosa
Bu darsda belgi yaratishni o'rgandik.
Eng muhim uch fikr:
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 daraxta/b > 0.4shartini to'g'ridan-to'g'ri ifodalay olmaydi. Nolga bo'lishni esareplace(0, np.nan)bilan hal qiling.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.Bining va polinomial — chiziqli modellar uchun. Daraxtlar chegaralarni o'zi topadi, shuning uchun qo'lda bining ularga keraksiz (va hatto zarar qiladi).
PolynomialFeaturesesa belgilar sonini portlatadi (20 belgi, daraja 2 → 230), shuning uchun uni faqat eng muhim 5-10 belgidainteraction_only=Truebilan qo'llang. Davriy belgilarni (soat, oy) esasin/cosbilan 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.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!