merge va concat: jadvallarni birlashtirish
Sahna: ma'lumot bir nechta faylda
Savdo faylida mahsulot raqami bor, nomi yo'q. Filial raqami bor, nomi yo'q. Mahsulot tannarxi — boshqa faylda. Bu tasodif emas: ma'lumotlar bazasida ma'lumot ataylab bo'linadi — har mahsulotning nomini million chekda takrorlamaslik uchun. Tahlil uchun esa jadvallarni qayta birlashtirish kerak. Bu bob — merge, va u yashiradigan ikkita qopqon.
import pandas as pd savdo = pd.read_csv(DATA / "dokon_savdo.csv", parse_dates=["sana"], date_format="%d.%m.%Y") savdo["tushum"] = savdo["miqdor"] * savdo["narx"] mahsulotlar = pd.read_csv(DATA / "mahsulotlar.csv") filiallar = pd.read_csv(DATA / "filiallar.csv") print(savdo.shape, mahsulotlar.shape, filiallar.shape) filiallar
(11352, 9) (11, 4) (4, 4)
| filial_id | nom | tuman | ochilgan | |
|---|---|---|---|---|
| 0 | 1 | Chilonzor | Chilonzor | 2019-03-01 |
| 1 | 2 | Yunusobod | Yunusobod | 2020-07-15 |
| 2 | 3 | Sergeli | Sergeli | 2022-01-10 |
| 3 | 4 | Olmazor | Olmazor | 2023-05-20 |
Birinchi merge va birinchi qopqon
Har qatorga mahsulot nomi va kategoriyasini qo'shamiz:
birga = savdo.merge(mahsulotlar, on="mahsulot_id") print("qatorlar:", len(savdo), "->", len(birga)) print("jami tushum:", savdo["tushum"].sum(), "->", birga["tushum"].sum())
qatorlar: 11352 -> 12536 jami tushum: 564276000 -> 637801000
To'xtang. Biz faqat ustun qo'shdik — qatorlar soni o'zgarmasligi kerak edi. Lekin qatorlar ko'paydi, va jami tushum ham oshib ketdi. Pul havodan paydo bo'lmaydi — demak xato. Sababini qidiramiz:
print(mahsulotlar["mahsulot_id"].is_unique) mahsulotlar[mahsulotlar["mahsulot_id"].duplicated(keep=False)]
False
| mahsulot_id | nom | kategoriya | tannarx | |
|---|---|---|---|---|
| 5 | 6 | Choy | Ichimliklar | 18000 |
| 6 | 6 | Choy (ko'k) | Ichimliklar | 17000 |
Katalogda 6-mahsulot ikki marta yozilgan: "Choy" va "Choy (ko'k)". merge har bir choy sotuvini ikkala yozuv bilan juftlab, qatorni ikkiga ko'paytirdi. Bunday xato Excel dagi VLOOKUP da bo'lmaydi (u birinchisini oladi) — shuning uchun pandas ga o'tganlar uni kutmaydi.
Himoya: validate
try: savdo.merge(mahsulotlar, on="mahsulot_id", validate="many_to_one") except pd.errors.MergeError as xato: print("MergeError:", xato)
MergeError: Merge keys are not unique in right dataset; not a many-to-one merge
validate="many_to_one" — pandas ga kutganimizni aytamiz: chap tomonda bitta mahsulot ko'p marta uchrashi mumkin (ko'p chek), o'ng tomonda esa bir marta. Kutilgan bo'lmasa — jim ko'payish o'rniga darhol xato. Har bir "ma'lumotnoma" jadvali bilan merge da validate yozing.
Katalogni tuzatamiz. Qaysi yozuv to'g'ri — bu biznes savoli (do'kondan so'rash kerak); biz eski yozuvni, ya'ni birinchisini qoldiramiz va qarorimizni yozib qo'yamiz:
katalog = mahsulotlar.drop_duplicates("mahsulot_id", keep="first") birga = savdo.merge(katalog, on="mahsulot_id", validate="many_to_one") print("qatorlar:", len(birga), "| tushum o'zgarmadimi:", birga["tushum"].sum() == savdo["tushum"].sum())
qatorlar: 11352 | tushum o'zgarmadimi: True
Endi qatorlar soni ham, jami tushum ham asl bilan bir xil. Har merge dan keyin shu ikki sonni tekshiring.
Ikkinchi qopqon: yo'qolgan qatorlar
Endi filial nomlari:

merge turlari: inner, left, right, outer — qaysi qatorlar qoladi
ichki = birga.merge(filiallar, on="filial_id", how="inner", suffixes=("", "_filial")) print("inner bilan qatorlar:", len(birga), "->", len(ichki)) print("yo'qolgan tushum:", birga["tushum"].sum() - ichki["tushum"].sum())
inner bilan qatorlar: 11352 -> 10618 yo'qolgan tushum: 35842000
Standart how="inner" — faqat ikkala jadvalda bor kalitlar qoladi. Qatorlar kamaydi, tushumning bir qismi yo'qoldi — xato bermasdan. Qaysilar?
chap = birga.merge(filiallar, on="filial_id", how="left", suffixes=("", "_filial"), indicator=True) print(chap["_merge"].value_counts().to_dict()) print("ro'yxatda yo'q filiallar:", chap.loc[chap["_merge"] == "left_only", "filial_id"].unique())
{'both': 10618, 'left_only': 734, 'right_only': 0}
ro'yxatda yo'q filiallar: [5]
how="left" — chap jadvaldagi hamma qator qoladi; o'ngda jufti yo'qlarga NaN. indicator=True — _merge ustuni har qator qayerdan kelganini aytadi: both (ikkalasida), left_only (faqat chapda). 5-filial savdo faylida bor, filiallar ro'yxatida — yo'q. 14-bobdagi "5-filial juda kam topgan" sirining javobi: u oktyabrda ochilgan va ro'yxatga hali qo'shilmagan.
Qaysi how ni tanlash — savolga bog'liq:
| how | Qoladi | Qachon |
|---|---|---|
inner |
ikkalasida bor | faqat to'liq ma'lumotli qatorlar kerak |
left |
chapdagi hammasi | asosiy jadval chapda, unga ma'lumot qo'shamiz (eng ko'p) |
right |
o'ngdagi hammasi | left ning teskarisi |
outer |
hammasi | ikki ro'yxatni solishtirish: nima qayerda yo'q |
Biz left ni tanlaymiz va yo'q nomni aniq belgilaymiz:
toliq = chap.drop(columns="_merge").assign(nom_filial=lambda d: d["nom_filial"].fillna("Yangi (ro'yxatda yo'q)")) toliq.groupby("nom_filial")["tushum"].sum().div(1e6).round(1).sort_values()
| nom_filial | tushum |
|---|---|
| Yangi (ro'yxatda yo'q) | 35.8 |
| Sergeli | 127.3 |
| Olmazor | 128.4 |
| Yunusobod | 133.3 |
| Chilonzor | 139.5 |
Natija: foyda kategoriya bo'yicha
Endi tannarx ham bor — foydani hisoblash mumkin. Bu savolga bitta jadval javob bera olmasdi:
toliq["foyda"] = (toliq["narx"] - toliq["tannarx"]) * toliq["miqdor"] kat = toliq.groupby("kategoriya")[["tushum", "foyda"]].sum().div(1e6) kat["marja_%"] = (kat["foyda"] / kat["tushum"] * 100).round(1) kat = kat.sort_values("foyda") fig, (chap_ax, ong_ax) = plt.subplots(1, 2, figsize=(11, 3.4), sharey=True) kat[["tushum", "foyda"]].plot(kind="barh", ax=chap_ax, color=["#a8c7fa", "#137333"], title="Tushum va foyda, mln so'm") chap_ax.legend(loc="center right") kat["marja_%"].plot(kind="barh", ax=ong_ax, color="#b06000", title="Foyda marjasi, %") plt.show() kat.round(1)
| kategoriya | tushum | foyda | marja_% |
|---|---|---|---|
| Non mahsulotlari | 14.3 | 4.3 | 30 |
| Ichimliklar | 85.1 | 24.9 | 29.3 |
| Bakaleya | 229.5 | 53.1 | 23.1 |
| Sut mahsulotlari | 235.4 | 64.5 | 27.4 |

Uchta jadval birlashdi va yangi savol ochildi: qaysi kategoriya ko'p pul keltiradi, qaysi biri esa har so'mdan ko'p foyda. Bular har doim bir xil emas — marja chizmasini tushum chizmasi bilan solishtiring.
concat: ustma-ust qo'yish
merge — kalit bo'yicha yoniga qo'shadi. Bir xil tuzilishdagi jadvallarni ostiga qo'shish esa concat:
yanvar = savdo[savdo["sana"].dt.month == 1] fevral = savdo[savdo["sana"].dt.month == 2] ikki_oy = pd.concat([yanvar, fevral], ignore_index=True) print(len(yanvar), "+", len(fevral), "=", len(ikki_oy)) belgili = pd.concat([yanvar, fevral], keys=["yanvar", "fevral"], names=["oy", None]) belgili.groupby(level="oy")["tushum"].sum()
636 + 564 = 1200
| oy | tushum |
|---|---|
| fevral | 27149000 |
| yanvar | 29338000 |
Har oy alohida faylda kelganda (eksportlarda tez-tez) — concat. ignore_index=True — yangi 0, 1, 2... index (aks holda takror index lar, 6-bob mashqidagi kabi). keys= — har qism qaysi fayldan kelganini index ga yozib qo'yadi.
Xulosa
merge(on="kalit", how=...)— kalit bo'yicha yoniga qo'shish;concat— ostiga.- Ma'lumotnomada takror kalit → qatorlar va summalar jimgina ko'payadi. Himoya:
validate="many_to_one". how="inner"(standart) jufti yo'q qatorlarni jimgina tashlaydi. Odatdaleft+indicator=True.- Har merge dan keyin: qatorlar soni va asosiy summa o'zgarmadimi?
- Takror kalit yoki yo'q yozuvni tuzatish — biznes qarori; uni kodda aniq yozing.
Mashqlar
outermerge bilan savdo va filiallar ro'yxatini solishtiring: ro'yxatda bor, lekin savdosi yo'q filial bormi?- Har filial va kategoriya bo'yicha foydani
pivot_tablebilan jadval qiling. - Nega
katalognikeep="first"bilan emas,keep="last"bilan tuzatsak, jami foyda o'zgaradi, tushum esa yo'q?
Javoblar
savdo.merge(filiallar, on="filial_id", how="outer", indicator=True)["_merge"].value_counts()—right_only0: hamma ro'yxatdagi filialda savdo bor.toliq.pivot_table(index="nom_filial", columns="kategoriya", values="foyda", aggfunc="sum").- Tushum savdo faylidagi narxdan hisoblanadi — katalogga bog'liq emas. Foyda esa tannarxga bog'liq, ikki choy yozuvining tannarxi har xil (18 000 va 17 000).
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.