Гузариш ба мундариҷа
Мавзӯи 6 аз 7

Муттаҳидсозии ҷадвалҳо

Барои чӣ ҷадвалҳоро пайваст мекунем

Дар дафтари таъмир рамзи дастгоҳ ва давомнокии кор нигоҳ дошта мешавад, номи дастгоҳ бошад, дар маълумотномаи алоҳида аст. Такрори ном дар ҳар сабт ислоҳро душвор мекунад ва хатари навишти гуногунро зиёд мекунад. Ҷудо нигоҳ доштани додаҳо такрорро кам мекунад, аммо барои ҳисобот онҳоро боз якҷо кардан лозим аст.

Пайвасткунӣ сабтҳоро аз рӯйи қимати умумӣ — калид — мувофиқ мекунад. Ин ба ёфтани варақаи дастгоҳ аз рӯйи рақами бақайдгирӣ монанд аст. Муайян кунед, ки ҳангоми наёфтани варақа кадом сабтҳо мемонанд ва барои як калид чанд мувофиқат иҷозат аст. Агар танҳо қисми нави сабтҳои ҳамсохторро илова кардан лозим бошад, воситаи дигар — concat — даркор аст.

1. merge: мувофиқкунӣ аз рӯйи калид

pd.merge(left, right, on="device") қиматҳои баробари сутуни муайяншударо дар ду ҷадвал меёбад ва майдонҳои ҷуфтҳоро якҷо мекунад. Индекси сатрҳо дар ин ҷо калид нест. Бо нобаёнӣ how="inner" истифода мешавад: танҳо сабтҳои дорои ҷуфт мемонанд. Як дастгоҳ метавонад дар дафтар чанд бор бошад ва ҳар сабт номи онро мегирад.

import pandas as pd

repairs = pd.DataFrame({"device": [20, 10, 20, 30], "minutes": [15, 40, 25, 10]})
catalog = pd.DataFrame({"device": [10, 20, 40], "label": ["Pump", "Fan", "Lamp"]})
joined = pd.merge(repairs, catalog, on="device", validate="many_to_one")
print(joined)

Натиҷа:

   device  minutes label
0      20       15   Fan
1      10       40  Pump
2      20       25   Fan

2. Кадом сабтҳои бе ҷуфт мемонанд

left ҳамаи сатрҳои ҷадвали чапро, right сатрҳои ҷадвали ростро ва outer ҳар дуро нигоҳ медорад. Майдонҳои тарафи номавҷуд NaN мегиранд. Тартиби аргументҳо муҳим аст: агар маълумотнома дар чап бошад, ҳамаи дастгоҳҳои он мемонанд; агар дафтар дар чап бошад, ҳамаи сабтҳои таъмир мемонанд.

indicator=True сутуни ёрирасони _merge-ро илова мекунад: both ҷуфти ёфтшуда ва left_only ё right_only сабти танҳо як тарафро нишон медиҳад. Ин барои санҷиши сифати додаҳо қулай аст. Ин қиматҳо тамғаҳои техникии стандартии pandas мебошанд.

left = pd.merge(repairs, catalog, on="device", how="left")
print(left["device"].tolist())
print(left["label"].isna().tolist())
audit = pd.merge(repairs, catalog, on="device", how="outer", indicator=True)
print(audit[["device", "_merge"]].to_records(index=False).tolist())

Натиҷа:

[20, 10, 20, 30]
[False, False, False, True]
[(10, 'both'), (20, 'both'), (20, 'both'), (30, 'left_only'), (40, 'right_only')]

3. Калидҳои такрорӣ ва санҷиши робита

Агар барои як калид дар чап ду сатр ва дар рост се сатр бошад, пайвасткунӣ шаш ҷуфт медиҳад. Ин ҳазфи такрорҳо нест: ҳар таркиби мувофиқ ба натиҷа медарояд. Аз ин рӯ маблағи ногаҳон зиёдшуда баъди merge аксар вақт аз такрори калид дар маълумотнома хабар медиҳад.

validate="many_to_one" месанҷад, ки дар рост барои ҳар калид бештар аз як сатр набошад. one_to_one ягонагиро аз ҳар ду тараф ва one_to_many аз чап талаб мекунад. Номувофиқатӣ хатои MergeError медиҳад ва ҳисоби нодурустро бозмедорад. Навъҳои калидҳо низ бояд мувофиқ бошанд: сатри "10" ва адади бутуни 10 тарҳи умумии боэътимод намесозанд.

a = pd.DataFrame({"key": [1, 1], "x": [5, 7]})
b = pd.DataFrame({"key": [1, 1, 1], "y": [2, 4, 6]})
print(len(pd.merge(a, b, on="key")))
try:
    pd.merge(a, b, on="key", validate="many_to_one")
except pd.errors.MergeError as error:
    print(type(error).__name__)

Натиҷа:

6
MergeError

4. Номҳои гуногуни калид ва майдонҳои ҳамном

Агар калидҳо номҳои гуногун дошта бошанд, left_on ва right_on диҳед. Барои сутунҳои ғайрикалидии ҳамном pandas пасвандҳои _x ва _y мегузорад; suffixes онҳоро бо номҳои фаҳмо иваз мекунад. Калидро ошкоро муайян кунед: бе on pandas метавонад ҳамаи сутунҳои ҳамномро истифода барад, гарчанде шумо танҳо рамзро муқоиса кардан мехостед.

current = pd.DataFrame({"device_id": [10], "label": ["P1"]})
details = pd.merge(current, catalog, left_on="device_id", right_on="device",
                   suffixes=("_local", "_catalog"))
print(details.columns.tolist())
print(details.loc[0, "label_local"], details.loc[0, "label_catalog"])

Натиҷа:

['device_id', 'label_local', 'device', 'label_catalog']
P1 Pump

5. Аввал ҷамъбаст кардан

Агар барои ҳар дастгоҳ як сатр лозим бошад, аввал дафтарро ба ҷамъбаст табдил дода, баъд ба маълумотнома пайваст кунед. Робитаи «як сатри маълумотнома — як ҷамъбаст» равшантар мешавад. Танҳо ҷойҳои холие бо сифр пур шаванд, ки маънояшон воқеан «ҳодиса набуд» аст. Ивази номи номаълуми дастгоҳ бо сифр номи дуруст намесозад.

summary = repairs.groupby("device", as_index=False)["minutes"].max()
report = pd.merge(catalog, summary, on="device", how="left", validate="one_to_one")
report["minutes"] = report["minutes"].fillna(0).astype(int)
print(report[["label", "minutes"]].to_records(index=False).tolist())

Натиҷа:

[('Pump', 40), ('Fan', 25), ('Lamp', 0)]

6. concat: илова кардани сатрҳо

pd.concat([first, second], ignore_index=True) ҷадвалҳоро зери ҳам мегузорад. Он аз рӯйи қиматҳои сутунҳо ҷуфт намеҷӯяд ва сатрҳои такрориро нест намекунад. Номҳои сутунҳо мувофиқ мешаванд: агар майдон танҳо дар як ҷадвал бошад, дар қисми дигар ҷойи холӣ пайдо мешавад. ignore_index=True сатрҳоро аз нав рақамгузорӣ мекунад; бе он индексҳои пешина мемонанд.

Бо axis=1 ҷадвалҳо паҳлуи ҳам меоянд, вале аз рӯйи индекси сатрҳо мувофиқ мешаванд. Ин аз пайвасткунӣ аз рӯйи сутуни калидӣ фарқ дорад. Беҳтар аст қисмҳоро дар рӯйхат ҷамъ карда, concat-ро як бор даъват кунед, на ин ки ҷадвалро дар давра борҳо калон кунед.

first = pd.DataFrame({"device": [10], "minutes": [5]}, index=[8])
second = pd.DataFrame({"device": [20], "minutes": [7]}, index=[9])
print(pd.concat([first, second], ignore_index=True))
x = pd.DataFrame({"x": [1]}, index=[0])
y = pd.DataFrame({"y": [2]}, index=[1])
print(pd.concat([x, y], axis=1))

Натиҷа:

   device  minutes
0      10        5
1      20        7
     x    y
0  1.0  NaN
1  NaN  2.0

7. Ду ҷадвали CSV дар як матн

Агар вуруд чанд ҷадвал бо сатри ҷудокунанда дошта бошад, аввал тамоми матнро хонед, баъд қисмҳоро ҷудо карда, ҳар кадомро тавассути io.StringIO ба read_csv диҳед. Ҷудокунанда аз рӯйи формати вуруд интихоб мешавад. Дар мисол сатри --- қисми CSV нест ва бояд пеш аз хондани ҷадвалҳо бардошта шавад.

import io

text = "device,minutes\n10,5\n---\ndevice,label\n10,Pump\n"
parts = text.split("---\n")
tables = [pd.read_csv(io.StringIO(part)) for part in parts]
print([table.shape for table in tables])

Натиҷа:

[(1, 2), (1, 2)]

Дар дохил чӣ рӯй медиҳад

Ҳангоми пайвасткунӣ pandas калидҳоро ба мавқеъҳои сатрҳо мувофиқ мекунад. Барои ҳар мувофиқат ҷуфти мавқеъҳо ҳосил мешавад ва майдонҳои натиҷа аз рӯйи онҳо ҷамъ мешаванд. Аз ин рӯ робитаи «бисёр ба бисёр» шумораи сатрҳоро зиёд мекунад, left бошад, барои тарафи рости номавҷуд ҷой медиҳад. concat вазифаи дигар дорад: меҳварҳоро муттаҳид карда, блокҳои додаҳоро дар сутунҳои мувофиқ мегузорад.

Хатоҳои маъмул ва ислоҳи онҳо

  1. Сабтҳои бе ҷуфтро гум кардан. merge бе how навъи inner-ро мегирад. Барои нигоҳ доштани ҳамаи сатрҳои асосӣ он ҷадвалро дар чап гузошта, how="left" диҳед.
  2. Бо маълумотномаи такрорӣ ҷамъҳоро зиёд кардан. Калиди такрорӣ дар рост сатрҳоро зиёд мекунад. catalog["device"].is_unique-ро санҷида, validate="many_to_one" илова кунед; аввал сабаби такрорро фаҳмед.
  3. Калидро муайян накардан. Бо сутунҳои умумии device ва label пайвасткунии ғайриошкор ҳар дуро месанҷад. Агар робита танҳо бо рақам бошад, on="device" нависед.
  4. Ба ҷойи ҷустуҷӯ аз рӯйи калид concat(axis=1) гирифтан. Бо индексҳои гуногун майдонҳо ҷудо шуда, NaN пайдо мешавад. Барои робита аз рӯйи рамз merge лозим аст, на мувофиқати тасодуфии мавқеъҳо.

Ёддошт

  • Калидҳои умумӣ: pd.merge(a, b, on=key, how="inner").
  • Ҳамаи сабтҳои чап: how="left"; ҳар ду тараф: how="outer".
  • Номҳои гуногуни калид: left_on, right_on; майдонҳои ҳамном: suffixes.
  • Санҷиши робита: validate="many_to_one", validate="one_to_one".
  • Санҷиши манбаъ: indicator=True ва сутуни _merge.
  • Иловаи сатрҳо: pd.concat(tables, ignore_index=True).
  • Баъди пайвасткунӣ шумораи сатрҳо, ҷойҳои холӣ ва маънои ҷамъбастро санҷед.