Dec 1st, 2019 [FIN][ES] Datatieteilijän työkalut: Elasticsearch ja Python

Datatieteilijän työkalut: Elasticsearch ja Python

Yleensä suurin osa datatieteilijän työajasta saattaa kulua datan keräämiseen ja puhdistamiseen ennen kuin päästään itse koneoppimisalgoritmien käyttämiseen. Yksi osa tätä prosessia on datan varastoiminen sopivaan tietokantaan. Yhä useammat sovellukset käyttävät Elasticsearchia tietojen tallentamiseen, hakemiseen ja analysoimiseen. Tietojen analysoimiseen voidaan Elasticsearchin sisäänrakennettujen toimintojen lisäksi käyttää myös ulkoisia, asiaan soveltuvia ohjelmistoja ja kirjastoja. Yksi tällä hetkellä suosituimmista datatieteeseen soveltuvista ohjelmointikielistä on ehdottomasti avoimen lähdekoodin omaava Python, jolle on ohjelmoitu lukuisia datan analysoimiseen ja koneoppimiseen soveltuvia kirjastoja.

Tässä lyhyessä artikkelissa tarkastelemme sitä, miten voimme käyttää Pythonin funktioita ja toimintoja Elasticsearchiin tallennetun datan analysoimiseen. Koska Python 2 on hiljalleen poistumassa käytöstä, tulevat kyseisen artikkelin esimerkit käyttämään Python 3:n syntaksia.

REST-rajapinnat ja elasticsearch-py ohjelmistokirjasto

Elasticsearch tarjoaa REST-ohjelmointirajapinnan. Jotta työskenteleminen tämän rajapinnan kanssa kävisi joutuisammin, on olemassa lukuisia pääteohjelmistoja ja -kirjastoja, joista löytyy hyödyllisiä toimintoja ja funktioita juuri tähän tarkoitukseen. Tässä artikkelissa esittelemme virallisen Python kirjaston elasticsearch-py:n, mutta vastaavia kirjastoja löytyy muun muassa seuraaville ohjelmointikielille: Ruby (elasticsearch-ruby), .NET(elasticsearch-net), PHP(elasticsearch-php) ja Javascript(elasticsearch-js).

elasticsearch-py:n ohjelman asentaminen onnistuu parhaiten käyttämällä Pythonin pip kirjastonhallintasovellusta. Jotta ohjelmointiympäristösi pysyisi mahdollisimman siistinä, on suositeltavaa, että käytämme niin kutsuttua Pythonin virtuaaliympäristöä (engl. virtualenv) . Tämän luominen Python 3:n avulla onnistuu näin

python3 -m venv es_env

Virtuaaliympäristölle annamme esimerkissä nimeksi es_env, mutta nimen voi tietenkin mukauttaa omaan tarkoitukseensa. Luomisen jälkeen, virtuaaliympäristö tulee ns. aktivoida, jolloin kaikki asennetut kirjastot pysyvät omassa nimiavaruudessaan eivätkä sotkeennu tietokoneen käyttöjärjestelmässä oleviin Python-kirjastoihin.

source es_env/bin/activate

Seuraavaksi asennetaan elasticsearch-py pip-ohjelmaa käyttäen. Huomaathan, että pip-ohjelman tietokannassa tämän kirjaston nimestä on jätetty py-pääte pois.

pip install elasticsearch

Kun ohjelma on asennettu, voimme aloittaa sen käytön Python-ohjelmissamme.

Jupyter - interaktiivinen ohjelmointiympäristö

Tässä vaiheessa on sopiva puhua hieman eri ohjelmointiympäristöistä, joissa Python-ohjelmia voidaan kehittää. Usein Python-ohjelma kirjoitetaan tekstieditorilla ja käynnistetään käyttämällä konsolin python komentoa esimerkiksi näin.

Datatiedettä varten sopivampi ympäristö on kuitenkin yleensä visuaalinen, selaimessa toimiva ympäristö nimeltä Jupyter.

Jupyter on avoimeen lähdekoodiin pohjautuva projekti ja sen voi asentaa samaan virtuaaliympäristöön käyttämällä pip ohjelmaa.

pip install jupyter

Niin kutsuttu Jupyter-palvelin käynnistetään seuraavalla tavalla

jupyter notebook

Kun palvelin on käynnistynyt, voimme navigoida selaimellamme osoitteeseen

localhost:8888

ja avata uuden Jupyter-ymparistön. Tässä interaktiivisessa ympäristössä on hyvä analysoida dataa, sillä koodin lomaan voi tallentaa myös kaavioita ja tekstiä.

Datan tallentaminen ja lukeminen

Ensiksi käytämme import-komentoa ja tuomme elasticsearch-py -ohjelmistokirjaston luokan nimeltä Elasticsearch, josta voimme luoda niin kutsutun pääteolion (engl. client object)

from elasticsearch import Elasticsearch

# luomme olion nimeltä es_client
es_client = Elasticsearch()

Tässä tapauksessa meillä on Elasticsearch-palvelin samalla koneella kuin Python-ohjelma, joten meidän ei tarvitse erikseen muokata es_client-olion asetuksia, sillä oletusasetukset muodostavat yhteyden paikallisella koneella, eli localhostilla olevalle Elasticsearch-palvelimelle.

Elasticsearch tallentaa dataa JSON (Javascript Object Notation) -olioina. Yksinkertaisimmillään olion muodostuvat aaltosulkeiden välissä olevista avain-arvo pareista. Esimerkiksi alla olevat JSON-oliot kuvailevat kuvitteellisella jouluruokalistalla olevia tuotteita

{"nimike":"porkkanalaatikko",
 "maidoton": true, 
 "hinta": 5.00,
 "valuutta": "EUR",
  "paino": 0.1,
"ainekset": ["porkkana", "puuroriisi", "tumma siirappi"]}

{"nimike":"joulupuuro",
 "maidoton": false, 
 "hinta": 2.00,
 "valuutta": "EUR",
  "paino": 0.2,
"ainekset": ["puuroriisi", "maito", "sokeri", "manteli"]}

{"nimike":"piparkakkutaikina",
 "maidoton": true, 
 "hinta": 4.35,
 "valuutta": "EUR",
  "paino": 0.2,
"ainekset": ["vesi", "vehnäjauho", "tumma siirappi", "sokeri", "piparkakkumauste"]}

Tallenetaan nämä JSON-oliot Elasticsearch-indeksiin nimeltä jouluruokalista.

# luodaan Pythonille natiivi tietorakenne, sanakirja, yllä olevasta Python oliosta
# ja annetaan sitä merkitsevälle muuttujalle nimeksi porkkana_laatikko.
# Huomaathan, että yllä JSON-oliossa oleva totuusarvo false
# on muttunut Pythonille ominaiseksi False:ksi

porkkana_laatikko = {"nimike":"porkkanalaatikko",
 "maidoton": True, 
 "hinta": 5.00,
 "valuutta": "EUR",
  "paino": 0.1,
"ainekset": ["porkkana", "puuroriisi", "tumma siirappi"]}

# käytetään yllä luomaamme es_client olion
# index-metodia tämän olion tallentamiseen

es_client.index(index='jouluruokalista', body=porkkana_laatikko)

Tämän onnistuessa, saamme Elasticsearchilta seuraavanlaisen vastauksen (kuvakaappaus Jupyter- ymparistöstä)

Datan tallentaminen Bulk API rajapinnan kautta

Dataintensiivisissä sovelluksissa kuitenkin harvoin tallennetaan tai luetaan vain yhtä JSON-oliota kerrallaan ja tämä on otettu huomioon myös elasticsearch-py kirjastojen funktioissa. Mikäli haluamme tallentaa Elasticsearchiin useamman kuin yhden JSON-olion, voimme käyttää niin kutsuttua Bulk API ohjelmointirajapintaa, jonka toimintoja voi käyttää helpers-moduulin funktioiden avulla. Katsotaan seuraavaksi esimerkki siitä, miten tämä onnistuu käytännössä.

Ensiksi käytämme import-käskyä ja tuomme helpers-moduulin Python-ohjelmaamme.


from elasticsearch import helpers

Funktio, jota tulemme käyttämään fiktiivisen jouluruokalistamme tallentamiseen Elasticsearchiin, on nimeltään bulk ja se tarvitsee argumentteinaan luomamme pääteolion es_client:in sekä listan niistä JSON-olioista, jotka haluamme tallentaa.

# luodaan JSON-olioita kuvaavat sanakirja-tietorakenteet 
# ja annetaan niille nimet

porkkana_laatikko = {"nimike":"porkkanalaatikko",
 "maidoton": True, 
 "hinta": 5.00,
 "valuutta": "EUR",
  "paino": 0.1,
"ainekset": ["porkkana", "puuroriisi", "tumma siirappi"]}

joulupuuro = {"nimike":"joulupuuro",
 "maidoton": False, 
 "hinta": 2.00,
 "valuutta": "EUR",
  "paino": 0.2,
"ainekset": ["puuroriisi", "maito", "sokeri", "manteli"]}

piparkakku = {"nimike":"piparkakkutaikina",
 "maidoton": True, 
 "hinta": 4.35,
 "valuutta": "EUR",
  "paino": 0.2,
"ainekset": ["vesi", "vehnäjauho", "tumma siirappi", "sokeri", "piparkakkumauste"]}


# tallenetaan sanakirjat listaan
# ja kaytetaan helpers-moduulin bulk-funktiota
# jolle argumenteiksi annetaan pääteolio, data-lista 
# sekä indeksin nimi
data = [porkkana_laatikko, joulupuuro, piparkakku]
helpers.bulk(es_client, data, index='jouluruokalista', 
 raise_on_error=True)

Datan lukeminen Elasticsearchista

Datan tallentamisen lisäksi on hyvä myös käydä läpi, miten jo tallennettuja olioita voi lukea Elasticsearchista Python-ohjelmaamme. Tämä voi tulla tarpeeseen esimerkiksi, jos haluat analysoida dataasi käyttäen koneoppimisalgoritmeja tai funktioita, joita ei ole Elasticsearchissa.

Elasticsearch sisältää useita eri toimintoja tietyn datan hakemiseen esimerkiksi hakusanoilla, mutta tässä tapauksessa haluamme lukea kaikki indeksissä olevat JSON-oliot. Tähän tarkoitukseen solveltuu Elasticsearchin Scroll-toiminto, joka elasticsearch-py:n kirjastossa on implementoitu helpers moduulin scan-funktioon.

Olemme jo aikaisemmassa esimerkissämme tuoneet helpers-moduulin ohjelmaamme. Näin ollen voimme suoraan siirtyä käyttämään scan-funktiota ja sen jälkeen voimme lukea JSON-olioita valitsemastamme indeksistä.

# luetaan kaikki oliot indeksistä jouluruokalista
# tallennetaan tulos muuttujaan "hakutulokset"

hakutulokset = helpers.scan(es_client, index='jouluruokalista')

Yksittäiset JSON-oliot on mahdollista lukea hakutulokset muuttujasta käyttämällä for-silmukkaa.

for olio in hakutulokset:
    print(olio)

Alla olevasta Jupyter-ympäristössä otetutta kuvakaappauksessa näkyy, että indeksissämme on yhteensä neljä JSON-oliota: yksi, jonka tallensimme käyttäen es_client.index-funktiota ja kolme, jotka tallensimme käyttäen Bulk API-rajapintaa.

5 Likes