Često korištena Python sintaksa u podatkovnoj znanosti (osnove)

Posljednjih dana čitam knjigu Data Science from Scratch (PDF adresa), koja je odličan i lako razumljiv uvod u podatkovnu znanost. Jedno poglavlje u njoj posvećeno je osnovnoj Python sintaksi i naprednijim funkcijama koje se često koriste u podatkovnoj znanosti. Budući da smatram kako je objašnjenje izvrsno, sažeto i jasno, preveo sam ga i objavljujem ovdje kao podsjetnik.
Često korištena Python sintaksa u podatkovnoj znanosti (osnove)
Često korištena Python sintaksa u podatkovnoj znanosti (napredno)

Ovo poglavlje usredotočeno je na predstavljanje osnovne Python sintakse i funkcija (temeljenih na Pythonu 2.7) koje su iznimno korisne u obradi podataka.

Formatiranje razmacima

Mnogi jezici koriste zagrade za kontrolu blokova koda, ali Python koristi uvlačenje:

for i in [1, 2, 3, 4, 5]:  
    print i          # prvi redak "for i" petlje  
    for j in [1, 2, 3, 4, 5]:  
        print j      # prvi redak "for j" petlje  
        print i + j  # zadnji redak "for j" petlje  
    print i          # zadnji redak "for i" petlje  
print "done looping"  

To Python kod čini vrlo čitljivim, ali istovremeno znači da morate uvijek paziti na formatiranje. Razmaci unutar zagrada se ignoriraju, što je korisno kod pisanja dugih izraza:

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

Također olakšava čitljivost koda:

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 ] ]  

Višelinijske naredbe

Dvije prekinute linije možete spojiti pomoću obrnute kose crte (ova se praksa rijetko koristi):

two_plus_three = 2 + \
                 3  

Moduli

Bilo da se radi o ugrađenim Python modulima ili modulima trećih strana koje ste sami preuzeli, svi se moraju ručno uvesti prije upotrebe.

  1. Jednostavno izravno uvozite cijeli modul:
import re  
my_regex = re.compile("[0-9]+", re.I)  

Ovdje uvezeni modul _re_ koristi se za regularne izraze. Nakon uvoza modula, možete izravno pozvati njegove funkcije koristeći naziv modula kao prefiks (npr. re.).

  1. Ako se naziv modula koji uvozite već koristi u kodu, možete ga uvesti pod drugim imenom:
import re as regex  
my_regex = regex.compile("[0-9]+", regex.I)  
  1. Ako ste jako nestašni, možete uvesti cijeli modul u trenutni imenski prostor, što bi slučajno moglo prebrisati varijable koje ste već definirali:
match = 10  
from re import *  # modul re sadrži funkciju match  
print match       # ispisuje funkciju match  

Budući da ste dobri, siguran sam da to nećete učiniti.

Aritmetika

Python 2.7 podrazumijevano koristi cjelobrojno dijeljenje, pa je $5 / 2 = 2$. No, često nam ne treba cjelobrojno dijeljenje, pa možemo uvesti ovaj modul:

from __future__ import division  

Nakon uvoza, dobivamo $5 / 2 = 2.5$.
Cjelobrojno dijeljenje: $5 // 2 = 2$.

Funkcije

Definicija funkcije

Funkcija je pravilo koje prima nula ili više ulaznih vrijednosti i vraća određeni izlaz. U Pythonu, funkciju definiramo pomoću def ime_funkcije(parametri):

def double(x):  
    """Ovdje možete napisati objašnjenje funkcije  
    npr., ova funkcija množi ulaz s 2"""  
    # Ovdje se piše tijelo funkcije, ne zaboravite uvlačenje  
    return x * 2  

Korištenje funkcija

U Pythonu su funkcije objekti prve klase, što znači da ih možemo dodijeliti varijabli ili ih proslijediti kao argumente drugim funkcijama:

def apply_to_one(f):  
    """Poziva funkciju f i prosljeđuje joj 1 kao argument"""  
    return f(1)  
my_double = double          # double se odnosi na funkciju definiranu u prethodnom odjeljku  
x = apply_to_one(my_double) # x je jednako 2  

Anonimne funkcije

Anonimne funkcije mogu se stvoriti i pomoću lambda izraza:

y = apply_to_one(lambda x: x + 4)     # jednako je 5  

lambda se može dodijeliti drugim varijablama, ali većina će vam preporučiti da se ipak držite def definicije:

another_double = lambda x: 2 * x      # ne preporučuje se  
def another_double(x): return 2 * x   # preporučena praksa  

Dodatno:

Prosljeđivanje argumenata funkcije

Parametri funkcije mogu imati zadane vrijednosti. Ako se funkcija pozove bez argumenata, koristit će se zadane vrijednosti; ako se argumenti proslijede, koristit će se specificirane vrijednosti:

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

Ponekad je također korisno navesti argumente izravno po imenu:

def subtract(a=0, b=0):  
    return a - b  
subtract(10, 5)   # vraća 5  
subtract(0, 5)    # vraća -5  
subtract(b=5)     # isto kao prethodno, vraća -5  

Stringovi

Stringove možete stvarati pomoću jednostrukih ili dvostrukih navodnika (navodnici se moraju podudarati):

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

Obrnuta kosa crta koristi se za znakove za bijeg, npr.:

tab_string = "\t"      # predstavlja tab znak  
len(tab_string)        # jednako je 1  

Kada želite koristiti samu obrnutu kosu crtu (za Windows direktorije ili regularne izraze), možete je definirati pomoću sirovih stringova r"":

not_tab_string = r"\t" # predstavlja znakove '\' i 't'  
len(not_tab_string)    # jednako je 2  

Višelinijske stringove možete stvoriti pomoću tri dvostruka navodnika:

multi_line_string = """Ovo je prvi redak  
Ovo je drugi redak  
Ovo je treći redak"""  

Rukovanje iznimkama

Kada program naiđe na pogrešku, Python će pokrenuti iznimku (exception). Ako je ne obradimo, program će se prekinuti. Iznimke se mogu uhvatiti pomoću try i except izraza:

try:  
    print 0 / 0  
except ZeroDivisionError:  
    print "Ne može se dijeliti s nulom"  

Iako se u drugim jezicima iznimke ponekad smatraju lošom praksom, u Pythonu obrada iznimaka može vaš kod učiniti elegantnijim i čišćim.

Liste

Stvaranje lista

Liste su jednostavne, uređene kolekcije i jedna od najosnovnijih struktura podataka u Pythonu (slično poljima u drugim jezicima, ali s dodatnim značajkama). Za stvaranje liste:

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

Pristupanje vrijednostima u listi

Vrijednostima u listi možete pristupiti putem indeksa u uglatim zagradama:

x = range(10)       # lista x postaje [0, 1, ..., 9]  
zero = x[0]         # jednako je 0, indeksiranje liste počinje od 0  
one = x[1]          # jednako je 1  
nine = x[-1]        # jednako je 9, zadnji element u listi  
eight = x[-2]       # jednako je 8, pretposljednji element u listi  
x[0] = -1           # sada je lista x = [-1, 1, 2, 3, ..., 9]  

Rezanje lista

Listu možete rezati pomoću uglatih zagrada:

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]  

Pomoću operatora in možete provjeriti nalazi li se element u listi:

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

Ovaj način pretraživanja elemenata je neučinkovit, stoga ga koristite samo ako je lista mala ili ako vam vrijeme pretraživanja nije kritično.

Spajanje lista

U Pythonu je vrlo lako spojiti dvije liste:

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

Ako ne želite mijenjati izvornu listu x, možete koristiti operator zbrajanja za stvaranje nove liste:

x = [1, 2, 3]  
y = x + [4, 5, 6]     # sada je y = [1, 2, 3, 4, 5, 6]; x ostaje nepromijenjen  

Često se element dodaje u listu na sljedeći način:

x = [1, 2, 3]  
x.append(0)           # sada je x = [1, 2, 3, 0]  
y = x[-1]             # jednako je 0  
z = len(x)            # jednako je 4  

Raspakiranje lista

Ako znate koliko elemenata ima lista, lako je možete raspakirati:

x, y = [1, 2]         # sada je x = 1, y = 2  

Ako broj elemenata na obje strane izraza nije jednak, dobit ćete ValueError. Zbog toga češće koristimo podcrtu za preostale elemente liste:

_, y = [1, 2]         # sada je y == 2, prvi element se ignorira  

Tuplovi

Liste i tuplovi su vrlo slični. Jedina razlika je u tome što se elementi u tuplu ne mogu mijenjati.

Stvaranje tuplova

Tuplove možete stvarati pomoću zagrada ili bez njih:

my_tuple = (1, 2)  
other_tuple = 3, 4  
my_list[1] = 3        # sada je my_list [1, 3]  
try:  
    my_tuple[1] = 3  
except TypeError:  
    print "Tupl se ne može mijenjati"  

Tuplovi su vrlo korisni za vraćanje više vrijednosti iz funkcije:

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

Tuplovi (i liste) podržavaju istovremeno dodjeljivanje više elemenata:

x, y = 1, 2       # sada je x = 1, y = 2  
x, y = y, x       # zamjena vrijednosti dviju varijabli u Pythonu; sada je x = 2, y = 1  

Rječnici

Stvaranje rječnika

Još jedna osnovna struktura podataka u Pythonu je rječnik, koji vam omogućuje brzo dohvaćanje vrijednosti (value) putem ključa (key):

empty_dict = {}                       # vrlo Pythonovski način definiranja praznog rječnika  
empty_dict2 = dict()                  # manje Pythonovski način definiranja praznog rječnika  
grades = { "Joel" : 80, "Tim" : 95 }  # pohranjivanje rječnika  

Pretraživanje elemenata rječnika

Vrijednosti možete pronaći pomoću uglatih zagrada i ključa:

joels_grade = grades["Joel"]          # jednako je 80  

Ako traženi ključ nije u rječniku, dobit ćete KeyError:

try:  
    kates_grade = grades["Kate"]  
except KeyError:  
    print "nema ocjene za Kate!"  

Pomoću operatora in možete provjeriti nalazi li se ključ u rječniku:

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

Rječnici imaju metodu koja vraća zadanu vrijednost ako traženi ključ nije pronađen (umjesto da se pokrene iznimka):

joels_grade = grades.get("Joel", 0)   # jednako je 80  
kates_grade = grades.get("Kate", 0)   # jednako je 0  
no_ones_grade = grades.get("No One")  # vraća zadanu vrijednost None  

Modifikacija rječnika

Parove ključ-vrijednost u rječniku možete stvarati i mijenjati pomoću uglatih zagrada:

grades["Tim"] = 99                    # zamjenjuje staru vrijednost  
grades["Kate"] = 100                  # dodaje par ključ-vrijednost  
num_students = len(grades)            # jednako je 3  

Rječnike ćemo često koristiti na ovaj način za predstavljanje strukture podataka:

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

Osim pretraživanja specifičnih ključeva, možemo manipulirati svim ključevima na sljedeći način:

tweet_keys = tweet.keys()             # dobiva listu ključeva  
tweet_values = tweet.values()         # dobiva listu vrijednosti  
tweet_items = tweet.items()           # dobiva tuplove (ključ, vrijednost)  
"user" in tweet_keys                  # vraća True, koristi se manje učinkovito pretraživanje `in` u listi  
"user" in tweet                       # više Pythonovski način, koristi se učinkovito pretraživanje `in` u rječniku  
"joelgrus" in tweet_values            # True  

Ključevi u rječnicima su jedinstveni, a liste se ne mogu koristiti kao ključevi. Ako vam je potreban višedijelni ključ, možete koristiti tuplove ili pretvoriti ključ u string na neki način.

Ugrađeni rječnici (Defaultdict)

Ako pokušavate prebrojati učestalost svake riječi u dokumentu, očigledan pristup je stvaranje rječnika gdje su riječi ključevi, a njihove učestalosti odgovarajuće vrijednosti. Zatim iterirate kroz dokument, i kada naiđete na riječ koja već postoji, povećate njezinu vrijednost za 1; kada naiđete na novu riječ, dodate novi par ključ-vrijednost u rječnik:

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

Naravno, možete se poslužiti i pristupom “prvo izvrši, pa provjeri” kako biste unaprijed obradili ključ koji nedostaje:

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

Treći način je korištenje metode get, koja izvrsno rješava problem nedostajućih ključeva:

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

Ugrađeni rječnici (defaultdict) su poput običnih rječnika, s jedinom razlikom što će, kada pokušate pronaći nepostojeći ključ, automatski stvoriti par ključ-vrijednost koristeći zadanu tvornicu funkcija koju ste im dali. Da biste koristili defaultdict, morate uvesti biblioteku collections:

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

Defaultdict je također vrlo koristan s listama, običnim rječnicima, pa čak i s prilagođenim funkcijama:

dd_list = defaultdict(list)           # list() generira praznu listu  
dd_list[2].append(1)                  # sada je dd_list {2: [1]}  
dd_dict = defaultdict(dict)           # dict() generira prazan rječnik  
dd_dict["Joel"]["City"] = "Seattle"   # sada je dd_dict { "Joel" : { "City" : "Seattle"}}  
dd_pair = defaultdict(lambda: [0, 0]) # stvara rječnik čije su vrijednosti liste  
dd_pair[2][1] = 1                     # sada je dd_pair {2: [0,1]}  

Ova je metoda vrlo korisna jer više nećemo morati provjeravati postojanje ključa kada dohvaćamo određene vrijednosti iz rječnika.

Brojač (Counter)

Brojač (Counter) može izravno pretvoriti skup vrijednosti u objekt sličan rječniku, gdje su ključevi elementi iz tog skupa, a odgovarajuće vrijednosti broj pojavljivanja tih elemenata. Ovo se često koristi pri stvaranju histograma:

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

Tako dobivamo vrlo prikladan način za brojanje učestalosti riječi:

word_counts = Counter(document)  

Brojač ima i vrlo korisnu metodu most_common, koja izravno vraća nekoliko najčešćih riječi i njihove frekvencije:

# Ispisuje 10 najčešćih riječi i njihove brojeve pojavljivanja  
for word, count in word_counts.most_common(10):  
    print word, count  

Skupovi

Još jedna struktura podataka u Pythonu su skupovi. Skup je kolekcija različitih elemenata.
Skup možete stvoriti i dodavati mu elemente na sljedeći način:

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

Dva glavna razloga za korištenje skupova su:

Prvo, operacija in unutar skupova vrlo je učinkovita. Kada je broj elemenata u skupu podataka iznimno velik, pretraživanje elemenata u obliku skupa očito je prikladnije nego u listi:

stopwords_list = ["a","an","at"] + hundreds_of_other_words + ["yet", "you"]  
"zip" in stopwords_list               # neuspješno, potrebno je provjeriti svaki element  
stopwords_set = set(stopwords_list)  
"zip" in stopwords_set                # pretraživanje uspješno i vrlo brzo  

Drugo, vrlo je prikladno koristiti skupove za dohvaćanje različitih elemenata iz skupa podataka:

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]  

Međutim, u praksi, skupovi se ne koriste tako često kao rječnici i liste.

Uvjetne izjave

U većini programskih jezika, uvjetne grane možete izraziti pomoću if izraza na sljedeći način:

if 1 > 2:  
    message = "kad bi samo 1 bio veći od dva…"  
elif 1 > 3:  
    message = "elif znači 'else if'"  
else:  
    message = "kad sve drugo zakaže, koristite else (ako želite)"  

Uvjetnu granu također možete napisati u jednom retku, ali to se rijetko koristi:

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

Petlje

while petlja

while petlja u Pythonu:

x = 0  
while x < 10:  
    print x, "je manje od 10"  
    x += 1  

for petlja

Češće se koristi for-in petlja:

for x in range(10):  
    print x, "je manje od 10"  

Za složenije logičke izraze mogu se koristiti continue i break naredbe:

for x in range(10):  
    if x == 3:  
        continue          # izravno prelazi na sljedeću iteraciju petlje  
    if x == 5:  
        break             # potpuno izlazi iz petlje  
    print x  

Rezultat će biti ispis brojeva 0, 1, 2 i 4.

Istinitost

Upotreba Booleovih varijabli u Pythonu slična je onoj u drugim jezicima, s jedinom razlikom što početno slovo mora biti veliko:

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

Python koristi None za označavanje nepostojanja vrijednosti, slično null u drugim jezicima:

x = None  
print x == None        # ispisuje True, nije elegantno  
print x is None        # ispisuje True, elegantnije  

Python vam dopušta korištenje drugih vrijednosti umjesto Booleovih, a sljedeće su ekvivalentne False:

Slično tome, postoji mnogo ekvivalentnih vrijednosti za True, što vam olakšava provjeru praznih lista, praznih stringova, praznih rječnika i slično.

Naravno, ako ne možete predvidjeti rezultat, moglo bi doći do pogrešaka tijekom korištenja:

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

Jednostavniji pristup, s istim učinkom kao i gore navedeni:

first_char = s and s[0]  

Ako je prva vrijednost istinita, vratit će se druga vrijednost; inače, vratit će se prva.

Slično tome, ako je x potencijalno broj ili None, ovako možete osigurati da x bude broj:

safe_x = x or 0  

Python također ima funkciju all koja vraća True ako su svi elementi istiniti. Funkcija any vraća True ako je barem jedan element istinit. Na primjer, za listu u kojoj je svaki element “istinit”, funkcija all će vratiti True, inače će vratiti False:

all([True, 1, { 3 }])       # True  
all([True, 1, {}])          # False, {} je ekvivalentno "False"  
any([True, 1, {}])          # True  
all([])                     # True, ne postoji element ekvivalentan "False"  
any([])                     # False, ne postoji element ekvivalentan "True"  

Napredno čitanje:
Često korištena Python sintaksa u podatkovnoj znanosti (napredno)