Mashq 2: iflos savdo ma'lumoti
Nega bu mashq
Penguins toza edi: 7 ustun, hammasi joyida, faqat 11 ta bo'sh qiymat.
Haqiqiy ma'lumot unday emas. Bu faylda:
- Sanalar ikki xil formatda
- Viloyat nomlari ikki alifboda va har xil registrda
- Narx matn ko'rinishida, ichida bo'shliq bilan
- Takrorlangan qatorlar
- Bo'sh qiymatlar
Va eng muhimi: target ustuni yo'q. Siz uni o'zingiz yaratasiz.
Bu mashqda modelning o'zi ishning atigi 20 foizi. Qolgan 80 foiz — tozalash. Amaliyotda har doim shunday.
Ma'lumot bilan tanishuv
import pandas as pd sv = pd.read_csv(DATA / "iflos_savdo.csv") print("shakli:", sv.shape) print() print(sv.dtypes.to_string()) print() show(sv.head(8))
shakli: (6180, 9) item_id int64 order_id int64 order_date object region object category object product_name object quantity float64 unit_price object status object
| item_id | order_id | order_date | region | category | product_name | quantity | unit_price | status |
|---|---|---|---|---|---|---|---|---|
| 16 386 | 8 236 | 2025-05-26 | Buxoro | Oziq-ovqat | Makaron 5kg | 5 | 64000 | cancelled |
| 5 934 | 2 970 | 26.02.2025 | Фарғона | Uy-ro'zg'or | Kastryulka 5L | 1 | 136 000 | delivered |
| 26 134 | 13 191 | 01.03.2025 | Toshkent shahri | Elektronika | Kabel Type-C | 1 | 38000 | delivered |
| 36 542 | 18 436 | 2025-01-03 | Samarqand | Kiyim | Bolalar kiyimi | 1 | 140000 | delivered |
| 33 976 | 17 125 | 2025-02-11 | Andijon | Uy-ro'zg'or | Idish-tovoq 12 dona | 1 | 280000 | delivered |
| 12 147 | 6 097 | 08.01.2025 | Toshkent shahri | Go'zallik | Shampun 400ml | 2 | 49 000 | delivered |
| 2 980 | 1 449 | 2025-01-20 | Namangan | Elektronika | Monitor 24 dyuym | 1 | 1850000 | delivered |
| 21 282 | 10 760 | 10.06.2025 | Toshkent shahri | Uy-ro'zg'or | Stol lampasi | 1 | 112 000 | cancelled |
print("1) Sana formatlari:") nuqtali = sv.order_date.astype(str).str.match(r"\d{2}\.\d{2}\.\d{4}").sum() iso = sv.order_date.astype(str).str.match(r"\d{4}-\d{2}-\d{2}").sum() print(f" nuqtali (26.02.2025): {nuqtali}") print(f" ISO (2025-05-26): {iso}") print(f" boshqa/bo'sh : {len(sv) - nuqtali - iso}") print() print("2) unit_price turi:", sv.unit_price.dtype) print(" bo'shliq bilan yozilgan:", sv.unit_price.astype(str).str.contains(" ").sum(), "ta") print(" namunalar:", sv.unit_price.astype(str).head(4).tolist()) print() print("3) region — nechta xil qiymat:", sv.region.nunique()) print(" lekin haqiqiy viloyatlar soni ancha kam. Mana nega:") for v in sorted(sv.region.dropna().unique())[:9]: print(" ", repr(v)) print(" ... jami", sv.region.nunique(), "xil yozilish") print() print("4) Takrorlangan qatorlar:", int(sv.duplicated().sum())) print() print("5) Bo'sh qiymatlar:") yoq = sv.isna().sum() print(yoq[yoq > 0].to_string()) print() print("6) status (bo'lajak target):") print(sv.status.value_counts(dropna=False).to_string())
1) Sana formatlari:
nuqtali (26.02.2025): 1559
ISO (2025-05-26): 3707
boshqa/bo'sh : 914
2) unit_price turi: object
bo'shliq bilan yozilgan: 1919 ta
namunalar: ['64000', '136 000', '38000', '140000']
3) region — nechta xil qiymat: 61
lekin haqiqiy viloyatlar soni ancha kam. Mana nega:
'ANDIJON'
'Andijon'
'Andijon '
'BUXORO'
'Buxoro'
'Buxoro '
"FARG'ONA"
"Farg'ona"
"Farg'ona "
... jami 61 xil yozilish
4) Takrorlangan qatorlar: 180
5) Bo'sh qiymatlar:
category 185
quantity 124
status 125
6) status (bo'lajak target):
status
delivered 5673
cancelled 215
returned 167
NaN 125
Topshiriq
0-bobdagi 12 qadamni o'zingiz yuring. Hech kim sizga yo'l ko'rsatmaydi. Daftaringizga quyidagilarni yozib boring:
import pandas as pd show(pd.DataFrame([ {"#": 1, "Yozib qo'yiladigan javob": "Nechta qator, nechta ustun"}, {"#": 2, "Yozib qo'yiladigan javob": "\"Men ___ ni ___ dan bashorat qilaman\" — bitta jumla"}, {"#": 3, "Yozib qo'yiladigan javob": "Vazifa turi: regression / binary / multiclass"}, {"#": 4, "Yozib qo'yiladigan javob": "Target nisbati va BAZA raqami"}, {"#": 5, "Yozib qo'yiladigan javob": "Split qildingizmi? stratify kerakmi?"}, {"#": 6, "Yozib qo'yiladigan javob": "Dummy natijasi"}, {"#": 7, "Yozib qo'yiladigan javob": "Qaysi ustunlarni tashladingiz va NEGA"}, {"#": 8, "Yozib qo'yiladigan javob": "Missing va turlar bilan nima qildingiz"}, {"#": 9, "Yozib qo'yiladigan javob": "Birinchi halol natijangiz"}, {"#": 10, "Yozib qo'yiladigan javob": "Qaysi metrikani tanladingiz va NEGA"}, {"#": 11, "Yozib qo'yiladigan javob": "Xatolar qaysi guruhda to'plangan"}, {"#": 12, "Yozib qo'yiladigan javob": "Qaysi BITTA o'zgarishni qildingiz va natija"}, ]))
| # | Yozib qo'yiladigan javob |
|---|---|
| 1 | Nechta qator, nechta ustun |
| 2 | "Men ___ ni ___ dan bashorat qilaman" — bitta jumla |
| 3 | Vazifa turi: regression / binary / multiclass |
| 4 | Target nisbati va BAZA raqami |
| 5 | Split qildingizmi? stratify kerakmi? |
| 6 | Dummy natijasi |
| 7 | Qaysi ustunlarni tashladingiz va NEGA |
| 8 | Missing va turlar bilan nima qildingiz |
| 9 | Birinchi halol natijangiz |
| 10 | Qaysi metrikani tanladingiz va NEGA |
| 11 | Xatolar qaysi guruhda to'plangan |
| 12 | Qaysi BITTA o'zgarishni qildingiz va natija |
Va eng muhim, 13-savol:
Qaysi gipotezangiz RAD ETILDI?
Agar bitta ham rad etilmagan bo'lsa — siz hech narsani sinab ko'rmadingiz. Qaytib boring.
Qo'shimcha topshiriqlar
Bu mashqda 12 qadamdan oldin tozalash bosqichi bor:
show(pd.DataFrame([ {"T": 1, "Tozalash vazifasi": "order_date ni bitta formatga keltirish (ikki format bor)"}, {"T": 2, "Tozalash vazifasi": "unit_price ni songa aylantirish (bo'shliqni olib tashlash)"}, {"T": 3, "Tozalash vazifasi": "region ni normallashtirish — registr, bo'shliq, KIRILL alifbosi"}, {"T": 4, "Tozalash vazifasi": "Takrorlangan qatorlarni olib tashlash"}, {"T": 5, "Tozalash vazifasi": "Bo'sh status bilan nima qilish (u sizning target ingiz)"}, {"T": 6, "Tozalash vazifasi": "Target yaratish: nimani bashorat qilasiz?"}, ]))
| T | Tozalash vazifasi |
|---|---|
| 1 | order_date ni bitta formatga keltirish (ikki format bor) |
| 2 | unit_price ni songa aylantirish (bo'shliqni olib tashlash) |
| 3 | region ni normallashtirish — registr, bo'shliq, KIRILL alifbosi |
| 4 | Takrorlangan qatorlarni olib tashlash |
| 5 | Bo'sh status bilan nima qilish (u sizning target ingiz) |
| 6 | Target yaratish: nimani bashorat qilasiz? |
Kirill alifbosi — eng ko'p o'tkazib yuboriladigan tuzoq
region ustunida Farg'ona ham, Фарғона ham bor. Ular — bitta viloyat, lekin kompyuter uchun ikki xil matn.
.str.lower().str.strip() buni tuzatmaydi — kirill harfi lotin harfiga aylanmaydi.
Agar siz buni o'tkazib yuborsangiz, OneHotEncoder bitta viloyat uchun ikkita ustun yaratadi va model ma'lumotni bo'lib tashlaydi.
Bu — o'zbek ma'lumoti bilan ishlaganda har doim uchraydigan muammo.
Target ni o'zingiz yaratasiz — bu qaror
status da delivered, cancelled, returned va bo'sh qiymat bor. Uchta yo'l:
cancelledni bashorat qilish -> binary, lekin faqat 3.5% — qattiq nomutanosib- Uchta statusni bashorat qilish -> multiclass
- Tushum (
quantity * unit_price) ni bashorat qilish -> regression
Har birining metrikasi boshqa. Tanlang va nega tanlaganingizni yozib qo'ying.
O'zingizni tekshirish: tozalash natijasi
import numpy as np KIRILL = {"андижон": "andijon", "бухоро": "buxoro", "самарқанд": "samarqand", "тошкент шаҳри": "toshkent shahri", "фарғона": "fargona"} def normallashtir(x): if pd.isna(x): return np.nan s = str(x).strip().lower() s = KIRILL.get(s, s) return s.replace("'", "").replace("`", "").replace("ʻ", "") toza = sv.drop_duplicates().copy() toza["region_toza"] = toza.region.map(normallashtir) toza["narx"] = pd.to_numeric( toza.unit_price.astype(str).str.replace(r"[^\d.]", "", regex=True), errors="coerce") toza["sana"] = pd.to_datetime(toza.order_date, format="mixed", dayfirst=True, errors="coerce") print(f"qatorlar : {len(sv)} -> {len(toza)} (dublikat olib tashlandi)") print(f"region xilligi: {sv.region.nunique()} -> {toza.region_toza.nunique()}") print(f"narx turi : {sv.unit_price.dtype} -> {toza.narx.dtype}") print(f"narx NaN : {int(toza.narx.isna().sum())}") print(f"sana NaN : {int(toza.sana.isna().sum())}") print() print("Normallashtirilgandan keyingi viloyatlar:") print(toza.region_toza.value_counts().to_string())
qatorlar : 6180 -> 6000 (dublikat olib tashlandi) region xilligi: 61 -> 14 narx turi : object -> int64 narx NaN : 0 sana NaN : 0 Normallashtirilgandan keyingi viloyatlar: region_toza toshkent shahri 2490 samarqand 559 toshkent viloyati 499 fargona 395 andijon 362 buxoro 287 namangan 277 qashqadaryo 275 navoiy 214 jizzax 176 surxondaryo 155 xorazm 149 qoraqalpogiston 104 sirdaryo 58
region 61 xil yozilishdan 14 ta haqiqiy viloyatga tushdi. Shu — bu mashqning asosiy ishi.
from sklearn.model_selection import train_test_split, cross_val_score from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.impute import SimpleImputer from sklearn.linear_model import LogisticRegression from sklearn.dummy import DummyClassifier from sklearn.metrics import accuracy_score, f1_score, average_precision_score m = toza.dropna(subset=["status", "narx", "sana"]).copy() m["bekor"] = (m.status == "cancelled").astype(int) m["oy"] = m.sana.dt.month m["hafta_kuni"] = m.sana.dt.dayofweek m["quantity"] = m.quantity.fillna(m.quantity.median()) m["tushum"] = m.quantity * m.narx son = ["narx", "quantity", "tushum", "oy", "hafta_kuni"] matn = ["region_toza", "category"] X, y = m[son + matn], m["bekor"] X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y) print(f"Model uchun qatorlar: {len(m)}") print(f"Target 'bekor' ulushi: {y.mean():.2%} <-- qattiq nomutanosib") print() baza = accuracy_score(y_te, DummyClassifier(strategy="most_frequent").fit(X_tr[son], y_tr).predict(X_te[son])) print(f"BAZA accuracy: {baza:.4f} <-- accuracy bu yerda FOYDASIZ")
Model uchun qatorlar: 5880 Target 'bekor' ulushi: 3.61% <-- qattiq nomutanosib BAZA accuracy: 0.9637 <-- accuracy bu yerda FOYDASIZ
quvur = Pipeline([ ("tayyorla", ColumnTransformer([ ("son", Pipeline([("toldir", SimpleImputer(strategy="median")), ("shkala", StandardScaler())]), son), ("matn", Pipeline([("toldir", SimpleImputer(strategy="most_frequent")), ("kod", OneHotEncoder(handle_unknown="ignore"))]), matn), ])), ("model", LogisticRegression(max_iter=3000, class_weight="balanced")), ]).fit(X_tr, y_tr) b = quvur.predict(X_te) e = quvur.predict_proba(X_te)[:, 1] print(f"Accuracy : {accuracy_score(y_te, b):.4f} (baza {baza:.4f})") print(f"F1 (bekor sinfi) : {f1_score(y_te, b, zero_division=0):.4f}") print(f"Average Precision : {average_precision_score(y_te, e):.4f}") print(f"AP bazasi : {y_te.mean():.4f} (= pozitiv ulushi)")
Accuracy : 0.5045 (baza 0.9637) F1 (bekor sinfi) : 0.0858 Average Precision : 0.0544 AP bazasi : 0.0363 (= pozitiv ulushi)
Natijaga qarang va xulosa chiqarishdan qo'rqmang: AP bazaga juda yaqin bo'lsa, demak bu ustunlarda bekor qilishni bashorat qiladigan signal deyarli yo'q.
Bu mashqning eng muhim darsi
Ba'zan to'g'ri javob — "bu ma'lumotda model qurish mumkin emas".
Buni aytish uchun jasorat kerak. Lekin bu halol xulosa, va u menejerga uch haftalik behuda ishdan ko'ra ko'proq foyda keltiradi.
Model qurib bo'lmasligini isbotlash uchun sizga baza, AP va CV kerak — ya'ni 6, 8 va 9-boblar. Siz "ishlamadi" demaysiz, siz o'lchab ko'rsatasiz.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.