Σημειωματάριο Πέμπτης 5 Μαρτίου 2015

Σήμερα μιλήσαμε κυρίως για αρχεία, list comprehensions και για dictionaries.

Διάβασμα και επεξεργασία αρχείων

Στο παρακάτω παράδειγμα ανοίγουμε ένα αρχείο με όνομα alice.txt για διάβασμα (μπορείτε να κατεβάσετε αυτό το αρχείο από τη θέση αυτή και να το αποθηκεύσετε στον υπολογιστή σας) και βρίσκουμε το πόσες γραμμές έχει. Με τη μέθοδο f.readlines() παίρνουμε πίσω μια λίστα της οποίας τα στοιχεία είναι οι γραμμές του αρχείου, ένα string η κάθε μία. Έτσι η ποσότητα len(f.readlines()) είναι το μήκος της λίστας αυτής, δηλ. το πλήθος των γραμμών του αρχείου. Τέλος κλείνουμε το αρχείο (έτσι πρέπει πάντα να κάνουμε αφού τελειώσουμε με αυτό).

In [3]:
f = open("alice.txt", "r")
print len(f.readlines())
f.close()
62

Στο επόμενο τυπώνουμε τις 20 πρώτες γραμμές του αρχείου. Παρατηρείστε ότι οι γραμμές αυτές χωρίζονται από μια κενή γραμμή επιπλέον, ενώ στο αρχείο δεν είναι έτσι. Ο λόγος είναι ότι το string που διαβάζουμε για κάθε γραμμή τελειώνει με τον ειδικό χαρακτήρα newline ('\n') και επειδή χρησιμοποιούμε τη συνάρτηση print για να τυπώσουμε η οποία ούτως ή άλλως αλλάζει γραμμή προκύπτουν δύο αλλαγές γραμμών μετά από το τύπωμα κάθε γραμμής. Μια λύση σε αυτό το πρόβλημα είναι να γράψουμε την τελευταία εντολή ως print ll[i],

In [4]:
f = open("alice.txt", "r")
ll = f.readlines()
for i in range(20):
    print ll[i]




                ALICE'S ADVENTURES IN WONDERLAND



                          Lewis Carroll



               THE MILLENNIUM FULCRUM EDITION 3.0









                            CHAPTER I



                      Down the Rabbit-Hole





  Alice was beginning to get very tired of sitting by her sister

on the bank, and of having nothing to do:  once or twice she had

peeped into the book her sister was reading, but it had no

pictures or conversations in it, `and what is the use of a book,'


Στο παρακάτω βλέπουμε πώς μπορούμε να απαλλαγούμε από τον τελευταίο χαρακτήρα των string που έχουν τα περιεχόμενα των γραμμών του αρχείου, το χαρακτήρα newline. Μία λύση είναι να χρησιμοποιήσουμε τη μέθοδο s.rstrip() που μας επιστρέφει το string s αφού πρώτα σβήσει από το τέλος του string όλους τους λευκούς χαρακτήρες. Αυτοί είναι οι ' ' (κενό ή space), '\n' (newline), '\t' (tab). Προσοχή, αν δε θέλουμε να σβήσουμε και τους όποιους άλλους λευκούς χαρακτήρες από το τέλος του string δεν πρέπει να χρησιμοποιήσουμε τον τρόπο αυτό. Μια άλλη μέθοδος είναι να αντικαταστήσουμε την τελευταία γραμμή με την print s[0:len(s)-1], που τυπώνει το string μείον ένα τελευταίο χαρακτήρα.

In [6]:
f = open("alice.txt", "r")
ll = f.readlines()
for s in ll:
    print s.rstrip()


                ALICE'S ADVENTURES IN WONDERLAND

                          Lewis Carroll

               THE MILLENNIUM FULCRUM EDITION 3.0




                            CHAPTER I

                      Down the Rabbit-Hole


  Alice was beginning to get very tired of sitting by her sister
on the bank, and of having nothing to do:  once or twice she had
peeped into the book her sister was reading, but it had no
pictures or conversations in it, `and what is the use of a book,'
thought Alice `without pictures or conversation?'

  So she was considering in her own mind (as well as she could,
for the hot day made her feel very sleepy and stupid), whether
the pleasure of making a daisy-chain would be worth the trouble
of getting up and picking the daisies, when suddenly a White
Rabbit with pink eyes ran close by her.

  There was nothing so VERY remarkable in that; nor did Alice
think it so VERY much out of the way to hear the Rabbit say to
itself, `Oh dear!  Oh dear!  I shall be late!'  (when she thought
it over afterwards, it occurred to her that she ought to have
wondered at this, but at the time it all seemed quite natural);
but when the Rabbit actually TOOK A WATCH OUT OF ITS WAISTCOAT-
POCKET, and looked at it, and then hurried on, Alice started to
her feet, for it flashed across her mind that she had never
before seen a rabbit with either a waistcoat-pocket, or a watch to
take out of it, and burning with curiosity, she ran across the
field after it, and fortunately was just in time to see it pop
down a large rabbit-hole under the hedge.

  In another moment down went Alice after it, never once
considering how in the world she was to get out again.

  The rabbit-hole went straight on like a tunnel for some way,
and then dipped suddenly down, so suddenly that Alice had not a
moment to think about stopping herself before she found herself
falling down a very deep well.

  Either the well was very deep, or she fell very slowly, for she
had plenty of time as she went down to look about her and to
wonder what was going to happen next.  First, she tried to look
down and make out what she was coming to, but it was too dark to
see anything; then she looked at the sides of the well, and
noticed that they were filled with cupboards and book-shelves;
here and there she saw maps and pictures hung upon pegs.  She
took down a jar from one of the shelves as she passed; it was
labelled `ORANGE MARMALADE', but to her great disappointment it
was empty:  she did not like to drop the jar for fear of killing
somebody, so managed to put it into one of the cupboards as she
fell past it.


Στο επόμενο κελλί υπολογίζουμε ποια είναι η μεγαλύτερη (σε αριθμό χαρακτήρων) γραμμή του αρχείου. Στη λίστα n υπολογίζουμε τα μήκη όλων των γραμμών του αρχείου και στην τελευταία γραμμή τυπώνουμε το μέγιστο.

In [7]:
f = open("alice.txt", "r")
ll = f.readlines()
n = []
for i in ll:
    n.append(len(i))
print max(n)
68

List comprehensions

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

Στο πρόγραμμα παρακάτω, που κάνει την ίδια δουλειά με το προηγούμενο, βλέπουμε πώς η λίστα των μηκών των γραμμών του αρχείου περιγράφεται ως

[len(x) for x in ll]

Αυτό είναι ένα παράδειγμα ενός list comprehension παραδείγματα από το οποίο μπορείτε π.χ. να δείτε εδώ.

In [8]:
f = open("alice.txt", "r")
ll = f.readlines()
print max([len(x) for x in ll])
68

Εδώ βλέπουμε άλλο ένα παράδειγμα ενός list comprehension, στο οποίο παράγουμε μια λίστα που αποτελείται από όλα τα ζεύγη \(x, y\) με \(x, y \in \{0, 1, 2, 3, 4\}\) (κάθε ζεύγος είναι κι αυτό μια λίστα μήκους 2).

In [9]:
print [[x,y] for x in range(5) for y in range(5)]
[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4]]

Στο επόμενο υπολογίζουμε το άθροισμα όλων των μηκών των γραμμών του αρχείου. Η συνάρτηση sum μιας λίστας επιστρέφει το άθροισμα των στοιχείων της.

In [10]:
f = open("alice.txt", "r")
ll = f.readlines()
print sum([len(x) for x in ll])
2680

Συνέχεια στην επεξεργασία του αρχείου

Εδώ θέλουμε να βρούμε όλες τις λέξεις που εμφανίζονται στο αρχείο, μία φορά την κάθε μία. Για τους σκοπούς αυτής της άσκησης λέξη είναι ό,τι δεν έχει μέσα κενό χαρακτήρα.

Αφού διαβάσουμε όλες τις γραμμές στη λίστα ll περνάμε όλες αυτές από τη μέθοδο strip ώστε να τους αφαιρέσουμε το τελικό newline (και τους όποιους τελικούς λευκούς χαρακτήρες) και αποθηκεύουμε το αποτέλεσμα στη λίστα l.

Αν x είναι ένα string τότε η μέθοδος x.split() επιστρέφει μια λίστα με strings, που είναι ό,τι προκύπτει από το string x αν θεωρήσουμε τους λευκούς χαρακτήρες ως διαχωριστικούς. Έτσι η εντολή

ll = [x.split() for x in l]

κάνει τη λίστα ll (δεν πειράζει που ξαναχρησιμοποιούμε το όνομα) να είναι μια λίστα από λίστες. Κάθε μια από αυτές τις λίστες είναι οι λέξεις μιας γραμμής.

Σκοπός μας είναι να βρούμε όλες τις λέξεις που εμφανίζονται στο αρχείο και θα τις τοποθετήσουμε (μία φορά την κάθε μία) στη λίστα allwords η οποία είναι αρχικά κενή. Με το διπλό loop που ακολουθεί τοποθετούμε κάθε ένα από τα strings που βρίσκονται στις λίστες που περιέχονται στην ll μέσα στη λίστα allwords αν δεν είναι ήδη μέσα.

Τέλος αναφέρουμε το πόσες διαφορετικές λέξεις βρήκαμε και τυπώνουμε τη λίστα allwords.

In [13]:
f = open("alice.txt", "r")
ll = f.readlines()
f.close()

l = [x.rstrip() for x in ll]
ll = [x.split() for x in l]
allwords = []
for line in ll:
    for s in line:
        if not(s in allwords):
            allwords.append(s)
allwords.sort()
print "Found {N} different words".format(N=len(allwords))
print allwords
Found 245 different words
['(as', '(when', '3.0', 'A', 'ADVENTURES', "ALICE'S", 'Alice', 'CHAPTER', 'Carroll', 'Down', 'EDITION', 'Either', 'FULCRUM', 'First,', 'I', 'IN', 'ITS', 'In', 'Lewis', "MARMALADE',", 'MILLENNIUM', 'OF', 'OUT', 'Oh', 'POCKET,', 'Rabbit', 'Rabbit-Hole', 'She', 'So', 'THE', 'TOOK', 'The', 'There', 'VERY', 'WAISTCOAT-', 'WATCH', 'WONDERLAND', 'White', '`ORANGE', '`Oh', '`and', '`without', 'a', 'about', 'across', 'actually', 'after', 'afterwards,', 'again.', 'all', 'and', 'another', 'anything;', 'as', 'at', 'bank,', 'be', 'before', 'beginning', 'book', "book,'", 'book-shelves;', 'burning', 'but', 'by', 'close', 'coming', 'considering', "conversation?'", 'conversations', 'could,', 'cupboards', 'curiosity,', 'daisies,', 'daisy-chain', 'dark', 'day', 'dear!', 'deep', 'deep,', 'did', 'dipped', 'disappointment', 'do:', 'down', 'down,', 'drop', 'either', 'empty:', 'eyes', 'falling', 'fear', 'feel', 'feet,', 'fell', 'field', 'filled', 'flashed', 'for', 'fortunately', 'found', 'from', 'get', 'getting', 'going', 'great', 'had', 'happen', 'have', 'having', 'hear', 'hedge.', 'her', 'her.', 'here', 'herself', 'hot', 'how', 'hung', 'hurried', 'in', 'into', 'is', 'it', 'it,', 'it.', 'itself,', 'jar', 'just', 'killing', 'labelled', 'large', "late!'", 'like', 'look', 'looked', 'made', 'make', 'making', 'managed', 'maps', 'mind', 'moment', 'much', 'natural);', 'never', 'next.', 'no', 'nor', 'not', 'nothing', 'noticed', 'occurred', 'of', 'on', 'on,', 'once', 'one', 'or', 'ought', 'out', 'over', 'own', 'passed;', 'past', 'peeped', 'pegs.', 'picking', 'pictures', 'pink', 'pleasure', 'plenty', 'pop', 'put', 'quite', 'rabbit', 'rabbit-hole', 'ran', 'reading,', 'remarkable', 'saw', 'say', 'see', 'seemed', 'seen', 'shall', 'she', 'shelves', 'sides', 'sister', 'sitting', 'sleepy', 'slowly,', 'so', 'some', 'somebody,', 'started', 'stopping', 'straight', 'stupid),', 'suddenly', 'take', 'that', 'that;', 'the', 'then', 'there', 'they', 'think', 'this,', 'thought', 'time', 'tired', 'to', 'to,', 'too', 'took', 'tried', 'trouble', 'tunnel', 'twice', 'under', 'up', 'upon', 'use', 'very', 'waistcoat-pocket,', 'was', 'watch', 'way', 'way,', 'well', 'well,', 'well.', 'went', 'were', 'what', 'when', 'whether', 'with', 'wonder', 'wondered', 'world', 'worth', 'would']

Dictionaries (λεξικά)

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

key: object

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

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

In [14]:
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 [15]:
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}

Συνέχεια στην επεξεργασία αρχείου

Στο επόμενο πρόγραμμα χρησιμοποιούμε ένα λεξικό για να μετρήσουμε για κάθε λέξη του αρχείου μας πόσες φορές αυτή εμφανίζεται. Όπως και στο προηγούμενο πρόγραμμά μας (που έβρισκε όλες τις λέξεις) έχουμε στη γραμμή 6 στη μεταβλητή ll μια λίστα από λίστες λέξεων.

Δημιουργούμε ένα κατ' αρχήν κενό λεξικό d={} και στο επόμενο διπλό loop ελέγχουμε αν το string s είναι στο λεξικό. Αν δεν είναι τότε το βάζουμε με αριθμό εμφανίσεων ίσο με 1 (στη γραμμή d[s] = 1) ενώ αν είναι ήδη μέσα τότε αυξάνουμε την τιμή του κατά 1 με την εντολή d[s] += 1.

Θέλουμε επίσης να βρούμε τη λέξη του αρχείου με το μεγαλύτερο αριθμό εμφανίσεων οπότε χρησιμοποιούμε τη μεταβλητή m για το τρέχον μέγιστο (όπως θα διανύουμε το λεξικό) και τη μεταβλητή onoma για τη λέξη με το τρέχον μέγιστο. Δίνουμε δύο αρχικές τιμές σε αυτές τις μεταβλητές και μετά διανύουμε το λεξικό με το for key in d: ούτως ώστε το key να πάρει διαδοχικά ως τιμή όλα τα διαφορετικά κλειδιά που εμφανίζονται στο d. Αν πρέπει να αλλάξουμε το τρέχον μέγιστο το κάνουμε αυτό στη γραμμή μετά το if.

Με το προτελευταίο print τυπώνουμε το ποια είναι αυτή η λέξη και το πόσες φορές εμφανίζεται και με το τελευταίο print τυπώνουμε όλες τις λέξεις μαζί με τους αριθμούς εμφάνισής τους.

In [18]:
f = open("alice.txt", "r")
ll = f.readlines()
f.close()

l = [x.rstrip() for x in ll]
ll = [x.split() for x in l]
d={}
for line in ll:
  for s in line:
    if not( s in d ):
     d[s] = 1
    else:
     d[s] += 1
m=0
onoma = ""
for key in d:
    if d[key] > m:
        m = d[key]; onoma = key
print "Max word appearance: \"{s}\" appears {M} times".format(s=onoma,M=m)
print d
Max word appearance: "the" appears 21 times
{'all': 1, 'managed': 1, 'they': 1, 'remarkable': 1, 'passed;': 1, 'over': 1, 'sleepy': 1, 'TOOK': 1, 'feet,': 1, 'falling': 1, '`ORANGE': 1, 'White': 1, 'THE': 1, 'cupboards': 2, 'seemed': 1, 'dipped': 1, 'just': 1, 'how': 1, 'herself': 2, 'had': 5, 'empty:': 1, 'actually': 1, 'to': 19, 'going': 1, 'about': 2, 'under': 1, 'Oh': 1, 'worth': 1, 'ought': 1, 'it.': 1, 'then': 3, 'it,': 5, 'sitting': 1, 'very': 5, 'conversations': 1, 'watch': 1, 'Alice': 6, 'dark': 1, 'were': 1, 'She': 1, "late!'": 1, 'not': 2, 'world': 1, 'trouble': 1, '`without': 1, 'day': 1, 'nor': 1, 'down': 6, 'like': 2, '`Oh': 1, 'In': 1, 'did': 2, 'OF': 1, 'next.': 1, 'drop': 1, 'large': 1, 'twice': 1, 'she': 19, 'anything;': 1, 'found': 1, 'went': 3, 'suddenly': 3, 'down,': 1, 'CHAPTER': 1, 'her.': 1, 'tired': 1, 'some': 1, 'hurried': 1, 'ADVENTURES': 1, 'past': 1, 'see': 2, '3.0': 1, 'close': 1, 'happen': 1, 'fear': 1, 'out': 4, 'what': 3, 'for': 5, 'could,': 1, 'pictures': 3, 'daisy-chain': 1, 'VERY': 2, 'somebody,': 1, 'Rabbit': 3, 'that;': 1, "book,'": 1, 'before': 2, 'across': 2, 'filled': 1, 'either': 1, 'be': 2, 'this,': 1, 'afterwards,': 1, 'never': 2, 'shelves': 1, 'here': 1, 'quite': 1, 'hung': 1, 'Either': 1, 'put': 1, 'from': 1, 'picking': 1, 'killing': 1, 'beginning': 1, 'by': 2, 'rabbit-hole': 2, 'on': 2, 'sister': 2, 'would': 1, 'getting': 1, 'of': 12, 'dear!': 2, 'pegs.': 1, 'natural);': 1, 'MILLENNIUM': 1, 'hedge.': 1, 'way,': 1, 'waistcoat-pocket,': 1, '(as': 1, 'or': 5, 'tunnel': 1, 'own': 1, 'burning': 1, 'feel': 1, 'jar': 2, 'into': 2, 'There': 1, 'pop': 1, 'one': 2, 'reading,': 1, '(when': 1, 'another': 1, 'hear': 1, 'book-shelves;': 1, 'considering': 2, 'use': 1, 'First,': 1, 'her': 9, "conversation?'": 1, 'POCKET,': 1, 'there': 1, 'noticed': 1, 'EDITION': 1, 'much': 1, 'too': 1, 'way': 1, 'deep,': 1, 'was': 12, 'OUT': 1, 'Rabbit-Hole': 1, 'eyes': 1, 'on,': 1, 'that': 4, 'started': 1, 'great': 1, 'daisies,': 1, 'occurred': 1, 'but': 5, 'maps': 1, 'So': 1, 'labelled': 1, 'rabbit': 1, 'WONDERLAND': 1, 'with': 4, 'peeped': 1, 'made': 1, 'look': 2, 'whether': 1, 'straight': 1, '`and': 1, 'Carroll': 1, 'up': 1, 'flashed': 1, 'slowly,': 1, 'Down': 1, 'to,': 1, "ALICE'S": 1, 'stopping': 1, 'making': 1, 'curiosity,': 1, 'ITS': 1, 'say': 1, 'and': 14, 'FULCRUM': 1, 'stupid),': 1, 'ran': 2, 'WATCH': 1, 'is': 1, 'mind': 2, 'it': 11, 'deep': 1, 'well,': 1, 'as': 4, 'well.': 1, 'at': 4, 'have': 1, 'in': 5, 'seen': 1, 'saw': 1, 'pink': 1, 'looked': 2, 'bank,': 1, 'fortunately': 1, 'make': 1, 'get': 2, 'when': 2, 'do:': 1, 'field': 1, 'hot': 1, 'book': 1, 'take': 1, 'wonder': 1, "MARMALADE',": 1, 'disappointment': 1, 'A': 1, 'WAISTCOAT-': 1, 'again.': 1, 'tried': 1, 'coming': 1, 'shall': 1, 'I': 2, 'after': 2, 'upon': 1, 'took': 1, 'moment': 2, 'fell': 2, 'itself,': 1, 'plenty': 1, 'IN': 1, 'nothing': 2, 'The': 1, 'sides': 1, 'a': 11, 'wondered': 1, 'Lewis': 1, 'no': 1, 'well': 2, 'think': 2, 'thought': 2, 'so': 4, 'time': 3, 'pleasure': 1, 'the': 21, 'having': 1, 'once': 2}

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

Αφού πάρουμε, όπως πριν, στη λίστα ll όλες τις γραμμές του αρχείου (ένα string η κάθε μία) θέτουμε το μετρητή count ίσο με το 0 και για κάθε γραμμή του αρχείου (for s in ll:) χρησιμοποιούμε τη μέθοδο s.find("ERROR") για να αποφασίσουμε αν το string "ERROR" υπάρχει ως substring στο s. Η μέθοδος find επιστρέφει -1 αν δε βρεθεί το string οπότε αρκεί να ελέγξουμε ότι η τιμή που επιστρέφει είναι διαφορετική από -1 για να ξέρουμε αν εμφανίζεται το "ERROR".

In [21]:
f = open("scores-0-min-max.csv", "r")
lines = f.readlines()
f.close()

ll = [s.rstrip() for s in lines]
count=0
for s in ll:
    if s.find("ERROR") != -1:
        count += 1
print "Number of errors:", count
Number of errors: 71