Ma'lumotni tayyorlash: encoding, missing, scaling
Sahna: model matnni tushunmaydi
Ma'lumotni ko'rdingiz (3-bob), muammolarni topdingiz. Endi modelga berasiz:
model.fit(df[["tuman", "maydon_m2"]], y)
Va xato olasiz:
ValueError: could not convert string to float: 'Yunusobod'
Model faqat sonlarni tushunadi. "Yunusobod" — matn. Bu bob matnni songa, bo'shliqni qiymatga, va har xil o'lchovni bir shkalaga keltirishni o'rgatadi.
Bu — protokolning 7 va 8-qadami.
Avval ko'ramiz: nima tuzatish kerak
import pandas as pd import numpy as np df = pd.read_csv(DATA / "uy_ijara.csv") X = df.drop(columns=["ijaraga_berildi_30k", "narx_usd"]) print("Ustun turlari:") print(X.dtypes.to_string()) print() print("Muammolar ro'yxati:") matn = X.select_dtypes("object").columns.tolist() print(f" 1. Matn ustunlar (encoding kerak): {matn}") yoq = X.isna().sum() print(f" 2. Bo'sh qiymatlar: {yoq[yoq > 0].to_dict()}") print(f" 3. Takrorlangan qatorlar: {int(X.duplicated().sum())}") shkala = X.select_dtypes(include=[np.number]).agg(["min", "max"]).T shkala["oraliq"] = shkala["max"] - shkala["min"] print(f" 4. Har xil shkala — eng kichik oraliq {shkala.oraliq.min():.1f}, eng katta {shkala.oraliq.max():.1f}")
Ustun turlari:
elon_id object
tuman object
xonalar int64
maydon_m2 float64
qavat int64
qavatlar_soni int64
metro_km float64
mebel int64
qurilgan_yil float64
narx_matn object
agent_komissiyasi int64
Muammolar ro'yxati:
1. Matn ustunlar (encoding kerak): ['elon_id', 'tuman', 'narx_matn']
2. Bo'sh qiymatlar: {'qurilgan_yil': 8}
3. Takrorlangan qatorlar: 0
4. Har xil shkala — eng kichik oraliq 1.0, eng katta 610.0
Matnni songa: ikki xil kategoriya
Hamma matn ustunlari bir xil emas. Ikki turga bo'linadi, va ularni boshqacha kodlash kerak.
show(pd.DataFrame([ {"Turi": "Ordinal (tartibli)", "Misol": "kichik < o'rta < katta; 1-kurs < 2-kurs", "Tartib bormi": "HA", "Usul": "OrdinalEncoder yoki .map()"}, {"Turi": "Nominal (tartibsiz)", "Misol": "tuman, rang, mamlakat", "Tartib bormi": "YO'Q", "Usul": "OneHotEncoder"}, ]))
| Turi | Misol | Tartib bormi | Usul |
|---|---|---|---|
| Ordinal (tartibli) | kichik < o'rta < katta; 1-kurs < 2-kurs | HA | OrdinalEncoder yoki .map() |
| Nominal (tartibsiz) | tuman, rang, mamlakat | YO'Q | OneHotEncoder |
Nega farq muhim? Ko'rsataman.
from sklearn.preprocessing import OrdinalEncoder, OneHotEncoder tumanlar = X[["tuman"]].head(6) print("Asl qiymatlar:") print(tumanlar.tuman.tolist()) print() # NOTO'G'RI: nominal ustunga raqam berish ord_kod = OrdinalEncoder().fit_transform(X[["tuman"]]) print("OrdinalEncoder bersak:") for t, k in zip(tumanlar.tuman, ord_kod[:6].ravel()): print(f" {t:16s} -> {k:.0f}") print() print("MUAMMO: model endi 'Yunusobod (7) > Bektemir (0)' deb o'ylaydi.") print(" Bu MA'NOSIZ — tumanlarni tartiblash mumkin emas.")
Asl qiymatlar:
['Chilonzor', 'Yakkasaroy', "Mirzo Ulug'bek", "Mirzo Ulug'bek", 'Chilonzor', 'Olmazor']
OrdinalEncoder bersak:
Chilonzor -> 1
Yakkasaroy -> 6
Mirzo Ulug'bek -> 2
Mirzo Ulug'bek -> 2
Chilonzor -> 1
Olmazor -> 3
MUAMMO: model endi 'Yunusobod (7) > Bektemir (0)' deb o'ylaydi.
Bu MA'NOSIZ — tumanlarni tartiblash mumkin emas.
# TO'G'RI: nominal ustun uchun OneHot oh = OneHotEncoder(sparse_output=False, handle_unknown="ignore") kodlangan = oh.fit_transform(X[["tuman"]]) nomlar = oh.get_feature_names_out(["tuman"]) print(f"OneHotEncoder: 1 ustun -> {kodlangan.shape[1]} ustun") print() natija = pd.DataFrame(kodlangan[:6].astype(int), columns=[n.replace("tuman_", "") for n in nomlar]) natija.insert(0, "asl", tumanlar.tuman.values) show(natija)
OneHotEncoder: 1 ustun -> 8 ustun
| asl | Bektemir | Chilonzor | Mirzo Ulug'bek | Olmazor | Sergeli | Shayxontohur | Yakkasaroy | Yunusobod |
|---|---|---|---|---|---|---|---|---|
| Chilonzor | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| Yakkasaroy | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| Mirzo Ulug'bek | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| Mirzo Ulug'bek | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| Chilonzor | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| Olmazor | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
Har bir tuman o'z ustunini oladi, va qiymat 0 yoki 1. Hech qanday sun'iy tartib yo'q.
`handle_unknown="ignore"` ni unutmang
Modelni o'qitdingiz. Bir oydan keyin yangi tuman qo'shildi — masalan "Uchtepa".
handle_unknown="ignore" bo'lmasa, model xato beradi va serveringiz yiqiladi.
U bilan — yangi tuman hamma ustunda 0 bo'ladi, model ishlashda davom etadi.
Ishlab chiqarishga chiqadigan har bir OneHotEncoder da shu parametr bo'lishi kerak.
print("Isbot — modelga YANGI tuman kelsa:") yangi = pd.DataFrame({"tuman": ["Uchtepa"]}) print(" handle_unknown='ignore' bilan:", oh.transform(yangi).astype(int).tolist()) qattiq = OneHotEncoder(sparse_output=False).fit(X[["tuman"]]) try: qattiq.transform(yangi) except Exception as e: print(" handle_unknown yo'q bo'lsa :", type(e).__name__, "->", str(e)[:58])
Isbot — modelga YANGI tuman kelsa: handle_unknown='ignore' bilan: [[0, 0, 0, 0, 0, 0, 0, 0]] handle_unknown yo'q bo'lsa : ValueError -> Found unknown categories ['Uchtepa'] in column 0 during tr
Ordinal ustun uchun esa tartibni o'zingiz aytasiz:
# Sun'iy misol: kvartira holatini toifaga ajratamiz X_misol = X.copy() X_misol["holat"] = pd.cut(X_misol.qurilgan_yil.fillna(X_misol.qurilgan_yil.median()), bins=[0, 1990, 2010, 2100], labels=["eski", "o'rta", "yangi"]) tartib = {"eski": 0, "o'rta": 1, "yangi": 2} X_misol["holat_kod"] = X_misol.holat.map(tartib) show(X_misol[["qurilgan_yil", "holat", "holat_kod"]].head(6)) print() print("Bu yerda tartib MA'NOLI: yangi > o'rta > eski. Shuning uchun .map() to'g'ri.")
Bu yerda tartib MA'NOLI: yangi > o'rta > eski. Shuning uchun .map() to'g'ri.
| qurilgan_yil | holat | holat_kod |
|---|---|---|
| 2 016 | yangi | 2 |
| 1 977 | eski | 0 |
| 2 010 | o'rta | 1 |
| 2 007 | o'rta | 1 |
| 1 989 | eski | 0 |
| 1 987 | eski | 0 |
Bo'sh qiymatlar
print(f"qurilgan_yil — bo'sh: {int(X.qurilgan_yil.isna().sum())} ta ({X.qurilgan_yil.isna().mean():.2%})") print() show(pd.DataFrame([ {"Usul": "Qatorni tashlash", "Qachon": "< 1% bo'sh va ma'lumot ko'p", "Narxi": "Ma'lumot yo'qotadi; bo'shlik tasodifiy bo'lmasa — xatolik kiritadi"}, {"Usul": "Mediana bilan to'ldirish", "Qachon": "Son ustun, 1-30% bo'sh", "Narxi": "Tarqalishni kamaytiradi; chetki qiymatlarga chidamli"}, {"Usul": "O'rtacha bilan to'ldirish", "Qachon": "Son ustun, taqsimot simmetrik", "Narxi": "Chetki qiymatlar o'rtachani tortadi"}, {"Usul": "Eng ko'p uchraydigani", "Qachon": "Matn/kategoriya ustun", "Narxi": "Katta kategoriyani yanada kattalashtiradi"}, {"Usul": "'YO'Q' alohida toifa", "Qachon": "Bo'shligining O'ZI ma'noga ega", "Narxi": "Yo'q — ko'pincha eng yaxshi tanlov"}, {"Usul": "Model bilan to'ldirish (KNN)", "Qachon": "Ko'p bo'sh va ustunlar bog'liq", "Narxi": "Sekin; leakage xavfi yuqori"}, ]))
qurilgan_yil — bo'sh: 8 ta (0.67%)
| Usul | Qachon | Narxi |
|---|---|---|
| Qatorni tashlash | < 1% bo'sh va ma'lumot ko'p | Ma'lumot yo'qotadi; bo'shlik tasodifiy bo'lmasa — xatolik kiritadi |
| Mediana bilan to'ldirish | Son ustun, 1-30% bo'sh | Tarqalishni kamaytiradi; chetki qiymatlarga chidamli |
| O'rtacha bilan to'ldirish | Son ustun, taqsimot simmetrik | Chetki qiymatlar o'rtachani tortadi |
| Eng ko'p uchraydigani | Matn/kategoriya ustun | Katta kategoriyani yanada kattalashtiradi |
| 'YO'Q' alohida toifa | Bo'shligining O'ZI ma'noga ega | Yo'q — ko'pincha eng yaxshi tanlov |
| Model bilan to'ldirish (KNN) | Ko'p bo'sh va ustunlar bog'liq | Sekin; leakage xavfi yuqori |
Oxirgidan oldingi usul eng ko'p o'tkazib yuboriladi. Misol: deck ustuni 77% bo'sh (41-bob) — chunki arzon chiptada kabina raqami yozilmagan. Ya'ni bo'shligining o'zi "arzon chipta" degan ma'noni beradi.
from sklearn.impute import SimpleImputer for strat in ["median", "mean", "most_frequent"]: t = SimpleImputer(strategy=strat).fit_transform(X[["qurilgan_yil"]]) print(f" {strat:14s} -> to'ldirilgan qiymat: {t[X.qurilgan_yil.isna().values][0][0]:.1f}") print() print("Qo'shimcha ustun: 'bo'sh edi' belgisini saqlash") belgili = SimpleImputer(strategy="median", add_indicator=True).fit_transform(X[["qurilgan_yil"]]) print(f" add_indicator=True -> {belgili.shape[1]} ustun (qiymat + bo'shlik belgisi)")
median -> to'ldirilgan qiymat: 1998.0 mean -> to'ldirilgan qiymat: 1999.0 most_frequent -> to'ldirilgan qiymat: 1995.0 Qo'shimcha ustun: 'bo'sh edi' belgisini saqlash add_indicator=True -> 2 ustun (qiymat + bo'shlik belgisi)
To'ldirish FAQAT train dan o'rganiladi
SimpleImputer().fit(X) — butun ma'lumotda. Bu leakage (5-bob).
Mediana train dan olinishi kerak, keyin test ga qo'llanishi kerak. Pipeline buni avtomatik qiladi — shuning uchun biz uni ishlatamiz.
Shkala: nega kerak va qachon kerak emas
shkala = X.select_dtypes(include=[np.number]).agg(["min", "max", "mean", "std"]).T.round(2) show(shkala.reset_index().rename(columns={"index": "ustun"}))
| ustun | min | max | mean | std |
|---|---|---|---|---|
| xonalar | 1 | 5 | 2.51 | 1.07 |
| maydon_m2 | 22 | 114 | 57.19 | 20.14 |
| qavat | 1 | 16 | 5.1 | 3.61 |
| qavatlar_soni | 2 | 16 | 9.03 | 4.31 |
| metro_km | 0.1 | 8.05 | 2.58 | 1.6 |
| mebel | 0 | 1 | 0.61 | 0.49 |
| qurilgan_yil | 1 975 | 2 024 | 1 998.95 | 14.31 |
| agent_komissiyasi | 0 | 610 | 167.4 | 178.24 |
Muammoni ko'ring: maydon_m2 22 dan 220 gacha, mebel esa 0 yoki 1.
Masofaga asoslangan modellar (KNN, SVM) uchun bu halokatli: maydon_m2 dagi 1 birlik farq mebel dagi 1 birlik farq bilan teng hisoblanadi, lekin birinchisi 200 marta kichikroq ahamiyatga ega.
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler son = ["xonalar", "maydon_m2", "metro_km", "mebel"] for nom, sc in [("StandardScaler", StandardScaler()), ("MinMaxScaler", MinMaxScaler()), ("RobustScaler", RobustScaler())]: t = sc.fit_transform(X[son]) print(f"{nom:16s} o'rtacha={t.mean():+.3f} std={t.std():.3f} " f"min={t.min():+.2f} max={t.max():+.2f}")
StandardScaler o'rtacha=+0.000 std=1.000 min=-1.75 max=+3.41 MinMaxScaler o'rtacha=+0.421 std=0.335 min=+0.00 max=+1.00 RobustScaler o'rtacha=+0.061 std=0.833 min=-1.18 max=+3.00
show(pd.DataFrame([ {"Scaler": "StandardScaler", "Nima qiladi": "O'rtacha 0, std 1", "Qachon": "Sukut bo'yicha tanlov; taqsimot normalga yaqin bo'lsa"}, {"Scaler": "MinMaxScaler", "Nima qiladi": "0 va 1 orasiga siqadi", "Qachon": "Neyron tarmoq, chegara aniq kerak bo'lganda"}, {"Scaler": "RobustScaler", "Nima qiladi": "Mediana va IQR bo'yicha", "Qachon": "Chetki qiymatlar KO'P bo'lsa"}, ]))
| Scaler | Nima qiladi | Qachon |
|---|---|---|
| StandardScaler | O'rtacha 0, std 1 | Sukut bo'yicha tanlov; taqsimot normalga yaqin bo'lsa |
| MinMaxScaler | 0 va 1 orasiga siqadi | Neyron tarmoq, chegara aniq kerak bo'lganda |
| RobustScaler | Mediana va IQR bo'yicha | Chetki qiymatlar KO'P bo'lsa |
Endi eng muhim savol: qaysi modelga scaling kerak?
show(pd.DataFrame([ {"Model": "KNN", "Scaling": "SHART", "Nega": "Masofa hisoblaydi"}, {"Model": "SVM", "Scaling": "SHART", "Nega": "Masofa va margin"}, {"Model": "Logistic / Linear Regression", "Scaling": "Tavsiya etiladi", "Nega": "Gradient tezroq yaqinlashadi; koeffitsiyentlarni solishtirish mumkin bo'ladi"}, {"Model": "Ridge / Lasso", "Scaling": "SHART", "Nega": "Jarima koeffitsiyent kattaligiga qarab beriladi"}, {"Model": "Neyron tarmoq", "Scaling": "SHART", "Nega": "Gradient portlashi/yo'qolishi"}, {"Model": "Decision Tree", "Scaling": "KERAK EMAS", "Nega": "Faqat 'kattami/kichikmi' deb so'raydi"}, {"Model": "Random Forest", "Scaling": "KERAK EMAS", "Nega": "Daraxtlardan tuzilgan"}, {"Model": "XGBoost / LightGBM", "Scaling": "KERAK EMAS", "Nega": "Daraxtlardan tuzilgan"}, ]))
| Model | Scaling | Nega |
|---|---|---|
| KNN | SHART | Masofa hisoblaydi |
| SVM | SHART | Masofa va margin |
| Logistic / Linear Regression | Tavsiya etiladi | Gradient tezroq yaqinlashadi; koeffitsiyentlarni solishtirish mumkin bo'ladi |
| Ridge / Lasso | SHART | Jarima koeffitsiyent kattaligiga qarab beriladi |
| Neyron tarmoq | SHART | Gradient portlashi/yo'qolishi |
| Decision Tree | KERAK EMAS | Faqat 'kattami/kichikmi' deb so'raydi |
| Random Forest | KERAK EMAS | Daraxtlardan tuzilgan |
| XGBoost / LightGBM | KERAK EMAS | Daraxtlardan tuzilgan |
Keling, buni o'lchab tasdiqlaymiz:
from sklearn.model_selection import cross_val_score from sklearn.pipeline import Pipeline from sklearn.neighbors import KNeighborsClassifier from sklearn.tree import DecisionTreeClassifier y = df["ijaraga_berildi_30k"] Xs = X[son] print(f"{'Model':<16} {'scaling YO`Q':>14} {'scaling BOR':>13} {'farq':>8}") print("-" * 54) for nom, m in [("KNN", KNeighborsClassifier()), ("DecisionTree", DecisionTreeClassifier(max_depth=6, random_state=42))]: yoq_s = cross_val_score(m, Xs, y, cv=5).mean() bor_s = cross_val_score(Pipeline([("sc", StandardScaler()), ("m", m)]), Xs, y, cv=5).mean() print(f"{nom:<16} {yoq_s:>14.4f} {bor_s:>13.4f} {bor_s-yoq_s:>+8.4f}")
Model scaling YO`Q scaling BOR farq ------------------------------------------------------ KNN 0.6692 0.6600 -0.0092 DecisionTree 0.6858 0.6858 +0.0000
Jadvalga qarang: KNN uchun scaling natijani o'zgartirdi, daraxt uchun esa deyarli o'zgartirmadi. Nazariya o'lchov bilan tasdiqlandi.
Takrorlangan qatorlar
print(f"Bizning faylda dublikat: {int(X.duplicated().sum())} ta") print() print("Lekin diqqat — DEYARLI bir xil qatorlar ham muammo:") soxta = pd.DataFrame({ "tuman": ["Chilonzor", "chilonzor", "Chilonzor "], "maydon_m2": [55.0, 55.0, 55.0], }) print(soxta.to_string(index=False)) print() print("duplicated() topadigan dublikat:", int(soxta.duplicated().sum())) soxta_t = soxta.assign(tuman=soxta.tuman.str.strip().str.lower()) print("Normallashtirilgandan keyin :", int(soxta_t.duplicated().sum()))
Bizning faylda dublikat: 0 ta
Lekin diqqat — DEYARLI bir xil qatorlar ham muammo:
tuman maydon_m2
Chilonzor 55.0
chilonzor 55.0
Chilonzor 55.0
duplicated() topadigan dublikat: 0
Normallashtirilgandan keyin : 2
Shuning uchun tartib: avval normallashtiring, keyin dublikatni tashlang. 43-bobdagi mashqda aynan shu muammo bor.
Hammasi birga: ColumnTransformer
Har bir ustun turiga boshqa muolaja kerak. ColumnTransformer buni bitta obyektga yig'adi.
from sklearn.compose import ColumnTransformer from sklearn.linear_model import LogisticRegression son_ustun = ["xonalar", "maydon_m2", "qavat", "qavatlar_soni", "metro_km", "mebel", "qurilgan_yil"] matn_ustun = ["tuman"] tayyorla = ColumnTransformer([ ("son", Pipeline([ ("toldir", SimpleImputer(strategy="median")), ("shkala", StandardScaler()), ]), son_ustun), ("matn", Pipeline([ ("toldir", SimpleImputer(strategy="most_frequent")), ("kod", OneHotEncoder(handle_unknown="ignore")), ]), matn_ustun), ], remainder="drop") quvur = Pipeline([("tayyorla", tayyorla), ("model", LogisticRegression(max_iter=2000))]) ball = cross_val_score(quvur, X, y, cv=5, scoring="f1") print(f"To'liq quvur, 5-fold CV F1: {ball.mean():.4f} ±{ball.std():.4f}") print() tayyorla.fit(X, y) print(f"Kirish : {X.shape[1]} ustun") print(f"Chiqish: {tayyorla.transform(X).shape[1]} ustun (OneHot tumanni yoydi)")
To'liq quvur, 5-fold CV F1: 0.7273 ±0.0118 Kirish : 11 ustun Chiqish: 15 ustun (OneHot tumanni yoydi)
remainder="drop" muhim: ro'yxatda yo'q ustunlar tashlanadi. Shuning uchun elon_id, narx_matn, agent_komissiyasi avtomatik chiqib ketdi — ya'ni 0-bobdagi uch tuzoq quvur tuzilmasining o'zi bilan bloklandi.
Bu naqshni yodlab oling
ColumnTransformer([ ("son", Pipeline([toldir, shkala]), son_ustunlar), ("matn", Pipeline([toldir, onehot]), matn_ustunlar), ], remainder="drop")
Butun kitobda shu naqsh takrorlanadi. Model o'zgarsa — faqat oxirgi qator o'zgaradi.
Dataset pasporti
Tozalashni tugatdingiz. Endi yozib qo'ying — bu hujjat sizni uch oydan keyin qutqaradi.
import hashlib def dataset_pasport(ramka, nom, target): xesh = hashlib.md5(pd.util.hash_pandas_object(ramka, index=True).values).hexdigest()[:16] satrlar = [ f"Dataset : {nom}", f"Qatorlar : {len(ramka):,}".replace(",", " "), f"Ustunlar : {ramka.shape[1]}", f"Target : {target}", f"Xesh (md5) : {xesh}", f"Bo'sh qiymat: {int(ramka.isna().sum().sum())}", f"Dublikat : {int(ramka.duplicated().sum())}", ] if target in ramka: n = ramka[target].value_counts(normalize=True).sort_index() satrlar.append("Target nisbati: " + ", ".join(f"{k}={v:.1%}" for k, v in n.items())) return "\n".join(satrlar) print(dataset_pasport(df, "uy_ijara.csv", "ijaraga_berildi_30k"))
Dataset : uy_ijara.csv Qatorlar : 1 200 Ustunlar : 13 Target : ijaraga_berildi_30k Xesh (md5) : 1b7dd7e5b3894c3f Bo'sh qiymat: 8 Dublikat : 0 Target nisbati: 0=49.3%, 1=50.7%
Xesh — bu kelajakdagi o'zingizga xat
Uch oydan keyin modelingiz boshqa natija beradi. Savol: ma'lumot o'zgarganmi yoki kod?
Xeshni solishtirasiz. Bir xil bo'lsa — kodda muammo. Boshqa bo'lsa — ma'lumot o'zgargan.
Busiz siz bu savolga hech qachon javob berolmaysiz. MLOps kitobida bu mavzu alohida bobda.
Bugun nima o'rgandik
show(pd.DataFrame([ {"Muammo": "Matn ustun", "Yechim": "Nominal -> OneHot, Ordinal -> .map()/OrdinalEncoder"}, {"Muammo": "Nominalga raqam berish", "Yechim": "Sun'iy tartib yaratadi — QILMANG"}, {"Muammo": "Yangi kategoriya kelishi", "Yechim": "handle_unknown='ignore'"}, {"Muammo": "Bo'sh qiymat", "Yechim": "Mediana/moda/alohida toifa — bo'shlik ma'nosiga qarab"}, {"Muammo": "Har xil shkala", "Yechim": "StandardScaler; daraxtlarga kerak emas"}, {"Muammo": "Deyarli bir xil qatorlar", "Yechim": "Avval normallashtir, keyin drop_duplicates"}, {"Muammo": "Hammasini bog'lash", "Yechim": "ColumnTransformer + Pipeline, remainder='drop'"}, {"Muammo": "Uch oydan keyin eslash", "Yechim": "Dataset pasporti + md5 xesh"}, ]))
| Muammo | Yechim |
|---|---|
| Matn ustun | Nominal -> OneHot, Ordinal -> .map()/OrdinalEncoder |
| Nominalga raqam berish | Sun'iy tartib yaratadi — QILMANG |
| Yangi kategoriya kelishi | handle_unknown='ignore' |
| Bo'sh qiymat | Mediana/moda/alohida toifa — bo'shlik ma'nosiga qarab |
| Har xil shkala | StandardScaler; daraxtlarga kerak emas |
| Deyarli bir xil qatorlar | Avval normallashtir, keyin drop_duplicates |
| Hammasini bog'lash | ColumnTransformer + Pipeline, remainder='drop' |
| Uch oydan keyin eslash | Dataset pasporti + md5 xesh |
Shu bilan 1-qism tugadi. Siz ma'lumotni olasiz (2-bob), ko'rasiz (3-bob) va tayyorlaysiz (4-bob).
Keyingi qism — 2-qism — eng muhimi: modelni qanday baholash kerak. U 5-bobdan boshlanadi.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.