Data Analysis - Programming - 1

Week 3

Onderwerpen week 3:

  • lists, tuples en sets
    • operators
    • functions
    • indexing
    • slicing

Lists, tuples en sets (1)

  • bij veel problemen heb je te maken met
    logische verzamelingen van waarden (ints, floats, strings)
  • een nieuwe variabelenaam definieren voor iedere waarde is
    niet alleen onoverzichtelijk, maar leidt ook snel tot herhaling van code,
    en een variabel aantal herhalingen zou onmogelijk zijn
  • een voorbeeldje:
    Een experiment bepaald de som de twee geworpen dobbelstenen.
    Simuleer het experiment drie keer en bepaal het gemiddelde en de (steekproef!)standaardafwijking.

In [1]:
from random import randint
from math import sqrt

t1 = randint(1, 6) + randint(1, 6)
t2 = randint(1, 6) + randint(1, 6)
t3 = randint(1, 6) + randint(1, 6)

mu = (t1 + t2 + t3) / 3
stddev = sqrt(((t1 - mu)**2 + (t2 - mu)**2 + t3 - mu)**2 / 2)
print("Som van worpen: {}, {}, {}".format(t1, t2, t3))
print("Gemiddelde: {:.3f}, standaard afwijking: {:.3f}".format(
    mu, stddev
))


Som van worpen: 4, 7, 8
Gemiddelde: 6.333, standaard afwijking: 5.343

Lists, tuples en sets (2)

  • in het voorgaande programma is het moeilijk om:
    • het aantal dobbelstenen te variëren
    • het aantal herhalingen van het experiment te variëren
  • wat we nodig hebben is een data type waarin we meerdere
    waarden op kunnen slaan
  • Python kent daarvoor (onder andere) lists en tuples
  • list:

    • nieuwe lists maak je met blokhaken:
    throws = []  # maakt een lege lijst (zonder elementen)
    throws = [11, 3, 8, 7]  # maakt een lijst met de opgegeven elementen
    
    • een list kan willekeurige andere datatypen bevatten,
      zelfs andere lists:
    throws = [[4, 6], [3, 6], [2, 2], [5, 4]]
    

Lists, tuples en sets (3)

  • elementen kunnen toegevoegd worden aan bestaande lists met de append()-functie
  • elementen kunnen op index verwijderd worden met het del-statement
  • een element op kan opgehaald of overschreven worden met indexing
  • het aantal elementen is op te vragen met de len()-functie:
from random import randint

throws = []
for i in range(100):
    throws.append(randint(1, 6) + randint(1, 6))
print("Element met index 10: {}".format(throws[10]))
throws[0] = 7
del throws[12]
print("Aantal worpen: {}".format(len(throws)))
  • in The Python Tutorial vind je nog veel meer handige functies die je op lists toe kunt passen

Lists, tuples en sets (4)

  • lists kunnen in Python aangepast worden:
    elementen kunnen toegevoegd en verwijderd worden,
    de list groeit en krimt vanzelf naar de benodigde grootte
  • deze functionaliteit is niet altijd nodig,
    soms kun je volstaan met tuples
  • tuples worden gemaakt met haakjes en de inhoud kan niet worden aangepast:
ace_of_hearts = ("H", "Ace")
deck = []
for colour in ["H", "D", "S", "C"]:
    for value in list(range(2, 11)) + ["J", "Q", "K", "A"]:
        deck.append((colour, value))
print(deck)
  • tuples worden vooral gebruikt voor:
    • modelleren van data die niet veranderlijk is
    • praktischer en leesbaarder maken van sommige assignments
    • meerdere waarden retourneren uit een functie

Lists, tuples en sets (4)

  • in wiskunde is er het concept van de verzameling:
    • een collectie van nul of meer elementen
      $A = \{\} = \emptyset, B = \{1\}, C = \{A, 5\}$
    • ieder element is uniek
      $\{1, 2, 2, 2, 3\} = \{1, 2, 3\}$
    • waarbij volgorde niet relevant is
  • voor een list in Python gelden de laatste twee kenmerken niet
  • daarom heeft Python een apart datatype dat een verzameling
    in de wiskundige zin modelleert, de set:
even = set([2, 4, 6])
greater_than_3 = set([4, 5, 6])
print("Vereniging {} en {} = {}".format(
    even, greater_than_3, even | greater_than_3
))
print("Doorsnede {} en {} = {}".format(
    even, greater_than_3, even & greater_than_3
))
print("Letters zonder s: {}".format(
    set("letters") - set("s")
))

Indexing en slicing (1)

  • list, tuple, str(ing) zijn allemaal sequentiele datatypen
    (set niet omdat volgorde niet relevant is!)
  • iedere sequence kan worden geindexeerd, zoals je bij strings al hebt gezien,
    waarmee je een element uit de sequence kunt ophalen, of,
    in het geval van list, een element kunt vervangen
    (string en tuple zijn immutable en kunnen niet veranderen nadat ze aangemaakt zijn)
  • indices $\ge 0$ geven toegang tot een element op index
  • indices $< 0$ ook, maar dan vanaf het einde geteld,
    dus throws[-1] levert het laatste element van de sequence throws en
    throws[-2] levert het op een na laatste element

Indexing en slicing (2)

  • waar je met indexing toegang krijgt tot één element,
    geeft slicing toegang tot meerdere elementen, tegelijk
  • de notatie voor slicing is als volgt:
    sequence[start:stop:step]
    waarbij start, stop en step allemaal optioneel zijn
    (maar niet allemaal tegelijk weggelaten kunnen worden)
  • start geeft de index van het eerste element dat je wilt,
    stop geeft de index van het eerste element dat je niet wilt, en
    step bepaalt de stapgrootte tussen de indices van de element die je wilt
die = (1, 2, 3, 4, 5, 6)
first = die[0]  # of: die[0:1:1]  -> (1)
middle = die[2:4]  # (3, 4)
odd = die[0::2]  # (1, 3, 5)
even = die[1::2]  # (2, 4, 6)
reverse_die = die[::-1]  # (6, 5, 4, 3, 2, 1)

Spam, spam, spam (1)

  • spam is ongewenste e-mail,
    e-mail die geen spam is, noemen we ham
  • Bayes' theorem:
    $P(A|B) = \large{\frac{P(B|A) * P(A)}{P(B)}}$
  • bepalen of een e-mail spam is:
    • gegeven een event M, de message,
      de event Spam, en de event Ham,
    • $P(Spam|M) = \large{\frac{P(M|Spam) \cdot P(Spam)} {P(M|Spam) \cdot P(Spam) + P(M|Ham) \cdot P(Ham)}}$
  • $P(Spam|M)$: de kans op spam, gegeven de message M
  • $P(M|Spam)$: de kans op de message M, gegeven dat het spam is
  • $P(Spam)$, $P(Ham)$: de kans dat een willekeurige message spam/ham is
  • $P(M|Ham)$: de kans op de message M, gegeven dat het ham is

Spam, spam, spam (2)

  • Spam berichten:
rolex replica korting
klik korting viagra
korting politiek krediet
  • Ham berichten:
politiek bepaalt korting
lariekoek in politiek
klik politiek verslag
journalist bespeelt politiek
politiek amsterdam stagneert
  • Merk op dat het kan gebeuren dat je de kans op spam moet inschatten voor berichten die woorden bevatten die niet in je trainingsdatabase zitten. Dat zou problemen opleveren in de Bayesian theorem (ga na waarom).
    In dat geval kun je de woorden negeren (door $P(W|Spam) = P(W|Ham) = 1$ te gebruiken) of Laplace smoothing gebruiken voor de onbekende woorden.

Spam, spam, spam (3)

  • met de hand:
    • nieuw bericht M: korting
    • bepaal $P(Spam|M)$
  • met Python:
    • nieuw bericht M: klik
    • bepaal $P(Spam|M)$

In [12]:
spam = [
    "rolex", "replica", "korting",
    "klik", "korting", "viagra",
    "korting", "politiek", "krediet",
]
ham = [
    "politiek", "bepaalt", "korting",
    "lariekoek", "in", "politiek",
    "klik", "politiek", "verslag",
    "journalist", "bespeelt", "politiek",
    "politiek", "amsterdam", "stagneert",
]

message = "korting"

P_message_spam = spam.count(message) / len(spam)
P_message_ham = ham.count(message) / len(ham)

P_spam = len(spam) / (len(spam) + len(ham))
P_ham = len(ham) / (len(spam) + len(ham))

P_spam_message = ((P_message_spam * P_spam) /
                  ((P_message_spam * P_spam) + (P_message_ham * P_ham)))

print("P(Spam|M) = {:.3f}".format(P_spam_message))


P(Spam|M) = 0.750