momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Un calcul de medii, intermediat de Python

Python | situații școlare
2012 dec

Să zicem că într-un text avem câte o secvenţă de medii - în ordinea obiectelor din catalogul şcolar - pentru fiecare elev:

Popa Dan 7.67 8.67 9 10 9.5 10 10 10 8.67 7.67 8.67 9 10 9 10 10
Albu Diana Ioana 8 9 10 6.67 10 10 10 10 7.67 8.67 9 10 6.67 10 10 10
...

Ar fi de calculat mediile generale pe orizontale (pentru fiecare elev) şi pe verticale (pentru fiecare obiect); "salvăm" interesul pentru acest calcul (altfel, banal) invocând faptul că rezultatele trebuie deservite de un site constituit cu Python.

În Python putem folosi funcţia sum(listă_de_numere[, start]), obţinând media astfel:

vb@vb:~$ python
>>> l_nr = [7.67, 8.67, 9, 10, 9.5]
>>> sum(l_nr) / len(l_nr)
8.968

Textul de forma redată mai sus trebuie prelucrat pentru a obţine listele de numere; folosim modulul Python re pentru a extrage subşirurile corespunzătoare mediilor şi funcţia float pentru a transforma aceste subşiruri în numere - ca în exemplul următor:

>>> import re
>>> text = "Popa Dan 7.67 8.67 9 10 9.5"
>>> l_nr = [float(sbs) for sbs in re.findall(r"(\d+\.*\d*)", text)]
>>> print l_nr
[7.67, 8.67, 9.0, 10.0, 9.5]

Şablonul "(\d+\.*\d*)" furnizat metodei re.findall() extrage din text secvenţele nevide de cifre ("\d+": cel puţin un "digit"), precum şi pe acelea care încep cu cifre şi sunt eventual urmate de punct zecimal ("\.") şi alte cifre ("*": zero sau mai multe apariţii ale caracterului precedent).

În prealabil, textul va trebui împărţit pe rânduri; folosim în acest scop metoda split(separator), cu separator='\n' (caracterul "end-line"):

>>> text = """Popa Dan 7.67 8.67 9 10 9.5
... Albu Diana Ioana 8 9 10 6.67 10
... """
>>> text.split('\n')
['Popa Dan 7.67 8.67 9 10 9.5', 'Albu Diana Ioana 8 9 10 6.67 10', '']

""" delimitează un şir care este preluat ca atare (fără vreo interpretare). La sfârşitul listei rezultate avem şi un şir vid, pentru că marcatorul final """ nu urmează imediat după încheierea ultimului rând, ci după "Enter"; putem elimina ultimul element din listă folosind text.split('\n')[:-1].

Cu aceste precizări, putem constitui următorul program:

# -*- coding: utf-8 -*-
import re

medii_elevi = """Popa Dan 7.67 8.67 9 10 9.5 10 10 10 8.67 7.67 8.67 9 10 9 10 10
Albu Diana Ioana 8 9 10 6.67 10 10 10 10 7.67 8.67 9 10 6.67 10 10 10
Popa Dan 7.67 8.67 9 10 9.5 10 10 10 8.67 7.67 8.67 9 10 9 10 10
Albu Diana Ioana 8 9 10 6.67 10 10 10 10 7.67 8.67 9 10 6.67 10 10 10
""" # COPY-PASTE de 10 ori, pentru a testa pe un tabel mai mare de date

def medii_generale(text_medii):
    _medii = text_medii.split('\n')[:-1]
    list_medii = [[float(sbs) 
                   for sbs in re.findall(r"(\d+\.*\d*)", _med)] 
                  for _med in _medii]
    n = len(_medii) # nr. elevi
    k = len(list_medii[0]) # nr. obiecte
    mg_elev = [round(sum(_m)/k, 3) for _m in list_medii]
    mg_obiect = [round(sum([_m[i] for _m in list_medii])/n, 3) 
                 for i in range(k)]
    return mg_elev, mg_obiect
    
print medii_generale(medii_elevi)

Obţinem lista mediilor generale ale elevilor şi lista mediilor generale pe obiecte:

vb@vb:~ python medii-test.py 
([9.241, 9.105, 9.241, 9.105], 
[7.835, 8.835, 9.5, 8.335, 9.75, 10.0, 10.0, 10.0, 8.17, 
 8.17, 8.835, 9.5, 8.335, 9.5, 10.0, 10.0])

O funcţie ca aceea pusă la punct prin programul de mai sus, ne-a servit într-o aplicaţie de reflectare dinamică a situaţiei şcolare pe clasele de elevi: la cerere, pe baza notelor curent înregistrate la diversele obiecte pentru elevii respectivi - se calculează mediile (pe obiect, elev, arii curriculare, clasă) şi se ambalează în răspuns statisticile rezultate în acel moment .

Tocmai caracterul dinamic sugerat mai sus, face preferabilă constituirea şi păstrarea în baza de date a unor texte de medii precum cel exemplificat la început, faţă de o modelare (obişnuită) de genul: (id_elev, id_obiect, medie_1, medie_2) - caz în care pentru fiecare elev (şi la oricare cerere) ar trebui accesate şi updatate în baza de date un număr mare de înregistrări.

Termenul "oricare" suportă aici o anumită relativizare: contul de "diriginte" al clasei decide momentele (de exemplu, la sfârşitul unei luni) în care se calculează şi se înscriu în baza de date "textele de medii" curente; conturile de "elev" doar vor accesa rezultatele deja înscrise. Desigur, după încheierea semestrului curent, "textele de medii" rămân definitiv în baza de date, urmând să fie completate (iarăşi, în "orice" moment) cu datele proprii viitorului ciclu şcolar.

vezi Cărţile mele (de programare)

docerpro | Prev | Next