EDA workflow
Oldingi har bir bo'lim bitta tool ni o'rgatdi. Bu bo'lim esa ularni qanday tartibda ishlatishni o'rgatadi.
Exploratory Data Analysis — bu ishning shunday qismiki, unga hech kim alohida ticket yozmaydi va shoshib qolganda hamma uni tashlab ketadi — vaholanki deyarli har qanday jiddiy xato aynan shu yerda ushlanadi. 60% i missing bo'lgan column da o'qitilgan model, ichida ikkita alohida population yashiringan qiymatning o'rtachasini chiqaradigan report, aslida takroran import qilingan "rekord oy" — bularning barchasi yigirma daqiqalik EDA da topiladi va EDA bo'lmasa, barchasi production gacha bemalol yetib boradi.
EDA nima uchun kerak
EDA ning uchta vazifasi bor va ularni ajratib ko'rsatish foydali, chunki o'quvchilar odatda faqat uchinchisini bajarishadi.
| Vazifa | Savol | Sizni nimadan asraydi |
|---|---|---|
| 1. Data ni tushunish | Bitta row nima? Har bir column nimani anglatadi? O'lchov birliklari qanday? | Noto'g'ri savolga mukammal javob berishdan |
| 2. Data ni tekshirish | Nima missing, nima duplicate, nima imkonsiz yoki noto'g'ri dtype da? | Oddiygina xato bo'lgan raqamni ishonch bilan e'lon qilishdan |
| 3. Signal ni topish | Bu yerda aslida qanday bog'liqliklar, guruhlar va trendlar bor? | Sizga topish uchun pul to'langan natijani o'tkazib yuborishdan |
EDA — bu shifokor dori yozishdan oldin qiladigan ish: harorat, puls, qon bosimi, ko'krak qafasini tinglash. Bularning hech biri davolash emas. Ammo qaysi davolash xavfsiz ekanini aynan shular hal qiladi. EDA ni tashlab, to'g'ridan-to'g'ri modelling ga o'tadigan data scientist — bemorni ko'rmasdan turib dori yozayotgan shifokordir.
Siz hali javobni qidirmayapsiz. Siz javobingiz noto'g'ri bo'lishi mumkin bo'lgan sabablarni qidiryapsiz. Shu tarzda qayta ifodalash odamlarning haqiqiy xatti-harakatini o'zgartiradi — ular correlation ga shoshilish o'rniga NULL va duplicate larni ov qila boshlashadi.
Olti bosqichli checklist
O'quvchilarga qat'iy tartib bering. Vaqt tig'iz bo'lganda checklist intuitsiyadan ustun keladi.
Tartib bejiz tanlanmagan. 2-bosqich 3-bosqichni qanday o'qishingizni o'zgartiradi; 3-bosqich 4-bosqichdagi qaysi juftliklarga qarash arziydiganini aytadi; 6-bosqich esa oxirida turadi, chunki normal holat qanday ekanini bilmaguningizcha outlier ni oddiy terish xatosidan ajrata olmaysiz.
1-bosqich — shape va types
import sqlite3 import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt sns.set_theme(style="whitegrid") penguins = sns.load_dataset("penguins") print(penguins.shape) # (344, 7) — row lar, column lar penguins.head() # bitta row aslida qanday ko'rinadi? penguins.info() # dtype lar + non-null count lar, bitta ko'rinishda penguins.describe() # numeric xulosa penguins.describe(include="object") # categorical xulosa
(344, 7) <class 'pandas.core.frame.DataFrame'> RangeIndex: 344 entries, 0 to 343 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 species 344 non-null object 1 island 344 non-null object 2 bill_length_mm 342 non-null float64 3 bill_depth_mm 342 non-null float64 4 flipper_length_mm 342 non-null float64 5 body_mass_g 342 non-null float64 6 sex 333 non-null object dtypes: float64(4), object(3) memory usage: 18.9+ KB
info() — pandas dagi eng qimmatli bitta chaqiruv: u sizga row soni, har bir column nomi, har bir dtype va non-null count ni bir vaqtning o'zida beradi. Uni hamma narsadan oldin o'qing.
Har qanday statistikadan oldin jumlani ovoz chiqarib ayting: "bitta row — bitta penguin", "bitta row — bitta enrollment", "bitta row — kuniga bitta product". Agar bu jumlani tugata olmasangiz, demak table ni hali tushunmagansiz va hisoblagan har bir o'rtachangiz noto'g'ri maxrajga bo'lingan bo'ladi.
JOIN aynan shu yerda sizni tishlaydi: join dan keyin bitta row endi bitta student bo'lmasligi mumkin — u har bir enrollment uchun bitta student bo'lib qolgan bo'lishi mumkin. Bu 5-bo'limdagi fan-out ning boshqa tomondan ko'rinishi.
Doimiy ahamiyatga ega uchtasi:
- Sanalar
objectsifatida —pd.to_datetime()qilinmaguncha vaqtga oid hech narsa ishlamaydi. - Raqamlar
objectsifatida — odatda adashib kirib qolgan"N/A", mingliklar ajratgichi yoki valyuta belgisi sabab.pd.to_numeric(col, errors="coerce")keraksiz qiymatlarni NaN ga aylantiradi va siz ularni sanay olasiz. - Butun sonlar
float64sifatida — bu SQL dagiNULLningNaNbo'lib kelishi (12-bo'lim). Bu xato emas, lekin column da missing qiymatlar borligidan signal.
2-bosqich — missing data
# har bir column da qancha missing bor: son va foiz sifatida miss = pd.DataFrame({ "missing": penguins.isna().sum(), "pct": (penguins.isna().mean() * 100).round(1) }).sort_values("missing", ascending=False) print(miss) # qaysi row lar to'liq emas? penguins[penguins.isna().any(axis=1)]
missing pct sex 11 3.2 bill_depth_mm 2 0.6 bill_length_mm 2 0.6 flipper_length_mm 2 0.6 body_mass_g 2 0.6 island 0 0.0 species 0 0.0
Keyin sanoqdan ham muhimroq savolni bering: u tasodifiy tarzda missing bo'lganmi? Agar missing row lar bitta guruhda to'plangan bo'lsa, ularni tashlab yuborish keyingi barcha hisob-kitoblarni sezdirmasdan buzadi.
# missing qiymatlar species bo'yicha bir tekis tarqalganmi?
penguins.assign(missing_sex=penguins.sex.isna()) \
.groupby("species")["missing_sex"].mean().round(3)
df.dropna() hech bo'lmasa bitta missing qiymati bor har bir row ni o'chiradi. Keng table da bu bitta yomon column ni tuzatish uchun data ning katta qismini yo'q qilishi mumkin — va buni sezdirmasdan qiladi. Har doim oldingi va keyingi shape ni chop eting:
python run
before = len(df)
df = df.dropna(subset=["body_mass_g"]) # faqat sizga haqiqatan kerak bo'lgan column
print(f"dropped {before - len(df)} of {before} rows")
To'ldirish ham xuddi shunday qaror. Bahoda fillna(0) "hali baholanmagan" ni "nol olgan" ga aylantiradi — bu 3-bo'limdagi COALESCE(grade, 0) tuzog'ining aynan o'zi. Mean bilan, median bilan, guruh median i bilan to'ldiring yoki NULL holida qoldiring — lekin tanlang va nima uchun tanlaganingizni yozib qo'ying.
3-bosqich — bir vaqtda bitta o'zgaruvchi
Har qanday bog'liqlikdan oldin har bir column ga alohida qarang. Numeric column da sizga shakl, markaz, tarqoqlik va dumlar kerak:
fig, axes = plt.subplots(1, 3, figsize=(15, 4)) sns.histplot(data=penguins, x="body_mass_g", kde=True, ax=axes[0]) sns.boxplot(data=penguins, y="body_mass_g", ax=axes[1]) sns.ecdfplot(data=penguins, x="body_mass_g", ax=axes[2]) plt.tight_layout()
Categorical column da esa sizga darajalar va ularning muvozanati kerak:
penguins["species"].value_counts() penguins["species"].value_counts(normalize=True).round(3) # proportion sifatida sns.countplot(data=penguins, x="species", hue="species", legend=False)
Har bir numeric column uchun yozib qo'yiladigan to'rt narsa: u simmetrikmi yoki skew ga egami, u bitta cho'qqilimi yoki ko'p cho'qqilimi, qiymatlar oralig'i ishonarlimi va unda nechta turli qiymat bor (3 ta turli qiymatga ega "numeric" column aslida category dir).
O'ngga skew qilgan column da (daromad, narx, sessiya davomiyligi) uzun dum mean ni median dan yuqoriga tortadi. Ta'sischisi ham hisobga olingan kompaniyaning o'rtacha maoshini e'lon qilish texnik jihatdan to'g'ri, amalda esa halol emas. Mean va median sezilarli farq qilganda buni report da aytib o'ting va median ni afzal ko'ring.
4-bosqich — ikkita o'zgaruvchi
Bor-yo'g'i uchta birikma mavjud va har birining o'z standart chart i bor.
| Juftlik | Chart | Statistika | Bo'lim |
|---|---|---|---|
| numeric × numeric | scatterplot, regplot, jointplot |
Correlation | 15, 19 |
| numeric × categorical | boxplot, violinplot, barplot |
Guruh mean/median lari | 17 |
| categorical × categorical | hue bilan countplot, crosstab ning heatmap i |
Cross-tabulation | 17, 18 |
# numeric x numeric sns.scatterplot(data=penguins, x="flipper_length_mm", y="body_mass_g", hue="species") # numeric x categorical sns.boxplot(data=penguins, x="species", y="body_mass_g", hue="species", legend=False) # categorical x categorical — xom count emas, proportion lar ct = pd.crosstab(penguins["island"], penguins["species"], normalize="index") sns.heatmap(ct, annot=True, fmt=".2f", cmap="Blues")
island × species bo'yicha xom count heatmap i asosan qaysi orolda ko'proq penguin borligini ko'rsatadi. normalize="index" har bir row ni proportion larga aylantiradi va "bu orolda species aralashmasi qanday?" degan savol aynan shuni so'raydi. "index", "columns" va "all" orasidan tanlash — bu ayni analitik savolning o'zi.
5-bosqich — bir vaqtda ko'p o'zgaruvchi
# correlation matrix — pandas 2+ da numeric_only majburiy corr = penguins.corr(numeric_only=True) sns.heatmap(corr, annot=True, fmt=".2f", cmap="coolwarm", center=0, vmin=-1, vmax=1) # barcha numeric juftliklar birdaniga, guruh bo'yicha ranglangan sns.pairplot(penguins, hue="species", diag_kind="kde", corner=True)
hue bilan chizilgan pairplot ko'pincha butun tahlildagi eng ko'p ma'lumot beradigan yagona chart bo'ladi. Penguins data sida u bir qarashda uchta species ning bill va flipper o'lchovlari bo'yicha deyarli mukammal ajralishini ko'rsatadi — bu esa butun natijaning o'zi.
Umumiy holda kuzatiladigan bog'liqlik har bir subgroup ichida teskarisiga aylanishi mumkin. Penguins data sida bill length va bill depth umumiy holda manfiy correlation ga ega ko'rinadi — ammo har bir species ichida bu bog'liqlik aniq musbat. Umumiy correlation esa Gentoo penguin larining uzun va yassi bill ga ega bo'lishidan kelib chiqqan sun'iy natija.
Birlashtirilgan scatter dan chiqarilgan har qanday xulosa aynan teskari bo'lar edi. Correlation ga ishonishdan oldin doim eng aniq guruhlovchi o'zgaruvchi bo'yicha rang bering.
6-bosqich — outlier lar va data sifati
Outlier avtomatik ravishda xato degani emas. Ularni uchta guruhga ajrating:
| Turi | Misol | Nima qilish kerak |
|---|---|---|
| Imkonsiz | 200 yosh; manfiy narx; 2154-yildagi sana | Tuzating yoki olib tashlang — bu kiritish yoki o'lchov birligi xatosi |
| Sentinel qiymat | "noma'lum" uchun -999, 0, 1900-01-01 |
Mean ni zaharlamasdan oldin NaN ga aylantiring |
| Haqiqatan ekstremal | Daromadning 40% ini beradigan bitta mijoz | Saqlab qoling — ko'pincha natijaning o'zi shu |
# standart 1.5 x IQR qoidasi — boxplot mo'ylovlari ishlatadigan aynan shu qoida q1, q3 = penguins["body_mass_g"].quantile([0.25, 0.75]) iqr = q3 - q1 lo, hi = q1 - 1.5 * iqr, q3 + 1.5 * iqr outliers = penguins[(penguins.body_mass_g < lo) | (penguins.body_mass_g > hi)] print(f"{len(outliers)} outliers outside [{lo:.0f}, {hi:.0f}]") # sentinel ovi: shubhali darajada butun yoki takrorlanuvchi qiymatlar penguins["body_mass_g"].value_counts().head(10)
0 outliers outside [1750, 6550]
Nuqtalarni noqulay bo'lgani uchun olib tashlash — bu soxtalashtirish. Qiymatni faqat u nima uchun noto'g'ri ekanini ayta olganingizda olib tashlang: o'lchov birligi xatosi, test yozuvi, imkonsiz sana. Agar u shunchaki ekstremal bo'lsa, uni saqlab qoling va log shkala, mustahkam statistika (median, IQR) yoki report dagi izohni ko'rib chiqing. "Men yuqori 1% ni tashlab yubordim" — bu, agar shunday qilingan bo'lsa, metodologiyangizda albatta paydo bo'lishi shart bo'lgan jumla.
Data sifatini tekshirishning qolgan qismi tez bajariladi va har safar qilishga arziydi:
df.duplicated().sum() # aynan bir xil duplicate row lar
df.duplicated(subset=["student_id", "course_id"]).sum() # business-key duplicate lari
df["city"].str.strip().str.lower().nunique() # 'Tashkent ' va 'tashkent' va 'Tashkent'
df.nunique() # o'zgarmas column lar (1) yoki ID ga o'xshash column lar (== len(df))
Qaysi savolga qaysi plot Sinf devoriga osib qo'yiladigan jadval.
| Savolingiz | Data | Plot |
|---|---|---|
| Bu qanday tarqalgan? | 1 numeric | histplot, kdeplot, ecdfplot |
| Har biridan nechtadan bor? | 1 categorical | countplot |
| Bu ikkisi birga harakatlanadimi? | 2 numeric | scatterplot, jointplot |
| Trend bormi? | 2 numeric | regplot, lmplot |
| Vaqt o'tishi bilan qanday o'zgaradi? | vaqt + numeric | lineplot |
| Guruhlar orasida farq qiladimi? | category + numeric | boxplot, violinplot |
| Har bir guruhdagi o'rtacha qancha? | category + numeric | barplot (count emas, mean) |
| Ikkita category qanday o'zaro ta'sir qiladi? | 2 categorical | crosstab ning heatmap i |
| Umumiy holda nima nima bilan bog'liq? | ko'p numeric | correlation heatmap, pairplot |
| Pattern har bir subgroup da saqlanadimi? | istalgan narsa + category | col= faceting, FacetGrid |
| Ekstremal qiymatlar qayerda? | 1 numeric | boxplot |
To'liq ishlangan EDA
titanic dataset ida yigirma daqiqa, checklist ni boshdan oxirigacha bajarib. Bu — darsda jonli ravishda ishga tushiriladigan variant.
1–2-bosqichlar: shape, types, missing data
titanic = sns.load_dataset("titanic") print(titanic.shape) # (891, 15) — bitta row bitta yo'lovchi titanic.info() pd.DataFrame({"missing": titanic.isna().sum(), "pct": (titanic.isna().mean()*100).round(1)}) \ .query("missing > 0").sort_values("missing", ascending=False)
(891, 15) <class 'pandas.core.frame.DataFrame'> RangeIndex: 891 entries, 0 to 890 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 survived 891 non-null int64 1 pclass 891 non-null int64 2 sex 891 non-null object 3 age 714 non-null float64 4 sibsp 891 non-null int64 5 parch 891 non-null int64 6 fare 891 non-null float64 7 embarked 889 non-null object 8 class 891 non-null category 9 who 891 non-null object 10 adult_male 891 non-null bool 11 deck 203 non-null category 12 embark_town 889 non-null object 13 alive 891 non-null object 14 alone 891 non-null bool dtypes: bool(2), category(2), float64(2), int64(4), object(5) memory usage: 80.7+ KB
columnmissingpctnima qilish kerak
deck68877.2Shu holicha ishlatish uchun juda siyrak — tashlab yuboring yoki "unknown" ni alohida daraja sifatida qarang
age17719.9Sezilarli miqdor. Row larni o'chirmang — impute qiling yoki ular bilan va ularsiz tahlil qiling
embarked / embark_town20.2Ahamiyatsiz — bu ikki row ni xavfsiz tashlab yuborsa bo'ladi
Darhol e'tibor bering: 891 − 177 = 714. Ya'ni yosh haqidagi har qanday xulosa 891 emas, 714 yo'lovchiga asoslangan. Bu jumla report ga kirishi kerak.
3-bosqich: bir vaqtda bitta o'zgaruvchi
fig, axes = plt.subplots(1, 3, figsize=(15, 4)) sns.histplot(data=titanic, x="age", bins=30, kde=True, ax=axes[0]) sns.countplot(data=titanic, x="pclass", hue="pclass", legend=False, ax=axes[1]) sns.histplot(data=titanic, x="fare", bins=40, ax=axes[2]) axes[2].set_title("fare is heavily right-skewed") plt.tight_layout() titanic["survived"].value_counts(normalize=True).round(3) # ~38% omon qolgan
Age taxminan simmetrik, yosh bolalar tomonida kichik do'nglik bilan. Fare esa kuchli o'ngga skew qilgan — bir nechta juda qimmat chipta bor — shuning uchun uning mean i median idan ancha yuqori va har qanday "o'rtacha fare" ehtiyotkorlikni talab qiladi.
4-bosqich: ikkita o'zgaruvchi — asosiy savol
fig, axes = plt.subplots(1, 3, figsize=(15, 4)) sns.barplot(data=titanic, x="sex", y="survived", hue="sex", legend=False, ax=axes[0]) sns.barplot(data=titanic, x="pclass", y="survived", hue="pclass", legend=False, ax=axes[1]) sns.boxplot(data=titanic, x="survived", y="age", hue="survived", legend=False, ax=axes[2]) axes[0].set_title("survival rate by sex") axes[1].set_title("survival rate by class") plt.tight_layout()
survived 0/1 sifatida kodlangan, shuning uchun uning mean i omon qolish darajasidir — 0.74 degani 74% omon qolgan. 0/1 column ning barplot i proportion chart idir va undagi error bar lar shu proportion uchun haqiqiy confidence interval hisoblanadi. Bu butun qo'llanmadagi eng foydali usullardan biri.
5-bosqich: ko'p o'zgaruvchi va o'zaro ta'sir
# class sex ning ta'sirini o'zgartiradimi? (ha — va juda kuchli) sns.catplot(data=titanic, x="pclass", y="survived", hue="sex", kind="bar", height=4, aspect=1.3) # xuddi shu narsa raqamlar to'ri ko'rinishida pd.crosstab(titanic["sex"], titanic["pclass"], values=titanic["survived"], aggfunc="mean").round(2)
Natija: omon qolish asosan sex bilan belgilanadi va class uni kuchaytiradi yoki susaytiradi. Birinchi class dagi ayollar uchinchi class dagilarga qaraganda ancha yuqori darajada omon qolishgan; uchinchi class dagi erkaklar esa eng yomon ahvolda bo'lgan. Bitta o'zgaruvchili xulosa bu o'zaro ta'sirni butunlay o'tkazib yuborar edi — 5-bosqich aynan shuning uchun mavjud.
6-bosqich: outlier lar va sifat
titanic[titanic["fare"] > 300][["pclass", "fare", "survived"]] # haqiqiy, xato emas
(titanic["fare"] == 0).sum() # 15 ta nol fare — ekipajmi yoki xodimlarmi? tekshiring
titanic.duplicated().sum()
£512 lik fare lar haqiqiy birinchi class chiptalari, shuning uchun ular joyida qoladi. Nol fare lar esa qiziqarli anomaliya — izohga arziydi va aynan shunday narsa faqat siz qarab chiqqaningiz uchun yuzaga chiqadi.
EDA anti-pattern lari
| Anti-pattern | Nega zarar keltiradi | Buning o'rniga |
|---|---|---|
| To'g'ridan-to'g'ri modelling ga o'tish | Kirishda chiqindi bo'lsa, chiqishda ishonchli chiqindi bo'ladi | Avval olti bosqichni bajaring — bu yigirma daqiqa |
Refleks tarzida df.dropna() |
Data ning katta qismini sezdirmasdan o'chirib, qolganini buzib qo'yishi mumkin | Kerakli column ga subset= bering; yo'qotilgan row larni chop eting |
n va tarqoqlik ko'rsatmasdan mean e'lon qilish |
3 ta qiymatning o'rtachasi 3 000 ta qiymatning o'rtachasidan farq qilmay ko'rinadi | Doim count va tarqoqlik o'lchovini ko'rsating |
hue ni hech qachon ishlatmaslik |
Simpson paradoksi xulosangizni teskarisiga aylantiradi | Eng aniq guruhlovchi o'zgaruvchi bo'yicha rang bering |
| Noqulay outlier larni o'chirish | Soxtalashtirish | Faqat noto'g'ri ekanini isbotlay olganingizni olib tashlang; buni hujjatlashtiring |
| dtype larga qaramasdan plot chizish | Sanalar string sifatida alifbo bo'yicha saralanadi; matn sifatidagi raqamlar aggregation ga tushmaydi | Har safar avval info() |
| Scatter ga qaramasdan correlation ga ishonish | Anscombe (19-bo'lim) | r ni keltirishdan oldin juftlikni plot qiling |
| Butun table ni pandas ga tortib olish | Sekin ishlaydi yoki xotira yetmaydi | Avval SQL da aggregation qiling (12-bo'lim) |
| Hech narsa yozib bormaslik | O'sha row larni nega tashlaganingizni eslay olmaysiz | Ish davomida natijalar ro'yxatini yuritib boring |
EDA davomida yoziladigan raqamlangan ro'yxat, har bir kuzatish uchun bitta qator: "1. yo'lovchilarning 20% ida age missing; 2. fare o'ngga skew qilgan, median ishlatilsin; 3. 15 ta fare aniq 0 — sababi noma'lum; 4. omon qolish sex va class ga bog'liq va ular o'zaro ta'sir qiladi."
Aynan o'sha ro'yxat — yakuniy mahsulot. Chart lar esa siz uni qanday topganingiz, ammo hech kim yigirmata chart ni o'qimaydi — odamlar to'rtta jumlani o'qishadi.
Xulosa
- EDA ning uchta vazifasi bor: data ni tushunish, data ni tekshirish, signal ni topish. Ko'pchilik faqat uchinchisini bajaradi.
- Checklist ni tartib bilan bajaring: shape & types → missing → bitta o'zgaruvchi → ikkita o'zgaruvchi → ko'p o'zgaruvchi → outlier lar.
- Doim avval "bitta row nima?" degan savolga javob bering. JOIN dan keyin javob o'zgargan bo'lishi mumkin — bu 5-bo'limdagi fan-out.
info()— pandas dagi eng qimmatli yagona chaqiruv: row lar, dtype lar va non-null count lar birdaniga.- Missing data — bu natija, zerikarli yumush emas. Qanchaligini o'lchang, tasodifiymi yoki yo'qligini tekshiring va
dropna()/fillna()ni hujjatlashtiriladigan qarorlar sifatida qarang. - Column skew ga ega bo'lsa median ni e'lon qiling va n siz hech qachon mean bermang.
- Doim eng aniq guruh bo'yicha rang bering — Simpson paradoksi birlashtirilgan correlation ni butunlay teskarisiga aylantirishi mumkin.
- Outlier larni imkonsiz, sentinel va haqiqatan ekstremal turlarga ajrating. Faqat dastlabki ikkitasi olib tashlanadi va faqat asos bilan.
- 0/1 column ning
barploti — proportion chart i, bu yerdagi eng foydali usullardan biri. - Yakuniy mahsulot — chart lar emas, natijalar ro'yxati.
Keyingisi: 23-bo'lim barcha Seaborn gotcha larini bitta takrorlash katalogiga jamlaydi va II qismni yakunlaydi.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.