Yleisiä Python-syntakseja datatieteessä (perusteet)

Olen parin viime päivän ajan lukenut kirjaa Data Science from Scratch (PDF-osoite), joka on erinomainen ja helposti lähestyttävä johdatus datatieteeseen. Yksi sen luvuista esittelee Pythonin perussyntaksia ja datatieteessä yleisesti käytettyjä edistyneempiä syntakseja. Minusta esitys oli niin selkeä ja ytimekäs, että päätin kääntää sen tänne muistiinpanoiksi.
Yleisiä Python-syntakseja datatieteessä (perusteet)
Yleisiä Python-syntakseja datatieteessä (edistyneet)

Tämä luku keskittyy Pythonin perussyntaksiin ja ominaisuuksiin, jotka ovat erittäin hyödyllisiä tiedonkäsittelyssä (perustuu Python 2.7:ään).

Sisennys ja muotoilu

Monissa ohjelmointikielissä koodilohkoja hallitaan sulkeilla, mutta Pythonissa käytetään sisennystä:

for i in [1, 2, 3, 4, 5]:  
    print i          # "for i" -silmukan ensimmäinen rivi  
    for j in [1, 2, 3, 4, 5]:  
        print j      # "for j" -silmukan ensimmäinen rivi  
        print i + j  # "for j" -silmukan viimeinen rivi  
    print i          # "for i" -silmukan viimeinen rivi  
print "done looping"  

Tämä tekee Python-koodista erittäin luettavaa, mutta tarkoittaa myös, että sinun on kiinnitettävä jatkuvasti huomiota muotoiluun. Sulkeiden sisällä olevat välilyönnit ohitetaan, mikä on hyödyllistä pitkiä lausekkeita kirjoitettaessa:

long_winded_computation = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20)  

Se tekee myös koodista helpommin luettavaa:

list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]  
easier_to_read_list_of_lists = [ [1, 2, 3],  
                                 [4 ,5 ,6 ],  
                                 [7 ,8 ,9 ] ]  

Moniriviset lausekkeet

Kaksi katkennutta riviä voi yhdistää kenoviivalla (tätä tapaa käytetään harvoin):

two_plus_three = 2 + \
                 3  

Moduulit Modules

Olipa kyseessä sitten Pythonin oma tai itse ladattu kolmannen osapuolen moduuli, ne on tuotava manuaalisesti käyttöön.

  1. Yksinkertainen tapa tuoda koko moduuli suoraan:
import re  
my_regex = re.compile("[0-9]+", re.I)  

Tässä tuotu re-moduuli on tarkoitettu säännöllisille lausekkeille. Moduulin tuonnin jälkeen sen toimintoja voi kutsua suoraan käyttämällä moduulin nimeä etuliitteenä (re.).

  1. Jos tuotavan moduulin nimi on jo käytössä koodissa, moduulin voi tuoda toisella nimellä:
import re as regex  
my_regex = regex.compile("[0-9]+", regex.I)  
  1. Jos olet tuhma, voit tuoda koko moduulin nykyiseen nimiavaruuteen, mikä saattaa tahattomasti korvata jo määrittelemiäsi muuttujia:
match = 10  
from re import *  # re-moduulissa on match-funktio  
print match       # Tulostaa match-funktion  

Koska olet hyvä ihminen, uskon ettet tee näin.

Aritmetiikka Arithmetic

Python 2.7 käyttää oletuksena kokonaislukujakoa, joten $5 / 2 = 2$. Kuitenkin monesti emme halua kokonaislukujakoa, joten voimme tuoda tämän moduulin:

from __future__ import division  

Tuonnin jälkeen $5 / 2 = 2.5$.
Kokonaislukujako: $5 // 2 = 2$.

Funktiot Functions

Funktion määrittely

Funktio on sääntö, joka voi vastaanottaa nolla tai useampia syötteitä ja palauttaa tietyn tuloksen. Pythonissa funktio määritellään def funktion_nimi(parametrit) -rakenteella:

def double(x):  
    """Tähän voit kirjoittaa selityksen funktion toiminnasta.  
    Esimerkiksi, tämä funktio kertoo syötteen kahdella."""  
    # Tähän tulee funktion runko, muista sisennys  
    return x * 2  

Funktion käyttö

Pythonissa funktiot ovat “ensiluokkaisia kansalaisia”, mikä tarkoittaa, että voimme sijoittaa funktion muuttujaan tai välittää sen parametrina toisille funktioille:

def apply_to_one(f):  
    """Kutsuu funktiota f ja välittää sille parametrin 1"""  
    return f(1)  
my_double = double          # double viittaa edellisessä osiossa määriteltyyn funktioon  
x = apply_to_one(my_double) # x on 2  

Nimettömät funktiot

Voimme luoda nimettömiä funktioita myös lambda-sanalla:

y = apply_to_one(lambda x: x + 4)     # tulos on 5  

lambda-funktioita voi sijoittaa muuttujiin, mutta useimmat suosittelevat kuitenkin def-rakenteen käyttöä:

another_double = lambda x: 2 * x      # Ei suositella  
def another_double(x): return 2 * x   # Suositeltava tapa  

Lisäksi:

Funktioparametrien välitys

Funktion parametreille voi määrittää oletusarvoja. Jos argumentteja ei anneta, käytetään oletusarvoja; jos argumentit annetaan, ne välitetään määritettynä arvona:

def my_print(message="my default message"):  
    print message  
my_print("hello")     # Tulostaa "hello"  
my_print()            # Tulostaa "my default message"  

Joskus on kätevää määrittää parametrit suoraan niiden nimillä:

def subtract(a=0, b=0):  
    return a - b  
subtract(10, 5)   # Palauttaa 5  
subtract(0, 5)    # Palauttaa -5  
subtract(b=5)     # Sama kuin edellinen, palauttaa -5  

Merkkijonot Strings

Merkkijonoja voi luoda käyttämällä yksinkertaisia tai kaksinkertaisia lainausmerkkejä (lainausmerkkien on oltava parillisia):

single_quoted_string = 'data science'  
double_quoted_string = "data science"  

Kenoviivaa käytetään escape-merkkien ilmaisemiseen, kuten:

tab_string = "\t"      # Tarkoittaa sarkainta (tab)  
len(tab_string)        # on 1  

Kun haluat käyttää itse kenoviivaa (esim. Windowsin hakemistoissa tai säännöllisissä lausekkeissa), voit määrittää sen käyttämällä raakaa merkkijonoa r"":

not_tab_string = r"\t" # Tarkoittaa merkkejä '\' ja 't'  
len(not_tab_string)    # on 2  

Kolmella lainausmerkillä voi luoda monirivisiä merkkijonoja:

multi_line_string = """Tämä on ensimmäinen rivi  
Tämä on toinen rivi  
Tämä on kolmas rivi"""  

Poikkeuskäsittely Exception

Kun ohjelmassa tapahtuu virhe, Python nostaa poikkeuksen (exception). Jos emme käsittele sitä, ohjelma keskeyttää suorituksensa. Poikkeukset voi ottaa kiinni try- ja except-lauseilla:

try:  
    print 0 / 0  
except ZeroDivisionError:  
    print "Ei voi jakaa nollalla"  

Vaikka poikkeuksia pidetään huonona asiana muissa kielissä, Pythonissa poikkeusten käsittely tekee koodistasi usein selkeämpää ja puhtaampaa.

Listat Lists

Listan luominen

Listat ovat yksinkertaisia, järjestettyjä kokoelmia ja Pythonin perusdatarakenne (samankaltaisia kuin taulukot muissa kielissä, mutta listoilla on joitakin lisäominaisuuksia). Listan luominen:

integer_list = [1, 2, 3]  
heterogeneous_list = ["string", 0.1, True]  
list_of_lists = [ integer_list, heterogeneous_list, [] ]  
list_length = len(integer_list)   # on 3  
list_sum = sum(integer_list)      # on 6  

Arvojen hakeminen listasta

Voit indeksoida arvoja listasta hakasulkeilla:

x = range(10)       # Lista x saa arvon [0, 1, ..., 9]  
zero = x[0]         # on 0, listan indeksit alkavat nollasta  
one = x[1]          # on 1  
nine = x[-1]        # on 9, listan viimeinen alkio  
eight = x[-2]       # on 8, listan toiseksi viimeinen alkio  
x[0] = -1           # Lista x on nyt [-1, 1, 2, 3, ..., 9]  

Listan viipalointi

Voit “viipaloida” listaa hakasulkeilla:

first_three = x[:3]                  # [-1, 1, 2]  
three_to_end = x[3:]                 # [3, 4, ..., 9]  
one_to_four = x[1:5]                 # [1, 2, 3, 4]  
last_three = x[-3:]                  # [7, 8, 9]  
without_first_and_last = x[1:-1]     # [1, 2, ..., 8]  
copy_of_x = x[:]                     # [-1, 1, 2, ..., 9]  

Voit tarkistaa, onko jokin alkio listassa, käyttämällä in-operaattoria:

1 in [1, 2, 3]        # True  
0 in [1, 2, 3]        # False  

Tämä tapa etsiä alkioita on tehoton. Käytä sitä vain, jos lista on pieni tai et välitä hakuaikaa.

Listojen yhdistäminen

Kahden listan yhdistäminen Pythonissa on helppoa:

x = [1, 2, 3]  
x.extend([4, 5, 6])   # Nyt x on [1, 2, 3, 4, 5, 6]  

Jos et halua muokata alkuperäistä listaa x, voit luoda uuden listan ’+’ -operaattorilla:

x = [1, 2, 3]  
y = x + [4, 5, 6]     # Nyt y on [1, 2, 3, 4, 5, 6]; x ei muuttunut  

Usein lisätään yksi alkio listaan tällä tavalla:

x = [1, 2, 3]  
x.append(0)           # Nyt x on [1, 2, 3, 0]  
y = x[-1]             # on 0  
z = len(x)            # on 4  

Listan purkaminen

Jos tiedät, kuinka monta alkiota listassa on, voit purkaa sen helposti:

x, y = [1, 2]         # Nyt x = 1, y = 2  

Jos yhtälön molemmilla puolilla on eri määrä alkioita, saat ValueError-virheen. Siksi on yleisempää käyttää alaviivaa tallentamaan listan loput osat:

_, y = [1, 2]         # Nyt y == 2, ensimmäisestä alkiosta ei välitetä  

Tuplet Tuples

Listat ja tuplet ovat hyvin samankaltaisia. Ainoa ero listaan on, että tuplen elementtejä ei voi muokata.

Tuplen luominen

Tupleja voi luoda käyttämällä sulkeita tai jättämällä ne pois kokonaan:

my_tuple = (1, 2)  
other_tuple = 3, 4  
my_list[1] = 3        # Nyt my_list on [1, 3]  
try:  
    my_tuple[1] = 3  
except TypeError:  
    print "Tuplen muokkaaminen ei ole mahdollista"  

Tuplejen avulla on kätevää saada useita palautusarvoja funktiosta:

def sum_and_product(x, y):  
    return (x + y),(x * y)  
sp = sum_and_product(2, 3)    # on (5, 6)  
s, p = sum_and_product(5, 10) # s = 15, p = 50  

Tuplet (ja listat) tukevat usean elementin samanaikaista sijoittamista:

x, y = 1, 2       # Nyt x = 1, y = 2  
x, y = y, x       # Pythonissa kahden muuttujan arvojen vaihtaminen; nyt x = 2, y = 1  

Sanakirjat Dictionaries

Sanakirjan luominen

Toinen Pythonin perusdatarakenne on sanakirja, joka mahdollistaa arvojen (value) nopean hakemisen avaimen (key) perusteella:

empty_dict = {}                       # Erittäin Python-mainen tyhjän sanakirjan määritys  
empty_dict2 = dict()                  # Ei niin Python-mainen tyhjän sanakirjan määritys  
grades = { "Joel" : 80, "Tim" : 95 }  # Sanakirjan tallennus  

Sanakirjan elementtien haku

Voit etsiä vastaavan arvon käyttämällä hakasulkeita ja avainsanaa:

joels_grade = grades["Joel"]          # on 80  

Jos etsittävää avainsanaa ei löydy sanakirjasta, palautetaan KeyError:

try:  
    kates_grade = grades["Kate"]  
except KeyError:  
    print "Ei arvosanaa Katelle!"  

Voit tarkistaa, onko avainsana sanakirjassa, käyttämällä in-operaattoria:

joel_has_grade = "Joel" in grades     # True  
kate_has_grade = "Kate" in grades     # False  

Sanakirjassa on metodi, joka voi palauttaa oletusarvon, jos etsittävää avainsanaa ei löydy (sen sijaan, että tapahtuisi poikkeus):

joels_grade = grades.get("Joel", 0)   # on 80  
kates_grade = grades.get("Kate", 0)   # on 0  
no_ones_grade = grades.get("No One")  # Palauttaa oletusarvon None  

Sanakirjan muokkaaminen

Voit luoda ja muokata sanakirjan avain-arvo-pareja hakasulkeilla:

grades["Tim"] = 99                    # Korvaa vanhan arvon  
grades["Kate"] = 100                  # Lisää avain-arvo-parin  
num_students = len(grades)            # on 3  

Käytämme usein sanakirjoja tietojen rakenteen esittämiseen tällä tavalla:

tweet = {  
    "user" : "joelgrus",  
    "text" : "Data Science is Awesome",  
    "retweet_count" : 100,  
    "hashtags" : ["#data", "#science", "#datascience", "#awesome", "#yolo"]  
}  

Tiettyjen avainsanojen etsimisen lisäksi voimme käsitellä kaikkia avainsanoja tällä tavalla:

tweet_keys = tweet.keys()             # Saa listan avainsanoista (avaimista)  
tweet_values = tweet.values()         # Saa listan arvoista  
tweet_items = tweet.items()           # Saa (avain, arvo) -tupleja  
"user" in tweet_keys                  # Palauttaa True, käyttäen listan tehotonta in-hakua  
"user" in tweet                       # Python-maisempi tapa, käyttää tehokasta sanakirjan in-hakua  
"joelgrus" in tweet_values            # True  

Sanakirjan avaimet ovat yksilöllisiä, eikä listoja voi käyttää sanakirjan avaimina. Jos tarvitset moniosaisen avaimen, voit käyttää tuplea tai muuntaa avaimen merkkijonoksi jollain tavalla.

Sisäänrakennetut sanakirjat (defaultdict)

Jos yrität laskea kunkin sanan esiintymistiheyden dokumentissa, ilmeinen tapa on luoda sanakirja, jossa sanat ovat avaimina ja niiden esiintymistiheydet vastaavina arvoina. Käy sitten dokumentti läpi: jos sana on jo sanakirjassa, kasvata sen arvoa yhdellä; jos sanaa ei ole, lisää sanakirjaan uusi avain-arvo-pari:

word_counts = {}  
for word in document:  
    if word in word_counts:  
        word_counts[word] += 1  
    else:  
        word_counts[word] = 1  

Voit tietysti käsitellä puuttuvan avaimen etukäteen tällä tavalla, “toimimalla ensin, kysymällä sitten” -periaatteella:

word_counts = {}  
for word in document:  
    try:  
        word_counts[word] += 1  
    except KeyError:  
        word_counts[word] = 1  

Kolmas tapa on käyttää get-metodia, joka toimii erinomaisesti puuttuvien avaimien käsittelyssä:

word_counts = {}  
for word in document:  
    previous_count = word_counts.get(word, 0)  
    word_counts[word] = previous_count + 1  

Sisäänrakennettu sanakirja (defaultdict) on kuin tavallinen sanakirja, ainoana erona on, että kun yrität etsiä sanakirjasta olemattoman avaimen, sisäänrakennettu sanakirja luo automaattisesti avain-arvo-parin antamasi avaimen perusteella. Sisäänrakennetun sanakirjan käyttämiseksi sinun on tuotava collections-kirjasto:

from collections import defaultdict  
word_counts = defaultdict(int)        # int() luo 0:n  
for word in document:  
    word_counts[word] += 1  

Defaultdict on hyödyllinen myös listojen, tavallisten sanakirjojen ja jopa mukautettujen funktioiden kanssa:

dd_list = defaultdict(list)           # list() luo tyhjän listan  
dd_list[2].append(1)                  # Nyt dd_list on {2: [1]}  
dd_dict = defaultdict(dict)           # dict() luo tyhjän sanakirjan  
dd_dict["Joel"]["City"] = "Seattle"   # Nyt dd_dict sisältää { "Joel" : { "City" : "Seattle"}}  
dd_pair = defaultdict(lambda: [0, 0]) # Luo sanakirjan, jossa avainten arvot ovat listoja  
dd_pair[2][1] = 1                     # Nyt dd_pair sisältää {2: [0,1]}  

Tämä menetelmä on erittäin hyödyllinen, sillä tulevaisuudessa, kun haluamme hakea tiettyjä avain-arvo-tuloksia sanakirjasta, meidän ei tarvitse enää tarkistaa, onko avain olemassa.

Laskuri Counter

Laskuri voi muuntaa arvojoukon suoraan sanakirjan kaltaiseksi objektiksi, jossa avaimena on jokin joukon elementti ja vastaava arvo on kyseisen elementin esiintymiskertojen määrä. Tätä käytetään usein histogrammien luomisessa:

from collections import Counter  
c = Counter([0, 1, 2, 0]) # c on (suunnilleen) { 0 : 2, 1 : 1, 2 : 1 }  

Näin meillä on erittäin kätevä tapa laskea sanatiheys:

word_counts = Counter(document)  

Laskurissa on myös erittäin hyödyllinen most_common-metodi, joka voi suoraan antaa useimmin esiintyvät sanat ja niiden tiheydet:

# Tulostaa 10 yleisintä sanaa ja niiden lukumäärät  
for word, count in word_counts.most_common(10):  
    print word, count  

Joukot Sets

Toinen Pythonin tietorakenne on joukko (set), joka on kokoelma erilaisia alkioita.
Joukko voidaan luoda ja siihen voidaan lisätä alkioita näin:

s = set()  
s.add(1)          # s on { 1 }  
s.add(2)          # s on { 1, 2 }  
s.add(2)          # s on { 1, 2 }  
x = len(s)        # on 2  
y = 2 in s        # on True  
z = 3 in s        # on False  

Kaksi tärkeintä syytä käyttää joukkoja:

Ensinnäkin, in-operaatio joukoissa on erittäin tehokas. Kun aineistossa on erittäin paljon elementtejä, elementtien etsiminen joukon muodossa on selvästi listaa sopivampaa:

stopwords_list = ["a","an","at"] + hundreds_of_other_words + ["yet", "you"]  
"zip" in stopwords_list               # Epäonnistuu, vaatii jokaisen elementin tarkistamista  
stopwords_set = set(stopwords_list)  
"zip" in stopwords_set                # Haku onnistuu, ja se on erittäin nopea  

Toiseksi, joukkojen avulla on erittäin kätevää saada aineistosta ainutlaatuiset elementit:

item_list = [1, 2, 3, 1, 2, 3]  
num_items = len(item_list)            # 6  
item_set = set(item_list)             # {1, 2, 3}  
num_distinct_items = len(item_set)    # 3  
distinct_item_list = list(item_set)   # [1, 2, 3]  

Käytännössä joukkoja ei kuitenkaan käytetä yhtä usein kuin sanakirjoja ja listoja.

Ehtolausekkeet

Useimmissa ohjelmointikielissä voit käyttää if-lausetta ehdollisten haarojen ilmaisemiseen tällä tavalla:

if 1 > 2:  
    message = "jos vain 1 olisi suurempi kuin kaksi…"  
elif 1 > 3:  
    message = "elif tarkoittaa 'else if'"  
else:  
    message = "kun kaikki muu pettää, käytä elseä (jos haluat)"  

Voit myös kirjoittaa ehdollisen haaran yhden rivin lausekkeeksi näin, mutta tätä käytetään harvoin:

parity = "even" if x % 2 == 0 else "odd"  

Silmukkalausekkeet

while-silmukka

Pythonin while-silmukka:

x = 0  
while x < 10:  
    print x, "on pienempi kuin 10"  
    x += 1  

for-silmukka

Yleisemmin käytetään for-in-silmukkaa:

for x in range(10):  
    print x, "on pienempi kuin 10"  

Monimutkaisemmissa loogisissa lausekkeissa voidaan käyttää continue- ja break-lauseita:

for x in range(10):  
    if x == 3:  
        continue          # Siirtyy suoraan seuraavaan kierrokseen  
    if x == 5:  
        break             # Poistuu silmukasta kokonaan  
    print x  

Tuloksena tulostetaan 0, 1, 2 ja 4.

Totuusarvot Truthiness

Pythonin totuusarvomuuttujat (Booleans) käyttäytyvät melko samoin kuin muissa kielissä, ainoa ero on, että niiden alkukirjaimen on oltava iso:

one_is_less_than_two = 1 < 2      # on True  
true_equals_false = True == False # on False  

Python käyttää None-arvoa ilmaisemaan, että arvoa ei ole olemassa, samankaltaisesti kuin null muissa kielissä:

x = None  
print x == None        # Tulostaa True, ei kovin tyylikäs  
print x is None        # Tulostaa True, tyylikkäämpi  

Python sallii muiden arvojen käytön totuusarvojen sijasta, ja seuraavat ovat kaikki vastaavia False-arvon kanssa:

Vastaavasti on monia True-arvon vastineita, mikä tekee tyhjien listojen, merkkijonojen ja sanakirjojen tarkistamisesta erittäin kätevää.

Tietysti, jos et voi ennustaa tulosta, käytössä voi tapahtua virheitä:

s = some_function_that_returns_a_string()  
if s:  
    first_char = s[0]  
else:  
    first_char = ""  

Yksinkertaisempi tapa, joka tuottaa saman tuloksen kuin yllä oleva:

first_char = s and s[0]  

Jos ensimmäinen arvo on tosi, palautetaan toinen arvo, muuten ensimmäinen arvo.

Vastaavasti, jos x voi olla numero tai tyhjä, tällä tavalla saadaan x, joka on varmasti numero:

safe_x = x or 0  

Pythonissa on myös all-funktio, joka palauttaa True, jos kaikki elementit ovat True. any-funktio palauttaa True, jos yksikin elementti on True. Esimerkiksi listalle, jossa jokainen elementti on “tosi”, all-funktio palauttaa True, muuten False:

all([True, 1, { 3 }])       # True  
all([True, 1, {}])          # False, {} vastaa "False"-arvoa  
any([True, 1, {}])          # True  
all([])                     # True, ei ole yhtään "False"-arvoa vastaavaa elementtiä  
any([])                     # False, ei ole yhtään "True"-arvoa vastaavaa elementtiä  

Lisälukemista:
Yleisiä Python-syntakseja datatieteessä (edistyneet)