La început, trebuie să introducem în Python datele de anul trecut, pe care le am deja fain frumos într-un tabel Excel, doar trebuie să îl salvez în format CSV și apoi cu ajutorul modulului csv îl citesc în Python în 3 linii. Totul se bagă într-un dicționar care conține și grupa căreia aparține studentul. Din motive de confidențialitate, ștergem numele dintre valori, acesta rămânând doar între chei și va fi folosit doar la adăugarea de note la alte materii. Ca să ne simplificăm un pic viața mai târziu, înlocuim liniuțele din nume cu spații și ne scăpăm de diacritice din nume (folosind un snippet copy-pasted de pe StackOverflow)
import csv
import unicodedata
file = 'C:\\Users\\Roland\\Documents\\Statistici\\note_an1.csv'
note = {}
with open(file) as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
row['Nume'] = u''.join(c for c in unicodedata.normalize('NFD', row['Nume'].replace('-',' '))
if unicodedata.category(c) != 'Mn')
note[row['Nume'].strip()] = row
del note[row['Nume'].strip()]['Nume']
print(list(note.values())[10:20])
[{'POO': '9', 'Grupa': 'Gr. 217', 'Geometrie': '10', 'SdA': '4', 'Nota AC': '7', 'Nota FP': '9', 'Nota algebra': '7'}, {'POO': '6', 'Grupa': 'Gr. 213', 'Geometrie': '8', 'SdA': '4', 'Nota AC': '4', 'Nota FP': '8', 'Nota algebra': '3'}, {'POO': '5', 'Grupa': 'Gr. 217', 'Geometrie': '7', 'SdA': 'a', 'Nota AC': '4', 'Nota FP': '7', 'Nota algebra': '3'}, {'POO': '6', 'Grupa': 'Gr. 215', 'Geometrie': '9', 'SdA': '4', 'Nota AC': '4', 'Nota FP': '5', 'Nota algebra': '5'}, {'POO': '', 'Grupa': 'Gr. 212', 'Geometrie': '3', 'SdA': 'a', 'Nota AC': '4', 'Nota FP': '6', 'Nota algebra': ''}, {'POO': '', 'Grupa': 'Gr. 217', 'Geometrie': 'A', 'SdA': 'a', 'Nota AC': '4', 'Nota FP': '6', 'Nota algebra': '4'}, {'POO': '4', 'Grupa': 'Gr. 213', 'Geometrie': '5', 'SdA': '4', 'Nota AC': '4', 'Nota FP': '6', 'Nota algebra': ''}, {'POO': '9', 'Grupa': 'Gr. 213', 'Geometrie': '9', 'SdA': '9', 'Nota AC': '7', 'Nota FP': '10', 'Nota algebra': '6'}, {'POO': '2', 'Grupa': 'Gr. 216', 'Geometrie': '4', 'SdA': '4', 'Nota AC': '4', 'Nota FP': '6', 'Nota algebra': '2'}, {'POO': '', 'Grupa': 'Gr. 215', 'Geometrie': 'A', 'SdA': 'a', 'Nota AC': '', 'Nota FP': '0', 'Nota algebra': ''}]
Uitându-ne peste formatul în care avem restul notelor, observăm câteva chestii: la PS și la BD avem și inițiala tatălui în nume. De aceasta va trebui să scăpăm. La PS numele este scris cu litere mari. Și aceasta va trebui corectat și făcut să fie scris Capitalizat Corect. La PLF pare a fi cel mai fain, deoarece rezultatele sunt într-un tabel HTML bine formatat, la fiecare student este trecută grupa și nota, dar nu sunt trecuți toți studenții, doar cei care au fost prezenți la cel puțin un examen. La PS și la BD în schimb sunt trecuți toți studenții, chiar dacă au lipsit complet. Unele au diacritice pe nume, alții nu, așa că va trebui să normalizăm și aceasta.
def capitalize_remove_father_name(nume):
nume = u''.join(c for c in unicodedata.normalize('NFD', nume.replace('-',' '))
if unicodedata.category(c) != 'Mn').split()
if len(nume) > 2 and "." in nume[1]:
nume = [nume[0]] + nume[2:]
return " ".join([x.capitalize() for x in nume])
Să adăugăm notele la BD. Cum acestea sunt fiecare într-un fișier HTML separat, vom parcurge pe rând fiecare fișier. Cum notele nu sunt sub forma unui tabel HTML, ci pur și simplu sunt trecuți între două taguri pre
, va trebui să parsăm manual, cu niște regexuri, fiecare linie. Din acest tabel vom extrage și informația legată de grupa în care se află studenul în acest an, deoarece unii s-au mai mutat de anul trecut, sau poate chiar avem colegi noi.
for i in range(1,7):
file = 'C:\\Users\\Roland\\Documents\\Statistici\\Rezultate 22'+str(i)+'.htm'
with open(file) as fisier:
in_pre = False
for line in fisier:
if '</pre>' in line:
break
if in_pre:
reg = re.match("^(([^\W\d_]|\.| |-)+)([0-9]+)?",line)
if reg:
gr = reg.groups()
nume = get_nume(capitalize_remove_father_name(gr[0]))
try:
nota = int(gr[2])
except TypeError:
nota = 0
if not nume:
nume = capitalize_remove_father_name(gr[0])
note[nume] = {}
print(nume)
if 'BD' in note[nume]:
print("erreur BD",nume)
note[nume]['BD'] = nota
note[nume]['Grupa 2'] = '22'+str(i)
if '<pre>' in line:
in_pre = True
Precup Ramona Cosmina Purcel Lucian Calin Catana Laurentiu Tudor Florea Calin Hoza Cristian Gabriel Iovanescu Victor Mate Adrian George Oprea Madalina Fedorov Elisabeta Rosu Ionut Vladut
Ce face funcția get_nume
? Încearcă să găsească corespondentul unui nume între cele pe care le avem deja în lista de anul trecut. Se poate întâmpla ca unii să fi folosit alte 2 nume din 3 (sau acum să fi folosit toate 3), se poate ca unii să fi folosit într-un loc numele loc scris cu liniuță și altundeva fără. Uneori se poate să apară mici typouri în liste. We'll do our best to match them all.
Ideea este următoarea: verificăm pentru fiecare nume din lista existentă (a notelor de anul trecut) dacă nu cumva este o submulțime a numelui curent sau invers. De exemplu „Tudor Ion” este submulțime a lui „Ion Tudor Gigel”. Apoi verificăm dacă primul nume (probabil cel de familie) este la fel. Dacă da, am găsit cu mare probabilitate numele din lista veche și îl returnăm. Cu asta am cam rezolvat cazul cu 2 nume din 3 într-una din liste și altfel în cealaltă.
Apoi ne folosim de librăria difflib
, care ne furnizează cel mai apropiat nume de cel curent, cu o similaritate mai mare decât 0.85 (valoare stabilită empiric astfel încât să nu avem rezultate greșite :D). Cu aceasta am rezolvat problema de typo-uri simple. Toate celelalte cazuri trebuiesc rezolvate în mod diferit, în funcție de unde apelăm, așa că returnăm None
și în funcția apelantă vom rezolva cum trebuie problema. La BD, considerăm că cei care nu au fost găsiți pe listă sunt nou veniți, așa că îi adăugăm la dicționar și doar apoi le adăugăm nota.
import difflib,re
def get_nume(nume):
if nume == '':
return None
if nume in note:
return nume
else:
nume_l = re.split('[ -]', nume)
loop = [" ".join(nume_l)]
for n in sorted(note.keys()):
split_n = list(filter(bool,re.split('[ -]',n)))
if set(nume_l) <= set(split_n) or set(split_n) < set(nume_l):
loop = [" ".join(split_n)," ".join(nume_l)]
break
if len(loop) > 1 and loop[0].split()[0] == loop[1].split()[0]:
nume_l = loop[0]
else:
closest = difflib.get_close_matches(nume,note.keys(),n=1,cutoff = 0.85)
if len(closest):
nume_l = closest[0]
else:
return None
return nume_l
Next up: PS. bangs head against desk. Notele de la 4 grupe sunt în format PDF, iar de la celelalte două în format doc. Cu ajutorul unor unelte online, am reușit să transform PDF-urile în Word, de acolo să scot tabelele în Excel și să salvez în format CSV. Grea îi viața de data miner.
file = 'C:\\Users\\Roland\\Documents\\Statistici\\ps.csv'
with open(file) as fisier:
reader = csv.DictReader(fisier)
for row in reader:
if row['Nume'] == '':
continue
nume = capitalize_remove_father_name(row['Nume'])
nume = get_nume(nume)
if not nume:
print('PS',row['Nume'])
continue
if 'PS' in note[nume]:
print("erreur PS",nume,row['Nume'])
try:
nota = int(row['Nota'])
except:
nota = 0
note[nume]['PS'] = row['Nota']
erreur PS Iovanescu Victor IOVANESCU VICTOR PS POP REMUS DAN
Avem un student lipsă și unul care apare de două ori (desigur, fără notă de fiecare dată). Studentul lipsă nu mai apare niciunde altundeva, așa că îl vom lăsa pe dinafară, mai ales că nici nu are notă.
Ultimul pas este de a adăuga notele la PLF. Notele sunt într-un tabel HTML, pe care îl putem copia ușor în Excel, păstrăm doar coloanele cu grupa, nume și nota finală și apoi salvăm în format CSV. În rest procedăm la fel ca mai înainte.
file = 'C:\\Users\\Roland\\Documents\\Statistici\\plf.csv'
with open(file) as fisier:
reader = csv.DictReader(fisier)
for row in reader:
if row['Nume prenume'] == '':
continue
nume = get_nume(row['Nume prenume'])
if not nume:
print('PLF',row['Nume prenume'])
continue
if 'PLF' in note[nume]:
print("erreur PLF",nume,row['Nume prenume'])
try:
nota = int(row['Nota'])
except:
nota = 0
note[nume]['PLF'] = nota
PLF Mircea Ionel erreur PLF Muntean Liviu Muntean Radu-Liviu
Să vedem acuma un mic sample din cum ne arată notele și apoi se ne uităm câte note avem pentru fiecare student, ca să ne verificăm un pic.
print(list(note.values())[30:40])
[{'BD': 4, 'POO': '8', 'Grupa': 'Gr. 215', 'Geometrie': '6', 'PLF': 6, 'SdA': '5', 'Nota AC': '6', 'PS': '6', 'Nota FP': '6', 'Grupa 2': '225', 'Nota algebra': '4'}, {'BD': 9, 'POO': '9', 'Grupa': 'Gr. 213', 'Geometrie': '9', 'PLF': 4, 'SdA': '6', 'Nota AC': '9', 'PS': '9', 'Nota FP': '8', 'Grupa 2': '223', 'Nota algebra': '6'}, {'BD': 7, 'POO': '7', 'Grupa': 'Gr. 211', 'Geometrie': '10', 'PLF': 8, 'SdA': '5', 'Nota AC': '8', 'PS': '9', 'Nota FP': '10', 'Grupa 2': '221', 'Nota algebra': '5'}, {'BD': 9, 'POO': '9', 'Grupa': 'Gr. 211', 'Geometrie': '10', 'PLF': 10, 'SdA': '8', 'Nota AC': '9', 'PS': '8', 'Nota FP': '8', 'Grupa 2': '221', 'Nota algebra': '8'}, {'BD': 10, 'POO': '10', 'Grupa': 'Gr. 211', 'Geometrie': '10', 'PLF': 10, 'SdA': '9', 'Nota AC': '10', 'PS': '9', 'Nota FP': '8', 'Grupa 2': '221', 'Nota algebra': '10'}, {'BD': 0, 'POO': 0, 'Grupa': 'Gr. 212', 'Geometrie': '8', 'PLF': 4, 'SdA': '4', 'Nota AC': '4', 'PS': 0, 'Nota FP': '5', 'Grupa 2': '222', 'Nota algebra': '3'}, {'PS': 0, 'Nota algebra': 0, 'Geometrie': 0, 'PLF': 0, 'SdA': 0, 'Nota AC': 0, 'POO': 0, 'Nota FP': 0, 'Grupa 2': '225', 'BD': 0}, {'BD': 0, 'POO': 0, 'Grupa': 'Gr. 211', 'Geometrie': 0, 'PLF': 0, 'SdA': 0, 'Nota AC': 0, 'PS': 0, 'Nota FP': 0, 'Nota algebra': 0}, {'BD': 7, 'POO': '6', 'Grupa': 'Gr. 212', 'Geometrie': '10', 'PLF': 5, 'SdA': '4', 'Nota AC': '4', 'PS': '9', 'Nota FP': '6', 'Grupa 2': '222', 'Nota algebra': '6'}, {'BD': 0, 'POO': 0, 'Grupa': 'Gr. 216', 'Geometrie': 0, 'PLF': 0, 'SdA': 0, 'Nota AC': 0, 'PS': 0, 'Nota FP': '0', 'Nota algebra': 0}]
for key in note.keys():
if len(note[key]) < 7:
print(key,note[key])
Catana Laurentiu Tudor {'PS': '4', 'BD': 4, 'Grupa 2': '225'} Iovanescu Victor {'PLF': 4, 'PS': '', 'BD': 0, 'Grupa 2': '225'} Mate Adrian George {'PS': 'Absent', 'BD': 0, 'Grupa 2': '225'} Oprea Madalina {'PLF': 4, 'PS': '4', 'BD': 5, 'Grupa 2': '225'} Florea Calin {'PLF': 6, 'PS': 'Absent', 'BD': 4, 'Grupa 2': '225'} Rosu Ionut Vladut {'PS': '', 'BD': 0, 'Grupa 2': '226'} Precup Ramona Cosmina {'PLF': 4, 'PS': '4', 'BD': 4, 'Grupa 2': '223'} Fedorov Elisabeta {'PS': '', 'BD': 0, 'Grupa 2': '226'} Purcel Lucian Calin {'PS': 'Absent', 'BD': 0, 'Grupa 2': '224'} Hoza Cristian Gabriel {'PLF': 4, 'PS': 'Absent', 'BD': 0, 'Grupa 2': '225'}
După cum ne așteptam, aceștia sunt colegi noi (mai puțin Mădă, care și-o schimbat numele :D)
Tot ce mai rămâne de făcut acuma este să scriem într-un fișier toate acestea, mai verificând o dată că scriem numere pentru note și nu alteceva.
file = 'C:\\Users\\Roland\\Documents\\Statistici\\grand.csv'
with open(file,"w") as fisier:
header = ['Grupa', 'Grupa 2', 'Nota algebra', 'Nota AC', 'Nota FP', 'SdA', 'Geometrie', 'POO', 'BD', 'PLF', 'PS']
writer = csv.DictWriter(fisier, fieldnames=header)
writer.writeheader()
for row in note:
for materie in header[2:]:
if materie not in note[row]:
note[row][materie] = 0
try:
int(note[row][materie])
except ValueError:
note[row][materie] = 0
writer.writerow(note[row])
A sample of the grand result:
Perfect. Gata amestecate, numai bune de analizat.