Σημειωματάριο Τετάρτης 6 Δεκ. 2017

Επεξεργασία δεδομένων από ένα αρχείο μαθημάτων του Τμήματος

Σήμερα δουλέψαμε πάνω σε ένα αρχείο τύπου .csv (comma separated values) στο οποίο υπήρχαν τα μαθήματα που έχουν περάσει όλοι οι φοιτητές του Τμήματος (που είναι ακόμη στο Τμήμα). Τα ονόματα και οι αριθμοί μητρώου των φοιτητών είχαν αντικατασταθεί από τυχαίους αριθμούς ώστε να διατηρείται η ανωνυμία.

Είδαμε κατ' αρχήν το πώς χρησιμοποιούμε το python module csv για να διαβάζουμε (και να γράφουμε, αν και δεν το κάναμε αυτό) αρχεία τύπου .csv.

Αφού ανοίξουμε το αρχείο με το συνηθισμένο τρόπο για διάβασμα (στη μεταβλητή f, π.χ.) καλούμε μετά τη συνάρτηση csv.reader (στην οποία δηλώνουμε ποιος είναι ο διαχωριστικός χαρακτήρας στο αρχείο και ποιος ο χαρακτήρας quote (κόμμα και διπλά εισαγωγικά αντίστοιχα στην περίπτωσή μας) και η συνάρτηση αυτή επιστρέφει ένα αντικείμενο, σα λίστα, την οποία μπορούμε να διανύσουμε με ένα απλό for loop και διανύοντάς το παίρνουμε σε μια λίστα κάθε φορά τα πεδία (αυτά που διαχωρίζονται με το κόμμα) κάθε γραμμής.

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

AM=0; NAME=1; STATUS=2; COURSEINTNUMBER=3; COURSENUMBER=4; COURSENAME=5;
DEPTNUMBER=6; DEPTNAME=7; YEAR=8; SEMESTER=9; UNITS=10; ECTS=11; GRADE=12;

Μερικές από τις γραμμές του αρχείου αυτού μοιάζουν κάπως έτσι:

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

In [1]:
import csv

AM=0; NAME=1; STATUS=2; COURSEINTNUMBER=3; COURSENUMBER=4; COURSENAME=5;
DEPTNUMBER=6; DEPTNAME=7; YEAR=8; SEMESTER=9; UNITS=10; ECTS=11; GRADE=12;

allam = [] # εδώ θα μπουν όλοι οι αριθμοί μητρώου, μόνο μια φορά ο καθένας

f = open("courses-anon.csv", "r")
reader = csv.reader(f, delimiter=',', quotechar='"')
firstrow=True # θέλουμε να πετάξουμε την πρώτη γραμμή του αρχείου (επικεφαλίδες)
for row in reader:
    if firstrow: # αυτό το if θα πιάσει μόνο την πρώτη φορά που θα μπούμε στο loop
        firstrow=False
        continue
    if row[AM] not in allam: # αν δεν έχουμε ξαναδεί τον φοιτητή αυτό πριν
        allam.append(row[AM]) # τον προσθέτουμε στη λίστα όλων των ΑΜ (allam)
f.close()

print("Αριθμός φοιτητών: ", len(allam))
Αριθμός φοιτητών:  1459

Στο επόμενο μετράμε τον αριθμό των διαφορετικών μαθημάτων που έχουν δοθεί (διαφορετικοί κωδικοί μαθημάτων). Επειδή η διαδικασία είναι ίδια με το μέτρημα των φοιτητών φτιάχνουμε μια συνάρτηση που αυτό που κάνει είναι να μας πει πόες μοναδικές τιμές υπάρχουν σε μια στήλη του αρχείου. Με τη στήλ AM μετράμε τους φοιτητές και με τη στήλη COURSENUMBER μετράμε τα διαφορετικά μαθήματα.

In [2]:
import csv

AM=0; NAME=1; STATUS=2; COURSEINTNUMBER=3; COURSENUMBER=4; COURSENAME=5;
DEPTNUMBER=6; DEPTNAME=7; YEAR=8; SEMESTER=9; UNITS=10; ECTS=11; GRADE=12;

def countdifferent(col): # Επιστρέφει το πόσες διαφορετικές τιμές υπάρχουν στη στήλη col
    L = []
    f = open("courses-anon.csv", "r")
    reader = csv.reader(f, delimiter=',', quotechar='"')
    firstrow = True
    for row in reader:
        if firstrow:
            firstrow=False; continue
        if row[col] not in L:
            L.append(row[col])
    f.close()
    return len(L)

print("Αριθμός φοιτητών: ", countdifferent(AM))
print("Αριθμός μαθημάτων: ", countdifferent(COURSENUMBER))
Αριθμός φοιτητών:  1459
Αριθμός μαθημάτων:  517

Στο επόμενο μετράμε διάφορα πράγματα.

Σε ένα πέρασμα του αρχείου κρατάμε διάφορες πληροφορίες, κατά κανόνα σε λεξικά.

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

Το ΜΑΘ-102 (τιτλος: Απειροστικός Λογισμός I) έχει δοθεί 28 φορές
Το ΜΑΘ-151 (τιτλος: Αγγλικά II) έχει δοθεί 24 φορές
Το ΜΑΘ-106 (τιτλος: Γλώσσα Προγραμματισμού) έχει δοθεί 22 φορές
Το ΜΑΘ-152 (τιτλος: Αγγλικά III) έχει δοθεί 21 φορές
Το ΜΑΘ-103 (τιτλος: Απειροστικός Λογισμός II) έχει δοθεί 21 φορές
Το ΜΑΘ-150 (τιτλος: Αγγλικά I) έχει δοθεί 20 φορές
Το ΜΑΘ-110 (τιτλος: Άλγεβρα) έχει δοθεί 20 φορές
Το ΜΑΘ-100 (τιτλος: Γενικά Μαθηματικά) έχει δοθεί 20 φορές
Το ΜΑΘ-101 (τιτλος: Εισαγωγή στη Θεωρία Συνόλων) έχει δοθεί 20 φορές
Το ΜΑΘ-107 (τιτλος: Φυσική Ι) έχει δοθεί 17 φορές
Το ΜΑΘ-105 (τιτλος: Γραμμική Άλγεβρα Ι) έχει δοθεί 17 φορές
Το ΜΑΘ-108 (τιτλος: Εισαγωγή στην Ανάλυση I) έχει δοθεί 17 φορές
In [4]:
import csv

AM=0; NAME=1; STATUS=2; COURSEINTNUMBER=3; COURSENUMBER=4; COURSENAME=5;
DEPTNUMBER=6; DEPTNAME=7; YEAR=8; SEMESTER=9; UNITS=10; ECTS=11; GRADE=12;

courseinfo={} # με κλειδιά τους κωδικούς μαθημάτων κρατάμε το πότε δόθηκαν
coursetitle={} # με κλειδιά τους κωδικούς μαθημάτων κρατάμε τον τίτλο του μαθήματος και τα ECTS του
student={} # με κλειδιά τους ΑΜ φοιτητών κρατάμε διάφορες πληροφορίες ανά φοιτητή: ποια μαθήματα έχει περάσει,
           # ποια τα συνολικά του ECTS και ποιο το άθροισμα των βαθμών του
f=open("courses-anon.csv", "r")
reader = csv.reader(f, delimiter=',', quotechar='"')
firstrow = True
for row in reader:
    if firstrow:
        firstrow=False; continue
    am = row[AM] # σε αυτή και τις επόμενες γραμμές κρατάμε σε μεταβλητές κάποιες από τις στήλες
    c = row[COURSENUMBER]
    y = row[YEAR]
    s = row[SEMESTER]
    t = row[COURSENAME]
    e = row[ECTS]
    g = row[GRADE]
    if c not in courseinfo.keys(): # βάζουμε σε κάθε μάθημα το πότε δόθηκε
        courseinfo[c] = [ [y, s]  ]
    else:
        if [y, s] not in courseinfo[c]:
            courseinfo[c].append( [y, s] )
    if c not in coursetitle.keys(): # σε κάθε μάθημα προσθέτουμε τον τίτλο του και τα ECTS του
        coursetitle[c]={"title": t, "ects": e}
    if am not in student.keys(): # για κάθε φοιτητή κρατάμε τα μαθήματά του, τα συνολικά ECTS του και το άθροισμα
                                 # των βαθμών του
        student[am] = {"courses":[c], "ects": float(e), "totalgrade": float(g)}
    else:
        student[am]["courses"].append(c)
        student[am]["ects"] += float(e) # βαθμοί και ECTS πρέπει να μετατραπούν σε floats
        student[am]["totalgrade"] += float(g)
f.close()

for c in courseinfo.keys(): # για κάθε κωδικό μαθήματος ταξινομούμε τις φορές που δόθηκε χρονολογικά
    courseinfo[c].sort(key=(lambda x: x[0]+x[1]))
    
allcourses=list(courseinfo.keys()) # όλοι οι κωδικοί μαθημάτων
allcourses.sort(key=lambda x: len(courseinfo[x]), reverse=True) # ταξινομούμε με βάση το πόσες φορές δόθηκε

count=0 # για να τυπώσουμε μόνο μερικούς από τους φοιτητές
for am in sorted(list(student.keys()), # οι φοιτητές με φθίνουσα σειρά μέσου όρου (βαθμολογίας)
                 key=lambda x:student[x]["totalgrade"]/len(student[x]["courses"]), reverse=True):
    print({} έχει ΜΟ {}".format(am, student[am]["totalgrade"]/len(student[am]["courses"])))
    if count>20: break
    count += 1
    
Ο 642 έχει ΜΟ 9.944444444444445
Ο 777 έχει ΜΟ 9.666666666666666
Ο 688 έχει ΜΟ 9.59375
Ο 773 έχει ΜΟ 9.5625
Ο 1166 έχει ΜΟ 9.535714285714286
Ο 1181 έχει ΜΟ 9.5
Ο 613 έχει ΜΟ 9.351851851851851
Ο 546 έχει ΜΟ 9.23076923076923
Ο 795 έχει ΜΟ 9.1875
Ο 652 έχει ΜΟ 9.055555555555555
Ο 810 έχει ΜΟ 9.0
Ο 1456 έχει ΜΟ 9.0
Ο 43 έχει ΜΟ 8.944444444444445
Ο 742 έχει ΜΟ 8.916666666666666
Ο 755 έχει ΜΟ 8.88888888888889
Ο 1390 έχει ΜΟ 8.875
Ο 812 έχει ΜΟ 8.866666666666667
Ο 1423 έχει ΜΟ 8.75
Ο 656 έχει ΜΟ 8.625
Ο 719 έχει ΜΟ 8.607142857142858
Ο 576 έχει ΜΟ 8.6
Ο 1018 έχει ΜΟ 8.6