Python: деректен өз есебіңізге дейін 29-сабақ (барлығы 56)
Топтау және жиынтық сандар: санаттар мен айлар бойынша санау
Python курсының жиырма сегізінші сабағы. `groupby` — сұрақ болып жазылған цикл: кестені кілт бойынша кесу, әр бөлігін санау, кері жинау. `agg` пен өз баған аттары, екі кілт, кестеде жоқ кілт, үлес үшін `transform`, `count` пен `size`-дың айырмасы және үнсіз жоғалатын топтар.
Не үшін керек
Өткен сабақ керекті жолдарды қалдырды. Бұл сабақ оларды әдетте не үшін қалдыратынына жауап береді: әр санат бойынша қанша, әр қала бойынша қанша, әр ай бойынша қанша.
Циклмен бұл былай жазылады: бос сөздік, жолдарды аралау, жинақтау, сосын орташаны санау үшін екінші айналым. Мұндай циклді біз бірнеше рет жаздық — алтыншы сабақта ол тақырыптың өзі болды. groupby соны бір жолмен істейді, әрі ондағы бастысы қысқалық емес, сұрақтың тұтас көрініп тұруы: нені кілт етеміз және нені санаймыз.
Жиырма екінші сабақтағы GROUP BY есіңізде болса — бұл сол, тек бағдарламаңыздың жадындағы кестенің үстінде.
Бірден тұтас
gruppy.py файлы. Екі айдағы он екі түбіртек және оларды қосудың жеті тәсілі.
"""28-сабақ: топтау және жиынтық сандар.
Екі айдағы он екі түбіртек. Санаттар, қалалар және айлар бойынша санаймыз —
бірде-бір циклсіз.
"""
import pandas as pd
rows = [
("2026-01-03", "Қостанай", "тамақ", 4200),
("2026-01-05", "Қостанай", "жанармай", 18500),
("2026-01-07", "Рудный", "тамақ", 2600),
("2026-01-09", "Қостанай", "байланыс", 4990),
("2026-01-15", "Қостанай", "жалдау", 95000),
("2026-01-18", "Рудный", "жанармай", 16200),
("2026-01-24", "Қостанай", "тамақ", 3100),
("2026-02-02", "Рудный", "тамақ", 2800),
("2026-02-06", "Қостанай", "жанармай", 19100),
("2026-02-11", "Қостанай", "байланыс", 4990),
("2026-02-15", "Қостанай", "жалдау", 95000),
("2026-02-20", "Рудный", "жалдау", 62000),
]
df = pd.DataFrame(rows, columns=["day", "city", "kind", "amount"])
df["day"] = pd.to_datetime(df["day"])
print("== санаттар бойынша сома")
print(df.groupby("kind")["amount"].sum().sort_values(ascending=False))
print()
print("== бірден үш сан")
print(df.groupby("kind")["amount"].agg(["count", "sum", "mean"]).round(0))
print()
print("== баған аттары өз атауымен")
report = df.groupby("kind").agg(
түбіртек=("amount", "count"),
барлығы=("amount", "sum"),
орташа=("amount", "mean"),
).round(0)
print(report.sort_values("барлығы", ascending=False))
print()
print("== екі кілт")
by_two = df.groupby(["city", "kind"])["amount"].sum()
print(by_two)
print("белгілер:", type(by_two.index).__name__)
print()
print("== қайтадан жазық кестеге")
print(by_two.reset_index().head(3))
print()
print("== айлар бойынша: кестеде жоқ кілт")
by_month = df.groupby(df["day"].dt.to_period("M"))["amount"].agg(["count", "sum"])
print(by_month)
print()
print("== count бос емес мәндерді санайды, size — жолдарды")
with_gap = df.copy()
with_gap.loc[6, "amount"] = None
print("count:", with_gap.groupby("kind")["amount"].count().to_dict())
print("size: ", with_gap.groupby("kind").size().to_dict())
print()
print("== қала ішіндегі санаттың үлесі")
df["үлесі"] = (df["amount"] / df.groupby("city")["amount"].transform("sum") * 100).round(1)
print(df.loc[df["city"] == "Рудный", ["kind", "amount", "үлесі"]].to_string(index=False))
Шығысы:
== санаттар бойынша сома
kind
жалдау 252000
жанармай 53800
тамақ 12700
байланыс 9980
Name: amount, dtype: int64
== бірден үш сан
count sum mean
kind
байланыс 2 9980 4990.0
жалдау 3 252000 84000.0
жанармай 3 53800 17933.0
тамақ 4 12700 3175.0
== баған аттары өз атауымен
түбіртек барлығы орташа
kind
жалдау 3 252000 84000.0
жанармай 3 53800 17933.0
тамақ 4 12700 3175.0
байланыс 2 9980 4990.0
== екі кілт
city kind
Рудный жалдау 62000
жанармай 16200
тамақ 5400
Қостанай байланыс 9980
жалдау 190000
жанармай 37600
тамақ 7300
Name: amount, dtype: int64
белгілер: MultiIndex
== қайтадан жазық кестеге
city kind amount
0 Рудный жалдау 62000
1 Рудный жанармай 16200
2 Рудный тамақ 5400
== айлар бойынша: кестеде жоқ кілт
count sum
day
2026-01 7 144590
2026-02 5 183890
== count бос емес мәндерді санайды, size — жолдарды
count: {'байланыс': 2, 'жалдау': 3, 'жанармай': 3, 'тамақ': 3}
size: {'байланыс': 2, 'жалдау': 3, 'жанармай': 3, 'тамақ': 4}
== қала ішіндегі санаттың үлесі
kind amount үлесі
тамақ 2600 3.1
жанармай 16200 19.4
тамақ 2800 3.3
жалдау 62000 74.2
Талдау
Кесу, санау, жинау
df.groupby("kind")["amount"].sum() үш нәрсе істейді: кестені kind мәні бойынша бөліктерге кеседі, әрқайсысында соманы санайды, бөліктерді қайта бір қатарға жинайды. Кілт белгіге айналады — жиырма бесінші сабақтағы сол индекске, — сондықтан жауапты бірден сұрыптауға, сүзуге және дәл сондай басқа қатармен қосуға болады.
Топтардың әдепкі реті — жауаптың шамасы бойынша емес, кілттің өсуі бойынша. Рейтинг керек болса, ол бөлек сұралады: .sort_values(ascending=False).
Бірден бірнеше сан және өз аттары
agg(["count", "sum", "mean"]) біреудің орнына үш баған береді. Олардың аттары — функция аттары, әрі есепте олар нобай болып көрінеді.
Атаулы түрі есептің анықтамасындай оқылады:
df.groupby("kind").agg(
түбіртек=("amount", "count"),
барлығы=("amount", "sum"),
орташа=("amount", "mean"),
)
Сол жағында — жауаптағы баған аты, оң жағында «қай баған бойынша санау» және «немен санау» жұбы. Бір agg ішінде әртүрлі бағанды араластыруға болады: қымбаты=("amount", "max"), бірінші_күн=("day", "min").
count бос емес мәндерді санайды, size — барлық жолды
Айырмашылық сырттай ғана емес. count бар мәндерді санайды — кез келгенін: сандарды да, жолдарды да, күндерді де; жетіспейтінін өткізіп жібереді. size топтың жолдарын тұтас санайды. Екеуінің арасындағы айырма — сіз санаған бағандағы жетіспейтін мәндердің саны. Мысалда бір мән өшірілгеннен кейін «тамақта» count 3, ал size 4 береді.
Өтініш түрі де содан өзгеше: count бағаннан сұралады (groupby(...)["amount"].count()), ал size топтың өзінен — оған баған керек емес.
Бұл — үнсіз қате жауап алудың бірінші тәсілі. Екіншісі одан жаман.
Жоғалып кететін топтар
groupby әдепкі күйінде кілті бос жолдарды лақтырып тастайды. Сонда топтар бойынша сома жалпы сомамен сәйкес келмейді, әрі бұл туралы ештеңе айтылмайды:
df["amount"].sum() → 1000
df.groupby("kind")["amount"].sum().sum() → 800
Емі — dropna=False параметрі: жетіспейтін мән бөлек топқа айналады. Әдетке айналдыруға тұрарлық нәрсе: топтаудан кейін қорытындыны бүкіл кесте бойынша қорытындымен салыстырып отыру. Олар ажырасса — қайдан іздеу керегін білесіз.
Екі кілт және екі қабатты белгі
groupby(["city", "kind"]) MultiIndex береді: жол белгісі жұптан тұрады. Оны қарауға ыңғайлы, әрі әрі қарай беруге ыңғайсыз, сондықтан көбіне бірден .reset_index() жазылады — сонда кілттері екі баған болып тұрған кәдімгі жазық кесте шығады.
Кестеде жоқ кілт
Топтауды бағанмен ғана емес, сол ұзындықтағы кез келген қатармен жасауға болады. «Айлар бойынша» содан: df["day"].dt.to_period("M") күнді айға айналдырады, сол бойынша топтаймыз — кестеде «ай» деген баған болмаса да.
to_period("M") "2026-01" деген жол емес, кезең береді: ол мәтін ретінде емес, уақыт ретінде сұрыпталады әрі ай санымен қосыла алады. Күндер бойынша топтауға dt.date, жылдар бойынша dt.year, апта күндері бойынша dt.day_name() бар.
transform: топтың жауабы әр жолдың қасында
agg топты бір санға сығады. transform жол саны қанша болса, сонша мән қайтарады — сол топтық санның өз тобының жолдары бойынша көбейтілген түрі. Үлес содан шығады:
df["amount"] / df.groupby("city")["amount"].transform("sum") * 100
Әр жолдың бөлімінде өз қаласының сомасы тұрады. Дәл сол тәсілмен топтағы орташадан ауытқуды және топ ішіндегі орынды (rank) санайды.
Сабақ картасы
Өз сөзіңізбен айтыңыз
Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптар — сабақтың соңында.
groupbyне істейді және жауапта кілт неге айналады?countsize-дан несімен ерекшеленеді және олардың айырмасы нені білдіреді?transformagg-тан несімен өзгеше және дәл ол қашан керек?
Жаттығу
Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, түзету. Жауаптар — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.
1. Болжаңыз. Бұл бағдарлама не басып шығарады?
import pandas as pd
df = pd.DataFrame({"kind": ["тамақ", "тамақ", "байланыс"], "amount": [1200, 800, 4990]})
print(df.groupby("kind")["amount"].sum().to_dict())
print(df.groupby("kind")["amount"].agg(["count", "max"]).to_string())
2. Бос орынды толтырыңыз. ... орнына бағанды «барлығы» деп атап, ішіне соманы санаңыз.
# атаулы жиынтық: сол жағында аты, оң жағында «баған, немен санау» жұбы
import pandas as pd
df = pd.DataFrame({"kind": ["тамақ", "тамақ", "байланыс"], "amount": [1200, 800, 4990]})
print(df.groupby("kind").agg(...).to_string())
3. Түзетіңіз. Топтар бойынша сома жалпы сомадан екі жүзге кем. Ештеңе жоғалған жоқ — жай ғана бір топ жауапқа кірмей қалды.
# 1000-ға қарсы 800: тағы екі жүз қайда
import pandas as pd
df = pd.DataFrame({"kind": ["тамақ", None, "тамақ", "байланыс"], "amount": [100, 200, 300, 400]})
print("барлығы:", int(df["amount"].sum()))
print("топтар бойынша:", int(df.groupby("kind")["amount"].sum().sum()))
Тапсырма
Міндетті. Сабақтағы сол он екі түбіртекті алып, айлар мен санаттар бойынша есеп құрыңыз: түбіртек саны, сома және санаттың өз айы ішіндегі үлесі. Есепті ай бойынша, ал ай ішінде сома бойынша жоғарыдан төмен сұрыптаңыз. Соңында айлар бойынша қорытындыны және шығыс ең көп болған айды басып шығарыңыз.
Күтілетін шығыс:
ай kind түбіртек барлығы үлесі
2026-01 жалдау 1 95000 65.7
2026-01 жанармай 2 34700 24.0
2026-01 тамақ 3 9900 6.8
2026-01 байланыс 1 4990 3.5
2026-02 жалдау 2 157000 85.4
2026-02 жанармай 1 19100 10.4
2026-02 байланыс 1 4990 2.7
2026-02 тамақ 1 2800 1.5
айлар бойынша қорытынды: {'2026-01': 144590, '2026-02': 183890}
шығыс ең көп ай: 2026-02
Дайын болғаны: шығыс жол-жолымен сәйкес келеді; үлес екі айдың қосындысынан емес, өз айының сомасынан есептеледі; бағандар кейін қайта аталмай, agg ішінде аталған; ай жолдан қиылып алынбай, күннен шығарылған; шығыс ең көп ай idxmax арқылы табылған.
Өз деректеріңізде. Өз кестеңізді кез келген өріс бойынша топтап, үш санды есептеңіз. Содан кейін салыстырыңыз: топтар бойынша сома бүкіл кесте бойынша сомаға тең бе? Тең болмаса — кілтте жетіспейтін мән жоқ па, соны қараңыз.
Қалауыңыз бойынша.
- Апта күні бойынша топтап (
dt.day_name()), қай күні шығыс көп екенін қараңыз. aggішіне басқа өрістен баған қосыңыз:бірінші_күн=("day", "min").transform("mean")арқылы әр түбіртектің өз санатының орташасынан ауытқуын есептеңіз.
Жобада бұл қайда тұрады
Тоғызыншы қадам: жиынтық бір елге қарауды қояды. Үшеуі бір сұраныспен келеді — банк кодтарды нүктелі үтірмен қабылдайды, — әрі кесте ұзын болады: ел мен жылға бір жол.
Бүкіл әңгіме осы пішін үшін еді: есеп елдер бойынша циклдің орнына бір сұраққа айналады. groupby("country").agg(...) жылдарды, жетіспейтін мәндерді, орташаны және ең жоғарысын береді, ал ең жоғарының жылы топтаудың ішіндегі idxmax-тан келеді — ең үлкен мәннің белгісі сол жолдың өзі, ал жол өз жылын біледі.
Қарыздар. Есепте елдер KAZ, UZB, RUS кодтарымен аталады — банк оларды солай береді. Атаулары сол жауаптың ішінде жатыр, бірақ оларды қасына қою үшін екінші кесте және кілт бойынша біріктіру керек. Бұл — келесі сабақ.
Жауаптар
Жауаптарды көрсету
Сұрақтарға
- Кестені кілт бойынша бөліктерге кеседі, әр бөлікті бөлек санайды және жауаптарды қайта кестеге жинайды. Кілт жол белгісіна — индекске — айналады, сондықтан жауапты сұрыптауға және сол белгілер бойынша басқа қатарлармен қосуға болады.
countбар мәндерді санайды — сан ба, жол ба, күн бе, бәрібір;sizeтоптың барлық жолын санайды. Олардың айырмасы — саналған бағандағы жетіспейтін мәндердің саны.aggтопты бір санға сығады,transformжол саны қанша болса, сонша мән қайтарады: топтық санды өз тобының әр жолына қайталайды. Ол топтық жауап бастапқы жолдардың қасына тұруға тиіс болғанда керек: үлес, орташадан ауытқу, топ ішіндегі орын.
Жаттығуға
- Санат бойынша топтау әрқайсысының сомасын береді, ал тізімі бар
aggфункция аттарымен екі баған береді.
{'байланыс': 4990, 'тамақ': 2000}
count max
kind
байланыс 1 4990
тамақ 2 1200
барлығы=("amount", "sum"). Баған аты теңдік белгісінің сол жағында, «нені санау, немен санау» жұбы — оң жағында.
import pandas as pd
df = pd.DataFrame({"kind": ["тамақ", "тамақ", "байланыс"], "amount": [1200, 800, 4990]})
print(df.groupby("kind").agg(барлығы=("amount", "sum")).to_string())
барлығы
kind
байланыс 4990
тамақ 2000
groupby("kind", dropna=False). Кілті бос жол әдепкі күйінде бірде-бір топқа кірмейді, әрі екі жүз үнсіз кетеді.
import pandas as pd
df = pd.DataFrame({"kind": ["тамақ", None, "тамақ", "байланыс"], "amount": [100, 200, 300, 400]})
print("барлығы:", int(df["amount"].sum()))
print("топтар бойынша:", int(df.groupby("kind", dropna=False)["amount"].sum().sum()))
барлығы: 1000
топтар бойынша: 1000
Тапсырмаға
Үлес ай бойынша transform("sum") арқылы саналады: әр жолдың бөлімінде жалпы сома емес, өз айының сомасы тұруға тиіс. report["барлығы"].sum() мәніне бөлсеңіз, үлестер әр айда емес, бүкіл есеп бойынша жүз пайызға жиналады — бұл басқа сұраққа берілген басқа жауап болады.
Сұрыптау — бірден екі кілт бойынша: sort_values(["ай", "барлығы"], ascending=[True, False]), өйткені айлар рет бойынша, ал ай ішіндегі сомалар үлкеннен кішіге қарай тұруға тиіс.
Дереккөздер
- Топтау: split-apply-combine — бұл қалай құрылған және тағы не істей алады.
- Атаулы жиынтықтар — есеп есеп болып оқылатын түрі.
- Күн мен уақытпен жұмыс —
dt, кезеңдер және уақытты топтауға болатынның бәрі.
Пікірлер (0)
Пікір қалдыру үшін кіріңіз →
Әзірге пікір жоқ. Бірінші болыңыз.