Индексирање и транспоновање табеле¶
У овој лекцији говорићемо о:
индексирању табеле ради флексибилнијег приступа елементима табеле;
приступу врстама и појединачним локацијама индексиране табеле;
рачунању са целим редовима и колонама табеле и
транспоновању табеле.
Индексирање¶
Видели смо да је рад са колонама табеле веома једноставан.
Да бисмо могли да радимо са редовима табеле треба прво да нађемо једну колону чија вредност једнозначно одређује цео ред табеле. На пример, у табели са прошлог часа:
Ime |
Pol |
Starost |
Masa |
Visina |
|---|---|---|---|---|
Ana |
ž |
13 |
46 |
160 |
Bojan |
m |
14 |
52 |
165 |
Vlada |
m |
13 |
47 |
157 |
Gordana |
ž |
15 |
54 |
165 |
Dejan |
m |
15 |
56 |
163 |
Đorđe |
m |
13 |
45 |
159 |
Elena |
ž |
14 |
49 |
161 |
Žaklina |
ž |
15 |
52 |
164 |
Zoran |
m |
15 |
57 |
167 |
Ivana |
ž |
13 |
45 |
158 |
Jasna |
ž |
14 |
51 |
162 |
колона „Ime“ је таква колона (колона „Visina“ није погодна јер имамо двоје деце са висином 165 цм, па, када кажемо „дете са висином 165“, није јасно о коме се ради; исто тако ни колоне „Pol“, „Starost“ и „Masa“ нису погодне).
Таква колона се зове кључ јер је она кључна за приступање редовима
табеле. Ако желимо да приступамо елементима табеле по редовима, морамо
систему да пријавимо коју колону ћемо користити као кључ. То се постиже
позивом функције set_index којој проследимо име колоне, а она врати
нову табелу „индексирану по датом кључу“:
import pandas as pd
podaci = [["Ana", "ž", 13, 46, 160],
["Bojan", "m", 14, 52, 165],
["Vlada", "m", 13, 47, 157],
["Gordana", "ž", 15, 54, 165],
["Dejan", "m", 15, 56, 163],
["Đorđe", "m", 13, 45, 159],
["Elena", "ž", 14, 49, 161],
["Žaklina", "ž", 15, 52, 164],
["Zoran", "m", 15, 57, 167],
["Ivana", "ž", 13, 45, 158],
["Jasna", "ž", 14, 51, 162]]
tabela = pd.DataFrame(podaci)
tabela.columns=["Ime", "Pol", "Starost", "Masa", "Visina"]
tabela1=tabela.set_index("Ime")
Нова табела (tabela1) се од старе (tabela) разликује само по
томе што редови више нису индексирани бројевима (0, 1, 2…) већ именима
деце (Ana, Bojan, Vlada…). Ево старе (неиндексиране табеле) која има
колону „Ime“ и чији редови су индексирани бројевима:
tabela
| Ime | Pol | Starost | Masa | Visina | |
|---|---|---|---|---|---|
| 0 | Ana | ž | 13 | 46 | 160 |
| 1 | Bojan | m | 14 | 52 | 165 |
| 2 | Vlada | m | 13 | 47 | 157 |
| 3 | Gordana | ž | 15 | 54 | 165 |
| 4 | Dejan | m | 15 | 56 | 163 |
| 5 | Đorđe | m | 13 | 45 | 159 |
| 6 | Elena | ž | 14 | 49 | 161 |
| 7 | Žaklina | ž | 15 | 52 | 164 |
| 8 | Zoran | m | 15 | 57 | 167 |
| 9 | Ivana | ž | 13 | 45 | 158 |
| 10 | Jasna | ž | 14 | 51 | 162 |
А ево и нове табеле у којој су редови индексирани именима деце:
tabela1
| Pol | Starost | Masa | Visina | |
|---|---|---|---|---|
| Ime | ||||
| Ana | ž | 13 | 46 | 160 |
| Bojan | m | 14 | 52 | 165 |
| Vlada | m | 13 | 47 | 157 |
| Gordana | ž | 15 | 54 | 165 |
| Dejan | m | 15 | 56 | 163 |
| Đorđe | m | 13 | 45 | 159 |
| Elena | ž | 14 | 49 | 161 |
| Žaklina | ž | 15 | 52 | 164 |
| Zoran | m | 15 | 57 | 167 |
| Ivana | ž | 13 | 45 | 158 |
| Jasna | ž | 14 | 51 | 162 |
Колона „Ime“ је и даље присутна у табели tabela1, али има посебан
статус. Ако покушамо да јој приступимо као „обичној“ колони
tabela1["Ime"]
добићемо грешку. Међутим, она је ту као индексна колона:
tabela1.index
Index(['Ana', 'Bojan', 'Vlada', 'Gordana', 'Dejan', 'Đorđe', 'Elena',
'Žaklina', 'Zoran', 'Ivana', 'Jasna'],
dtype='object', name='Ime')
Приступ врстама и појединачним ћелијама индексиране табеле¶
Структура података DataFrame је оптимизована за рад са колонама табеле.
Срећом, када имамо индексирану табелу као што је то tabela1,
користећи „аксесоре“, тј. функције чији су аргументи индекси у угластим
заградама. Конкретно, помоћу аксесора loc (од енгл. location, што
значи „локација, положај, место“) можемо да приступамо редовима табеле,
као и појединачним ћелијама табеле.
Податке о појединачним редовима табеле можемо да видимо овако:
tabela1.loc["Dejan"]
Pol m
Starost 15
Masa 56
Visina 163
Name: Dejan, dtype: object
Као аргумент аксесора .loc можемо да наведемо и распон, и тако ћемо
добити одговарајући део табеле:
tabela1.loc["Dejan":"Zoran"]
| Pol | Starost | Masa | Visina | |
|---|---|---|---|---|
| Ime | ||||
| Dejan | m | 15 | 56 | 163 |
| Đorđe | m | 13 | 45 | 159 |
| Elena | ž | 14 | 49 | 161 |
| Žaklina | ž | 15 | 52 | 164 |
| Zoran | m | 15 | 57 | 167 |
Ако као други аргумент аксесора .loc наведемо име колоне, рецимо
овако tabela1.loc["Dejan", "Visina"], добићемо информацију о
Дејановој висини.
tabela1.loc["Dejan", "Visina"]
163
Ево како можемо да добијемо информацију о телесној маси и висини неколико деце:
tabela1.loc["Dejan":"Zoran", "Masa":"Visina"]
| Masa | Visina | |
|---|---|---|
| Ime | ||
| Dejan | 56 | 163 |
| Đorđe | 45 | 159 |
| Elena | 49 | 161 |
| Žaklina | 52 | 164 |
| Zoran | 57 | 167 |
Рачун по врстама и колонама табеле¶
Кренимо од једног примера. У ћелији испод дате су оцене неких ученика из информатике, енглеског, математике, физике, хемије и ликовног:
razred = [["Ana", 5, 3, 5, 2, 4, 5],
["Bojan", 5, 5, 5, 5, 5, 5],
["Vlada", 4, 5, 3, 4, 5, 4],
["Gordana", 5, 5, 5, 5, 5, 5],
["Dejan", 3, 4, 2, 3, 3, 4],
["Đorđe", 4, 5, 3, 4, 5, 4],
["Elena", 3, 3, 3, 4, 2, 3],
["Žaklina", 5, 5, 4, 5, 4, 5],
["Zoran", 4, 5, 4, 4, 3, 5],
["Ivana", 2, 2, 2, 2, 2, 5],
["Jasna", 3, 4, 5, 4, 5, 5]]
Сада ћемо од ових података направити табелу чије колоне ће се звати „Ime“, „Informatika“, „Engleski“, „Matematika“, „Fizika“, „Hemija“, „Likovno“ и која ће бити индексирана по колони „Ime“:
ocene = pd.DataFrame(razred)
ocene.columns=["Ime", "Informatika", "Engleski", "Matematika", "Fizika", "Hemija", "Likovno"]
ocene1 = ocene.set_index("Ime")
ocene1
| Informatika | Engleski | Matematika | Fizika | Hemija | Likovno | |
|---|---|---|---|---|---|---|
| Ime | ||||||
| Ana | 5 | 3 | 5 | 2 | 4 | 5 |
| Bojan | 5 | 5 | 5 | 5 | 5 | 5 |
| Vlada | 4 | 5 | 3 | 4 | 5 | 4 |
| Gordana | 5 | 5 | 5 | 5 | 5 | 5 |
| Dejan | 3 | 4 | 2 | 3 | 3 | 4 |
| Đorđe | 4 | 5 | 3 | 4 | 5 | 4 |
| Elena | 3 | 3 | 3 | 4 | 2 | 3 |
| Žaklina | 5 | 5 | 4 | 5 | 4 | 5 |
| Zoran | 4 | 5 | 4 | 4 | 3 | 5 |
| Ivana | 2 | 2 | 2 | 2 | 2 | 5 |
| Jasna | 3 | 4 | 5 | 4 | 5 | 5 |
Ако желимо да израчунамо просек по предметима, треба на сваку колону ове
табеле да применимо функцију .mean. Листа са именима свих колона
табеле ocene1 се добија као ocene1.columns, па сада само треба
да прођемо кроз ову листу и за сваку колону да израчунамо просек:
for predmet in ocene1.columns:
print(predmet, "->", round(ocene1[predmet].mean(), 2))
Informatika -> 3.91
Engleski -> 4.18
Matematika -> 3.73
Fizika -> 3.82
Hemija -> 3.91
Likovno -> 4.55
Да бисмо израчунали просечне оцене сваког ученика функцију .mean ћемо
применити на врсте табеле које добијамо позивом аксесора .loc.
Погледајмо, прво, како то можемо да урадимо за једног ученика:
print("Đorđe ima sledeće ocene:")
print(ocene1.loc["Đorđe"])
print("Prosek njegovih ocena je:",
round(ocene1.loc["Đorđe"].mean(), 2)) # računamo sr. vrednost ocena za Đorđa pa zaokružujemo na dve cifre"
Đorđe ima sledeće ocene:
Informatika 4
Engleski 5
Matematika 3
Fizika 4
Hemija 5
Likovno 4
Name: Đorđe, dtype: int64
Prosek njegovih ocena je: 4.17
Списак свих ученика се налази у индексној колони, па просеке по свим ученицима можемо да израчунамо овако:
for ucenik in ocene1.index:
print(ucenik, "->", round(ocene1.loc[ucenik].mean(), 2))
Ana -> 4.0
Bojan -> 5.0
Vlada -> 4.17
Gordana -> 5.0
Dejan -> 3.17
Đorđe -> 4.17
Elena -> 3.0
Žaklina -> 4.67
Zoran -> 4.17
Ivana -> 2.5
Jasna -> 4.33
Ево и кратке видео-илустрације:
Транспоновање табеле¶
Замена врста и колона табеле се зове транспоновање. Приликом транспоновања имена колона полазне табеле постају индекси нове табеле, док индексна колона полазне табеле одређује имена колона нове табеле:
Транспоновање се често користи када табела има мало веома дугачких
редова, па је у неким ситуацијама лакше посматрати транспоновану табелу
која онда има много релативно кратких редова. Функције .head() и
.tail() нам тада омогућују да се брзо упознамо са почетком и крајем
табеле и да стекнемо неку интуицију о томе како табела изгледа.
Важно је рећи и то да се са табелама може радити и без транспоновања, јер све што можемо да урадимо на колонама табеле можемо да урадимо и на врстама. И поред тога, транспоновање се често користи јер је библиотека pandas оптимизована за рад по колонама табеле.
Табела се транспонује тако што се на њу примени функција .Т која као
резултат враћа нову, транспоновану табелу.
Ево примера са оценама:
ocene1
| Informatika | Engleski | Matematika | Fizika | Hemija | Likovno | |
|---|---|---|---|---|---|---|
| Ime | ||||||
| Ana | 5 | 3 | 5 | 2 | 4 | 5 |
| Bojan | 5 | 5 | 5 | 5 | 5 | 5 |
| Vlada | 4 | 5 | 3 | 4 | 5 | 4 |
| Gordana | 5 | 5 | 5 | 5 | 5 | 5 |
| Dejan | 3 | 4 | 2 | 3 | 3 | 4 |
| Đorđe | 4 | 5 | 3 | 4 | 5 | 4 |
| Elena | 3 | 3 | 3 | 4 | 2 | 3 |
| Žaklina | 5 | 5 | 4 | 5 | 4 | 5 |
| Zoran | 4 | 5 | 4 | 4 | 3 | 5 |
| Ivana | 2 | 2 | 2 | 2 | 2 | 5 |
| Jasna | 3 | 4 | 5 | 4 | 5 | 5 |
Транспоновану табелу добијамо овако:
ocene2 = ocene1.T
ocene2
| Ime | Ana | Bojan | Vlada | Gordana | Dejan | Đorđe | Elena | Žaklina | Zoran | Ivana | Jasna |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Informatika | 5 | 5 | 4 | 5 | 3 | 4 | 3 | 5 | 4 | 2 | 3 |
| Engleski | 3 | 5 | 5 | 5 | 4 | 5 | 3 | 5 | 5 | 2 | 4 |
| Matematika | 5 | 5 | 3 | 5 | 2 | 3 | 3 | 4 | 4 | 2 | 5 |
| Fizika | 2 | 5 | 4 | 5 | 3 | 4 | 4 | 5 | 4 | 2 | 4 |
| Hemija | 4 | 5 | 5 | 5 | 3 | 5 | 2 | 4 | 3 | 2 | 5 |
| Likovno | 5 | 5 | 4 | 5 | 4 | 4 | 3 | 5 | 5 | 5 | 5 |
Хајде још да се уверимо да су врсте и колоне замениле места и у пољима
index и columns. У полазној табели је:
ocene1.index
Index(['Ana', 'Bojan', 'Vlada', 'Gordana', 'Dejan', 'Đorđe', 'Elena',
'Žaklina', 'Zoran', 'Ivana', 'Jasna'],
dtype='object', name='Ime')
ocene1.columns
Index(['Informatika', 'Engleski', 'Matematika', 'Fizika', 'Hemija', 'Likovno'], dtype='object')
У транспонованој табели је:
ocene2.index
Index(['Informatika', 'Engleski', 'Matematika', 'Fizika', 'Hemija', 'Likovno'], dtype='object')
ocene2.columns
Index(['Ana', 'Bojan', 'Vlada', 'Gordana', 'Dejan', 'Đorđe', 'Elena',
'Žaklina', 'Zoran', 'Ivana', 'Jasna'],
dtype='object', name='Ime')
Како смо раније већ видели, просек оцена по предметима добијамо лако:
for predmet in ocene1.columns:
print(predmet, "->", round(ocene1[predmet].mean(), 2))
Informatika -> 3.91
Engleski -> 4.18
Matematika -> 3.73
Fizika -> 3.82
Hemija -> 3.91
Likovno -> 4.55
Да бисмо добили просек оцена по ученицима, можемо да приступимо врстама
табеле користећи аксесор loc како смо то већ видели, али можемо и да
употребимо транспоновану табелу (рачунање просека по колонама, јер су
колоне транспоноване табеле заправо врсте полазне табеле):
for ucenik in ocene2.columns:
print(ucenik, "->", round(ocene2[ucenik].mean(), 2))
Ana -> 4.0
Bojan -> 5.0
Vlada -> 4.17
Gordana -> 5.0
Dejan -> 3.17
Đorđe -> 4.17
Elena -> 3.0
Žaklina -> 4.67
Zoran -> 4.17
Ivana -> 2.5
Jasna -> 4.33
Задаци¶
Препоручујемо да примере из ове лекције покренеш на свом рачунару тако што ћеш у пакету фајлова за вежбу покренути Џупитер свеску 08_indeksiranje.ipynb, или тако што ћеш отићи на овај линк и тамо радити задатке. За детаљније инструкције погледај поглавље Фајлови за вежбу и коришћење Џупитер окружења.