Σημειωματάριο διάλεξης Πέμπτης 12/11/2015

Λεξικά (dictionaries)

Ένα dictionary (δείτε π.χ. εδώ) στην python είναι σα μια λίστα, χωρίς εσωτερική διάταξη, που τα στοιχεία της γίνονται indexed όχι κατ' ανάγκη από φυσικούς αριθμούς αλλά και από λέξεις. Τα περιεχόενα ενός λεξικού είναι ζεύγη της μορφής

key: object

όπου το κλειδί key μπορεί να είναι αριθμός ή string και το object μπορεί να είναι οτιδήποτε. Για κάθε κλειδί πρέπει να υπάρχει ένα μοναδικό ζεύγος (αν γράψουμε κι άλλο επικρατεί το τελευταίο στη σειρά).

Στο παρακάτω παράδειγμα βλέπουμε ένα λεξικό με όνομα age, τα ζεύγη του οποίου είναι της μορφής "όνομα: ηλικία". Παρατηρείστε ότι στον ορισμό του λεξικού χρησιμοποιούμε άγκιστρα {} και όχι αγκύλες [] όπως κάνουμε για τις λίστες. Όταν όμως αναφερόμαστε σε κάποιο στοιχείο όπως κάνουμε στη δεύτερη και στην τρίτη γραμμή του προγράμματος τότε χρησιμοποιούμε αγκύλες.

In [27]:
age = {"Mihalis": 48, "Manolis": 50, "Nikos": 12}
age["Nikos"] += 2
print age["Nikos"]
print age
14
{'Mihalis': 48, 'Nikos': 14, 'Manolis': 50}

Εδώ βλέπουμε στη δεύτερη γραμμή το πώς ελέγχουμε αν ένα κλειδί "Yannis" υπάρχει σε ένα λεξικό. Αν δεν υπάρχει το προσθέτουμε με ηλικία 55 ενώ αν υπάρχει τυπώνουμε την ηλικία του (συμβαίνει το πρώτο). Στην προτελευταία γραμμή διαγράφουμε το ζεύγος με key "Manolis" από το λεξικό με την εντολή del.

In [28]:
age = {"Mihalis": 48, "Manolis": 50, "Nikos": 12}
if "Yannis" in age:
    print age["Yannis"]
else:
    age["Yannis"] = 55
del age["Manolis"]
print age
{'Mihalis': 48, 'Yannis': 55, 'Nikos': 12}

Στο επόμενο πρόγραμμα βλέπουμε πώς διανύουμε όλα τα κλειδιά ενός λεξικού με το `for'.

In [4]:
D = { "Mihalis": 49, "Maria": 25, "Yannis": 150, "Eleni": 22, "Markos": 30}

for k in D:
    print "The age of %s is %d" % (k, D[k])
The age of Mihalis is 49
The age of Markos is 30
The age of Yannis is 150
The age of Eleni is 22
The age of Maria is 31

Κι εδώ αυξάνουμε την ηλικία κάθε ατόμου (κλειδιού) στο λεξικό.

In [5]:
D = { "Mihalis": 49, "Maria": 25, "Yannis": 150, "Eleni": 22, "Markos": 30}

for k in D:
    D[k] += 1

for k in D:
    print "The age of %s is %d" % (k, D[k])
The age of Mihalis is 50
The age of Markos is 31
The age of Yannis is 151
The age of Eleni is 23
The age of Maria is 26

Τα κλειδιά ενός λεξικού μπορούν να είναι αριθμοί ή strings (ή και tuples, αλλά δεν έχουμε μιλήσει ιδιαίτερα για αυτά). Δε μπορεί π.χ. να είναι λίστα, όπως φαίνεται παρακάτω όπου παράγεται σφάλμα όταν πάμε να ορίσουμε ένα λεξικό με κλειδί που είναι λίστα.

In [6]:
D = { [1, 2]: 3.5 }
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-38acac7a165f> in <module>()
----> 1 D = { [1, 2]: 3.5 }

TypeError: unhashable type: 'list'

Εδώ βλέπουμε ένα λεξικό όπου σε κάθε κλειδί (όνομα ατόμου) αντιστοιχεί ως τιμή ένα ολόκληρο λεξικό. Το λεξικό του κάθε ατόμου περιέχει διάφορες πληροφορίες για το άτομο με το αντίστοιχο κλειδί.

In [10]:
D = {
     "Mihalis": {"age": 49, "weight": 90, "height": 1.75},
     "Eleni": {"weight": 55, "height": 1.70}
     }

for k in D:
    if "age" in D[k]: # Ελέγχουμε αν υπάρχει το κλειδί "age" στην τιμή του συγκεκριμένου ατόμου k
        a = D[k]["age"]
    else:
        a = 0 # Αν δεν υπάρχει η πληροφορία ηλικίας στο άτομο k το βάζουμε ίσο με 0
    print "The height of %s is %.2f" % (k, a)
The height of Mihalis is 49.00
The height of Eleni is 0.00

Στο παρακάτω πρόγραμμα μετράμε πόσες φορές εμφανίζεται κάθε γράμμα σε ένα string (μεταβλητή text). Κρατάμε ένα λεξικό N όπου κλειδιά είναι τα γράμματα που εμφανίζονται (μπαίνουν όταν πρωτοεμφανίζονται) και τιμή του κάθε κλειδιού-γράμματος είναι το πόσες φορές εμφανίζεται στο text. Περνάμε τα γράμματα του text ένα ένα και για κάθε γράμμα αυξάνουμε το μετρητή του στο λεξικό N.

In [29]:
text = r"""
  Presently she began again.  `I wonder if I shall fall right
THROUGH the earth!  How funny it'll seem to come out among the
people that walk with their heads downward!  The Antipathies, I
think--' (she was rather glad there WAS no one listening, this
time, as it didn't sound at all the right word) `--but I shall
have to ask them what the name of the country is, you know.
Please, Ma'am, is this New Zealand or Australia?' (and she tried
to curtsey as she spoke--fancy CURTSEYING as you're falling
through the air!  Do you think you could manage it?)  `And what
an ignorant little girl she'll think me for asking!  No, it'll
never do to ask:  perhaps I shall see it written up somewhere.'
""".lower() # μετατρέπουμε το προηγούμενο μεγάλο string σε μικρά γράμματα πριν το αναθέσουμε
            # στη μεταβλητή text. To r μπροστά από το string σημαίνει τα πάρει το string σε raw (ωμή) μορφή,
            # να μην ερμηνεύσει δηλ. τους ειδικούς χαρακτήρες σύμφωνα με το συνηθισμένο τους ρόλο αλλά
            # σα να είναι οποιοιδήποτε χαρακτήρες. Τα τριπλά εισαγωγικά είναι επειδή το string επεκτείνεται
            # σε παραπάνω από μια γραμμή.

N = {}

for x in text:
    if  not(  ord('a') <= ord(x) <= ord('z') ): # Αν ο χαρακτήρας δεν είναι γράμμα
        continue
    if x not in N: #Αν το γράμμα δεν υπάρχει ήδη στο λεξικό N (το συναντάμε για πρώτη φορά)
        N[x] = 1   #το βάζουμε με μετρητή (τιμή) ίση με 1 (αφού το έχουμε δει μια φορά μέχρι τώρα)
    else:
        N[x] += 1 # Αν το γράμμα υπάρχει ήδη στο λεξικό αυξάνουμε την τιμή του (το μετρητή του) κατά 1

keys = [] # Εδώ φτιάχνουμε μια λίστα με όλα τα κλειδιά (γραμματα που έχουν εμφανιστεί)
for letter in N:
    keys.append(letter)
keys.sort() # Ταξινομούμε τη λίστα

for letter in keys: # Τυπώνουμε πόσες φορές έχει εμφανιστεί το κάθε γράμμα που υπάρχει στο N
    print "Letter [%s] appears [%2d] times" % (letter, N[letter]) 
        
Letter [a] appears [49] times
Letter [b] appears [ 2] times
Letter [c] appears [ 6] times
Letter [d] appears [16] times
Letter [e] appears [55] times
Letter [f] appears [ 7] times
Letter [g] appears [15] times
Letter [h] appears [41] times
Letter [i] appears [39] times
Letter [k] appears [ 9] times
Letter [l] appears [30] times
Letter [m] appears [11] times
Letter [n] appears [36] times
Letter [o] appears [33] times
Letter [p] appears [ 9] times
Letter [r] appears [28] times
Letter [s] appears [34] times
Letter [t] appears [54] times
Letter [u] appears [16] times
Letter [v] appears [ 2] times
Letter [w] appears [15] times
Letter [y] appears [10] times
Letter [z] appears [ 1] times

Εδώ λύνουμε με χρήση λεξικών μια προηγούμενη άσκηση (10-likes). Στο πρώτο κομμάτι του προγράμματος μετατρέπουμε την πληροφορία μας από τη λίστα likes στο λεξικό B, όπου κλειδιά είναι τα αγόρια και η τιμή κάθε αγοριού είναι η λίστα των κοριτσιών που του αρέσουν.

Στο δεύτερο κομμάτι φτιάχνουμε το λεξικό G, όπου τα κλειδιά είναι όλα τα κορίτσια που εμφανίζονται κάπου στο λεξικό B και η τιμή κάθε κοριτσιού είναι η λίστα όλων των αγοριών στα οποία αρέσει.

In [18]:
likes = [
    ["Yannis", ["Eleni", "Maria", "Katerina"]],
    ["Giorgos", ["Georgia", "Maria", "Niki"]],
    ["Mihalis", ["Maria"]],
    ["Manolis", ["Georgia", "Ioanna", "Katerina"]],
    ["Kostas", ["Georgia", "Ioanna", "Niki", "Katerina"]],
    ["Leonardo", []],
]

B = {} # Λεξικό κενό αρχικά
for x in likes: # Για κάθε στοιχείο της λίστας likes (για κάθε αγόρι)
    B[x[0]] = [] # Βάζουμε το όνομα του αγοριού (x[0]) στο λεξικό B, με κενή λίστα κοριτσιών αρχικά.
    for g in x[1]: # Για κάθε κορίτσι g στη λίστα του αγοριού (x[1]) 
        B[x[0]].append(g) # Προσθέτουμε το g στη λίστα του αγοριού στο λεξικό B
        
G = {} # Λεξικό κενό αρχικά
for b in B: # Για κάυε αγόρι b στο λεξικό B
    gl = B[b] # gl είναι η λίστα των κοριτσιών που του αρέσουν
    for g in gl: # Για κάθε κορίτσι από τη λίστα gl
        if g not in G: # Αν δεν υπάρχει ήδη στο G το βάζουμε, με λίστα αγοριών να περιέχει αρχικά μόνο το b
            G[g] = [ b ]
        else:
            G[g].append(b) # Αν είναι ήδη στο G τότε προσθέτουμε το αγόρι b στη λίστα του g

print B
print "-----------------------"
print G
{'Kostas': ['Georgia', 'Ioanna', 'Niki', 'Katerina'], 'Yannis': ['Eleni', 'Maria', 'Katerina'], 'Manolis': ['Georgia', 'Ioanna', 'Katerina'], 'Mihalis': ['Maria'], 'Giorgos': ['Georgia', 'Maria', 'Niki'], 'Leonardo': []}
-----------------------
{'Georgia': ['Kostas', 'Manolis', 'Giorgos'], 'Ioanna': ['Kostas', 'Manolis'], 'Niki': ['Kostas', 'Giorgos'], 'Katerina': ['Kostas', 'Yannis', 'Manolis'], 'Eleni': ['Yannis'], 'Maria': ['Yannis', 'Mihalis', 'Giorgos']}

Στο παρακάτω πρόγραμμα ξεκινάμε με ένα λεξικό F που περιέχει πατεράδες (κλειδιά) και τους γιούς τους (λίστα, τιμή για κάθε κλειδί). Σκοπός μας είναι να φτιάξουμε το λεξικό GP που περιέχει παππούδες (κλειδιά) και εγγονούς (μια λίστα για κάθε κλειδί).

In [19]:
F = { "Nikos": ["Yannis", "Mihalis"],
       "Manolis": ["Kostas"],
       "Kostas": ["Mihalis1", "Pantelis"],
       "Mihalis": ["Nikos1"],
       "Yannis": ["Nikos2", "Antonis"]
       }

GP = {} # Λεξικό παππούδων κενό αρχικά

for f in F: # Για κάθε παερα f
    for s in F[f]: # Για κάθε γιο s του πατέρα f
        if s in F: # Αν ο S είναι κι αυτός πατέρας
            GP[f] = [] # τότε ο f είναι σίγουρα παππούς, οπότε τον βάζουμε στο GP με κενή λίστα εγγονών αρχικά
            
for gp in GP: # Για κάθε παππού gp
    for s in F[gp]: # Για κάθε γιο s του gp
        for gs in F[s]: # Για κάθε γιο gs του s
            GP[gp].append(gs) # Προσθέτουμε τον gs στους εγγονούς του gp

print GP
            
{'Nikos': ['Nikos2', 'Antonis', 'Nikos1'], 'Manolis': ['Mihalis1', 'Pantelis']}

Το κείμενο που είναι στη μεταβλητή dna είναι ένα κομμάτι DNA (ακολουθία από A,C,G ή T).

Το επόμενο πρόγραμμα μετράει για κάθε \(k\)-άδα γραμμάτων (\(k=3\) στο συγκεκριμένο παράδειγμα) που εμφανίζεται στο dna πόσες φορές εμφανίζεται.

Γι' αυτό φτιάχνουμε ένα λεξικό freq όπου τα κλειδιά είναι όλες οι \(k\)-άδες γραμμάτων που εμφανίζονται στο dna και η τιμή της κάθε \(k\)-άδας είναι το πόσες φορές αυτή εμφανίζεται στο string dna.

In [31]:
dna = "ACAAGATGCCATTGTCCCCCGGCCTCCTGCTGCTGCTGCTCTCCGGGGCCACGGCCACCGCTGCCCTGCCCCTGGAGGGTGGCCCCACCGGCCGAGACAGCGAGCATATGCAGGAAGCGGCAGGAATAAGGAAAAGCAGCCTCCTGACTTTCCTCGCTTGGTGGTTTGAGTGGACCTCCCAGGCCAGTGCCGGGCCCCTCATAGGAGAGGAAGCTCGGGAGGTGGCCAGGCGGCAGGAAGGCGCACCCCCCCAGCAATCCGCGCGCCGGGACAGAATGCCCTGCAGGAACTTCTTCTGGAAGACCTTCTCCTCCTGCAAATAAAACCTCACCCATGAATGCTCACGCAAGTTTAATTACAGACCTGAA"

bases = "ACGT"

k = 3

freq = {} # Αρχικά το λεξικό είναι κενό, αφού ακόμη δεν έχουμε συναντήσει καμία k-άδα

N = len(dna) # Το μήκος του dna

for i in range(0, N-k+1): # Τσουλάμε ένα "παράθυρο" μήκους k κατά μήκος του dna.
                          #Το i είναι η αρχική θέση του παραθύρου, και η μέγιστη τιμή του i είναι η N-k
    s = dna[i:i+k] # s το μήκους k κομμάτι του dna που αρχίζει από τη θέση i
    if s not in freq: # Αν η k-άδα δεν είναι ήδη στο λεξικό freq
        freq[s] = 1 # τη βάζουμε με αριθμό εμφανίσεων 1
    else:
        freq[s] += 1 # αν είναι ήδη μέσα αυξάνουμε τον αριθμό εμφανίσεών της κατά 1

for s in freq:
    print "Word [%s] appears %3d times" % (s, freq[s])
Word [ACC] appears   8 times
Word [ATG] appears   5 times
Word [ACA] appears   4 times
Word [ACG] appears   2 times
Word [ATC] appears   1 times
Word [AAC] appears   2 times
Word [ATA] appears   4 times
Word [AGG] appears  12 times
Word [CCT] appears  15 times
Word [ACT] appears   2 times
Word [AGC] appears   7 times
Word [AAG] appears   8 times
Word [AGA] appears   6 times
Word [CAT] appears   4 times
Word [AAT] appears   6 times
Word [ATT] appears   2 times
Word [CTG] appears  12 times
Word [CTC] appears  12 times
Word [CAC] appears   6 times
Word [AAA] appears   5 times
Word [CCG] appears   8 times
Word [TTT] appears   3 times
Word [CCA] appears   9 times
Word [CAA] appears   4 times
Word [CCC] appears  18 times
Word [TAG] appears   1 times
Word [TAT] appears   1 times
Word [GGT] appears   4 times
Word [AGT] appears   3 times
Word [TGT] appears   1 times
Word [CGA] appears   2 times
Word [CAG] appears  12 times
Word [TCT] appears   4 times
Word [GAT] appears   1 times
Word [CGG] appears   9 times
Word [CTT] appears   5 times
Word [TGC] appears  13 times
Word [GGG] appears   6 times
Word [TGA] appears   4 times
Word [GGA] appears  12 times
Word [TGG] appears   7 times
Word [GGC] appears  12 times
Word [TAC] appears   1 times
Word [GAG] appears   7 times
Word [TCG] appears   2 times
Word [TTA] appears   2 times
Word [TTG] appears   3 times
Word [TCC] appears   9 times
Word [GAA] appears  10 times
Word [TAA] appears   3 times
Word [GCA] appears  10 times
Word [GCC] appears  15 times
Word [GTC] appears   1 times
Word [GCG] appears   6 times
Word [GTG] appears   5 times
Word [TTC] appears   4 times
Word [GTT] appears   2 times
Word [GCT] appears   8 times
Word [GAC] appears   6 times
Word [TCA] appears   3 times
Word [CGC] appears   7 times