Selles praktikumis tutvume tabelkujul olevate andmete töötluse võimalustega Pythonis.
csv formaat on väga levinud andmete säilitamiseks tabelkujul. Võite proovida avada andmefaili MS Exceli või muu tabelitöötlusprogrammiga - enne avamist küsib programm üle, mida kasutada eraldajana. Ehk mis märk eraldab veerge üksteisest failis. Nagu nime järgi võib oletada, on standardseks eraldajaks koma.
CSV-failid talletavad lihttekstilise tabeli kujul andmeid, kus rea defineerib üldiselt reavahetuse sümbol ning veeru ettemääratud eraldusmärk. Kui tekstilised tunnused sisaldavad reavahetuse sümbolit või veeru eraldajat, piiritletakse tunnuse väärtus jutumärkidega (""). Traditsiooniliselt kasutatakse veergude eralduseks koma, reavahetuseks süsteemi reavahetuse sümbolite jada ning jutumärgiks jutumärki " . Kuna CSV pole aga standardiseeritud, võib kohata väga erinevaid kujusid, mistõttu CSV-dega töötavad süsteemid võimaldavad kasutajal määrata erinevaid formaadi parameetreid.
CSV talletab struktureeritud andmeid (igal andmepunktil - näiteks raamatul, tootel, kasutajal - on fikseeritud tunnused), mistõttu on see ajalooliselt käinud käsikäes andmebaaside ja meie aine valdkonnas ka masinõppemeetoditega. CSV hiilgab kompaktsuse ning platvormist sõltumatusega (kui välja jätta standardiseerimatus). CSV-ga töötamiseks on Pythonis olemas csv teek.
csv teek lubab nii lugeda kui kirjutada CSV formaadis faile. Andmeridadega manipuleerimiseks saab kasutada kas ühetasemelisi itereeritavaid andmestruktuure (nt list või tuple) või sõnaraamatuid.
import csv
with open('ilusad_inimesed.csv') as csv_file:
rows = []
reader = csv.reader(csv_file)
header = next(reader)
rows.append(header)
print(header)
print()
for row in reader:
rows.append(row)
print(row)
['nimi', 'sugu', 'vanus'] ['Teele', 'naine', '25'] ['Ivan', 'mees', '87'] ['Arfi', 'mees', '12'] ['Leida', 'naine', '58']
Eelnevas koodiplokis nägime, et csv.reader() võimaldab lugeda CSV ridu ükshaaval, teisendades rea Pythoni sõnede listiks. Kui me teame, et mingi tunnus on sõnest erinevat tüüpi (nt täisarv, ujukomaarv või kuupäev), peame selle käsitsi vastavale kujule teisendama.
Lisaks tutvusime eelnevas plokis iteraatorist järgmise elemendi pärimisega. Kõik objektid, mille saame for-tsüklisse pista, on itereeritavad ning pakuvad iteraatorit, mis võimaldab andmestruktuuris sisalduvaid elemente ükshaaval läbida. Iteraatoritel on üldiselt kaks meetodit - next ja has_next. For-tsükkel itereerib üle andmestruktuuri, kuni andmestruktuuris on veel elemente.
Antud näites annab next() järgmise rea. Esimest korda kutsudes saame esimese (päise) rea. Edaspidi anname reader'i for-tsüklile, mis kutsub next() meetodit ülejäänud korrad ülejäänud ridade saamiseks.
with open('ilusad_inimesed.csv.copy','w') as csv_file:
writer = csv.writer(csv_file)
for row in rows:
writer.writerow(row)
Nägime, et csv.writer() võimaldab kirjutada csv ridu, kui need on Pythonis itereeritaval kujul. Antud juhul olid ridadeks sõnede järjendid.
Lisaks list'idele saame töötada ka sõnaraamatutega (dict'idega).
with open('ilusad_inimesed.csv') as csv_file:
rows = []
reader = csv.DictReader(csv_file)
for row in reader:
rows.append(row)
print(row)
{'nimi': 'Teele', 'sugu': 'naine', 'vanus': '25'} {'nimi': 'Ivan', 'sugu': 'mees', 'vanus': '87'} {'nimi': 'Arfi', 'sugu': 'mees', 'vanus': '12'} {'nimi': 'Leida', 'sugu': 'naine', 'vanus': '58'}
Kui listide korral pidime esimese rea ehk päisega ise toimetama, siis sõnaraamatute korral loetakse see vaikimisi sisse ning selle veergude väärtused määratakse ülejäänud ridade veergude nimedeks.
Ettevaatust: kui CSV-l puudub päis, lähevad salaja esimese rea andmed kaduma.
with open('ilusad_inimesed.csv.copy','w') as csv_file:
fieldnames = [fieldname for fieldname in rows[0]]
print("Fieldnames:", fieldnames)
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for row in rows:
writer.writerow(row)
Fieldnames: ['nimi', 'sugu', 'vanus']
csv teek sobib eelkõige CSV formaadis kirjutamiseks ja lugemiseks. Kui on soovi keerulisemaid või suuremaid numbrilisi tabelitöötlusi teha, osutub kasulikuks pandas'e teek.
Failis comments.csv on toodud 200 lugejakommentaari ühest eesti meediaväljaandest. Veerus "Staatus" on märgitud, kas moderaator on kommentaari ära keelanud (staatus 1) või mitte (staatus 2). Veergudes "Pos" ja "Neg" on kirjas, kui palju on kommentaar saanud lugejatelt vastavalt positiivseid ja negatiivseid hinnanguid.
Lugege fail sisse, kasutades csv teeki, ning leidke:
Millised 10 kommentaari on pälvinud kõige enam hindeid lugejatelt? (0,5p)
Kas lugejad ja moderaatorid on ühel meelel kommentaaride sobilikkuse osas: milline osakaal moderaatori poolt keelatud kommentaaridest on saanud lugejatelt rohkem negatiivseid hääli kui positiivseid? Milline osakaal lubatud kommentaaridest? (1p)
Pandas on Pythoni teek, mis võimaldab mugavalt töödelda tabelkujul olevaid andmeid - muuhulgas ka lugeda-kirjutada csv faile. Vaatame järgmist näidet:
import pandas as pd
ilusad_inimesed = pd.read_csv("ilusad_inimesed.csv")
ilusad_inimesed
nimi | sugu | vanus | |
---|---|---|---|
0 | Teele | naine | 25 |
1 | Ivan | mees | 87 |
2 | Arfi | mees | 12 |
3 | Leida | naine | 58 |
Nagu näeme, oskab pandas kuvada meie csv-faili ilusa tabelina. Millega tegu?
type(ilusad_inimesed)
pandas.core.frame.DataFrame
Mida sellega teha saab? Töödelda andmeid nii, et neid on samal ajal ka mugav vaadata.
Andmeid saab töödelda nii rea kui veeru kaupa. Mõned näited paljudest võimalikest operatsioonidest (põhjalikumat juhendit vaata siit):
# Veergude lisamine
ilusad_inimesed['pensionär'] = ilusad_inimesed['vanus'] > 65
ilusad_inimesed['laste arv'] = 0
ilusad_inimesed['perekonnanimi'] = ['Kask', 'Smirnov', 'Jalakas', 'Kuusepuu']
ilusad_inimesed
nimi | sugu | vanus | pensionär | laste arv | perekonnanimi | |
---|---|---|---|---|---|---|
0 | Teele | naine | 25 | False | 0 | Kask |
1 | Ivan | mees | 87 | True | 0 | Smirnov |
2 | Arfi | mees | 12 | False | 0 | Jalakas |
3 | Leida | naine | 58 | False | 0 | Kuusepuu |
# Veeru eemaldamine
del ilusad_inimesed['laste arv']
ilusad_inimesed
nimi | sugu | vanus | pensionär | perekonnanimi | |
---|---|---|---|---|---|
0 | Teele | naine | 25 | False | Kask |
1 | Ivan | mees | 87 | True | Smirnov |
2 | Arfi | mees | 12 | False | Jalakas |
3 | Leida | naine | 58 | False | Kuusepuu |
# Transponeerimine (vahetame veerud-read omavahel)
ilusad_inimesed2 = ilusad_inimesed.T
ilusad_inimesed2
0 | 1 | 2 | 3 | |
---|---|---|---|---|
nimi | Teele | Ivan | Arfi | Leida |
sugu | naine | mees | mees | naine |
vanus | 25 | 87 | 12 | 58 |
pensionär | False | True | False | False |
perekonnanimi | Kask | Smirnov | Jalakas | Kuusepuu |
# Ridade ja veergude arvu leidmine
ilusad_inimesed.shape
(4, 5)
ilusad_inimesed2.shape
(5, 4)
ilusad_inimesed
nimi | sugu | vanus | pensionär | perekonnanimi | |
---|---|---|---|---|---|
0 | Teele | naine | 25 | False | Kask |
1 | Ivan | mees | 87 | True | Smirnov |
2 | Arfi | mees | 12 | False | Jalakas |
3 | Leida | naine | 58 | False | Kuusepuu |
Tabelite oluliseks funktsiooniks on võimalus saada oma andmetest hea ülevaade. Selleks on pandases erinevaid filtreerimise ja sorteerimise võimalusi, millest mõnesid järgnevalt vaatame.
# Leiame read, kus veeru 'vanus' väärtus on suurem kui 30
ilusad_inimesed[ilusad_inimesed['vanus'] > 30]
nimi | sugu | vanus | pensionär | perekonnanimi | |
---|---|---|---|---|---|
1 | Ivan | mees | 87 | True | Smirnov |
3 | Leida | naine | 58 | False | Kuusepuu |
# Võime seada ka mitu tingimust korraga
ilusad_inimesed[(ilusad_inimesed['vanus'] < 65) & (ilusad_inimesed['sugu'] == 'naine')]
nimi | sugu | vanus | pensionär | perekonnanimi | |
---|---|---|---|---|---|
0 | Teele | naine | 25 | False | Kask |
3 | Leida | naine | 58 | False | Kuusepuu |
# Muudame sorteerimise järjekorda
ilusad_inimesed.sort_values(['vanus'], ascending = False)
nimi | sugu | vanus | pensionär | perekonnanimi | |
---|---|---|---|---|---|
1 | Ivan | mees | 87 | True | Smirnov |
3 | Leida | naine | 58 | False | Kuusepuu |
0 | Teele | naine | 25 | False | Kask |
2 | Arfi | mees | 12 | False | Jalakas |
Samuti on võimalik matplotlibi abiga pandase DataFrame'is olevad andmed kerge vaevaga joonisele panna. Rohkem näiteid jooniste kohta leiab näiteks siit http://queirozf.com/entries/pandas-dataframe-plot-examples-with-matplotlib-pyplot.
import matplotlib.pyplot as plt
ilusad_inimesed.plot(kind='bar',x='nimi',y='vanus', figsize=(6,2))
<matplotlib.axes._subplots.AxesSubplot at 0x219d717e080>
# Lisaks veergudele võime ka ridadele numbrite asemel "nimed" panna
ilusad_inimesed.index = ['a', 'b', 'c', 'd']
ilusad_inimesed
nimi | sugu | vanus | pensionär | perekonnanimi | |
---|---|---|---|---|---|
a | Teele | naine | 25 | False | Kask |
b | Ivan | mees | 87 | True | Smirnov |
c | Arfi | mees | 12 | False | Jalakas |
d | Leida | naine | 58 | False | Kuusepuu |
ilusad_inimesed['nimi']['a']
'Teele'
# DataFrame'i võime konvertida erinevateks Pythoni sõnastikeks
ilusad_inimesed_dict = ilusad_inimesed.to_dict("split")
ilusad_inimesed_dict
{'columns': ['nimi', 'sugu', 'vanus', 'pensionär', 'perekonnanimi'], 'data': [['Teele', 'naine', 25, False, 'Kask'], ['Ivan', 'mees', 87, True, 'Smirnov'], ['Arfi', 'mees', 12, False, 'Jalakas'], ['Leida', 'naine', 58, False, 'Kuusepuu']], 'index': ['a', 'b', 'c', 'd']}
ilusad_inimesed_dict = ilusad_inimesed.to_dict("list")
ilusad_inimesed_dict
{'nimi': ['Teele', 'Ivan', 'Arfi', 'Leida'], 'pensionär': [False, True, False, False], 'perekonnanimi': ['Kask', 'Smirnov', 'Jalakas', 'Kuusepuu'], 'sugu': ['naine', 'mees', 'mees', 'naine'], 'vanus': [25, 87, 12, 58]}
ilusad_inimesed.values
array([['Teele', 'naine', 25, False, 'Kask'], ['Ivan', 'mees', 87, True, 'Smirnov'], ['Arfi', 'mees', 12, False, 'Jalakas'], ['Leida', 'naine', 58, False, 'Kuusepuu']], dtype=object)
# Csv-sse salvestamine käib lihtsalt
ilusad_inimesed.to_csv("test1.csv")
Failis vanasõnad.txt on toodud hulk eesti vanasõnu, mis pärinevad originaalis Anne Hussari, Arvo Krikmanni ja Ingrid Sarve "Vanasõnaraamatust" (1984), kokku on korjatud aga siit. Tutvuge toodud andmefailiga - näete, et peale vanasõnade leidub seal veel natuke üht-teist. Lisaks on osa vanasõnu kirjakeelsed, osa aga murdekeelsed.
Kuna järgmises kahes ülesandes on vaja seda andmestikku kasutada, siis looge omale puhastatud andmefail, mis vastaks järgmistele tingimustele:
Vihjeid:
ebavajaliku eemaldamisel on abiks regulaaravaldised
murdekeelt aitab kirjakeelest eristada morfoloogiline analüsaator - kui lülitame välja oletamise, siis jätab analüsaator tundmatud sõnad analüüsimata. Seega, lisame puhastatud faili ainult laused, mille kõik sõnad saavad analüüsi ka ilma oletamiseta.
NB1! Ilma oletamiseta jäävad ka kirjavahemärgid analüüsideta, aga neid tuleks siinkohal sõnadena mitte käsitleda
NB2! Oletamise väljalülitamine toimib ainult siis, kui lülitame välja ka ühestamise ja pärisnimeanalüüsi
puhastatud faili jõudvate vanasõnade arv võiks olla neljakohaline, et järgmised ülesanded ka õnnestuksid
from estnltk import Text
# Morf analüüs ilma oletamiseta.
Text("Kuda külvad, nõnna lõikad.", guess = False, disambiguate = False, propername = False).analysis
[[], [{'clitic': '', 'ending': 'd', 'form': 'd', 'lemma': 'külvama', 'partofspeech': 'V', 'root': 'külva', 'root_tokens': ['külva']}], [], [], [{'clitic': '', 'ending': 'd', 'form': 'd', 'lemma': 'lõikama', 'partofspeech': 'V', 'root': 'lõika', 'root_tokens': ['lõika']}], []]
Leidke, millised nimisõnast ja omadussõnast koosnevad fraasid (nt 'sinine ämber') esinevad eesti vanasõnades. Looge csv fail, milles niisuguste fraaside sagedused vanasõnades oleks esitatud risttabelina: reatunnusteks on nimisõnad, veergudeks omadussõnad ja tabelis sisalduksid vastavate koosesinemiste sagedused.
Selleks iga vanasõna puhul:
Csv-faili kirjutage ainult need nimisõnad, mis esinevad vähemalt 5 erineva omadussõnaga
Vihjeid:
Looge lihtne vanasõnatundmismäng, kus kasutajale kuvatakse lüngaga vanasõna ning variandid, mille vahel valida - milline sõna lünka käib.
Vihjeid: