Asosiy qismga o'tish

Ma'lumotni tayyorlash: encoding, missing, scaling

5-bob 20 kod bloki 3415 so'z

Bu bobdagi kod shu fayllar bilan ishlaydio'z kompyuteringizda ishga tushiring

Sahna: model matnni tushunmaydi

Ma'lumotni ko'rdingiz (3-bob), muammolarni topdingiz. Endi modelga berasiz:

python1 qator
model.fit(df[["tuman", "maydon_m2"]], y)

Va xato olasiz:

yaml1 qator
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

python18 qator
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}")
Chiqish
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.

python6 qator
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"},
]))
TuriMisolTartib bormiUsul
Ordinal (tartibli)kichik < o'rta < katta; 1-kurs < 2-kursHAOrdinalEncoder yoki .map()
Nominal (tartibsiz)tuman, rang, mamlakatYO'QOneHotEncoder

Nega farq muhim? Ko'rsataman.

python15 qator
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.")
Chiqish
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.
python9 qator
# 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)
Chiqish
OneHotEncoder: 1 ustun -> 8 ustun
aslBektemirChilonzorMirzo Ulug'bekOlmazorSergeliShayxontohurYakkasaroyYunusobod
Chilonzor01000000
Yakkasaroy00000010
Mirzo Ulug'bek00100000
Mirzo Ulug'bek00100000
Chilonzor01000000
Olmazor00010000

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.

python8 qator
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])
Chiqish
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:

python10 qator
# 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.")
Chiqish
Bu yerda tartib MA'NOLI: yangi > o'rta > eski. Shuning uchun .map() to'g'ri.
qurilgan_yilholatholat_kod
2 016yangi2
1 977eski0
2 010o'rta1
2 007o'rta1
1 989eski0
1 987eski0

Bo'sh qiymatlar

python16 qator
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"},
]))
Chiqish
qurilgan_yil — bo'sh: 8 ta (0.67%)
UsulQachonNarxi
Qatorni tashlash< 1% bo'sh va ma'lumot ko'pMa'lumot yo'qotadi; bo'shlik tasodifiy bo'lmasa — xatolik kiritadi
Mediana bilan to'ldirishSon ustun, 1-30% bo'shTarqalishni kamaytiradi; chetki qiymatlarga chidamli
O'rtacha bilan to'ldirishSon ustun, taqsimot simmetrikChetki qiymatlar o'rtachani tortadi
Eng ko'p uchraydiganiMatn/kategoriya ustunKatta kategoriyani yanada kattalashtiradi
'YO'Q' alohida toifaBo'shligining O'ZI ma'noga egaYo'q — ko'pincha eng yaxshi tanlov
Model bilan to'ldirish (KNN)Ko'p bo'sh va ustunlar bog'liqSekin; 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.

python9 qator
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)")
Chiqish
  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

python2 qator
shkala = X.select_dtypes(include=[np.number]).agg(["min", "max", "mean", "std"]).T.round(2)
show(shkala.reset_index().rename(columns={"index": "ustun"}))
ustunminmaxmeanstd
xonalar152.511.07
maydon_m22211457.1920.14
qavat1165.13.61
qavatlar_soni2169.034.31
metro_km0.18.052.581.6
mebel010.610.49
qurilgan_yil1 9752 0241 998.9514.31
agent_komissiyasi0610167.4178.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.

python9 qator
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}")
Chiqish
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
python8 qator
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"},
]))
ScalerNima qiladiQachon
StandardScalerO'rtacha 0, std 1Sukut bo'yicha tanlov; taqsimot normalga yaqin bo'lsa
MinMaxScaler0 va 1 orasiga siqadiNeyron tarmoq, chegara aniq kerak bo'lganda
RobustScalerMediana va IQR bo'yichaChetki qiymatlar KO'P bo'lsa

Endi eng muhim savol: qaysi modelga scaling kerak?

python10 qator
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"},
]))
ModelScalingNega
KNNSHARTMasofa hisoblaydi
SVMSHARTMasofa va margin
Logistic / Linear RegressionTavsiya etiladiGradient tezroq yaqinlashadi; koeffitsiyentlarni solishtirish mumkin bo'ladi
Ridge / LassoSHARTJarima koeffitsiyent kattaligiga qarab beriladi
Neyron tarmoqSHARTGradient portlashi/yo'qolishi
Decision TreeKERAK EMASFaqat 'kattami/kichikmi' deb so'raydi
Random ForestKERAK EMASDaraxtlardan tuzilgan
XGBoost / LightGBMKERAK EMASDaraxtlardan tuzilgan

Keling, buni o'lchab tasdiqlaymiz:

python14 qator
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}")
Chiqish
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

python12 qator
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()))
Chiqish
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.

python24 qator
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)")
Chiqish
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

python4 qator
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.

python19 qator
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"))
Chiqish
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

python10 qator
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"},
]))
MuammoYechim
Matn ustunNominal -> OneHot, Ordinal -> .map()/OrdinalEncoder
Nominalga raqam berishSun'iy tartib yaratadi — QILMANG
Yangi kategoriya kelishihandle_unknown='ignore'
Bo'sh qiymatMediana/moda/alohida toifa — bo'shlik ma'nosiga qarab
Har xil shkalaStandardScaler; daraxtlarga kerak emas
Deyarli bir xil qatorlarAvval normallashtir, keyin drop_duplicates
Hammasini bog'lashColumnTransformer + Pipeline, remainder='drop'
Uch oydan keyin eslashDataset 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.

Telegramda yozish Narxlar