Asosiy qismga o'tish

Mashq 2: iflos savdo ma'lumoti

42-bob 7 kod bloki 2205 so'z

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

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

python8 qator
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))
Chiqish
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_idorder_idorder_dateregioncategoryproduct_namequantityunit_pricestatus
16 3868 2362025-05-26BuxoroOziq-ovqatMakaron 5kg564000cancelled
5 9342 97026.02.2025ФарғонаUy-ro'zg'orKastryulka 5L1136 000delivered
26 13413 19101.03.2025Toshkent shahriElektronikaKabel Type-C138000delivered
36 54218 4362025-01-03SamarqandKiyimBolalar kiyimi1140000delivered
33 97617 1252025-02-11AndijonUy-ro'zg'orIdish-tovoq 12 dona1280000delivered
12 1476 09708.01.2025Toshkent shahriGo'zallikShampun 400ml249 000delivered
2 9801 4492025-01-20NamanganElektronikaMonitor 24 dyuym11850000delivered
21 28210 76010.06.2025Toshkent shahriUy-ro'zg'orStol lampasi1112 000cancelled
python25 qator
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())
Chiqish
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:

python15 qator
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
1Nechta qator, nechta ustun
2"Men ___ ni ___ dan bashorat qilaman" — bitta jumla
3Vazifa turi: regression / binary / multiclass
4Target nisbati va BAZA raqami
5Split qildingizmi? stratify kerakmi?
6Dummy natijasi
7Qaysi ustunlarni tashladingiz va NEGA
8Missing va turlar bilan nima qildingiz
9Birinchi halol natijangiz
10Qaysi metrikani tanladingiz va NEGA
11Xatolar qaysi guruhda to'plangan
12Qaysi 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:

python8 qator
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?"},
]))
TTozalash vazifasi
1order_date ni bitta formatga keltirish (ikki format bor)
2unit_price ni songa aylantirish (bo'shliqni olib tashlash)
3region ni normallashtirish — registr, bo'shliq, KIRILL alifbosi
4Takrorlangan qatorlarni olib tashlash
5Bo'sh status bilan nima qilish (u sizning target ingiz)
6Target 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:

  1. cancelled ni bashorat qilish -> binary, lekin faqat 3.5% — qattiq nomutanosib
  2. Uchta statusni bashorat qilish -> multiclass
  3. Tushum (quantity * unit_price) ni bashorat qilish -> regression

Har birining metrikasi boshqa. Tanlang va nega tanlaganingizni yozib qo'ying.

O'zingizni tekshirish: tozalash natijasi

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

python26 qator
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")
Chiqish
Model uchun qatorlar: 5880
Target 'bekor' ulushi: 3.61%   <-- qattiq nomutanosib

BAZA accuracy: 0.9637   <-- accuracy bu yerda FOYDASIZ
python16 qator
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)")
Chiqish
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.

Telegramda yozish Narxlar