$$ \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.

Чишћење података у Пајтону

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

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

Увек треба имати на уму да је чишћење података, у крајњој линији, произвољно мењање података о којима знамо мање од онога ко је податке уносио. Нормално је да они који уносе податке направе превид или податак унесу на погрешан начин. Исто тако је нормално да ми то приметимо и направимо корекцију. За неке друге податке, опет, не знамо шта да мислимо; можда је грешка, можда није. Граница је свакако субјективна. Онај ко чисти податке мора да има знање из области на коју се подаци односе, да зна шта је могуће а шта није, шта може да буде веродостојно, а шта је сигурно грешка. Баш због тога што је критеријум за корекцију података субјективан не можемо потпуно да га аутоматизујемо. То морају да раде људи који знају контекст података. Машина може да помогне да то урадимо брже.

Нас у овом курсу интересују пре свега подаци спаковани у табеле, односно једну конкретну табеларну структуру DataFrame. Ова структура, као и функције за рад са њом, саставни су део библиотеке pandas па је упутно на почетку рада са подацима у Пајтону увек прво учитати ову библиотеку.

In [1]:
import pandas as pd

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

In [2]:
bibreg=pd.read_csv("data/BL-Flickr-Images-Book.csv")
In [3]:
bibreg.head(8)
Out[3]:
Identifier Edition Statement Place of Publication Date of Publication Publisher Title Author Contributors Corporate Author Corporate Contributors Former owner Engraver Issuance type Flickr URL Shelfmarks
0 206 NaN London 1879 [1878] S. Tinsley & Co. Walter Forbes. [A novel.] By A. A A. A. FORBES, Walter. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12641.b.30.
1 216 NaN London; Virtue & Yorston 1868 Virtue & Co. All for Greed. [A novel. The dedication signed... A., A. A. BLAZE DE BURY, Marie Pauline Rose - Baroness NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12626.cc.2.
2 218 NaN London 1869 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr... A., A. A. BLAZE DE BURY, Marie Pauline Rose - Baroness NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12625.dd.1.
3 472 NaN London 1851 James Darling Welsh Sketches, chiefly ecclesiastical, to the... A., E. S. Appleyard, Ernest Silvanus. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 10369.bbb.15.
4 480 A new edition, revised, etc. London 1857 Wertheim & Macintosh [The World in which I live, and my place in it... A., E. S. BROOME, John Henry. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 9007.d.28.
5 481 Fourth edition, revised, etc. London 1875 William Macintosh [The World in which I live, and my place in it... A., E. S. BROOME, John Henry. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 9006.ee.10.
6 519 NaN London 1872 The Author Lagonells. By the author of Darmayne (F. E. A.... A., F. E. ASHLEY, Florence Emily. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12637.e.3.
7 667 NaN pp. 40. G. Bryan & Co: Oxford, 1898 NaN NaN The Coming of Spring, and other poems. By J. A... A., J.|A., J. ANDREWS, J. - Writer of Verse NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 011652.g.73.

Чак и површан увид у податке открива да има много поља у којима пише NaN што значи да тог податка нема, а да је неки софтвер празно поље заменио овом ознаком. Надајмо се да се ниједан издавач или књига не зову баш "NaN". Такође, видимо да неки подаци нису конзистентно уношени. За годину издања (Date of Publication) углавном стоји број, али има и оних са угластим заградама. Имена аутора изгледају као хаос посебне врсте.

Индексна колона

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

Да бисмо се на сличан начин позивали на колоне у табели, потребан нам је "индексни ред". Он већ постоји. То је заправо заглавље табеле, односно ред у ком су називи колона. Називи колона не могу да се понављају па је су вредности у том реду јединствене.

Сваки DataFrame аутоматски добија своју индексну колону. Ако ми не кажемо машини која је то колона, Пајтон ће сам доделити колону са индексом 0, 1, 2, 3... Најважније својство индексне колоне је да садржи јединствене податке. Ако се подаци у колони понављају онда она не може да буде индексна.

У табели bibreg постоји колона Identifier која би могла да буде тај јединствени идентификатор који су унели библиотекари много пре него што се неко сетио да прави табеле у Пајтону. Да ли су све вредности у колони јединствене можемо да проверимо функцијом is.unique().

In [4]:
bibreg['Identifier'].is_unique
Out[4]:
True

Јесте јединствена. То значи да можемо њу да користимо уместо аутоматски додељеног низа ненегативних целих бројева. То ћемо учинити помоћу функције set_index(). Главни аргумент ове функције је назив колоне коју ћемо прогласити за индексну, али аргумената може да буде више. Овде ћемо искористити аргумент inplace=True који Пајтону саопштава да хоћемо да промени и запамти садржај табеле. Без тога бисмо само добили испис у ком је постављена индексна колона док би у меморији остао исти онај стари DataFrame. Наравно, исти резултат бисмо могли да добијемо и ако новој табели придружимо измењену табелу: bibreg=bibreg.set_index('Identifier'). То су два начина да добијемо исти резултат. Бирајте шта вам се више свиђа.

In [5]:
bibreg.set_index('Identifier', inplace = True)
bibreg.head()
Out[5]:
Edition Statement Place of Publication Date of Publication Publisher Title Author Contributors Corporate Author Corporate Contributors Former owner Engraver Issuance type Flickr URL Shelfmarks
Identifier
206 NaN London 1879 [1878] S. Tinsley & Co. Walter Forbes. [A novel.] By A. A A. A. FORBES, Walter. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12641.b.30.
216 NaN London; Virtue & Yorston 1868 Virtue & Co. All for Greed. [A novel. The dedication signed... A., A. A. BLAZE DE BURY, Marie Pauline Rose - Baroness NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12626.cc.2.
218 NaN London 1869 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr... A., A. A. BLAZE DE BURY, Marie Pauline Rose - Baroness NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 12625.dd.1.
472 NaN London 1851 James Darling Welsh Sketches, chiefly ecclesiastical, to the... A., E. S. Appleyard, Ernest Silvanus. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 10369.bbb.15.
480 A new edition, revised, etc. London 1857 Wertheim & Macintosh [The World in which I live, and my place in it... A., E. S. BROOME, John Henry. NaN NaN NaN NaN monographic http://www.flickr.com/photos/britishlibrary/ta... British Library HMNTS 9007.d.28.

Приступање подацима помоћу "приступника" loc[] и iloc[]

Сад кад имамо индексну колону, подацима у табели можемо да приступамо на различите начине. loc[] и iloc[] су посебни начини приступа подацима преко ознака и индекса. У првом случају користимо ознаке, тј. податке из индексне колоне и називе колона, а у другом редне бројеве редова и колона који почињу нулом. Ево примера како можемо да их употребимо. Приметите како се индексна колона не броји као ни ред у ком су називи колона.

In [6]:
bibreg.loc[216,'Publisher']
Out[6]:
'Virtue & Co.'
In [7]:
bibreg.iloc[1,3]
Out[7]:
'Virtue & Co.'

Слично можемо да приступимо само одређеним редовима или колонама. На пример, овако:

In [8]:
bibreg.loc[216]
Out[8]:
Edition Statement                                                       NaN
Place of Publication                               London; Virtue & Yorston
Date of Publication                                                    1868
Publisher                                                      Virtue & Co.
Title                     All for Greed. [A novel. The dedication signed...
Author                                                            A., A. A.
Contributors                   BLAZE DE BURY, Marie Pauline Rose - Baroness
Corporate Author                                                        NaN
Corporate Contributors                                                  NaN
Former owner                                                            NaN
Engraver                                                                NaN
Issuance type                                                   monographic
Flickr URL                http://www.flickr.com/photos/britishlibrary/ta...
Shelfmarks                                British Library HMNTS 12626.cc.2.
Name: 216, dtype: object
In [9]:
bibreg.iloc[:,1]
Out[9]:
Identifier
206                          London
216        London; Virtue & Yorston
218                          London
472                          London
480                          London
                     ...           
4158088                      London
4158128                       Derby
4159563                      London
4159587         Newcastle upon Tyne
4160339                      London
Name: Place of Publication, Length: 8287, dtype: object

Избор колона

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

Ако нам је потребно свега неколико колона, најбоље је да њихове називе ставимо у листу и сведемо DataFrame тако да садржи само њих. У супротном, ако хоћемо само неке да искључимо, ставимо их у листу и позовемо функцију drop() из Pandas библиотеке. На пример, можемо да ставимо као аргумент функције drop() листу za_brisanje коју смо претходно дефинисали као za_brisanje=['Edition Statement','Corporate Author','Corporate Contributors','Former owner','Engraver','Contributors','Issuance type','Shelfmarks']. Признаћете, мало је заметно наводити све називе колона које хоћемо да обришемо. Алтенатива је да исту листу дефинишемо преко редних бројева колона: za_brisanje=bibreg.columns[[0,6,7,8,9,10,11,13]].

In [10]:
za_brisanje=bibreg.columns[[0,6,7,8,9,10,11,13]]
In [11]:
bibreg.drop(za_brisanje, axis = 1, inplace = True)

Аргумент axis=1 каже функцији да треба обрисати колоне. Да је та вредност 0, функција би обрисала редове.

In [12]:
bibreg.head()
Out[12]:
Place of Publication Date of Publication Publisher Title Author Flickr URL
Identifier
206 London 1879 [1878] S. Tinsley & Co. Walter Forbes. [A novel.] By A. A A. A. http://www.flickr.com/photos/britishlibrary/ta...
216 London; Virtue & Yorston 1868 Virtue & Co. All for Greed. [A novel. The dedication signed... A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
218 London 1869 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr... A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
472 London 1851 James Darling Welsh Sketches, chiefly ecclesiastical, to the... A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
480 London 1857 Wertheim & Macintosh [The World in which I live, and my place in it... A., E. S. http://www.flickr.com/photos/britishlibrary/ta...

Припрема нумеричких података

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

Пробајте да нађете најстарију књигу у регистру. То би требало да буде најмањи број у колони помоћу функције min().

In [13]:
# bibreg['Date of Publication'].min()

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

In [14]:
bibreg.dtypes
Out[14]:
Place of Publication    object
Date of Publication     object
Publisher               object
Title                   object
Author                  object
Flickr URL              object
dtype: object

Видимо да је и Data of Publication типа object. Разлог за то су угласте заграде, зарези и цртице које се налазе у неким пољима те колоне. Док год постоји барем једно поље у ком није број, Пајтон колону неће препознати као нумеричку. Уколико тих поља има мало, уређивање можемо да урадимо и ручно. Да видимо исплати ли се то написаћемо мали програм који покушава да елемент низа претвори у цео број. За сваки елемент где не успе у томе програм ће повећати број за један.

In [15]:
n=0
for element in bibreg['Date of Publication']:
    try:
        tmp = int(element)
    except:
        n=n+1
In [16]:
n
Out[16]:
1759
In [17]:
len(bibreg)
Out[17]:
8287

Видимо да чак 1759 од 8287 елемената у овој колони не можемо да претворимо у број. То свакако не треба да радимо ручно.

In [18]:
print (bibreg['Date of Publication'][:30])
Identifier
206            1879 [1878]
216                   1868
218                   1869
472                   1851
480                   1857
481                   1875
519                   1872
667                    NaN
874                   1676
1143                  1679
1280                  1802
1808                  1859
1905                  1888
1929           1839, 38-54
2836                  1897
2854                  1865
2956               1860-63
2957                  1873
3017                  1866
3131                  1899
4598                  1814
4884                  1820
4976                  1800
5382    1847, 48 [1846-48]
5385               [1897?]
5389               [1897?]
5432                  1893
6036                  1805
6821                  1837
7521                  1896
Name: Date of Publication, dtype: object

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

Да се договоримо прво шта треба да препознамо као број. Једно решење (које не мора да буде најбоље) је да тражимо само оне елементе у колони који почињу са четири цифре, да те четири цифре претворимо у број, а да све остало занемаримо. То значи да би први елемент био 1879, други 1868 итд.

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

Регуларни изрази

Регуларни изрази (енг. regular expressions) су кодирани описи садржаја у неком тексту. Пајтон може да претражи низ текстуалних података у потрази за садржајем који одговарају том опису. Начин на који се кодирају регуларни изрази нису баш интуитивни. Другим речима, ако су вам начини на који се записују и раде регуларни изрази збуњујући и неразумљиви, то је сасвим нормално.

Ми ћемо помоћу регуларних израза да потражимо групе од четири цифре на почетку текстуалне променљиве. Регуларни израз који то описује је ^(\d{4}). Код \d означава digit, односно било коју цифру, {4} значи да се цифре понављају тачно 4 пута, заграде означавају групу коју треба обележити, а код ^ да се тим текстом почиње ред. За разлику од обичног текста који стављамо под наводнике кад га придружујемо променљивој, овде испред наводника стоји слово r које означава да то није обичан текст већ кодирани опис текста.

In [19]:
maska=r'^(\d{4})'

Сада треба да прођемо кроз све елементе колоне Date of Publication и извучемо стринг са траженим описом уколико постоји у том елементу. То ћемо урадити помоћу функције str.extract(). Аргументи ове функције су регуларни израз који смо ставили у променљиву maska и напомена Пајтону да излаз буде само једна колона.

In [20]:
year = bibreg['Date of Publication'].str.extract(maska, expand=False)
In [21]:
year
Out[21]:
Identifier
206        1879
216        1868
218        1869
472        1851
480        1857
           ... 
4158088    1838
4158128    1831
4159563     NaN
4159587    1834
4160339    1834
Name: Date of Publication, Length: 8287, dtype: object

Видимо да је Пајтон препознао и издвојио групе од четири цифре које би требало да представљају године. Тамо где му то није пошло за руком пише NaN. Приметите да ови подаци и даље нису бројеви него стрингови. Да би постали бројеви потребно је да их претворимо у бројеве помоћу функције to_numeric(). То није било могуће пре чишћења. Надајмо се да ће сада радити.

In [22]:
year = pd.to_numeric(year)

Ако је све у реду онда ћемо моћи да нађемо најстарију књигу.

In [23]:
year.min()
Out[23]:
1510.0

Најстарија књига је из 1510. године. То је баш старо. Можете ли да откријете која је то књига?

In [24]:
bibreg.loc[year==1510]
Out[24]:
Place of Publication Date of Publication Publisher Title Author Flickr URL
Identifier
2938347 Paris 1510?] in aedibus Nicolai Crispini Piutarchi Chaeronensis Regum  Imperatorum Apo... NaN http://www.flickr.com/photos/britishlibrary/ta...

Све књиге за које нисмо успели да препознамо годину издавања имају ознаку NaN коју уочавамо помоћу функције isnull(). Ако саберемо колико таквих има, знаћемо колико је чишћење било успешно.

In [25]:
year.isnull().sum()
Out[25]:
971

У поређењу са 1759 број 971 јесте мањи, али и даље значајан. Међутим, оно што је значајније јесте да смо текстуалне записе претворили у бројеве које можемо даље да анализирамо.

Било би добро очишћене нумеричке податке убацити назад у табелу, али није препоручљиво да то урадите тако што пребришете оригиналне податке у колони Date of Publication. Тако бисмо изгубили могућност да проверимо је ли чишћење било добро и евентуално променимо критеријум касније. Боље је да имамо оригиналну и очишћену колону једну поред друге. Помоћу функције insert() можемо да убацимо на одређено место у табели колону са именом и вредностима као аргументима.

In [26]:
bibreg.insert(2, "Year", year)
In [27]:
bibreg
Out[27]:
Place of Publication Date of Publication Year Publisher Title Author Flickr URL
Identifier
206 London 1879 [1878] 1879.0 S. Tinsley & Co. Walter Forbes. [A novel.] By A. A A. A. http://www.flickr.com/photos/britishlibrary/ta...
216 London; Virtue & Yorston 1868 1868.0 Virtue & Co. All for Greed. [A novel. The dedication signed... A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
218 London 1869 1869.0 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr... A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
472 London 1851 1851.0 James Darling Welsh Sketches, chiefly ecclesiastical, to the... A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
480 London 1857 1857.0 Wertheim & Macintosh [The World in which I live, and my place in it... A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
... ... ... ... ... ... ... ...
4158088 London 1838 1838.0 NaN The Parochial History of Cornwall, founded on,... GIDDY, afterwards GILBERT, Davies. http://www.flickr.com/photos/britishlibrary/ta...
4158128 Derby 1831, 32 1831.0 M. Mozley & Son The History and Gazetteer of the County of Der... GLOVER, Stephen - of Derby http://www.flickr.com/photos/britishlibrary/ta...
4159563 London [1806]-22 NaN T. Cadell and W. Davies Magna Britannia; being a concise topographical... LYSONS, Daniel - M.A., F.R.S., and LYSONS (Sam... http://www.flickr.com/photos/britishlibrary/ta...
4159587 Newcastle upon Tyne 1834 1834.0 Mackenzie & Dent An historical, topographical and descriptive v... Mackenzie, E. (Eneas) http://www.flickr.com/photos/britishlibrary/ta...
4160339 London 1834-43 1834.0 NaN Collectanea Topographica et Genealogica. [Firs... NaN http://www.flickr.com/photos/britishlibrary/ta...

8287 rows × 7 columns

Чишћење текстуалних података

За разлику од нумеричких података, текстуалне можемо да анализирамо и пре чишћења. За текст имамо другу врсте анализе (колико има којих елемената, ког има највише итд.). Резултати можда неће бити најпоузданији, али ћемо добити нешто информативно. Да видимо, на пример, статистику места издавања књига из регистра bibreg.

In [28]:
bibreg['Place of Publication'].value_counts()
Out[28]:
London                        3868
Paris                          479
Edinburgh                      208
New York                       177
Leipzig                        119
                              ... 
London; printed in Bavaria       1
Cur                              1
Aldine House                     1
St. Petersburg, Leipzig          1
Pécsett                         1
Name: Place of Publication, Length: 1441, dtype: int64

Имамо 1441 различит запис места издавања. Вероватно места издавања има мање, али су записани другачије. Промена тог броја може да буде показатељ колико смо добро очистили колону.

In [29]:
bibreg['Place of Publication'].value_counts()[:40]
Out[29]:
London                3868
Paris                  479
Edinburgh              208
New York               177
Leipzig                119
Philadelphia            89
Berlin                  70
Boston [Mass.]          52
Dublin                  48
Glasgow                 45
Oxford                  44
Boston                  41
Wien                    38
Madrid                  33
Stockholm               33
Bruxelles               28
Cambridge               26
Amsterdam               25
Stuttgart               24
Chicago                 24
Firenze                 24
Birmingham              23
Manchester              20
Milano                  20
Tours                   19
enk                     19
Calcutta                19
Kjøbenhavn              18
Bristol                 18
Norwich                 16
С.-Петербургъ           16
New-York                16
Edinburgh & London      16
Buenos Aires            16
Lisboa                  16
Cincinnati              14
México                 14
Haarlem                 14
London]                 14
Napoli                  14
Name: Place of Publication, dtype: int64

Оно што видимо на први поглед је да имамо више записа који се односе на исти град. На пример, London (3868) и London] (14) или Boston [Mass.] (52) и Boston (41). Чишћење податка би то требало да обједини. Већ из 40 најчешћих места издавања видимо да се називи места понављају кроз различите записе. Међу осталих хиљаду мора да их има још много.

Да видимо за записе у којима се појављују речи "London", "Paris" или "Boston" каквих све облика има.

In [30]:
place=bibreg['Place of Publication']

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

In [31]:
place[place.str.contains("Boston")]
Out[31]:
Identifier
16544              Boston
28758              Boston
45172              Boston
53627              Boston
69331              Boston
                ...      
3954038    Boston [Mass.]
3954691            Boston
3954692            Boston
3954693    Boston [Mass.]
3970785     Boston [U.S.]
Name: Place of Publication, Length: 119, dtype: object
In [32]:
place[place.str.contains("York")].value_counts()
Out[32]:
New York                                            177
New-York                                             16
London & New York                                    10
York                                                  8
London; New York [printed]                            3
New York & London                                     3
Macmillan & Co.; New York                             2
Nueva-York                                            1
New York, 1866                                        1
London & New York, Macmillan & Co                     1
London, Edinburgh and New York                        1
pp. 171. J. Lane: London & New York, 1896             1
New York; Kegan Paul & Co                             1
London; New York printed                              1
Boston and New York                                   1
New York, London                                      1
London [printed], New York                            1
New York [printed]; London                            1
Boston & New York                                     1
pp. 110. John Lane: London, New York, 1918            1
pp. viii. 291. Harper & Bros.: New York, 1888         1
4 vol. The Century Co.: New York, 1887, 8º            1
Nueva York                                            1
pp. 330. J. Lane: London & New York, 1899 [1898]      1
New York [printed], Chicago                           1
London, York [printed]                                1
Neuva York [sic]                                      1
Nouvelle-York                                         1
Name: Place of Publication, dtype: int64

Има чак 245 различитих записа у којима се помиње Лондон. То неће бити једноставно за чишћење. Неке од ових записа вероватно не би требало мењати: Edinburgh & London би требало да остане тако како јесте, али би Longman & Co. London требало променити у London. Међутим, код чишћења података треба бити практичан. Ако већ знамо да чишћење податка тражи много времена и да никад не можемо да будемо сигурни јесмо ли све исправили како треба, онда треба почети од очигледних и значајних измена. Прво исправљајте грешке које су најбројније. Спојте Boston [Mass.] и Boston у један запис, промените New-York у New York па онда редом.

Постоји ли нешто што можемо да урадимо пре него што конкретне називе места кренемо да замењујемо другима? Видимо на пример да постоје различити записи "Boston and New York" и "Boston & New York". Ако заменимо " and " са " & " онда ће то имати ефекта код свих комбинација градова, не само код Бостона и Њујорка.

In [33]:
place=place.str.replace(" and ", " & ")
In [34]:
place.value_counts()
Out[34]:
London                        3868
Paris                          479
Edinburgh                      208
New York                       177
Leipzig                        119
                              ... 
Singapore                        1
London; printed in Bavaria       1
Cur                              1
Aldine House                     1
Pécsett                         1
Name: Place of Publication, Length: 1439, dtype: int64

Смањили смо број различитих облика записа за 2. Није превише успешно. Да видимо даље. Изгледа да знак ; у запису значи да после тога иде нешто мање важно, нпр. где је књига штампана или се још једном појави ко је издавач.

In [35]:
place[place.str.contains(";")].value_counts()
Out[35]:
London; Edinburgh [printed]                     6
London; Guildford [printed]                     4
London; Edinburgh printed                       3
London; Nuremberg [printed                      3
London; Perth [printed                          3
                                               ..
Boston [Mass.]; Cambridge [Mass., printed]      1
Philadelphia; London                            1
E. Nister: London; Nuremberg [printed, 1890]    1
London: E. Nutt; E. Cooke, 1733                 1
Leeds: Bean & Son; Leamington: Hamblen, 1883    1
Name: Place of Publication, Length: 69, dtype: int64

Можда бисмо могли да помоћу функције уклонимо све што се појављује после ";", односно да поделимо стрингове на листе split() тако што ће управо тачка-зарез бити сепаратор па да онда узмемо само први елемент листе. Аргумент expand=True служи да резултат претвори у DataFrame како бисмо могли да узмемо само први део, односно колону са индексом 0.

In [36]:
place=place.str.split(";",expand=True)[0]
In [37]:
place.value_counts()[:40]
Out[37]:
London                3923
Paris                  480
Edinburgh              211
New York               178
Leipzig                119
Philadelphia            90
Berlin                  70
Boston [Mass.]          53
Dublin                  48
Glasgow                 45
Oxford                  44
Boston                  42
Wien                    38
Madrid                  34
Stockholm               33
Bruxelles               28
Cambridge               26
Amsterdam               25
Firenze                 24
Chicago                 24
Stuttgart               24
Birmingham              23
Manchester              22
Edinburgh & London      22
Milano                  20
enk                     19
Bristol                 19
Calcutta                19
Tours                   19
Kjøbenhavn              18
New-York                16
С.-Петербургъ           16
Buenos Aires            16
Norwich                 16
Lisboa                  16
México                 14
Cincinnati              14
London]                 14
Haarlem                 14
Napoli                  14
Name: 0, dtype: int64

Ово је изгледа помогло више. Сада имамо 49 облика записа мање. Број записа где је Лондон место издавања се повећао са 3868 на 3923. То значи да смо за кратко време направили помак. Да смо ручно чистили податке, много више времена би нам требало. На сличан начин бисмо могли да чистимо и угласте заграде, да бришемо називе држава из записа (нпр. U.S.A.), али би коришћење регуларних израза било врло пожељно. Пробајте сами шта можете да очистите на овај начин.

Сада да заменимо конкретне записе које смо помињали раније.

In [38]:
place[place=='Boston [Mass.]']='Boston'
place[place=='New-York']='New York'
In [39]:
place.value_counts()[:40]
Out[39]:
London                3923
Paris                  480
Edinburgh              211
New York               194
Leipzig                119
Boston                  95
Philadelphia            90
Berlin                  70
Dublin                  48
Glasgow                 45
Oxford                  44
Wien                    38
Madrid                  34
Stockholm               33
Bruxelles               28
Cambridge               26
Amsterdam               25
Firenze                 24
Stuttgart               24
Chicago                 24
Birmingham              23
Edinburgh & London      22
Manchester              22
Milano                  20
Calcutta                19
enk                     19
Bristol                 19
Tours                   19
Kjøbenhavn              18
С.-Петербургъ           16
Norwich                 16
Buenos Aires            16
Lisboa                  16
Napoli                  14
Cincinnati              14
Haarlem                 14
London]                 14
México                 14
Mexico                  13
Budapest                13
Name: 0, dtype: int64

Табела сада изгледа чистије, али посао чишћења ни изблиза није готов. Ми ћемо се, међутим, овде зауставити јер не желимо да цео курс прође у чишћењу података у овој једној колони. Коначно, убацићемо низ place као нову колону поред Place of Publication како бисмо могли да контролишемо је ли чишћење било успешно.

In [40]:
bibreg.insert(1, "Place", place)
In [41]:
bibreg
Out[41]:
Place of Publication Place Date of Publication Year Publisher Title Author Flickr URL
Identifier
206 London London 1879 [1878] 1879.0 S. Tinsley & Co. Walter Forbes. [A novel.] By A. A A. A. http://www.flickr.com/photos/britishlibrary/ta...
216 London; Virtue & Yorston London 1868 1868.0 Virtue & Co. All for Greed. [A novel. The dedication signed... A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
218 London London 1869 1869.0 Bradbury, Evans & Co. Love the Avenger. By the author of “All for Gr... A., A. A. http://www.flickr.com/photos/britishlibrary/ta...
472 London London 1851 1851.0 James Darling Welsh Sketches, chiefly ecclesiastical, to the... A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
480 London London 1857 1857.0 Wertheim & Macintosh [The World in which I live, and my place in it... A., E. S. http://www.flickr.com/photos/britishlibrary/ta...
... ... ... ... ... ... ... ... ...
4158088 London London 1838 1838.0 NaN The Parochial History of Cornwall, founded on,... GIDDY, afterwards GILBERT, Davies. http://www.flickr.com/photos/britishlibrary/ta...
4158128 Derby Derby 1831, 32 1831.0 M. Mozley & Son The History and Gazetteer of the County of Der... GLOVER, Stephen - of Derby http://www.flickr.com/photos/britishlibrary/ta...
4159563 London London [1806]-22 NaN T. Cadell and W. Davies Magna Britannia; being a concise topographical... LYSONS, Daniel - M.A., F.R.S., and LYSONS (Sam... http://www.flickr.com/photos/britishlibrary/ta...
4159587 Newcastle upon Tyne Newcastle upon Tyne 1834 1834.0 Mackenzie & Dent An historical, topographical and descriptive v... Mackenzie, E. (Eneas) http://www.flickr.com/photos/britishlibrary/ta...
4160339 London London 1834-43 1834.0 NaN Collectanea Topographica et Genealogica. [Firs... NaN http://www.flickr.com/photos/britishlibrary/ta...

8287 rows × 8 columns

Датум и време

Слично као што Пајтон не препознаје бројеве у "прљавој" колони, не препознаје ни датуме и време. Да бисмо могли да анализирамо временске податке, неопходно је да их забележимо у истом формату и да их конвертујемо у одговарајући, DateTime формат помоћу функције to_datetime().

Стринг који садржи датум, према ISO стандарду, треба да има формат 'YYYY-MM-DD' или 'YYYY-MM-DD HH:MM:SS' ако садржи и време. Уколико Пајтон прочита овакав стринг, умеће лако да га претвори у променљиву типа датум одакле можемо да прочитамо годину, дан, месец итд. Ако је датум записан стринг у овом формату, онда нема потребе да наглашавамо који је формат. Међутим, ако уместо 2021-07-13 стоји 12. 7. 2021. или July 13, 2021 онда морамо Пајтону тачно да кажемо како да прочита тај податак.

In [42]:
datum_string='2021-07-13'
# datum_string='2021-07-13 10:15:00'

datum = pd.to_datetime(datum_string)
# datum = pd.to_datetime(datum_string, format='%Y-%m-%d')
godina= datum.year
mesec = datum.month
dan = datum.day
sat=datum.hour
minut=datum.minute
sekund=datum.second
In [43]:
dan
Out[43]:
13
In [44]:
pd.to_datetime("23. 5. 1970.",format="%d. %m. %Y.")
Out[44]:
Timestamp('1970-05-23 00:00:00')

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

In [45]:
y=pd.to_datetime(bibreg['Year'],format='%Y-%m-%d')
In [46]:
y.head()
Out[46]:
Identifier
206   1970-01-01 00:00:00.000001879
216   1970-01-01 00:00:00.000001868
218   1970-01-01 00:00:00.000001869
472   1970-01-01 00:00:00.000001851
480   1970-01-01 00:00:00.000001857
Name: Year, dtype: datetime64[ns]

Бонус проблеми

Чишћење података у Србији је мало изазовније него у већини других (европских) земаља. Први разлог за то је што користимо два писма па се често суочавамо са неконзистентно унетим подацима. Други разлог је недоследна употреба децималног зареза. На рачунару и калкулатору обично користимо тачку, а званични документи траже зарез. Још горе, сепаратор за хиљаде је у српском језику тачка, а у енглеском зарез. Коначно, трећи разлог за изазовније чишћење података је недоследно записивање датума.

Ћирилица и латиница

У следећој ћелији изгледа као да смо исти кôд написали два пута. Логично, очекујемо да ће излаз у оба случаја бити исти. Међутим, слово А је једном укуцано коришћењем латиничне, а други пут коришћењем ћириличне тастатуре. Пајтон је приметио ту разлику и запамтио две различите вредности ASCII кôдова. То што A и А изгледају исто, не значи да их рачунар исто региструје.

In [47]:
slovo = 'A' # А латиницом
print("ASCII вредност знака '" + slovo + "' је", ord(slovo))

slovo = 'А' # А ћирилицом
print("ASCII вредност знака '" + slovo + "' је", ord(slovo))
ASCII вредност знака 'A' је 65
ASCII вредност знака 'А' је 1040

Заиста, кад питамо Пајтон да ли су ова два знака иста, он каже да нису.

In [48]:
"A"=="А"
Out[48]:
False

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

Пајтон има библиотеке које могу да помогну у уједначавању текста. Библиотека cyrtranslit има функције које могу да ураде транслитерацију и ћирилични текст препишу у латинични и обрнуто.

In [49]:
# Ако библиотека cyrtranslit није већ инсталирана, инсталирајте је са
# pip install cyrtranslit

import cyrtranslit

# из латинице у ћирилицу
print(cyrtranslit.to_cyrillic("daždevnjak"))

# и из ћирилице у латиницу
print(cyrtranslit.to_latin("мравињак"))
даждевњак
mravinjak

Децимални зарез и децимална тачка

Када учитавате или снимате фајл са подацима који су децимални бројеви, водите рачуна да ли се користи децимална тачка или зарез. Званични подаци у Србији би требало да увек имају децимални зарез. Ако користимо CSV формат, то ће сигурно бити проблем. Не може исти знак да одваја целобројни и децимални део броја, као и да одваја бројчане податке у фајлу.

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

In [50]:
# pokazatelji=pd.read_csv("https://data.gov.rs/sr/datasets/r/b1f1f94a-fe21-3a2a-b801-6f0f05b24257",sep=";")

Уколико ово учитавање због интернет везе или сервера не ради, учитајте исти фајл који смо претходно преузели и снимили.

In [51]:
pokazatelji=pd.read_csv("data/03IND01.csv",sep=";")

Приметите како је други аргумент функције sep=";". То значи да користим CSV формат у ком сепаратор (знак који одваја податке) није зарез (,) него тачка-зарез (;). У Европи где већина земаља користи децимални зарез ништа не би било погрешније од избора знака , за сепаратор. То би одмах обесмислило све децималне бројеве. Због тога као сепаратор користимо или "таб" или ";".

JSON формат

Често се користи робуснији формат података који није осетљив на писмо које користимо или избор сепаратора. JSON (JavaScript Object Notation) формат служи за преношење сложенијих структура података између различитих платформи. Тај формат за пренос података, исти за вољу, није баш прегледан али је разумљив и људима и машинама. Отворени подаци су најчешће доступни управо у ова два формата: CSV и JSON.

Када бисмо погледали какав се текст налази у једном JSON фајлу, видели бисмо структуру која наликује листи у Пајтону где поред сваког податка уписаног под знацима навода стоји назив колоне, такође под знацима навода. Овакав фајл делује непотребно редундантно, али се то показује као велика предност код сложених структура података.

[{"IDIndikator":"IND00M01","Indikator":"Градоначелник/председник општине","mes":"06","god":"2021","idter":"70017","nter":"Александровац","vrednost":"МИРКО МИХАЈЛОВИЋ","CreateDate":"6/1/2021 9:41:50 AM","LastUpdate":"6/1/2021 9:41:50 AM","IDLegenda":"A","nIzvorI":"Републички завод за статистику (РЗС)"},{"IDIndikator":"IND01G01","Indikator":"Површина (у км²)","mes":"00","god":"2020","idter":"70017","nter":"Александровац","vrednost":"387","CreateDate":"1/27/2021 10:15:39 AM","LastUpdate":"1/27/2021 10:22:18 AM","IDLegenda":"A","nIzvorI":"Републички геодетски завод (РГЗ)"},{"IDIndikator":"IND01G02","Indikator":"Број становника - процена (последњи расположив податак за

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

In [52]:
pokazatelji = pd.read_json("data/03IND01.json")

Снимање табеле типа data frame у фајл је исто тако једноставно. За то служе функције to_csv() и to_json().

In [53]:
pokazatelji.to_csv("data/temp.csv",sep=";")