$$ \newcommand{\floor}[1]{\left\lfloor{#1}\right\rfloor} \newcommand{\ceil}[1]{\left\lceil{#1}\right\rceil} \renewcommand{\mod}{\,\mathrm{mod}\,} \renewcommand{\div}{\,\mathrm{div}\,} \newcommand{\metar}{\,\mathrm{m}} \newcommand{\cm}{\,\mathrm{cm}} \newcommand{\dm}{\,\mathrm{dm}} \newcommand{\litar}{\,\mathrm{l}} \newcommand{\km}{\,\mathrm{km}} \newcommand{\s}{\,\mathrm{s}} \newcommand{\h}{\,\mathrm{h}} \newcommand{\minut}{\,\mathrm{min}} \newcommand{\kmh}{\,\mathrm{\frac{km}{h}}} \newcommand{\ms}{\,\mathrm{\frac{m}{s}}} \newcommand{\mss}{\,\mathrm{\frac{m}{s^2}}} \newcommand{\mmin}{\,\mathrm{\frac{m}{min}}} \newcommand{\smin}{\,\mathrm{\frac{s}{min}}} $$

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Припрема и дескриптивна анализа нумеричких података

У овој лекцији је приказано како се обрађују бројчани подаци у табелама. За разлику од текстуалних података, са бројевима можемо да рачунамо и зато је велики број Пајтон функција резервисан за нумеричке податке. Треба обратити пажњу да нису сви бројчани подаци истог типа и да функције могу да буду осетљиве на ту разлику. То што је нама "очигледно" који је тип података у питању, не мора да значи да су подаци до краја чисти и да Пајтон то препознаје.

Пајтон, у својим библиотекама, има мноштво функција за обраду и експлоративну анализу нумеричких података. Уколико је колона у табели чиста и не садржи ништа сем бројева, највећи део уводних анализа можемо да урадимо са врло мало труда.

Да бисмо на располагању имали потребне функције, учитаћемо на почетку три библиотеке: pandas (за рад са табелама), numpy (за рад са нумеричким подацима) и matplotlib.pyplot и seaborn (за цртање графикона).

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

Уколико нису све библиотеке инсталиране, потребно је да их инсталирате, нпр. као pip install seaborn.

За потребе вежбања, учитаћемо једну табелу са Националног портала отворених података (Општински показатељи последње стање) која садржи и текстуалне и бројчане податке. За случај да учитавање преко интернета не ради, учитаћемо је из директоријума .\data.

In [2]:
#df=pd.read_csv("https://data.gov.rs/sr/datasets/r/b1f1f94a-fe21-3a2a-b801-6f0f05b24257",sep=";")
opps=pd.read_csv("data/03IND01.csv",sep=";")
In [3]:
opps
Out[3]:
IDIndikator Indikator mes god idter nter vrednost CreateDate LastUpdate IDLegenda nIzvorI
0 IND00M01 Градоначелник/председник општине 1 2021 70017 Александровац МИРКО МИХАЈЛОВИЋ 1/4/2021 7:39:27 AM 1/4/2021 7:39:27 AM A Републички завод за статистику (РЗС)
1 IND01G01 Површина (у км²) 0 2020 70017 Александровац 387 1/27/2021 10:15:39 AM 1/27/2021 10:22:18 AM A Републички геодетски завод (РГЗ)
2 IND01G02 Број становника - процена (последњи расположив... 0 2019 70017 Александровац 23898 7/1/2020 1:01:17 PM 7/1/2020 1:01:17 PM A Републички завод за статистику (РЗС)
3 IND01G03 Густина насељености (број становника / км2) 0 2019 70017 Александровац 62 7/1/2020 1:01:17 PM 7/1/2020 1:01:17 PM A Републички завод за статистику (РЗС)
4 IND01G04 Број насеља 0 2020 70017 Александровац 55 1/27/2021 10:15:39 AM 1/27/2021 10:22:18 AM A Републички завод за статистику (РЗС)
... ... ... ... ... ... ... ... ... ... ... ...
13241 IND02M55 Вредност извоза у УСД 11 2020 89010 Град Нови Сад 163079592.279077 12/30/2020 1:45:56 PM 12/30/2020 1:45:56 PM P Управа царине и Електро мрежа Србије (УЦ и ЕМС)
13242 IND02M56 Вредност извоза у ЕУР 11 2020 89010 Град Нови Сад 192545516.285837 12/30/2020 1:45:56 PM 12/30/2020 1:45:56 PM P Управа царине и Електро мрежа Србије (УЦ и ЕМС)
13243 IND03M03 Број регистрованих незапослених 12 2020 89010 Град Нови Сад 13015 1/14/2021 3:42:52 PM 1/14/2021 3:42:52 PM A Национална служба за запошљавање (НСЗ)
13244 IND03M08 Просечна нето зарада за месец, према општини п... 11 2020 89010 Град Нови Сад 69238 1/19/2021 12:27:15 PM 1/25/2021 12:00:17 PM A Републички завод за статистику (РЗС)
13245 IND03M09 Просечна нето зарада за период јануар-текући м... 11 2020 89010 Град Нови Сад 67104 1/19/2021 12:27:14 PM 1/25/2021 12:00:18 PM A Републички завод за статистику (РЗС)

13246 rows × 11 columns

Евидентно, у табели имамо различите индикаторе (показатеље) по општинама и годинама који су распоређени на начин који није најзгоднији. Ова огромна табела (има 13246 редова) изгледа као колекција истих табела за појединачне индикаторе. Видимо да ту, на пример, постоји индикатор Површина (у km²), али тај податак не може лако да се обради јер су вредности тог индикатора стављене у колону vrednost са мноштвом других података различитог типа. Када бисмо хтели да нађемо укупну површину свих општина, то не може без додатне обраде података.

Да погледамо за почетак, које типове података овде имамо.

In [4]:
opps.dtypes
Out[4]:
IDIndikator    object
Indikator      object
mes             int64
god             int64
idter           int64
nter           object
vrednost       object
CreateDate     object
LastUpdate     object
IDLegenda      object
nIzvorI        object
dtype: object

Месец, година и матични број општине су типе integer, док су сви остали сложени. Да бисмо ишта могли да анализирамо, мораћемо да издвојимо под-табеле за конкретне показатеље. Које показатеље овде уопште имамо видећемо коришћењем функције unique() за податке у колони Indikator.

In [5]:
opps['Indikator'].unique()
Out[5]:
array(['Градоначелник/председник општине', 'Површина (у км²)',
       'Број становника - процена (последњи расположив податак за годину)',
       'Густина насељености (број становника / км2)', 'Број насеља',
       'Број живорођених',
       'Степен развијености ЈЛС према Закону о регионалном развоју',
       'Степен развијености е-управе', 'Укупна дужина водоводне мреже',
       'Број становника према Попису 2011',
       'Број домаћинстава према Попису 2011',
       'Укупна дужина канализационе мреже', 'Број великих предузећа ',
       'Број средњих предузећа ', 'Број малих предузећа ',
       'Број микро предузећа', 'Број без ознаке за величину',
       'Број активних привредних друштава',
       'Број активних привредних друштава по NACE Rev.2  (сектор A)',
       'Број активних привредних друштава по NACE Rev.2  (сектор B)',
       'Број активних привредних друштава по NACE Rev.2  (сектор C)',
       'Број активних привредних друштава по NACE Rev.2  (сектор D)',
       'Број активних привредних друштава по NACE Rev.2  (сектор E)',
       'Број активних привредних друштава по NACE Rev.2  (сектор F)',
       'Број активних привредних друштава по NACE Rev.2  (сектор G)',
       'Број активних привредних друштава по NACE Rev.2  (сектор H)',
       'Број активних привредних друштава по NACE Rev.2  (сектор I)',
       'Број активних привредних друштава по NACE Rev.2  (сектор J)',
       'Број активних привредних друштава по NACE Rev.2  (сектор K)',
       'Број активних привредних друштава по NACE Rev.2  (сектор L)',
       'Број активних привредних друштава по NACE Rev.2  (сектор M)',
       'Број активних привредних друштава по NACE Rev.2  (сектор N)',
       'Број активних привредних друштава по NACE Rev.2  (сектор O)',
       'Број активних привредних друштава по NACE Rev.2  (сектор P)',
       'Број активних привредних друштава по NACE Rev.2  (сектор Q)',
       'Број активних привредних друштава по NACE Rev.2  (сектор R)',
       'Број активних привредних друштава по NACE Rev.2  (сектор S)',
       'Број активних привредних друштава по NACE Rev.2  (сектор T)',
       'Број активних привредних друштава по NACE Rev.2  (сектор U)',
       'Број основаних привредних друштава ',
       'Број брисаних привредних друштава ',
       'Број активних привредних друштава у ликвидацији',
       'Број активних привредних друштава у стечају',
       'Број активних предузетника',
       'Број активних предузетника по NACE Rev.2  (сектор A)',
       'Број активних предузетника по NACE Rev.2  (сектор B)',
       'Број активних предузетника по NACE Rev.2  (сектор C)',
       'Број активних предузетника по NACE Rev.2  (сектор D)',
       'Број активних предузетника по NACE Rev.2  (сектор E)',
       'Број активних предузетника по NACE Rev.2  (сектор F)',
       'Број активних предузетника по NACE Rev.2  (сектор G)',
       'Број активних предузетника по NACE Rev.2  (сектор H)',
       'Број активних предузетника по NACE Rev.2  (сектор I)',
       'Број активних предузетника по NACE Rev.2  (сектор J)',
       'Број активних предузетника по NACE Rev.2  (сектор K)',
       'Број активних предузетника по NACE Rev.2  (сектор L)',
       'Број активних предузетника по NACE Rev.2  (сектор M)',
       'Број активних предузетника по NACE Rev.2  (сектор N)',
       'Број активних предузетника по NACE Rev.2  (сектор O)',
       'Број активних предузетника по NACE Rev.2  (сектор P)',
       'Број активних предузетника по NACE Rev.2  (сектор Q)',
       'Број активних предузетника по NACE Rev.2  (сектор R)',
       'Број активних предузетника по NACE Rev.2  (сектор S)',
       'Број активних предузетника по NACE Rev.2  (сектор T)',
       'Број активних предузетника по NACE Rev.2  (сектор U)',
       'Број основаних предузетника', 'Број брисаних предузетника',
       'Број активних предузетника у ликвидацији',
       'Број активних предузетника у стечају', 'Број извозника ',
       'Вредност извоза у РСД', 'Вредност извоза у УСД',
       'Вредност извоза у ЕУР', 'Број регистрованих незапослених ',
       'Просечна нето зарада за месец, према општини пребивалишта запослених',
       'Просечна нето зарада за период јануар-текући месец, према општини пребивалишта запослених'],
      dtype=object)

Једнодимензионални скупови података

За једноставне анализе је довољно да извучемо само један индикатор који нас интересује. Примера ради, сада ћемо извући само индикатор везан за површину општине.

In [6]:
povrsina=opps[opps['Indikator']=='Површина (у км²)']
In [7]:
povrsina.head()
Out[7]:
IDIndikator Indikator mes god idter nter vrednost CreateDate LastUpdate IDLegenda nIzvorI
1 IND01G01 Површина (у км²) 0 2020 70017 Александровац 387 1/27/2021 10:15:39 AM 1/27/2021 10:22:18 AM A Републички геодетски завод (РГЗ)
77 IND01G01 Површина (у км²) 0 2020 70025 Алексинац 707 1/27/2021 10:15:40 AM 1/27/2021 10:22:18 AM A Републички геодетски завод (РГЗ)
153 IND01G01 Површина (у км²) 0 2020 70033 Аранђеловац 376 1/27/2021 10:15:40 AM 1/27/2021 10:22:18 AM A Републички геодетски завод (РГЗ)
229 IND01G01 Површина (у км²) 0 2020 70041 Ариље 349 1/27/2021 10:15:39 AM 1/27/2021 10:22:18 AM A Републички геодетски завод (РГЗ)
305 IND01G01 Површина (у км²) 0 2020 70050 Бабушница 529 1/27/2021 10:15:40 AM 1/27/2021 10:22:18 AM A Републички геодетски завод (РГЗ)

Изгледа да су сада у колони vrednost само бројеви. Можемо онда да их саберемо и видимо колика је површина свих општина у Србији заједно.

In [8]:
povrsina['vrednost'].sum()
Out[8]:
'38770737634952967313651721314833003115038333941410451301427151573063842648288565716064619052493453443383663086022393253678366234831212647602141069109062941312023582578351530342854952721186337102561245427935693218426952419332910905817423195426551232374426264553827759289216670366470326497105948442152562864735737044874212428745631264763679755146288141182117101032582032345964778606672276023803675905961583531844868004005251327385230399782419730481181273609305610203411756489383582294523121628476251350100717026137632168789699'

Упс! Ово нисмо очекивали. Уместо суме бројева, добили смо суму стрингова, тј. спојене вредности свих стрингова у колони vrednost од '387', '707' па редом. Изгледа да Пајтон и даље не зна да су у колони vrednost бројеви.

In [9]:
povrsina.dtypes
Out[9]:
IDIndikator    object
Indikator      object
mes             int64
god             int64
idter           int64
nter           object
vrednost       object
CreateDate     object
LastUpdate     object
IDLegenda      object
nIzvorI        object
dtype: object

Да, Пајтон је задржао тип променљиве који смо имали у оригиналној табели. То треба да променимо помоћу функције astype(). Тако ћемо Пајтону објаснити да подаци у колони vrednost треба да буду типа float.

In [10]:
povrsina = povrsina.astype({'vrednost':float})
In [11]:
povrsina.dtypes
Out[11]:
IDIndikator     object
Indikator       object
mes              int64
god              int64
idter            int64
nter            object
vrednost       float64
CreateDate      object
LastUpdate      object
IDLegenda       object
nIzvorI         object
dtype: object
In [12]:
povrsina['vrednost'].sum()
Out[12]:
84122.0

Ово делује у реду. Толика је површина Србије.

Сада ћемо обрадити један други нумерички индикатор, просечну нето зараду. Тај индикатор се зове тачно Просечна нето зарада за месец, према општини пребивалишта запослених.

In [13]:
neto_zarada=opps[opps['Indikator']=='Просечна нето зарада за месец, према општини пребивалишта запослених']

С обзиром да нам за нумеричку анализу табеле neto_zarada нису потребне све колоне, задржаћемо само три. Матични број задржавамо само због контроле.

In [14]:
neto_zarada=neto_zarada[['idter','nter','vrednost']]
In [15]:
neto_zarada = neto_zarada.astype({'vrednost':float})

Ако желимо да видимо где су у Србији просечне зараде највеће, потребно је да сортирамо табелу према вредности. Сортирање не мења вредности у табели већ нам приказује податке на начин који нама одговара.

In [16]:
neto_zarada.sort_values('vrednost',ascending=False).head(10)
Out[16]:
idter nter vrednost
832 70114 Врачар 99052.0
1282 70181 Нови Београд 96326.0
1730 70246 Стари град 96010.0
1580 70220 Савски венац 88020.0
756 70106 Вождовац 79008.0
982 70149 Звездара 78229.0
9384 79014 Град Београд 75649.0
1132 70165 Лазаревац 72684.0
1804 70254 Чукарица 72669.0
9161 71340 Костолац 70934.0

Највеће зараде су, очигледно, у Београду. Прва општина на овој листи која није београдска је Костолац.

Када бисмо помоћу функције max() тражили општину са највећим зарадама, то би изгледало овако:

In [17]:
neto_zarada[neto_zarada.vrednost==neto_zarada.vrednost.max()]
Out[17]:
idter nter vrednost
832 70114 Врачар 99052.0

Функцију max можемо да применимо и на целу табелу, али ће нам тад излаз бити низ максималних вредности по колонама. То нам сад није посебно корисно. Приметите да максимум за нумеричке и текстуалне вредности ради другачије. У првом случају тражи највећу вредност, а у другом податке поређа лексикографски па прикаже последњи у низу.

In [18]:
neto_zarada.max()
Out[18]:
idter         89010
nter            Шид
vrednost    99052.0
dtype: object

Ако цео низ бројева због скраћеног приказа или комуникације морамо да сведемо на један број, онда је најбоље узети једну од мера просека: средњу вредност или медијану. Максимална или минимална вредност зарада су куриозитети и не говоре нам много о целом скупу. Средња вредност нам више говори о томе колике су зараде у Србији дајући нам аритметичку средину. Медијана нам даје просек на мало другачији податак, она нам даје типичну вредност зараде, односно колику зараду имају они који су на средини листе о висини зараде. Код симетричних расподела података, средња вредност и медијана се поклапају, али расподела зарада није таква. Увек има више оних са малим зарадама и неколико оних са веома великим. То се врло често злоупотребљава. Већина људи има мање зараде од средње вредности. Кад год је новац у питању, средња вредност буде већа од медијане.

Пајтон нема функцију за средњу вредност у основном окружењу па је потребно учитати библиотеку statistics да бисмо имали функцију mean. Срећом, средња вредност се лако рачуна -- само треба поделити збир свих елемената са њиховим бројем. (Ако вам је лакше учитајте библиотеку па користите mean.)

In [19]:
neto_zarada['vrednost'].sum()/neto_zarada['vrednost'].size
Out[19]:
52942.137142857144
In [20]:
neto_zarada.vrednost.median()
Out[20]:
50655.0

Видимо да средња вредност просечне нето зараде по општинама за више од две хиљаде динара већа него медијана.

Дескриптивна анализа

Рекли смо раније да се у овом курсу нећемо бавити (озбиљном) анализом података јер она подразумева стручно, доменско знање. Дескриптивна анализа је више дескрипција него анализа. Њу рачунар ради по алгоритму и приказује нам мере централне тенденције и мере растура за одређени скуп података. Тај део посла не захтева било какво доменско знање. Тумачење мера које нам даје дескриптивна анализа је већ нешто друго.

Дескриптивна анализа даје главне мере једног скупа бројчаних података као што су број елемената (count), средња вредност (mean), стандардна девијација (std), минимум (min) итд. Иако све ове мере можемо да добијемо и директно, згодно је да их једном функцијом добијемо све. Функција describe() то омогућава.

In [21]:
neto_zarada.vrednost.describe()
Out[21]:
count      175.000000
mean     52942.137143
std       9576.694515
min      41851.000000
25%      47292.000000
50%      50655.000000
75%      54687.000000
max      99052.000000
Name: vrednost, dtype: float64

По свему судећи, расподела вредности из ове колоне није баш симетрична па би било добро да је визуелизујемо, тј. да прикажемо графички. Због тога смо на почетку учитавали библиотеку за цртање. Анализу података је, по правилу, лакше урадити ако "видимо" податке. Зато се нумеричка анализа и визуелизација података обично раде истовремено.

За визуелизацију једнодимензионалног скупа података (низа, листе или колоне у табели) најчешће се користи хистограм који нам приказује број елемената у одређеним интервалима, односно расподелу података. Хистограме цртамо помоћу функције hist(). Аргумент ове функције, сем низа чије бројеве хоћемо да прикажемо, јесте и опсег бројева у ком посматрамо хистограм са кораком (величином интервала). Помоћу функције axvline из библиотеке matplotlib.pyplot цртамо вертикалне линије одређене боје, дебљине и стила. Овде ћемо нацртати две вертикалне линије: наранџасту за медијану низа и белу за средњу вредност.

Приметите да смо функције median() и mean() преузели из numpy библиотеке.

In [22]:
y=neto_zarada.vrednost
y.hist(bins=range(35000,105000,4000))
plt.axvline(x=np.median(y), color='orange', linewidth=2, linestyle=":")
plt.axvline(x=np.mean(y), color='white', linewidth=2, linestyle="--")
Out[22]:
<matplotlib.lines.Line2D at 0x29507191160>

Слично, можемо да користимо функцију hist и из библиотеке за цртање. Разлике у начину приказивања нису велике.

In [23]:
plt.hist(y,bins=20)
plt.axvline(x=np.median(y), color='orange', linewidth=3, linestyle=":")
plt.axvline(x=np.mean(y), color='white', linewidth=2, linestyle="--")
Out[23]:
<matplotlib.lines.Line2D at 0x295072c3820>

Графичко приказивање расподеле података је, вероватно, најбољи начин да разумемо шта се у том скупу налази, да ли је расподела униформна или има "репове", где су тачке нагомилавања, да ли има "немогућих вредности" итд. Врло често је визуелизација податка део процеса чишћења података. Много је лакше уочити нелогичности у великом скупу података ако успемо да га добро прикажемо графички.

Пивотирање табеле

Табела коју смо на почетку учитали има 175 редова који се односе на било који индикатор. Ако исецамо само оне редове које нас у том тренутку интересују, изгубићемо све остале. Можда би било боље да трансформишемо табелу opps тако да по редовима буду општине, а по колонама индикатори. То се зове пивотирање табеле.

Заправо, узећемо и матични број и назив општине по редовима због контроле. Никад не знате да ли ће се у листи појавити две општине са истим именом, нпр. Палилула. Зато је добро да увек имамо и јединствени матични број. Та два назива колона ћемо ставити у листу и придружити аргументу index. Аргументу columns ћемо придружити све различите вредности које постоје у колони vrednost, а аргументу values ћемо придружити вредности из колоне која се баш тако зове, vrednost. Пивотирање табеле сада радимо помоћу функције pivot().

In [24]:
pivopps=opps.pivot(index=['idter','nter'], columns='Indikator', values='vrednost')
In [25]:
pivopps
Out[25]:
Indikator Број активних предузетника Број активних предузетника по NACE Rev.2 (сектор A) Број активних предузетника по NACE Rev.2 (сектор B) Број активних предузетника по NACE Rev.2 (сектор C) Број активних предузетника по NACE Rev.2 (сектор D) Број активних предузетника по NACE Rev.2 (сектор E) Број активних предузетника по NACE Rev.2 (сектор F) Број активних предузетника по NACE Rev.2 (сектор G) Број активних предузетника по NACE Rev.2 (сектор H) Број активних предузетника по NACE Rev.2 (сектор I) ... Вредност извоза у УСД Градоначелник/председник општине Густина насељености (број становника / км2) Површина (у км²) Просечна нето зарада за месец, према општини пребивалишта запослених Просечна нето зарада за период јануар-текући месец, према општини пребивалишта запослених Степен развијености ЈЛС према Закону о регионалном развоју Степен развијености е-управе Укупна дужина водоводне мреже Укупна дужина канализационе мреже
idter nter
70017 Александровац 1021 22 1 325 0 0 118 225 45 79 ... 1893655.65059975 МИРКО МИХАЈЛОВИЋ 62 387 44615 45383 II група 0.309491409041355 199 51
70025 Алексинац 1320 22 4 232 1 8 126 354 156 102 ... 9850106.09590895 ДАЛИБОР РАДИЧЕВИЋ 67 707 49394 47896 IV група 0.313142788382875 205 43
70033 Аранђеловац 1828 12 1 336 1 4 240 361 165 181 ... 9682257.53441178 БОЈАН РАДОВИЋ 115 376 53319 52354 II група 0.424362734010375 464 182
70041 Ариље 898 7 0 315 0 3 71 150 79 85 ... 6754621.8552789 ПРЕДРАГ МАСЛАР 51 349 44510 43791 II група 0.355725723285914 459 68
70050 Бабушница 287 5 0 46 0 0 50 78 25 24 ... 915210.700355534 ИВАНА СТОЈИЧИЋ 19 529 47249 46951 IV група - девастирано подручје 0.24716560533865 112 30
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
80462 Врбас 1206 12 0 185 0 5 138 221 188 131 ... 4534537.72750178 ПРЕДРАГ РОЈЕВИЋ 104 376 52544 50305 I група 0.462009050383169 232 84
80489 Чока 167 0 0 28 0 0 16 45 12 16 ... 629137.305081099 СТАНА ЂЕМБЕР 32 321 49356 48447 III група 0.380526017239722 186 29
80497 Шид 795 18 0 117 0 3 55 199 92 87 ... 5150027.81529499 ЗОРАН СЕМЕНОВИЋ 45 687 49784 48868 III група 0.451184243662082 209 67
80519 Петроварадин NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... 8228530.02773421 NaN 89 48700 48202 I група NaN NaN NaN
89010 Град Нови Сад 19805 89 0 2214 3 26 1676 2719 1994 1640 ... 163079592.279077 МИЛОШ ВУЧЕВИЋ 516 699 69238 67104 I група 0.633145156491082 NaN NaN

175 rows × 76 columns

Приметите како у табели нема оних колона IDIndikator, mes, god_ итд. Када смо "разапињали" пивот-табелу рекли смо шта хоћемо по редовима, а шта по колонама. Те заборављене колоне нисмо помињали. Нису нам сад ни потребне.

In [26]:
pivopps.columns
Out[26]:
Index(['Број активних предузетника',
       'Број активних предузетника по NACE Rev.2  (сектор A)',
       'Број активних предузетника по NACE Rev.2  (сектор B)',
       'Број активних предузетника по NACE Rev.2  (сектор C)',
       'Број активних предузетника по NACE Rev.2  (сектор D)',
       'Број активних предузетника по NACE Rev.2  (сектор E)',
       'Број активних предузетника по NACE Rev.2  (сектор F)',
       'Број активних предузетника по NACE Rev.2  (сектор G)',
       'Број активних предузетника по NACE Rev.2  (сектор H)',
       'Број активних предузетника по NACE Rev.2  (сектор I)',
       'Број активних предузетника по NACE Rev.2  (сектор J)',
       'Број активних предузетника по NACE Rev.2  (сектор K)',
       'Број активних предузетника по NACE Rev.2  (сектор L)',
       'Број активних предузетника по NACE Rev.2  (сектор M)',
       'Број активних предузетника по NACE Rev.2  (сектор N)',
       'Број активних предузетника по NACE Rev.2  (сектор O)',
       'Број активних предузетника по NACE Rev.2  (сектор P)',
       'Број активних предузетника по NACE Rev.2  (сектор Q)',
       'Број активних предузетника по NACE Rev.2  (сектор R)',
       'Број активних предузетника по NACE Rev.2  (сектор S)',
       'Број активних предузетника по NACE Rev.2  (сектор T)',
       'Број активних предузетника по NACE Rev.2  (сектор U)',
       'Број активних предузетника у ликвидацији',
       'Број активних предузетника у стечају',
       'Број активних привредних друштава',
       'Број активних привредних друштава по NACE Rev.2  (сектор A)',
       'Број активних привредних друштава по NACE Rev.2  (сектор B)',
       'Број активних привредних друштава по NACE Rev.2  (сектор C)',
       'Број активних привредних друштава по NACE Rev.2  (сектор D)',
       'Број активних привредних друштава по NACE Rev.2  (сектор E)',
       'Број активних привредних друштава по NACE Rev.2  (сектор F)',
       'Број активних привредних друштава по NACE Rev.2  (сектор G)',
       'Број активних привредних друштава по NACE Rev.2  (сектор H)',
       'Број активних привредних друштава по NACE Rev.2  (сектор I)',
       'Број активних привредних друштава по NACE Rev.2  (сектор J)',
       'Број активних привредних друштава по NACE Rev.2  (сектор K)',
       'Број активних привредних друштава по NACE Rev.2  (сектор L)',
       'Број активних привредних друштава по NACE Rev.2  (сектор M)',
       'Број активних привредних друштава по NACE Rev.2  (сектор N)',
       'Број активних привредних друштава по NACE Rev.2  (сектор O)',
       'Број активних привредних друштава по NACE Rev.2  (сектор P)',
       'Број активних привредних друштава по NACE Rev.2  (сектор Q)',
       'Број активних привредних друштава по NACE Rev.2  (сектор R)',
       'Број активних привредних друштава по NACE Rev.2  (сектор S)',
       'Број активних привредних друштава по NACE Rev.2  (сектор T)',
       'Број активних привредних друштава по NACE Rev.2  (сектор U)',
       'Број активних привредних друштава у ликвидацији',
       'Број активних привредних друштава у стечају',
       'Број без ознаке за величину', 'Број брисаних предузетника',
       'Број брисаних привредних друштава ', 'Број великих предузећа ',
       'Број домаћинстава према Попису 2011', 'Број живорођених',
       'Број извозника ', 'Број малих предузећа ', 'Број микро предузећа',
       'Број насеља', 'Број основаних предузетника',
       'Број основаних привредних друштава ',
       'Број регистрованих незапослених ', 'Број средњих предузећа ',
       'Број становника - процена (последњи расположив податак за годину)',
       'Број становника према Попису 2011', 'Вредност извоза у ЕУР',
       'Вредност извоза у РСД', 'Вредност извоза у УСД',
       'Градоначелник/председник општине',
       'Густина насељености (број становника / км2)', 'Површина (у км²)',
       'Просечна нето зарада за месец, према општини пребивалишта запослених',
       'Просечна нето зарада за период јануар-текући месец, према општини пребивалишта запослених',
       'Степен развијености ЈЛС према Закону о регионалном развоју',
       'Степен развијености е-управе', 'Укупна дужина водоводне мреже',
       'Укупна дужина канализационе мреже'],
      dtype='object', name='Indikator')

Сада можемо да променимо тип променљивих у табели. Један од начина да то урадимо је да наведемо тачно којим колонама хоћемо да променимо тип података и да наведемо који то тип треба да буде. Овде ћемо изабрати три колоне које ћемо убудуће третирати као нумеричке.

In [27]:
pivopps=pivopps.astype({'Број регистрованих незапослених ': 'float',
                        'Број становника - процена (последњи расположив податак за годину)': 'float',
                        'Просечна нето зарада за месец, према општини пребивалишта запослених': 'float'})

Пивот-табела нам даје вишедимензионални скуп података где анализа може да буде произвољно сложена. Ми се нећемо упуштати у анализу већ ћемо само дати примере два типа графичког приказа ових података. У једном примеру ћемо видети како изгледа зависност нумеричке вредности од категоријалне, а у другом нумеричке од нумеричке.

In [28]:
df=pivopps[['Степен развијености ЈЛС према Закону о регионалном развоју',
         'Просечна нето зарада за месец, према општини пребивалишта запослених']]

Користећи библиотеку seaborn и функцију displot() пробаћемо да прикажемо мало сложенији, семитранспарентни хистограм где свака боја означава један од пет степени развијености јединице локалне самоуправе. Овде нећемо улазити у детаље функције и њене аргументе.

In [29]:
sns.displot(
  data=df,
  x="Просечна нето зарада за месец, према општини пребивалишта запослених",
  hue="Степен развијености ЈЛС према Закону о регионалном развоју",
  kind="hist",
  aspect=1.8,
  bins=15
)
Out[29]:
<seaborn.axisgrid.FacetGrid at 0x2950734edf0>

Оно што видимо са хистограма је да су општине са највећим приходом управо оне најразвијеније, а да у доњем делу ранг-листе има највише општина из треће групе. Такође, видимо и да редослед група у легенди није баш смислен. Пробајте то да средите самостално.

Користећи исту функцију, уместо пет хистограма који се преклапају можемо да нацртамо пет хистограма један поред другог. То ће можда бити прегледније.

In [30]:
sns.displot(
  data=df,
  x="Просечна нето зарада за месец, према општини пребивалишта запослених",
  col="Степен развијености ЈЛС према Закону о регионалном развоју",
  kind="hist",
  aspect=1.4,
  bins=20
)
Out[30]:
<seaborn.axisgrid.FacetGrid at 0x29507350d60>

Нема сумње да су слова на овом графикону превише мала, али то није нарочит проблем. Ови графикони су слика на html страни. Можемо ту слику да отворимо у новом табу браузера (десни клик на слику па Open Image in New Tab) да бисмо је после увећали и пажљиво проучили. Даље сређивање и анализу ових слика препуштамо вама.

Други начин приказа који смо овде хтели да демонстрирамо је обичан scatter plot. Примера ради, приказаћемо помоћу функције replot() у каквој су вези просечна зарада и проценат незапослених у општини. Податак о проценту незапослених немамо. Мораћемо прво да га израчунамо.

In [31]:
pivopps['Проценат незапослених']=pivopps['Број регистрованих незапослених ']/pivopps['Број становника - процена (последњи расположив податак за годину)']
In [32]:
sns.relplot(x="Просечна нето зарада за месец, према општини пребивалишта запослених", 
            y="Проценат незапослених", 
            hue="Степен развијености ЈЛС према Закону о регионалном развоју",
            data=pivopps);

Овај графикон изгледа прилично убедљиво. Јасно видимо каква је веза између зарада и броја незапослених и како она изгледа по степену развијености општина. Даљу анализу препуштамо вама.

Приказ података на мапи

Многи подаци које имамо у табелама представљају гео-податке, односно податке у вези са разним топонимима, нпр. државама, општинама или локацијама. То нам омогућава да одређене податке придружимо гео-локацијама и представимо их на мапи. Тај тип представљања података захтева посебну библиотеку geopandas коју морамо посебно да инсталирамо и то није сасвим једноставно. Свеједно, претпоставићемо да је тај проблем успешно решен и идемо даље.

In [33]:
import geopandas as gpd

Фајл који нам је неопходан за приказивање мапа је колекција контура разних области. Те контуре су дате као низови тачака, односно полигони. У табелу коју смо преузели са сајта Републичког геодетског завода за сваку општину имамо податке о полигонима у два формата: wkt и geometry.

In [34]:
konture=gpd.read_file('data/opstina.csv', encoding='UTF-8')
In [35]:
konture.head(2)
Out[35]:
opstina_maticni_broj opstina_ime opstina_imel opstina_povrsina okrug_sifra okrug_ime okrug_imel wkt geometry
0 80071 БАЧКА ТОПОЛА BAČKA TOPOLA 0 1 СЕВЕРНОБАЧКИ УПРАВНИ ОКРУГ SEVERNOBAČKI UPRAVNI OKRUG POLYGON((378765.834100001 5089008.2704,379027.... POLYGON ((378765.834 5089008.270, 379027.486 5...
1 70637 КОЦЕЉЕВА KOCELJEVA 0 8 МАЧВАНСКИ УПРАВНИ ОКРУГ MAČVANSKI UPRAVNI OKRUG POLYGON((404664.3786 4935834.7331,404674.65500... POLYGON ((404664.379 4935834.733, 404674.655 4...

Уколико DataFrame садржи колону geometry са полигонима, Пајтон ће помоћу функције plot() знати да их исцрта чак и без икаквих аргумената. Овако изгледају контуре свих 197 општина у Србији.

In [36]:
konture.plot()
Out[36]:
<AxesSubplot:>

Приметите да координате на мапи не одговарају степенима географске дужине и ширине. Разлог за то је што се у геодезији више користе UTM координате. Ко хоће може да изврши конверзију ових података, али нама сада то није потребно. Хоћемо само мапу.

Сад треба да повежемо две табеле: ону са просечним зарадама по општинама neto_zarada и ову са контурама општина konture. Ако пажљиво погледате називи општина нису исти у првој и другој табели. У првој, на пример, пише "Александровац", док у другој имамо "АЛЕКСАНДРОВАЦ" и "ALEKSANDROVAC". То би отежало повезивање да, срећом, нисмо сачували матичне бројеве општина. У првој табели то је била idter колона, а у другој је то opstina_maticni_broj. Помоћу функције rename() преименоваћемо назив колоне у обе табеле у MBO и конвертовати податке у тип integer уколико то нисмо већ урадили.

In [37]:
konture=konture.rename(columns={'opstina_maticni_broj':'MBO'})
In [38]:
konture['MBO']=konture['MBO'].astype(int)
In [39]:
neto_zarada=neto_zarada.rename(columns={'idter':'MBO'})

Повезивање две табеле ћемо урадити помоћу функције merge() по колони MBO.

In [40]:
konture2 = pd.merge(konture,neto_zarada,on='MBO')
konture2.head(2)
Out[40]:
MBO opstina_ime opstina_imel opstina_povrsina okrug_sifra okrug_ime okrug_imel wkt geometry nter vrednost
0 80071 БАЧКА ТОПОЛА BAČKA TOPOLA 0 1 СЕВЕРНОБАЧКИ УПРАВНИ ОКРУГ SEVERNOBAČKI UPRAVNI OKRUG POLYGON((378765.834100001 5089008.2704,379027.... POLYGON ((378765.834 5089008.270, 379027.486 5... Бачка Топола 51694.0
1 70637 КОЦЕЉЕВА KOCELJEVA 0 8 МАЧВАНСКИ УПРАВНИ ОКРУГ MAČVANSKI UPRAVNI OKRUG POLYGON((404664.3786 4935834.7331,404674.65500... POLYGON ((404664.379 4935834.733, 404674.655 4... Коцељева 46425.0

Сада би за сваку општину у табели требало да имамо и податке о заради и контуре. Остало је још само да то прикажемо на мапи.

In [41]:
konture2.plot(column='vrednost')
Out[41]:
<AxesSubplot:>

Када пажљиво погледате мапу, видећете да подаци за општине на Косову и Метохији нису приказани. Табела општина са контурама има 197 редова, а табела са подацима о зарадама 175. Републички завод за статистику у својим извештајима нема месечне показатеље за КиМ. Због тога је спојена табела краћа за та 24 реда. Имајте у виду да званични подаци различитих државних органа могу да се разликују по овом питању.

Што се естетике мапе тиче, исту мапу можемо да прикажемо и са нешто више детаља, легендом, насловом итд. У наредној ћелији је пример како то може да се уради. Слободно варирајте параметре и изаберите мапу која се вама највише свиђа.

In [42]:
konture2.plot(column='vrednost', legend=True, figsize=(12, 10))
plt.axis('off')
plt.title('Prosečna mesečna neto zarada\n\n', fontdict={'fontsize': '15', 'fontweight': '1'})
plt.annotate('Izvori podataka: \nhttp://data.gov.rs/\nhttps://opendata.geosrbija.rs/',
             xy=(0.15, 0.10),  xycoords='figure fraction', horizontalalignment='left', verticalalignment='top', 
             fontsize=8, color='#555555');