Sunteți pe pagina 1din 632

Python & Librării

Fundamentele utilizarii limbajului Python

In acest curs NU este necesar sa aveti cunostinte de


programare
Scopul cursului

Transformăm datele în informație

Accent pe analiza și generarea rezultatelor

Moduri diferite de vizualizare

INCREZĂTOR și COMPETENT !
Materiale

platforma Moodle

http://fim.curs.pub.ro/2021
InformaticăAplicată2
Structura cursului

Saptamanile 1...6 curs si laborator Introducere in PYTHON

Saptamana 7 – Verificare laborator PYTHON

Saptamanile 8...13 curs si laborator PROCESARE DATE (PD)

Saptamana 14 – Verificare laborator PD

Ultimul curs – Colocviu final


Ce se asteapta de la voi


Prezenta obligatorie la laborator


NU se fac recuperari la laborator.

Prezenta obligatorie la:

Verificarea practica laborator PYTHON

Verificarea practica laborator PROCESARE DATE


Calculul Notei Finale

30% din nota Verificare practica laborator PYTHON

30% din nota Verificare practica laborator PROCESARE DATE

20% din nota teste/teme

20% din nota Colocviu final

Cerinte minimale pentru promovare:


-acumularea min 50% din fiecare verificare practică de laborator
-promovarea cu punctajul acumulat 5.00 (nu se rotunjeste 4.50)
Curs 1

Limbajul PYTHON - Avantaje


Mediul Ipython
Librarii
Scopul intregului curs

Încrezător și competent!
Cum să scriem cod

Cum să citim/interpretam cod

Ce pot și ce nu pot face dpdv al calculelor

Translatarea problemelor științifice la calculul științific


Calcul Științific
ȘTIINȚA
Teoretică
Experimentală

ȘTIINȚA
Teotetică (Calcul Științific)
Experimentală (Calcul Științific)
Calcul Științific

In cele mai multe domenii ale științei pe langa partea de calcul


numeric sunt necesare diverse simulări sau modelari pe calculator.

Limbajele de programare PYTHON, R și MATLAB sunt folosite în


mediul academic pentru cercetare și calcul știiințific.
Calcul Stiintific
În mediul științinfic/academic rezultatele și metodele utilizate sunt publicate
și puse la dispoziția altor oameni de știință, iar toate datele experimentale
trebuie să fie disponibile la cerere.

Se consideră ne științific procedura de a reține detaliile cruciale într-o probă


teoretică sau o metodă experimentală, care ar putea împiedica al ți oameni de
știință de a replica și reproduce rezultatele.

În științele computaționale nu există încă reguli bine stabilite pentru modul în


care ar trebui să fie manipulat codul sursă și datele generate.

Codul sursă pentru software-ul de simulare este reținut și considerat un


avantaj competitiv (sau inutil să publice).

Unele reviste de prestigiu au început să ceară autorilor să furnizeze codul sursă


pentru software-ul de simulare utilizat în publicații, la cerere.

The case for open computer programs, D.C. Ince, Nature 482, 485-488 (2012).
https://www.nature.com/nature/volumes/482/issues/7386
Calcul Științific
Rezultatele științifice trebuie să fie:

Reproduse: trebuie să fie posibilă reproducerea rezultatelor obținute în urma


simulărilor numerice aplicând o altă metodă independentă, sau utilizând o altă
metodă cu totul nouă.

Replicabile: aceleași calculele numerice/simulările ar trebui să poată fi rulate


de alti oameni de știință și să se obțină aceleași rezultate, urmând metodele
specificate într-o publicație.

Trebuie:

-să se păstreze mediul/codul sursă/versiunea softwar-elor externe care au fost


utilizate pentru producerea datelor numerice/ simularilor în lucrările publicate.

-să se ofere informații suplimentare cu privire la metodele utilizate, și, probabil,


de asemenea, codurile de simulare, la un cititor interesat care solicită acest lucru
(chiar și după ani lucrarea a fost publicată!).
Calcul Științific

Ce presupune cercetarea științifică ?

Să se obțină date ( din simulări, din experimente de control)

Să se manipuleze și să se proceseze datele

Să se vizualizeze rezultatele (pentru a înțelege ceea ce facem!)

Să se comunice rezultatele: rapoarte, publicații, să se scrie prezentări.


De ce Python ?
De ce Python ?
De ce Python ?

Python foarte asemanator cu orice limbaj de vorbire

Exemplu:

grup = ['Ion', 'Elena', 'Angela', 'Ilie']


for prieten in grup:
print("Buna" + prieten)

In Python usor de inteles concepte fundamentale ale


programarii : structuri de date si algoritmi
De ce Python ?
Python
for i in range(10):
print("Hello.World!")

Bash
for in in {1..10}; do
echo Hello, World!
done

PowerShell
for ( $i=1; $i –le 10; $i++) {
Write-Host "Hello, World!"
}
De ce PYTHON pentru Calculul Științific ?
• Comunitate mare de utilizatori, multă documentație
• Vast ecosistem de biblioteci științifice/pachete/medii:
NumPy: http://numpy.scipy.org –Numerical Python
SciPy: http://www.scipy.org - Scientific Python
Matplotlib: http://www.matplotlib.org - Grafică
Pandas – http://pandas.pydata.org/ - Analiza Datelor

• Performanță mare datorită integrării cu coduri optimizate scrise în


C / C++ și Fortran: blas, atlas blas, LAPACK, arpack, Intel MKL, …

•Suport bun pentru procesare paralelă cu procese și threads:


comunicare interprocese (MPI), GPU de calcul (OpenCL si CUDA)

•Adecvat pentru utilizarea în calcule de înaltă performanță - clustere.

•Nu există costuri de licență -perfect atât pentru aplicații industriale cât și
pentru cercetarea academică.
ISTORIA PYTHON
•Python a fost creat de Guido van Rossum la sfârșitul anilor 1980, la Institutul
Național de Cercetare pentru Matematică și Informatică în Țările de Jos. La
fel ca Perl, codul sursă Python este disponibil sub licența GNU General
Public License (GPL).

• Python este un limbaj de programare de scop general , de nivel înalt,


interpretat, de scripting orientat pe obiecte și interactiv.

• Inca din 1991 s-au dezvoltat mai multe versiuni disponibile pentru
WINDOWS, UNIX, și Mac OS .
Python 2.4, 2.6, 2.7 …(2000)… Python …. 3.7.11 (2020)
Ultima versiunea Python 3.8.12 – 3 Mai 2021
https://www.python.org/downloads/

Verificati : Windows PC : in cmd.exe C: \Users\>python –version


Mac sau Linux: in Terminal python --version
ISTORIA PYTHON

Python are multe module/biblioteci care sunt open-source si gratuite:

Statistica, calcule numerice de optimizare care implică, algebra liniară,


integrare, interpolare, precum și alte funcții speciale folosind obiecte,
lucru cu matrici, machine learning, data mining, desenare 2D, 3D,
dezvoltare web, inteligenta artificiala, etc

Aceste module/biblioteci au un anumit scop și joacă un rol important în


cercetarea în domenii diverse: economie, finanțe, științe biologice, știin țe
sociale, sănătate, și multe altele.
ISTORIA PYTHON

Python are variate implementări:

Jython, scris in limbajul Java pentru Java Virtual Machine

IronPython scris in C# pentru Common Language Infrastructure

PyPy scris in RPython si translatat in C

Aceste implementări funcționează în limbajul în care au fost scrise, dar sunt


capabile să interacționeze cu alte limbaje prin utilizarea modulelor.

Majoritatea acestor module funcționează pe modelul de dezvoltare


comunitară, sunt open-source și gratuite.
Istoria PYTHON si status-ul curent
Puteti citi despre istoria Python:

Istoria Python- https://en.wikipedia.org/wiki/History_of_Python


Istoria sofrwares https://docs.python.org/3.0/license.html

Python a fost numit recent limbajul de programare cu


Click to add text
cea mai rapidă creștere,puteți afla mai multe în aceste articole:

https://stackoverflow.blog/2017/09/06/incredible-growth-python/

https://www.netguru.com/blog/future-of-python

https://insights.stackoverflow.com/survey/2018#technology
Ce este PYTHON ?
• Python este un limbaj de programare de
nivel înalt, interpretat, interactiv,
scripting orientat pe obiecte, și de scop
general.
• Python este interpretat: codul este
procesat în timpul rulării de către un
interpretor, nu este nevoie să compilați
programul înainte de a-l executa - similar
cu PERL și PHP.
• Python este interactiv: puteți sta la un
Python prompt și să interacționați în mod
direct pentru a scrie programe, ușor de
testat si depanat.
• Python este orientat-obiect: Python acceptă
stilul orientat pe obiect sau tehnica de
programare care incapsuleaza codul în
interiorul obiectelor.
Caracteristicile PYTHON
• Python este portabil: rulează pe o varietate de platforme hardware și are
aceeași interfață pe Windows,UNIX și Macintosh OS.

• Python este extensibil: se pot adăuga module de nivel jos pentru interpretor,
module ce permit adăugarea, personalizarea sau eficientizarea diverselor
instrumentele.

• Python este DataBaseAware: oferă interfețe pentru toate bazele de date


comerciale importante.

• Python are Programare GUI: suportă aplicații GUI, care pot fi create și
portate, are multe apeluri de sistem, biblioteci și sisteme de ferestre pentru
Windows MFC, Macintosh și sistemul X Window of Unix.

• Python este scalabil: oferă o structură mai bună și suport pentru programe
mai mari decât shell scripting.
Caracteristicile limbajului PYTHON ?
• Python este ușor de învățat: puține cuvinte cheie, structură simplă/clară și cu
sintaxa minimalistă, perioadă scurtă de învățare:
Tipuri de date nivel înalt: siruri de caractere, liste, dic ționare, etc.
Structurile obișnuite de control: if-else, if-elif-else, while, plus un for
iterator

• Python este intuitiv și ușor de citit: dacă este bine scris e simplu de citit și de
înțeles.

• Python este expresiv, ușor de întreținut: mai puține linii de cod, mai puține
bug-uri, mai ușor de întreținut- mentenabilităte foarte bună cu dimensiunea
proiectelor.

• Python poate fi folosit ca un limbaj de scripting sau pot fi compilat octet-cod


pentru construirea de aplicații mari. Suporta Garbage Colection automat.
Se integreaza usor cu C, Java, R, Julia, Haskell
Avantaje PYTHON
• Avantajul principal este ușurința de programare, minimizarea timpului necesar
pentru dezvoltare, depanare și menținerea codului.

• Limbaj bine conceput, încurajează multe bune practici de programare:


modular și orientat pe obiecte, sistem bun pentru ambalarea și reutilizarea
codului, cod mult mai transparent

• Documentație strâns integrată cu codul

• O maaaare bibliotecă standard care include protocoale de internet, operatii pe


stringuri, instrumente pentru servicii web si interfate cu sistemul de operare

• Python Package Index (PyPI) contine numeroase modules care faciliteaza


interactiunea dintre Python si alte limbaje/platforme de programare
Dezavantaje PYTHON

• Python este un limbaj interpretat, dinamic- executarea codului Python poate fi


lentă în comparație cu limbajele de programare compilate, C și Fortran.

• Python este GNU General Public License (GPL) -oarecum descentralizat, cu


mediu diferit, pachete și documentație răspândită în locuri diferite.

• Python poate fi mai greu la început.


PAUZA ....revenim in 10 min
PYTHON Interpretor
Dacă aveți Python instalat pe computer, puteți executa interpretorul

Interpretorul Python este un program care citește și executa codul


Python în fișierele transmise acestuia ca argumente.

De exemplu, pentru a rula un fișier


test.py care conține codul Python in
linia de comandă, scrieți :
python test.py

De asemenea, putem apela


interpretorul pur și simplu prin
tastarea >>>python (la linia de
comandă) și în mod interactiv scriem
cod Python în interpretor.
Instalare
Există diferite moduri de a instala Python, dar distribuții specializate
au fost create/pre-construite cu diferite module specifice pentru
comunitatea științifică, care funcționează similar indiferent dacă
utilizațiWindows, Linux sau Mac OS X.

• Miniconda vă oferă interpretul Python împreună cu o linie de


comandă numită conda care funcționează ca un manager
multiplataforma de pachete Python
https://docs.conda.io/en/latest/miniconda.html
• Anaconda include atât Python, cât și conda și, în plus include o
suită de alte pachete preinstalate orientate spre calculul științific (să
vă așteptați ca instalarea să consume mulți gigaocteți de spaţiu pe
disc)
https://www.anaconda.org
Instalare
Oricare din pachetele incluse în Anaconda poate fi instalat manual
deasupra în Miniconda.

Descărcați și instalați pachetul Miniconda (alegeți versiunea cu


Python 3)
https://docs.conda.io/en/latest/miniconda.html

Apoi instalați pachetele de bază prin tastarea comenzii cu sintaxa:


conda install numele pachetului
Exemplu:
$ conda install numpy pandas scikit-learn matplotlib seaborn
ipython-notebook

Pentru mai multe informații despre conda, inclusiv informații despre


crearea și folosirea mediilor conda consultați documentația online:
https://conda.io/en/latest/
Medii de dezvoltare - PYTHON

Integrated Development Environments (IDEs) - medii diferite de


dezvoltare prin care interpretorul Python poate fi folosit:
• PythonXY
• PyCharm
• IDLE - offline editor
• Trinket – online editor https://docs.trinket.io
• IPython
• IPython notebook / Jupyter notebook
• Spyder

Ideal de folosit IPython plus un editor de text, ceea ce vine integrat


in distribuția Anaconda (https://www.anaconda.org)
IPython shell
IPython (Interactive Python ) - Fernando Perez 2001
“Tools for the entire lifecycle of research computing.”
IPython este interpretorului Python îmbunătățit foarte mult.
Python - motorul, IPython - un control panel interactiv
Ipython Notebook - Jupyter Notebook
Jupyter Notebook - a browser-based graphical interface to the
IPython shell - util în dezvoltare, colaborare, partajarea și chiar
publicarea rezultatelor din comunitatea științifică.

Notebook-ul permite utilizatorului să introducă text formatat și


vizualizări dinamice, ecuații matematice, widget-uri JavaScript, etc
Aceste documente pot fi salvate într-un mod care permite altor
utilizatori să le deschidă și să execute codul în propriile sisteme.

Notebook-ul IPython este de fapt un caz special al structurii mai


largi a notebook-ului Jupyter, care cuprinde notebook-uri pentru
Julia, R și alte limbaje de programare.

IPython shell sau IPython notebook


IPython shell
Lansați interfeța IPython prin tastarea ipython pe linia de comandă

gabi@koko:~$ ipython
Python 3.8.8 (default, Apr 13 2021, 19:58:26)
Type 'copyright', 'credits' or 'license' for more
information
IPython 7.26.0 -- An enhanced Interactive Python.
Type '?' for help.

In [1]:

Dacă ați instalat distribuția Anaconda, doar selectati IPython


Ipython shell
Câteva din caracteristicile utile din IPython:
Istoricul comenzilor - folosind săgețile în sus și în jos de pe
tastatură

Tasta TAB – auto-completarea aproape pe orice, nu numai pe


obiecte, nume de funcții sau comenzi python

Extragerea automata a documentației Python, obiecte, clase și


funcții – folosind punct . ? sau ??

Interacțiunea cu sistemul de operare prin intermediul comenzilor


precedate de % și de !

Suport pentru mai multe procese de back-end paralele, care pot


rula pe clustere sau de servicii de tip cloud - Amazon EE2.
Jupyter Notebook
Jupyter Notebook este vizualizat și editat prin browserul web care
va trebui să se conecteze la un proces Python care va rula si
executa codul -acest proces este cunoscut sub numele de
„kernel”
Lansați interfeța prin tastarea in linia de comandă :
$ jupyter notebook
[NotebookApp] Serving notebooks from local
directory: /Users/jakevdp/...
[NotebookApp] 0 active kernels
[NotebookApp] The IPython Notebook is running at:
http://localhost:8888/
[NotebookApp] Use Control-C to stop this server
and shut down all kernels...

Dacă ați instalat distribuția Anaconda, doar selectati Jupyter


Jupyter Notebook
Spyder
Spyder este un IDE Python pentru calcul științific asemănător cu
MATLAB-ul - totul, de la editare de cod, execuție și depanare se
efectuează într-un singur mediu de lucru:
-editor de cod puternic, cu sintaxa high-lighting, introspectia
codulului dinamică și integrarea cu debugger-ul Python.
explorarea variabilelor, comanda prompt IPython.
- documentație integrată.
Versiunea Python2 sau Python3 ?
Python 3 a apărut în 2008, versiunea finala Python 2.7 în 2010
astfel vesiunile Python 3.0 și 3.1 au fost portate și în versiunile 2.6
și 2.7

O mulțime de cod existent și pachete au fost scrise pentru Python2,


Python2 este în continuare versiunea cea mai răspândită.

Majoritatea comunitații stiințifice nu a migrat incă la Python3.

Mai multe versiuni de Python pot fi instalate în paralel !

Exista convertoare 2to3 (code 3.x să fie generat din code 2.x) și
3to2 convertește code3.x la code2.x

https://wiki.python.org/moin/Python2orPython3
https://docs.python.org/3/whatsnew/3.0.html
Python3
Îmbunătățiri pentru a face limbajul mai clar și mai consistent:
Funcția print a fost înlocuită cu print()

Python2: print "Răspunsul este", 2*2


Python3: print("Răspunsul este", 2*2)

Python2: print x, # virgula suspenda crearea unei linii noi


Python3: print(x, end=" ") # virgula apendează un spațiu

Python2: print "A\n", "B" va printa "A\nB\n"


Python3: print("A\n", "B") writes "A\n spatiu B\n"

Python2: print >>sys.stderr, "fatal error"


Python3: print("fatal error", file=sys.stderr)

Python3:print("Sunt <", 2**32, "> posibilități!", sep="") va printa:


Sunt <4294967296> posibilități!
Python3
Exemplu:
Python3: functia print() permite ca argumente cuvintele cheie sep
și end, pentru a specifica ce „tip de string” e folosit pentru a
separa argumentele sau a termina linia printată

Aceste „stringuri” sunt:


""– spațiu (default)
"\n" o linie noua

Această caracteristică face posibilă printarea mai multor valori, pe


linii separate cu un singur apel de funcție:

print(x, y, z, sep=”\n”)

Următoarea comanda cum va printa?


print("Sunt <", 2**3, "> posibilități!")
Built-in types: Python3
Python2: ambiguitatea “poate fi text, pot fi date arbitrare binare”-
nu există un tip de caracter, astfel încăt caracterele sa poată fi
reprezentate pur și simplu ca șiruri de lungime 1.
type str (utilizat, derutant, pentru ambele șiruri de caractere ASCII și
șiruri arbitrare de octeți )
type unicode (ptr siruri de caractere compus din caractere Unicode).

Python3:
type str (comportamentul și punerea în aplicare a tipului unicode
vechi).
type bytes (o secvență de numere întregi între 0 și 255 inclusiv,
conține întotdeauna date binare arbitrare)
Tipurile str și bytes nu pot fi amestecate! Trebuie convertite între ele!
str.encode() - de la str la bytes str(b, encoding=...)
bytes.decode() de la bytes la str. bytes(s, encoding=...)
Python3
Comparații clare și curate:

Python2: permite compararea tipurilor diferite folosind <, >, etc:


>>> 42 < 'hello'
True

De ce se evalueaza la True ? Trebuia să fie evaluata ?

Python3: astfel de operații dau excepții


>>> 42 < 'hello'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()

Python2: <> # operator pentru „nu sunt egale” sau „sunt diferite”
Python3: != # operator pentru „nu sunt egale” sau „sunt diferite”
Python3
Operatorul de împărțire:

Python2: la fel ca în Java, C sau C++ , rezultatul e cel al funcției


'floor' când împărțim doi intregi:
>>> 1 / 2
0

Python3: împărțirea a două nr intregi returneaza o valoare reală:


>>> 1 / 2
0.5

Python3: 1 // 2
0
# operator // pentru împarțirea cu rezultatul funcției 'floor'
NumPy
NumPy- Numerical Python și SciPy - Scientific Python sunt
pachetele fundamentale folosite în calculul științific.

NumPy printre altele conține:


• Obiectul matrice multidimensionala – ndarray modalitate mult
mai eficientă de stocare și manipulare a datelor decât celelalte
structuri de date built-in Python
• Funcții pentru efectuarea de calcule matematice element-wise sau
pe matrice, operații între matrici
• Instrumente pentru citirea și scrierea seturilor de date bazate pe
matrice pe disc
• Operații de algebră liniară, transformate Fourier, și generarea de
numere aleatoare
• Instrumente pentru integrarea cu limbajele C, C ++, Fortran –
• Bibliotecile scrise în C sau Fortran pot opera pe datele stocate
într-o matrice NumPy fără a copia date.
SciPy

SciPy extinde pachetul NumPy cu o colecție de algoritmi și tehnici


matematice aplicate.
• scipy.integrate: rutine de integrare numerică și rezolvarea
ecuațiilor diferențiale
• scipy.linalg: rutine de algebră liniară și descompuneri matriceale
• scipy.optimize: algoritmi de optimizare (minimizare)
• scipy.signal: instrumente de prelucrare a semnalului
• scipy.sparse: rutine ptr matrici rare și de rezolvarea sistemelor
liniare rare
• scipy.stats: distribuții de probabiliăți continue și discrete (funcții
de densitate, samplere, funcțiile de distribuție continuă), diferite
teste statistice, statistică descriptivă
• scipy.weave: instrument pentru utilizarea inlinie a codului C ++
pentru accelerarea calculelor matriciale
Pandas

Pandas - panel data (panoul de date) un termen din econometrie


folosit pentru seturile de date structurate multidimensional

Pandas ofera structuri de date bogate și funcții concepute pentru a


face rapid și ușor analiza datelor.

Pandas combină caracteristicile de calcul matriciale de înaltă


performanță din NumPy cu capabilitățile de manipulare a datelor,
spreadsheets și a bazelor de date relaționale ( SQL)- oferă
funcționalități de indexare sofisticate, remodelare, agregări și
selectări de subseturi de date.

DataFrame este structura de date bidimensională cu rânduri și


coloane etichetate.
MatPlotLib

Matplotlib este cea mai populară biblioteca Python pentru


vizualizarea datelor 2D.

A fost inițial creat de John D. Hunter (JDH) și acum este menținut


de către o echipă mare de dezvoltatori.

Matplotlib nu este singurul pachet pentru vizualizarea datelor în


Python - alte pachete sunt ggplot, ggplot2.

Mayavi : vizualizarea datelor 3D


http://code.enthought.com/projects/mayavi/
PYTHON Interpretor – on line
Puteți practica si folosind unul dintre numeroasele interpretoare sau
codepad-uri Python disponibile online.

Un interpretor este mai interactiv decât un codepad, dar ambele vă


permit să executați cod și să vedeți rezultatele.

Linkuri către cele mai populare interpretoare și codepad-uri online:

https://www.python.org/shell/
https://www.onlinegdb.com/online_python_interpreter
https://repl.it/languages/python3
https://www.tutorialspoint.com/execute_python3_online.php
https://rextester.com/l/python3_online_compiler
https://trinket.io/python3
Test

Folosind unul din interpretoarele on line:

1. Scrieți comanda Python care afișează pe ecran


Programez în Python!

Amintiți-vă că trebuie să utilizați funcția print () și să folosiți


ghilimele pentru a delimita șirul.

2 Scrieti comanda Python ptr a calcula (((1+2)*3)/4) la puterea


5
Trebuie să utilizați funcția print () și să folosiți atatea seturi de
paranteze cate aveti nevoie, iar ridicarea la putere se face cu **
Instalare

Distribuții specializate au fost create/pre-construite cu diferite


module specifice pentru comunitatea științifică, pentru Windows,
Unix, Mac OS :

•Anacona www.anaconda.org

Editoarele de text Spyder sau Scite sunt deja incorporate!

Veniti cu laptop-uri la laborator !


PEP - Python Enhancement Proposals

În Python, există sute de Python Enhancement Proposals, denumite


în mod obișnuit PEP.

PEP8 este un ghid stilistic standard pentru Python, scris chiar de


Guido van Rossum însuși.

PEP20, numit de obicei Zen-ul Python scris de Tim Peters sunt 20


de aforisme pentru a scrie cod Python stilistic și corect.
PEP20 – Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Bibliografie

Python: http://www.python.org/
Python Documentations: https://docs.python.org/3/
Python Tutorial https://docs.python.org/3/tutorial/
IPython: http://ipython.org/
Jupyter Notebook: https://jupyter.org/
NumPy: http://www.numpy.org/
SciPy: https://www.scipy.org/
Pandas: http://pandas.pydata.org/
Matplotlib -2D: http://matplotlib.org/
Mayavi - 3D:http://code.enthought.com/projects/mayavi/
Bibliografie

https://github.com/jrjohansson/scientific-python-
lectures/blob/master/Lecture-1-Introduction-to-Python-
Programming.ipynb

Think Python – How to Think like a Computer Scientist


Allen B. Downey, 2nd Edition ptr Python 3
http://greenteapress.com/wp/think-python-2e/
Curs 2

Limbajul PYTHON
Tipuri de date/Variabile
Expresii/Operatii
Programe Iterative
Important!

• Totul in Python este un Obiect !

• Orice entitate se creaza e un Obiect

• Principiul: Read Eval Print Loop


Valori si tipuri de date

Valorile(datele) sunt entitățile fundamentale (scalare sau non-scalare) pe


care programele/scripturile le manipulează:

Valori scalare (numerice)

Intregi (întregi cu semn ) - C precizie lungă – 4 bytes


Float (reale de virgulă mobilă) - C dublă precizie – 8 bytes
Complex (numere complexe) - C dublă precizie – 8 bytes

Valori non-scalare (sting-uri =siruri de caractere)

Valorile sunt clasificate în Tipuri de date (sunt asociate cu valorile):

type() - funcția built-in Python, întoarce tipul de date


Valori si tipuri de date

Datele stocate în memorie pot fi de mai multe tipuri.

Exemplu: vârsta persoanelor este stocată ca valoare numerică și adresa


este stocata ca string – șir de caractere.

• Python are tipuri de date standard, care sunt folosite pentru a defini
operațiile posibile pe ele și metodele pentru fiecare dintre ele.

• Python are cinci tipuri de date standard:


- Număr
- String
- Lista (date introduse între [], disponibile metode de liste )
- Tuple (valori separate de virgulă, acceptă tipuri de date diferite)
- Dicționar (secvențe neordonate cheie: valoare introduse între [])
Valori Numerice
Interpretor = evalueaza și printeaza
In[]: 4
In[]: 4 4 # eroare
In[]: 3.2 # pentru a reprezenta numere float nu se foloseste , ci .
In[]:type(3.2)
In[]:2 +6.5
In[]:complex(2,5)

Intr-o expresie care conține tipuri mixte de date Python convertește


numerele intern la un tip comun de evaluare.

Cateodata, va trebui să convertiți un număr în mod explicit de la un tip la


altul pentru a satisface cerințele unui parametru de funcție sau operator.
- int (x) pentru a converti x la un număr întreg simplu
- float (x) pentru a converti x la un număr în virgulă mobilă.
- complex(x, y) pentru a converti x și y la un număr complex
cu o parte x reală și imaginară partea y.
Built in Funcții Numerice

Python built in funcții numerice:

https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-
long-complex

Unele funcții trebuiesc adaugate prin declarațiile de import:

https://docs.python.org/3/library/math.html

Exemplu:
In[]:import math
In[]:x=[1,2,3,4,5]
In[]:math.fsum(x)
In[]:15.0
String- uri
Interpretor = evalueaza și printeaza

String-urile =colecție ordonată de caractere identificate ca un set


continuu de caractere scrise între ghilimele.

String-urile pot fi introduse între ghilimele: simple (') sau duble ("), sau
triple (asa ''' sau asa """)

String-urile sunt IMUTABILE ( nu se schimbă ordinea caracterelor)

In[]: type(‘3.2’)
In[]: print('''"Oh no", she exclaimed, "Ben's bike is broken!"''')

String-urile cu ghilimele triple se pot extinde pe mai multe linii:


In[]: print("""Acest mesaj
... se intinde pe mai
... multe linii"""
Built in Funcții pe String- uri

Datele de tip string sunt str și nu char ca în alte limbaje

Python include built in funcții pe string-uri:

https://docs.python.org/3/library/stdtypes.html#string-methods

Sau le putem vizualiza :


In[]: help(str)

Exemplu:
In[]: "George Vasilescu".upper()
In[]: ‘GEORGE VASILESCU’
Expresii
Trebuie să manipulăm date cât mai complexe, deci trebuie să
combinăm aceste tipuri de date și să creeam expresii:

Expresie – operand(obiect) operator operand (obiect)

>>4+5 #4 și 5 sunt operanzi și + se numește operatorul.

Python acceptă următoarele tipuri de operatori:

- Aritmetici
- Comparație
- Logici (sau relaționali)
- Atribuire
- Condiționare
Operatori Aritmetici
+ Adunare - adună valorile de pe fiecare parte a operatorului: a + b

- Scadere - scade valorile de pe fiecare parte a operatorului :a - b

* Multiplicare - înmulțește valorile din ambele părți : a * b

/ Diviziunea (float) - împarte valorile: a/b – rezultatul fiind float


In[]: 9/2 # rezultatul este 4 .5

// Diviziunea (întreg) - împarte valorile: a//b - rezultatul fiind întreg


In[]:9//2 # rezultatul este 4
In[]: 9.0//2.0 # rezultatul este 4

% Modulus - întoarce restul împarțirii lui b la a


In[]: 9%2 # rezultatul este 1

** Exponentul – ridicarea la putere calcul a ** b


In[]: 2**3 # rezultatul este 8
Operatori Aritmetici
Operator overloaded – depinde de tipul operanzilor (obiectelor) din expresie :

- numeric și string
In[]: 3*’abc’ # rezultatul va fi ‘abcabcabc’

- string+ string = concatenare


In[]: ‘a’ + ‘bcd’ # rezultatul va fi ‘abcd’

Care va fi valoarea acestei expresii?


In[]: 3+’abc’

Rețineți: Python face type checking! Trebuie să faceți conversie de tipuri!


In[]: str(3) + ‘abc’

Rețineți pentru stringuri : operatorul ( + ) este concatenare,


operatorul ( * ) este repetiție
Operatori de Comparație
== în cazul în care valorile celor doi operanzi sunt egale, atunci condiție
devine adevărată

! = dacă valorile celor doi operanzi nu sunt egale, atunci condiția devine
adevărată

> în cazul în care valoarea operandului stâng este mai mare decât
valoarea operandului din dreapta, atunci condiția devine adevărată

> = în cazul în care valoarea operandului stâng este mai mare sau egală
cu valoarea operandului din dreapta, atunci condiția devine adevărată.

< în cazul în care valoarea operandului din stânga este mai mică decât
valoarea din dreapta, atunci condiția devine adevărată.

<= în cazul în care valoarea operandului stâng este mai mică sau egală
cu cea a operandului din dreapta, atunci condiția devine adevărată
Reprezentarea Float point

Standardul IEEE 754 specifică modul științific de a reprezenta numerele


float point în calculator

Numerele float point nu sunt același lucru cu numerele reale!

Numerele float point sunt reprezentate ca o pereche : (mantisa,exponent)

1<= mantisa < 2


-1022 < exponent < 1023

64 biti = 1 bit semn, 11 exponent, 52 mantisa de unde rezultă că avem


17 digiti de precizie când reprezentăm numerele zecimale

Numerele raționale (fracțiile) și cele iraționale (radical) nu pot fi


reprezentate exact pe float
Reprezentarea Float point
1/8 = 0.125

baza 10 =1*10-3+ 2*10-2+5*10-1


baza 2 =1.0*2-3 adică reprezentarea 0.001

1/10
baza 10=1*10-1
baza2 = ? nu există un nr binar care să reprezinte aceasta fracție.

Dacă încercăm, ceea ce obținem este o serie ce se repetă la infinit


0011001100110011001100110011…
Dacă luăm arbitrar un nr de biti o să obținem doar o aproximare a nr
1/10

În Python există funcția built-in repr() - convertește reprezentarea


numerică în sting și o afișează. Ptr float se rotunjește la 17 digiti

Implicații ptr reprezentarea inexacta cand facem calcule complexe .


Reprezentarea Float point
In[]: s=0.0
In[]:for i in range(11):
In[]:… s+=0.1
In[]: s
In[]:1.099999999999 # erorile se acumulează

Atentie! Ce inseamna == pe floats


In[]: import math
In[]: a= math.sqrt(2)
In[]: a*a == 2
In[]: False # ceea ce am stocat in a e doar o aproximare a sqrt()
In[]: a*a
In[]: 2.0000000000004
Niciodată nu testam ptr egalitate pe floats, ci testam dacă sunt aproape
egale : abs(a*a-2.0) < epsilon ?

# Epsilon o valoare ffff mica proprie fiecărei aplicații


Operatori Logici
AND – dacă ambii operanzi sunt adevărați atunci condiția devine
adevărată.
In[]: True and True # True
In[]: True and False #False
In[]: False and True #False
In[]: False and False #False

OR - În cazul în care oricare dintre cei doi operanzi sunt non zero,
atunci condiția devine adevărată.
In[]: True or True # True
In[]: True or False # True
In[]: False or True # True
In[]: False or False #False

NOT -se utilizează pentru a inversarea logică a stării operandului. În


cazul în care o condiție este adevărată, atunci NOT o va face falsă și
viceversa
Operatori Logici
Orice variabilă evaluată la 0 sau la nimic ‘ ‘ are valoarea FALSE
Orice variabilă evaluată la ‘ceva ‘ are valoarea TRUE
Operatorii de comparație întotdeauna întorc TRUE sau FALSE

In[]: ‘a’< 3 – ce se va intampla? Care va fi raspunsul ?


In[]:False – deoarece se face comparația între codurile ASCII
( numerele sunt înaintea literelor)
Trebuie să vă faceți o regulă în a verifica tipurile de date astfel încât
operațiile pe care le faceți să fie corecte.
Există o ordine lexicografică cand combinam tipuri diferite (“o ordine a
simbolurilor “)
In[]: 4 <’3’ True
In[]: ‘4’ <3 dar acum ? False

Operatorii logici pot întoarce orice !


In[]: True and 50 50
In[]: False and 33 False
Ordinea operațiilor
Când avem mai mulți operatori într-o expresie folosim ordinea operațiilor :
PEMDAS
P (paranteze)E (exponent)MD(multiplicare,diviziune)AS(adunare scadere)

()

**

NOT , AND , OR

* ,/ , // , %

+, -

Exemplu: Care va fi rezultatul


len(‘Rezultat?’)*2**3/4
Ordinea operațiilor

Exemplu: Care va fi rezultatul expresiei :

len(‘Rezultat?’)*2**3/4

len(‘Rezultat?’) - functia len returneaza lungimea stringului ‘Rezultat’ = 9

Expresia devine 9*2**3/4

Se executa intai ridicarea la putere (2**3 =8) apoi inmultirile si impartirile

9*8 /4

Rezultatul final 18
Variabile
Variabilele sunt nume ptr obiecte, “care se referă la valori”

Folosim variabile ori de cate ori vrem să stocăm informația

Variabilele în Python nu trebuie să fie explicit declarate, ca să se rezerve


spațiu în memorie. Declararea se face automat când se asignează o
valoare variabilei, folosind semnul (=)

n = 100 # asignăm un număr întreg


pi = 3.14159265358979 # asignăm un număr float point
mesaj = "In curând placăm acasă" # asignăm un șir de caractere

Alegeți numele variabilelelor să aibă sens:


- pot conține litere și cifre, dar niciodată nu încep cu cifre
- pot fi oricat de lungi
- semnul “underscore” _ poate sa apară în numele variabilelor
- Python este case sensitive
Dacă dați unei variabile un nume ilegal o să aveți eroare de sintaxă
Nume de variabile

Python3 are 33 de cuvinte rezervate ce nu pot fi folosite ca nume de


variabile.

Interpretorul folosește aceste cuvinte cheie să recunoască structura


programelor

Aceste cuvinte cheie sunt scrise în culori diferite în IDE-uri.

False None True and as assert break class


continue def del elif else except finally for
from global if import is in lambda nonlocal not or
pass raise return try while with yield
Variabile

In[]: x=3**2 # numele variabilelei x creeaza o legatura (un link =


un pointer) cu valoarea expresiei 9
In[]: y=9 # se creaza o legatura (un pointer) către aceeași valoare 9
In[]: z=x #variabile z va pointa catre aceeasi valoare 9, nu catre x

Este o singură locație în memorie 9 și către ea pointează toate aceste


variabile

Tipul variabilei este acela pe care o are valoarea variabilei = tipul se


moștenește din valoare

Tipul variabilelor este dinamic, se schimbă cu valoarea asociata!

In[]: x=5
In[]: x=’abc’
Operatori de Asignare
= asignare simplă; asignează valoarea din partea dreaptă, valorii din
stanga operandului: c = a + b se va asigna valoarea a + b în c

+= adunare AND; aduna operandul din dreapta la cel din stânga și


asignează rezultatul operandului din stânga: c += a este echivalent cu
c=c+a
-= scade AND; scade operandul din dreapta din cel din stânga și
asignează rezultatul operandului din stânga: c -= a este echivalent cu
c=c–a

*= multiplică AND ; multiplică operandul din dreapta cu cel din stânga


și asignează rezultatul operandului din stânga: c *= a este echivalent
cu c = c * a

Tot asa si pentru : /= ( c/=a echivalent cu c=c/a),


//= ( c//=a echivalent cu c=c//a)
%= ( c%=a echivalent cu c=c%a)
**= ( c**=a echivalent cu c=c**a)
Operatori de Asignare

X= 5 asignare simplă

X =Y =5 asignare multiplă

X, Y = 3,4 asignare multiplă în pachet X=3 Y=4

X+=7 asignare multiplă cu operator X=X+7

Exemplu:
In[]:X=10
In[]: X,Y= 20,X
In[]: print(X,Y)
20 10
Statements (declarații)
Statements comenzi pe care Python le interpretează și execută

Au efect imediat cum ar fi crearea de variabile, afișarea unor valori

Straight line programming = se execută o secvență de instructiuni


una cate una
Creez un script = scriu secvența de instrucțiuni într-un editor, salvez cu
extensia *.py și rulez scriptul:
x=3
x=x*x
print (x)
n=input(‘Introduceti un nr=’)
print(n)
Atentie! Fct input interpreteaza ceea ce introduceți ca string. Dacă în
loc de val numerică introduceti ‘abc’, va printa exact cu ghilimele
‘abc’.Trebuie să scrieți int(input(‘n=’) - daca vreți un scalar
Python are doua moduri de funcționare : interactiv și script
In modul script expresiile sunt evaluate, dar nu au efect vizibil până
Condiționare – If, Else, Elif
Branching programming - schimbă ordinea instrucțiunilor pe baza
unor teste (de obicei valoarea unor variabile)

Declarația if – la fel ca în toate limbajele

Declarația if conține o expresie logică și decizia este facută pe baza


rezultatului comparației:

Sintaxa declarației if este:


if expresie:
declarații
Declarații

În Python toate declarațiile indentate cu același numar de spații sunt


considerate ca fiind parte din același bloc de code.

Python folosește indentarea ca metodă de grupare a declarațiilor.


Condiționare – If, Else, Elif
O singură declarație else poate fi combinată cu o declarație if, declarația
else este opțională.
O declarație else conține un bloc de cod care se execută dacă expresia
din declarația if este False (se evaluează la valoarea 0)

Declarația elif permite să se verifice expresii multiple și se execută un


bloc de cod atât timp cât valoarea expresiei are valoare True
Declarația elif este opțională și pot fi mai multe declarații elif în
interiorul declarației if.
if expresie1:
declarații
declarații
elif expresie2:
declarații
elif expresie3:
declarații
else:
declarații
Condiționare – If, Else

Se poate scrie un bloc de instrucțiuni în interiorul condiției else, nu doar


o singură declarație:

x=15 x=int(input(‘x=’))
if ( x/2 )*2 == x : if x%2 == 0:
print(“par”) print(“par”)
else : else :
print(“impar”) print(“impar”)
if x%3 !=0:
print(“nu e divizibil cu 3”)
Condiționare – If, Else

Ce text va fi scris ?

Exemplu1: Exemplu2:

z=’b’ z=’b’
if ‘x’ < z: if ‘x’ < z:
print(“Buna”) print(“Buna”)
print(“Mama”) print(“Mama”)
Condiționare – If, Else
Ce text va fi scris ?

x=15
y=13
z=11
print(‘x=’,x,’y=’, y, ‘z=’,z)
if x< y:
if x<z:
print(“x e cel mai mic număr”)
else:
print(“z e cel mai mic număr”)
else:
print(“y e cel mai mic număr”)

Operatorii logici se folosesc pentru a simplifica condițiile if imbricate


Condiționare – If, Else, Elif

Trebuie să scriu codul corect, sa testez pentru toate valorile posibile:

x=15
y=13
z=11
print(‘x=’,x,’y=’, y, ‘z=’,z)
if x< y and x<z:
print(“x e mai mic”)
elif:
print(“y e mai mic”)
else:
print(“z e mai mic”)
Bucla While

O expresie logică trebuie să întoarcă una din valorile True sau False

Bucla while continuă până când expresia devine False.

Sintaxa buclei while este:


while expresie :
declarații
declarații

Exemplu:

nr = 0
while nr < 9 :
print (' Numărul este:', nr)
nr = nr + 1
print (‘Gata, am terminat!’)
Bucla WHILE infinită / break
Următorul cod va contina pâna când veți tasta CNTL+c :

var=1
while var == 1 : # bucla infinită
nr = input("Introduceti un număr =")
print ("Ati introdus: ", nr)

Declarația break termină execuția buclei curente și predă controlul să


se execute următoarea declarație:

while True : # bucla infinită


nr = input("Introduceti un număr =")
print ("Ati introdus: ", nr)
if nr == “100”:
break # termină execuția buclei
print ("Ne-am oprit, ați introdus: ", nr )
Reguli de bază când scriem scripturi iterative
Care va fi rezultatul afișat în următorul exemplu ?
y=0
x=3
it=x
while(it >0):
y=y+x
it=it-1
print(y)

Reguli de bază când scriem scripturi iterative:


1. alegem variabilele (ceea ce se schimbă, ceea ce se “numără”)
2. inițializam variabile în afara buclelor
3. setez condiția de end test – cum ies din bucle
4. scriu setul de intrucțiuni din buclă, ceea ce se va repeta/schimba
de un nr de ori, schimb variabile
5. ce voi face cu rezultatul când se termină bucla ?
Întrebări de bază când scriem scripturi iterative

Exemplu: scriem un număr și aflăm dacă e pătrat perfect

x= int(input(‘Introduceti un nr =’))
ans =0 # initializez variabilele în afara buclei
while ans*ans < = x: # setez condiția de ieșire din buclă
ans=ans+1 # scriu setul de instrucțiuni
print (ans) # ce se va întampla dupa ce ies din buclă

Tot timpul îmi pun întrebările:


Face codul ce trebuie ?
Îmi dă răspunsul corect?
Am testat toate posibilitățile?
Se termină codul?
Rescriem codul
Scriem codul mai bine:

x = int(input('Introduceti un număr: '))


ans = 0
if x>0:
while ans*ans < x:
ans = ans + 1
#print (‘ans =’, ans)
if ans*ans != x:
print ('nu e pătrat perfect', x)
else:
print(ans)
else:
print ('e număr negativ’, x)

# nu mai e nevoie de if x>0 daca pun conditia < abs(x) in while


Cod Iterativ

Codul scris în structuri ramificate (condiționale : if/elif/else) permite


pe baza unui test, să sărim la diferite alte zone de cod

programele au un timp constant

Codul scris în structuri de loop (while) permite să repetăm bucăți de


cod, până când o condiție este îndeplinită

programele au timp, care depinde de valorile variabilelor, precum și


de lungimea programului
Bucla For
Bucla for are abilitatea de a itera peste elementele oricărei secvențe
(inclusiv stringuri, liste, tuple)

Sintaxa buclei for este:


for var_iterare in secvența :
declarații
declarații
Cand vrem sa facem o buclă for în stilul convențional (la fel ca în C for
(i=1; i =<10; i++) folosim sintaxa:

for i in range(1,n) :
declarații
declarații

NOTĂ:funcția range(start, stop,step) genereaza numerele intregi, de la


start inclusiv pâna la stop exclusiv. Adică range(1,n) generează numerele
intregi de la 1 la n-1 (step e setat la 1, daca nu se specifică altă )
Bucla For

Exemplu: aflăm divizorii unui număr cu bucla while și cu bucla for:

x=10 x=10
i=1 for i in range(1, x+1)
while i < x: if x%i==0:
if x%i==0: print(‘divizor’,i)
print(‘divizor’,i)
i=i+1

Avantajul buclei for este acela al variabilelor care se updateaza automat


Bucla For
Exemplu for pe string:
for litera in 'Python':
print( 'Litera curentă :', litera)

Exemplu for pe liste:


fructe = ['banane', 'mere', 'mango']
for fr in fructe:
print ('Fructul curent :', fr)

Exemplu for pe tuple:


fructe = ('banana', 'apple', 'mango')
for index in range(len(fructe)):
print ('Fructul curent :', fructe[index])

NOTĂ: Funcția built-in len() furnizează numărul total de elemente din


tuplă
For vs While

bucla for bucla while

se știe numărul de iterații nu se știe numărul de iterații

se poate termina mai repede se poate termina mai repede


via break via break

folosește o variabilă de folosește o variabilă de


incrementare automată incrementare ce trebuie ințializată
înaintea buclei și trebuie
incrementată în buclă

se poate rescrie bucla for nu se poate rescrie bucla while


folosind o buclă while folosind o buclă for
DE CITIT!

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Capitolul 1, Capitolul 2 , Capitolul 5 pana la Recursivitate, 5.8


Capitolul 7 până la Square root, 7.5
TEMA

Scrieți codul Python pentru a afla dacă un număr este pătrat perfect
apoi modificați acest cod Python pentru a afla dacă un număr
(poate să fie și un nr negativ) este cub perfect.

Folosiți aceasta sintaxa pentru functia print:


print(‘cubul lui’ + str(x) + ‘este’ + str(ans))
Bibliografie

Python: http://www.python.org/
Python Documentations: https://docs.python.org/3/
Python Tutorial https://docs.python.org/3/tutorial/
IPython: http://ipython.org/
Jupyter noNebook: https://jupyter.org/
NumPy: http://www.numpy.org/
SciPy: https://www.scipy.org/
Pandas: http://pandas.pydata.org/
Matplotlib -2D: http://matplotlib.org/
Mayavi - 3D:http://code.enthought.com/projects/mayavi/
Bibliografie

Eric Grimson, and John Guttag. 6.00 Introduction to Computer


Science and Programming. Fall 2008. Massachusetts Institute of
Technology: MIT OpenCourseWare, https://ocw.mit.edu. License:
Creative Commons BY-NC-SA.
Curs 3

Limbajul PYTHON
String-uri/
Funcții /
Recursivitate
String-uri

Exemplu: str()

In[] : x=str(3)

In[]: print(x)

‘3’

In[]: print(type(x))

<class ‘str’>

Datele de tip string sunt ‘str’ și nu ‘char’ ca în alte limbaje


String-uri
Secvență ordonată de caractere, case senzitive, introduse între “ “ sau ‘ ‘
In[]:s1=’abcdefgh’
In[]:s2=”Abc”

O secvență de caractere pe mai multe rânduri se introduce între ghilimele


triple “ “ “

In[]:sir="""acum
...plec
...de
...acasa"""

len() - funcția care returnează lungimea unui string incluzănd spațiile


libere, \n etc.

len(sir) # va fi 18 sir ='acum\nplec\nde\nacasa'


String-uri
[ ] - se folosesc pentru a indexa/poziționa în string, să obținem valoarea de
la un anumit index/poziție. Indexarea în string incepe de la 0.
Ultimul index este -1.
s= “hello world”
01……….10
-11………..-1

In[]: s[0] =’h’


In[]: s[-3] =’r’

[start : stop : step ] - selecție din string, start se include, stop se


exclude, ca si cum am avea [start : stop: step)

In[]: s[:] =’hello world’


In[]: s[3:6] = ‘lo ’
In[]: s[: 4]=’hell’
In[]: s[: : 2]=’hlowrd’
In[]: s[: :-1]=’dlrow olleh’
String-urile sunt IMUTABILE

IMUTABIL = valoarea lor nu se schimbă

In[]: s=’hello’
In[]: s[0]=’c’

TypeError: 'str' object does not support item assignment

In[]: s = ‘c’ + s[1 : ] #sau puteam și asa ‘c’ + s[1:len(s)]


In[]: s=’cello’

Concatenare de siruri de caractere prin operatorul +


In[]: a=’hello’ + ‘world’
In[]: a=’helloworld’
Repetare sir de caractere prin operatorul *
In[]: b=’cici’*3
In[]: b=’cicicicicici’
Bucla For pe String-uri

Folosirea operatorului boolean in - compara doua siruri si returneaza true


daca primul apare ca un subsir în al doilea:

s = "abc,def gh"

for index in range(len(s)):


if s[index] == 'e' or s[index] == 'u':
print(“ Am gasit litera e sau litera u”)

for nume_var in s:
if nume_var == 'e' or nume_var == 'u':
print("am gasit litera e sau u”)

Python tratează diferit litere cu majuscule și cele cu minuscule: toate


litere mari vin înainte de toate literele mici.
Metode pe string-uri
Datele de tip string sunt str și nu char ca în alte limbaje

Python include metode ( funcții built-in) pe string-uri:

https://docs.python.org/3/library/stdtypes.html#string-methods

Sau le putem vizualiza :


In[]: help(str)

Invocare = procesul de apelare a unei metode folosind sintaxa


nume_string.nume_metodă()

Exemplu:
In[]: s=’Vreau sa merg la Madrid’
In[]: ss=s.split()
In[]: ss = [‘Vreau’,‘sa’, ‘merg’, ‘la’, ‘Madrid’]
In[]: ss[4]=’Madrid’
In[]: s.find(‘la’) # întoarce locația substringului sau -1 daca nu-l gasește
Funcții
O funcție este:
un bloc de cod organizat,
ce întoarce o singură valoare/obiect
și este reutilizabilă

Funcțiile oferă:
decompoziție - separare în module
abstractizare - eliminăm detaliile

Python oferă multe funcții predefinite - funcții built-in, dar va puteți


crea și propriile funcții -funcții definite de utilizator

Funcțiile nu sunt rulate automat, ci trebuiesc apelate/invocate.

Funcțiile pot fi apelate din interiorul altor funcții sau direct în


interpretorul Python
Funcții cu și fără parametrii de intrare
def calculsq(): # header-ul functiei
rez= 5**2 # corpul funcției
print(rez)

calculsq() # apelul funcției

def calculsq( x): # header-ul functiei x parametru de intrare


rez= x**2 # corpul funcției
print(rez)
#return rez

calculsq(10) # apelul funcției 10 argumentul functiei

Putem face apelul funcției cu orice valoare a parametrului x


calculsq(2)
Cum definim o funcție?
Reguli pentru a defini o funcție în Python:

- funcțiile încep cu cuvântul cheie def urmat de numele funcției, un


set de paranteze () urmat de două puncte :
- orice parametru de intrare sau argumente trebuie să fie plasate în
interiorul acestora paranteze. De asemenea,puteți defini în interiorul
acestor parametri paranteze.
- prima declarație din corpul funcției poate fi o declarație opțională –
docstring - pentru documentarea funcției
- blocul de cod din cadrul fiecărei funcții este indentat
- ieșirea din funcție se face cu declarația
return expresie - se pasează expresia apelantului
sau
return None - când nu se întoarce niciun argument
Sintaxa funcților
Caracteristicile funcțiilor: nume, un set de parametrii de intrare,
docstring ( opțional, dar recomandat), corp: secvență de
instrucțiuni/comenzi

Funcțiile au următoarea sintaxă:


def nume(prm1, prm2 , prm3=-1):
‘’‘DocString – specificațiile aceastei fct, input/output’’’
Instrucțiuni
return: par
sau
return None #variabilă specială, dacă nu returnez nimic.

Atenție! În mod implicit, parametrii de intrare au un comportament


pozițional, și trebuie apelați în aceeași ordine în care au fost definiți.
Parametrii de intrare pot fi obligatorii sau opționali (prm3)

Atenție! Parametrii de intrare pot fi orice tip de date, insclusiv alte funcții
Return – întoarcem valori din funcții

Dacă nu avem nicio declarație de return Python folosește None


def cnt (x):
x=x+1
print(cnt(5)) --> None #variabilă specială, nu returnez nimic.

Putem returna valori multiple dintr-o funcție:


def minmax(x,y):
return min(x,y), max(x,y)
a,b= minmax(15,49)
print(a,b) --> 15,49
Pasăm argumente funcțiilor
Argumentele funcțiilor sunt pasate de la stânga la drepta:
def div(x,y):
return x/y

a =30
print(div(a,10)) --> x este 30 și y este 10 printează 3

x=10
y=30
print(div(y,x)) --> x este 30 și y este 10 printează 3

Fiecare funcție are un spațiu alocat – name space și ceea ce facem e să =


map values

print(div(x=30, y=10)) --> 3


print(div(y=30, x=10)) --> 3 nu contează ordinea dacă sunt valori alocate
Parametrii opționali = default value
Parametrii opționali nu trebuie specificați când funcția este apelată. În
acest caz, sunt utilizate valorile implicite.

def salut(nume=’tuturor’):
print(‘Buna’ + nume + ‘!’)

In[]: salut(‘Vasile’)
In[]: salut()

def Nume(prenume, nume, set = False):


if set:
print(nume + prenume)
else:
print(prenume + nume)

In[]: Nume(‘Freddie’, Mercury ’)


In[]: Nume(‘Freddie’, Mercury ’, set=True)
In[]: Nume(nume=’Mercury ’, prenume=’Freddie’, set=False)
Număr arbitrar de argumente

Sunt situații în care nu se cunoaște exact, a-priori numărul parametrilor


de intrare.

Un număr de parametri arbitrar poate fi realizat cu așa-numita referința


prin tuple prin adăugarea unui asterisc "*" în fața ultimului nume al
parametrului pentru al desemna ca referință de tuple.

def mediaAritmetica(prima, *valori):


""" calculez media aritmetica a unui nr arbitrar de valori numerice """

return (prima + sum(valori)) / (1 + len(valori))

In[]: print(mediaAritmetica(45,32,89,78))
Număr arbitrar de argumente **kwargs

Un număr de parametri arbitrar poate fi realizat cu așa-numitele


argumente precedate de identificatori - adăugarea unui dublu asterisc
"**" în fața ultimului nume al parametrului pentru al desemna ca
referință

Exemplu referinta dicționar:

def printtot( **kwargs):


""" printez perechea cheie – valoare a unui nr arbitrar de parametrii """
for k,v in kwargs.items():
print(k +” : ” + v)

In[]: printtot( nume=”Vasile” varsta=”35” firma=”Adobe”)


nume:Vasile
vârsta:35
firma:Adobe
Argumentul funcției altă funcție

First order function în limbajul de programare înseamnă ca variabilele și


funcțiile sunt considerate echivalente.

În Python funcțiile sunt și ele obiecte:

def sortez(a,b, order):


return order(a,b)

sortez(44,77,max) --> am pasat funcția max ca argument -> max(44,77)

Ce se va printa în următoarele exemple?

sortez(‘mere’, 50, min)


sortez(‘mar’, ‘para’, print)
return vs print()

return print ()

are sens numai în interiorul poate fi utilizată în afara funcţiei


unei funcții

doar un singur return se se pot executa mai multe


execută în interiorul unei funcții declarații în interiorul unei funcții

blocul de cod, din interiorul blocul de cod, din interiorul


funcției, dar după declarația funcției, dar după declarația print
return nu se execută se execută

are o valoare asociată care se va are o valoare asociată care se va


pasa apelantului pasa la consolă
Exemplu :
def estepar( i ):
„„„
input- i , un număr pozitiv
output – true dacă nr e par, altfel false
”““
print(„Este par i ?”)
return i % 2 == 0

In[]: i=44
In[]: estepar(45) # am făcut apelul funcție: estepar(45)

Atenție! Țineți cont de variabile locale (din corpul funcției) și cele globale
(din afara funcției)

Ce va întoarce funcția?
Variabile locale / globale

Toate variabilele au un „scop” exact acolo unde au fost definite

Toate variabilele definite în interiorul unei funcții sunt locale cu


excepția celor specificate ca fiind globale: global s

Aceeași variabilă nu poate fi și locală și globală în interiorul unei


funcții.

Orice modificări asupra variabilelor locale= definite în interiorul


funcțiilor, NU au efect în afara funcțiilor, chiar dacă au același nume
Variabile locale / globale

În interiorul funcțiilor pot accesa variabile care au fost definite în


afara corpului funcției - globale

În interiorul funcțiilor nu pot modifica variabilele care au fost


definite în afara corpului funcției – globale

def fun1(y): def fun2(y): def fun3(y):


x=7 print(x) x+=5
x*=10 print(x+1)
print(x)

In[]: x=5 In[]: x=5 In[]: x=5


In[]: fun1(x) In[]: fun2(x) In[]: fun3(3)
In[]: print(x) In[]: print(x) In[]: print(x)
Funcții în alte funcții

Ce va afisa următorul code?

def fg(x):
def fh():
x=’abc’
x=x+1
print(‘in functia fg(x): x= ’, x)
fh()
return x

In[]: x=3
In[]: z=fg(x)
Funcții lambda
Pentru a crea o funcție anonimă (Matlab) de forma unei expresii
folosim sintaxa lambda:
lambda arg: expresie

Funcția lambda poate avea orice numar de argumente, dar o singura


expresie – print, for while NU pot apărea într-o expresie lambda:
In[]: x=lambda a : a*2
In[]: print(x(3))

In[]:y = lambda a: a + "10"


In[]: print(y("10"))

In[]: z=lambda a, b : a**b


In[]: print(z(2, 5))

Folosim functia lambda când o funcție anonimă este necesară pentru


o perioadă scurtă de timp.
Folosim funcția lambda cu funcția map()
Funcția lambda este folosită atunci când funcționalități simple sunt
încorporate în mod anonim în expresii mai mari.

Funcționalitatea nu este stocată în mediu, spre deosebire de o funcție


definită cu def.

Exemplu folosim o funcție lambda în contextul funcției map ().


t = map(func, s)

Se aplică funcția func fiecărui element din s și se returnează o lista


t.

Fiecare element a listei t este t[i] = func(s[i])

Funcția din map() trebuie să aibă doar un argument deoarece se


aplica doar pe o listă, s.
Folosim funcția lambda cu funcția map()

Exemplu folosim o funcție lambda în contextul funcției map ():

In[]: nr=[2,4,6,8,10]

# functia map() e un iterator, aplica functia lambda peste obiectul


lista, nr

In[]: rez=map(lambda a: a*2, nr)

# variabila rez este tot un iteretator trebuie sa convertim rez la o lista


In[]: ll=list(rez)

In[]: print(ll)
Folosim funcția lambda cu funcția map()
Funcția map() se poate aplica pe liste multiple:

t = map(func, s1, s2, ..., sn)

Fiecare element a listei t este t[i] = func(s1[i], s2[i], ...,sn[i])

Funcția din map() trebuie sa accepte același număr de argumente ca


numărul de liste.

Rezultatul are același număr de elemente ca dimensiunea celei mai


scurte liste

In[]: m=[2,4]
In[]: n=[10, 20, 30]
In[]: p=map(lambda a,b: a*b, m, n)
In[]: print(list(p)) # [20,80]
Exemplu: modulatitate și abstractizare

Într-o fermă sunt porci si gaini, 20 capete și 56 picioare.


Câti porci și câte gaini?

Soluția cu sistem de ecuații:

porci+gaini=20
4*porci+2*gaini=56

Din punct de vedere al programării nu este soluția cea mai ușor de


implementat.
Exemplu: modularitate

Brute force algoritm: enumărăm toate posibilitățile și verificăm:


0 gaini si 20 porci ?
1 gaina si 19 porci?
2 gaini si 18 porci?

def solve(nCap, nPic):


for gaini in range(0, nCap+1):
porci = nCap - gaini
totPic = 4*porci+2*gaini
if totPic==nPic:
return (porci,gaini)
return (None,None)
Exemplu: abstractizare

def ferma():
capete=int(input(‘Introduceți nr de capete= ’))
picioare = int(input(‘Introduiceți nr de picioare=’ ))
porci, gaini =solve(capete, picioare)
if porci ==None:
print(‘nu am găsit o soluție’)
else:
print(‘nr de porci:’, porci)
print(‘nr de gaini:’, gaini)
Exemplu: modularitate

Adaug o noua variabilă: viermi de matase

def solveN(nCap, nPic):


for viermi în range(0, nCap+1):
for gaini in range(0, nCap +1-viermi):
porci = nCap – gaini - viermi
totPic = 4*porci+2*gaini+8*viermi
if totPic==nPic:
return (porci,gaini, viermi)
return (None,None, None)
Exemplu: abstractizare

def fermaN():
capete=int(input(‘Introduceți nr de capete= ’))
picioare = int(input(‘Introduiceți nr de picioare=’ ))
porci, gaini, viermi =solveN(capete, picioare)
if porci ==None:
print(‘nu am găsit o soluție’)
else:
print(‘nr de porci:’, porci)
print(‘nr de gaini:’, gaini)
print(‘nr de viermi:’, viermi)
Exemplu: generalizare
Mai multe soluții posibile (2 variabile și 3 necunoscute)
def solveG(nCap, nPic):
solutieGasită = False
for viermi în range(0, nCap+1):
for gaini in range(0, nCap +1-viermi):
porci = nCap – gaini - viermi
totPic = 4*porci+2*gaini+8*viermi
if totPic==nPic:
print(‘nr de porci:’, porci)
print(‘nr de gaini:’, gaini)
print(‘nr de viermi:’, viermi)
solutieGasita=True
if not solutieGasita:
print(‘nu am găsit o soluție’)

solveG(36,88) : 8,28,0; 5,30,1; 2,32,2


Recursivitate

Recursivitate este o caracteristică fundamentală a limbajului natural

Recursivitate este o caracteristică a capacitățiilor cognitive – felul în care


gândim este bazat pe procese recursive

Adjectivul recursiv provine din verbul latin "recurrere", ceea ce


înseamnă a reapare – mă întorc la mine însămi -"running back"

„ Să înțelegi recursivitatea ai nevoie să înțelegi recursivitatea”


Recursivitate

Recursivitate: o metodă de a rezolva anumite tipuri de probleme

Metoda recursivă se mai numește metoda Divide and Conquer

Împart problema în bucăți de code (subprobleme) pe care le refolosesc.

Ideea recursivității : o funcție care se apelează pe ea insăși , adică


definesc o funcție f și în corpul acestei funcții apelez din nou funcția f

Funcția recursivă trebuie să se termine – nu este o buclă infinită!


Recursivitate

Recursivitatea se folosește în cazurile când:


- avem unul, două cazuri de bază ușor de rezolvat
- trebuie sa rezolvăm aceeași problemă, dar cu alt input cu scopul de a
simplifica când avem un input complex

O funcție recursivă se termină, în cazul în care cu fiecare apel recursiv


rezolvarea problemei este redusă și ne îndreaptăm spre un caz de bază.

Un caz de bază este un caz în care problema poate fi rezolvată fără a mai
apela la recursivitate în continuare.

Recursivitate poate ajunge într-o buclă infinită când cazul de bază nu este
îndeplinit în apelurile funcției.
Exemplu : iterativ

a*b = echivalent cu a aduna a cu el însuși de b ori

Ca să scriu funcția care multiplică două numere a*b am nevoie


- de un iterator b, care-mi spune de căte ori să multiplic, și-l descresc
- de o valoare rez în care o să salvez rezultatul

def multiplic(a,b):
rez=0
while b> 0:
rez+=a
b-=1
return rez
Exemplu : recursiv
a*b = echivalent cu a aduna a cu el însuși de b ori = a+a+a+a+...a

- ne gândim cum să reducem problema inițială la o problemă mai simplă


- aplicam acest pas, până rezolvarea problemei se reduce la cel mai
simplu caz care se poate rezolva direct b=1 a*b=a

a*b= a+a+a....+a (b ori)


= a+a +a....+a =a+a*(b-1)
= a+a+a....+a = a+a +a*(b-2)

def multiplic(a, b):


if b ==1:
return=a
else:
return a+multiplic(a, b-1)
Factorial : iterativ vs recursiv
n! = n*(n-1)*(n-2)....*1

def factI(n): def factR(n):


rez=1 if n ==1:
for i in range(2, n+1): return=1
rez* =i else:
return rez return n * factR(n-1)

print(factR(4))

Se calculează: 4*factR(3)=4*3*factR(2)=4*3*2*factR (1)=4*3*2*1

Voi decideți care metodă este mai simplă!


Șirul lui Fibonacci

Șirul lui Fibonacci: 0,1,1,2,3,5,8,13,21,34,55,89, ...

Matematicianul Leinardo de Pisa (1202) voia sa afle populația de iepuri


numărul de perechi de iepuri (mascul,femelă) după n luni

Știind că : o pereche produce întotdeauna o nouă pereche (un mascul, o


femelă) în fiecare lună de la a doua lună începând și sunt imortali

F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2)

Avem două cazuri de bază!


Fibonacci : iterativ vs recursiv

def fibI(n): def fibR(n):


vechi, nou= 0,1 if n==0 or n ==1:
for i in range(n-1): return=1
vechi,nou=nou,vechi+nou else:
return nou return fibR(n-1)+ fibR(n-2)

print(fibR(4))

Se calculează: 4*fibR(3)=4*3*fibR(2)=4*3*2*fibR(1)=4*3*2*1

Metodă iterativă este mai rapidă!

Metoda recursivă nu memorează valorile calculate anterior!


Turnurile din Hanoi : recursiv
Știu:

64 de discuri de diferite mărimi toate puse pe un singur stâlp

trebuie să le rearanjez descrescător pe un alt stâlp, cel mai mare disc


jos...cel mai mic sus, în vârf

 pot să mă folosesc de un stâlp de lucru (am 3 stâlpi)

Reguli:
1.trebuie să mut un singur disc odată
2.niciodată nu trebuie să am un disc mai mare peste unul mai mic

Se cere:
Să scrieți program care rezolvă problema, printând ordinea mutărilor.
Turnurile din Hanoi : recursiv
Metodă iterativă este foarte complex de scris!
Metoda recursivă ușor de scris:
Rezolvăm problemă mai simplă
Rezolvăm cazul de baza
Rezolvăm problemă mai simplă

def printMuta(sursa, destinatie):


print('Mut de la ' + str(sursa) + ' la ' + str(destinatie))

def Turn(n, sursa, destinatie, bufer):


if n == 1:
printMuta(sursa, destinatie)
else:
Turn(n-1, sursa, bufer, destinatie)
Turn(1, sursa, destinatie, bufer)
Turn(n-1, bufer, destinatie, sursa)

print(Turn(4, ‘Ss’, ‘Sd’, ‘Sb’ ))


Recursivitate pe string - palindrom

Palindrom: un șir de caractere ce se citește la fel și de la dreapta la stânga


și de la stânga la dreapta

Exemple: apa, sus, capac, cojoc

Napoleon :
“Able was I, ere I saw Elba” – a zis-o

Anne Michaels:
“Are we not drawn onward, we few, drawn onward to new era?”
Recursivitate pe string - palindrom
Cum rezolv ?

În primul rând, mă asigur că șirul este format doar din caractere mici
(elimin semnele de punctuație, convertesc șirul la caractere mici, etc)

Cazul de bază:
un șir de lungime 0 ( inițial am un nr par de caractere în șir) sau
un șir de lungime 1 ( inițial am un nr impar de careactere în șir)
este un palindrom

Cazul recursiv:
dacă primul caracter se potrivește cu ultimul caracter, atunci este un
palindrom dacă și secțiunea de mijloc este un palindrom....și tot repet
procesul
Recursivitate pe string - palindrom
def estePalindrom(s):
def curatSir(s):
s = s.lower()
ans = ' '
for c in s:
if c in 'abcdefghijklmnopqrstuvwxyz':
ans = ans + c
return ans

def ePal(s):
if len(s) <= 1:
return True
else:
return s[0] == s[-1] and ePal(s[1:-1])
return ePal(curatSir(s))

Testez: print(estePalindrom(‘Cojoc’))
Recursivitate pe string - ADN

Găsim cea mai lungă secvență comună scriem functia secvcom:

AGGACAT
ATTACGAT

In[]:secvcom(“AGGACAT”, “ATTACGAT”)
5

In[]: sevcom(“transparent”, “spam”)


3
Recursivitate pe string - ADN

def secvcom(s1,s2):
if s1==”” or s2=””:
return 0 # cazul de bază
else:
if: s1[0]==s2[0]:
return 1 + secvcom(s1[1:], s2[1:])
else:
return max(secvcom(s1, s2[1:]), secvcom(s1[1:], s2))
Redenumirea unui modul
După această declarație de import există doar namespace-ul mm și nu
există namespace-ul math.

In[]: import math as mm


In[]: print(mm.cos(mm.pi))
In[]: -1.0

Este posibil să import doar câteva metode dintr-un modul și să le


redenumesc folosind operatorul as:

In[]: from math import pi, pow as power, sin as sinus


In[]: power(2,3)
8.0
In[]: sinus(pi)
1.2246467991473532e-16
Module
Pănă acum am presupus că toate funcțiile noi definite( codul vostru) este
stocat într-un singur fișier:

Abordare greoaie pentru colecții mari de funcții, sau când anumite


funcțiile ar trebui să fie utilizate de mai multe persoane sau în diferite alte
părți de code

Creăm un modul = un fișier .py care conține o colecție de definiții de


funcții și declarații, obiecte arbitrare, clase sau atribute, alte fișiere.

Nu este necesară o sintaxă specială pentru a face un astfel de fișier =un


modul. Inițializăm modulul cu declarația import

In[]: import math, os

Declarațiile de import pot fi poziționate oriunde în program, dar este


indicat să le plasați direct la începutul unui program.
Module
Fișierul circle.py conține:

pi = 3.14159

def area(r):
return pi*(r**2)
def circumference(r):
return 2*pi*r

Putem importa modulul circle în shell sau în alt fișier pentru a-l folosi:
In[]:import circle
In[]:pi = 3
In[]:print(pi)
In[]:print(circle.pi)
In[]:print(circle.area(3))
In[]:print(circle.circumference(3))
Module
Dacă vrem să apelăm funcțiile și alte declareții din modul, dar nu vrem
să folosim sintaxa nume_modul.funcții/declarații trebuie să scriem
declarația de import așa:

In[]: from circle import *


In[]:print(pi)
In[]:print(area(3))

Aceast import are efectul de a crea o legatură între starea curentă și toate
obiectele definite în modul – atenție numele de variabile sau funcții
din modul să fie diferite de cele folosite în shell sau în alte fișiere

Declarațiile din modul sunt executate doar o singură dată când modulul
este importat ! Dacă schimb declarațiile sau funcțiile din modul trebuie sa
invoc din nou declarația: from nume_modul import *
Locația unui modul
Aflăm locația modului după ce a fost importat:
In[]: import numpy
In[]: numpy.__file__
In[]: '/home/gabi/anaconda3/lib/python3.5/site-packages/ numpy /
__init__.py'

Atributul __file__ nu exista pentru toate modulele, ca în cazul modulelor


care sunt static linked ca librarii C.
In[]: import math
In[]: math.__file__
Traceback (most recent call last):
File "<stdin>", line 1, in
AttributeError: 'module' object has no attribute '__file__'

Cu funcția built-in dir(), precum și numele modulului ca argument, puteți


lista toate atributele valide și metode pentru acel modul.
In[]: dir(math)
Librării de funcții - module Python

Python este un limbaj open source.

Sunt dezvoltate multe librarii de funcții, majoritatea necesitând instalarea

Lista librăriilor de funcții: https://pypi.python.org/pypi

Instalăm o librărie folosind comanda pip ( pip este un program


independent și poate fi rulat din Windows powershell sau un terminal
mac)

In[]: pip install easygui


In[]: import easygui
In[]: easygui.msgbox(‘Vrem acasă!’, ‘Mesaj’)
# afișează o fereastră de mesaj având un buton ok
TEMA

Din carte, de la sfârsitul Capitolului 3: exercițiul 3.3


DE CITIT!

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Stringuri - Capitolul 2: 2.6-2.8 , Capitolul 8


Funcții și Recursivitare- Capitolul 3,
Capitolul 5: 5.8-5.12 , Capitolul 6
DE CITIT!

Strings
https://docs.python.org/3/tutorial/introduction.html#strings
Functions:
https://docs.python.org/3/tutorial/controlflow.html#defining-
functions
Module
https://docs.python.org/3/tutorial/modules.html
Curs 4

Limbajul PYTHON
Data Collections - Arrays
Python Collections - Arrays

Există patru tipuri de date (arrays- date de colectare) în Python:

Tuple - colecție ordonată și neschimbabilă (imutabila) Permite duplicate.

Seturi - colecție neordonată și neindexabilă. Nu permite duplicate.

Liste - colecție ordonată și schimbătoare (mutabila). Permite duplicate.

Dicționare - colecție neordonata, indexabila si schimbătoare. Nu permite


duplicate.
Tuple
Secvență ordonată de valori, ce pot reprezenta diferite tipuri de date

Valorile sunt separate de virgulă , și reprezentate între ( )

In[]:t1=(’a’, 23, ‘bcd’, ‘efgh’, 5.0)

In[]:t3= (23 , ) # tupla cu un singur element este urmată de virgulă


In[]:t4=(‘a’) # ATENȚIE t4 este string

In[]:t=( ) # crearea unei tuple de dimensiune 0


In[]:tt= tuple( ) # crearea unei tuple cu funcția built-in

Dacă argumentul funcției built-in tuple () este o secvență (string, lista sau
altă tuplă), rezultatul va fi o tuplă cu elementele din secvență

In[]: x=tuple(‘materie’) # (‘m’, ‘a’, ‘t’, ‘e’, ‘r’, ‘i’, ‘e’)


In[]: xx=tuple((‘mere’, ‘pere’, nuci))
Tuple
[ ] - se folosesc pentru a indexa/poziționa în tuplă, să ob ținem valoarea de
la un anumit index/poziție. Indexarea în tuplă incepe de la 0.
Ultimul index este -1.

In[]:t=(’a’, 23, ‘b’, ‘efgh’, 5.0)


In[]: t[0] =’a’
In[]: t[-3] =’b’

[start : stop : step ] - selecție din tuplă, start se include, stop se


exclude, ca si cum am avea [start : stop: step)

In[]: t[:] =(’a’, 23, ‘b’, ‘efgh’, 5.0)


In[]: t[1:3] = (23, ‘b’)
In[]: t[1:2] = (23, ) # tupla cu un singur element se termină cu virgulă
In[]: t[: : 2]= (‘a’, ‘b’, 5.0) # step =2
Tuplele sunt IMUTABILE
IMUTABIL = valoarea lor nu se poate schimba

In[]: t[1] = 11 # eroare


TypeError: 'tuple' object does not support item assignment

In[]: t = ('A',) + t[1:] #(‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0)


In[]: t, (1, 2, 3) #((’a’, 23, ‘b’, ‘efgh’, 5.0), (1, 2, 3))
In[]: t[1] # (1, 2, 3)
In[]: t[-1] # (1, 2, 3)
Concatenare pe tuple prin operatorul +
In[]: t=(‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0)
In[]: v=((22, 44), (33, 55))
In[]: z =t+v # z=(‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0, (22, 44), (33, 55))
Repetare tuple prin operatorul *
In[]: t=(’c’, 1)
In[]: x=t*3
In[]: x= (’c’,1, ‘c’, 1, ‘c’, 1)
Metode pe Tuplele
Tuplele sunt IMUTABILE, nu se poate sterge un anumit element dintr-o
tupla.

del() - sterge toata tupla


In[]: t=(‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0, ‘a’, 23, ‘b’)
In[]: del t

count() - returneaza numarul de aparitii a unei valori specifice in tupla


In[]: t=(‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0, ‘a’, 23, ‘b’)
In[]: print(t.count(23))
In[]: 2

index() - gaseste prima aparitie a unei valori si intoarce pozitia ei


In[]: t=(‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0, ‘a’, 23, ‘b’)
In[]: print(t.index(‘b’))
In[]: 3
Tuplele
Se folosesc când vrem să facem swap la valorile a două variabile:

Greșit Clasic Elegant

x=y temp = x (x, y) = (y, x)


x=y
y=x y = temp

Se folosesc când vrem să întoarcem mai multe valori dintr-o funcție:

def cat_rest(x, y):


c = x//y
r = x%y
return (c, r)

In[]: (c, r) = cat_rest(7,5)


Iterații pe Tuple
Funcția care va întoarce valoarea minima, valoarea maxima și numărul
cuvintelor unice dintr-o tuplă:
aTuple= ( (int, string), (int, string), (int,string)) - colec ție de tuple

Soluția: strâng toate valorile int într-o tuplă și toate valorile string în altă tupla

def coldata(aTuple):
nrs = () # cream tupla nrs
cuvs = () # cream tupla cuvs
for t in aTuple:
nrs = nrs + (t[0] ,) # colectez valorile int și concatenez cu +
if t[1] not in cuvs:
cuvs = cuvs + (t[1] ,) # colectez valorile string
mic = min(nrs)
mare = max(nrs)
nr_unice = len(cuvs)
return (mic, mare, nr_unice)
(mic, mare, nr_unice) = coldata(((13, ‘masa’), (7, ‘s’), (4, ‘we’), (99,’s’)))
Set-uri
Secvență neordonată de valori, ce pot reprezenta diferite tipuri de date
eliminând duplicatele.

Set-urile se folosesc pentru testarea de apartenența la o mulțime (membru)


și operații pe mulțimi

Valorile sunt separate de virgulă , și reprezentate între { }

In[]: s={’a’,33, ‘b’, ‘c’, d’, 5.0, 33, ‘c’, ‘f’}


In[]: print(s)
{33, 5.0, 'a', 'c', 'b', 'd', 'f'}

In[]: ss=set( ) # crearea unui set cu 0 elemente cu funcția built-in, NU {}

In[]: 33 in s
True
In[]: ‘p’ in s
False
Set-uri
Seturile sunt colectii neordonate, deci nu putem accesa elementele setului
folosindu-ne de un index

Putem folosi bucla for:

In[]: s={’a’,33, ‘b’, ‘c’, d’, 5.0, 33, ‘c’, ‘f’}


for x in s:
print(x)

‘a’
33
‘b’
‘c’
‘d’
5.0
33
‘c’
‘f’
Metode pe Set-uri
In[]: help(set) # metodele asociate cu tipul de date set

In[]: s={’a’, ‘b’, ‘c’, 1, 22, 33}


In[]: s.add(555) # adaugă setului o nouă valoare
{'a', 'b', 'c', 1, 22, 33, 555}

In[]: s={’a’, ‘b’, ‘c’, 1, 22, 33}


In[]: s.update([5, 55, 555]) # adaugă setului mai multe valori
{'a', 'b', 55, 'c', 5, 1, 22, 33, 555}

In[]: s={’a’, ‘b’, ‘c’, 1, 22, 33}


In[]: s.remove(33) # sterge din set o anumita valoare
{‘a’, 'b', 'c', 1, 22} sau
In[]: s.discard(1) # sterge din set o anumita valoare
{‘a’, 'b', 'c', 22}
In[]: s.pop( ) # elimină o valoare random din set
{‘a’, 'b', 'c', 1, 33}
Set-uri
Set-urile se folosesc pentru operații matematice pe mulțimi: intersecție,
reuniune, diferență, și diferența simetrică.

In[]: a={1, 2 , 3, 4, 6, 8}
In[]: b ={2, 3, 4, 5, 9}

a&b – intersecție and ( și în a și în b)


{2 ,3, 4}

a | b – reuniune or (fie în a, fie în b)


{1, 2, 3, 4, 5, 6, 8, 9}

a – b – diferență - (doar în a și nu în b)
{1, 6, 8}

a ^ b – diferență simetrică xor (în a și în b fără duplicate)


{1, 5, 6, 8, 9}
Liste
Secvență ordonată de valori, ce pot reprezenta diferite tipuri de date

Valorile din listă se numesc elemente sau itemi

Valorile sunt separate de virgulă , și reprezentate între [ ]

In[]: x= [11, ‘unu’, 33, 88, (3,99), [10.0, [2,100.0]], ‘curs’]


In[]: len(x) # numărul de elemente din listă

In[]:l=[] # crearea unei liste de dimensiune 0


In[]:ll= list( ) # crearea unei liste cu funcția built-in

Dacă argumentul funcției built-in list() este o secvență (string, lista sau
altă tuplă), rezultatul va fi o listă cu elementele din secvență

In[]: ex=list(‘materie’) # [‘m’, ‘a’, ‘t’, ‘e’, ‘r’, ‘i’, ‘e’]


Liste
[ ] - se folosesc pentru a indexa/poziționa în listă, să obținem valoarea de
la un anumit index/poziție. Indexarea în listă incepe de la 0.
Ultimul index este -1. Index-ul poate fi o varialibă sau expresie care se
evaluează la int

In[]:t=[’a’, 23, ‘b’, ‘efgh’, 5.0]


In[]: t[0] =’a’
In[]: t[-3] =’b’
In[]: t[1]+1 # 24
In[]: i=2
In[]: t[i-1] #23

[start : stop : step ] - selecție din listă start se include, stop se exclude,
ca si cum am avea [start : stop: step)

In[]: t[:] =[’a’, 23, ‘b’, ‘efgh’, 5.0]


In[]: t[1:3] = [23, ‘b’]
In[]: t[: : 2]= [‘a’, ‘b’, 5.0] # step =2
Listele sunt MUTABILE

MUTABIL = valoarea lor se poate schimba

In[]: t = [‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0]


In[]: t[1] = 11
In[]: t # [‘A’, 11, 23, ‘b’, ‘efgh’, 5.0] – aceeași listă, nu una nou creată

Concatenare pe liste prin operatorul +


In[]: t=[‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0]
In[]: v=[22, 44]
In[]: z =t+v # z=[‘A’, ’a’, 23, ‘b’, ‘efgh’, 5.0, 22, 44]

Repetare pe liste prin operatorul *


In[]: t=[’x’, 15] *3
In[]: x= [’x’,15, ‘x’, 15, ‘x’, 15]
Iterații pe Liste
Calculăm suma elementelor unei liste

Clasic Liste

total = 0 total = 0
for i in range(len(L)): for i in L:
total += L[i] total += i
print(total) print(total)

• elementele din listă sunt indexed 0 to len(L)-1


• range(n) generează de la 0 la n-1

Bucla for pe o lista goală nu execută codul :


for x in []:
print('Nu se execută niciodată')
Metode pe liste
In[]: help(list) # metodele asociate cu tipul de date list

Căteva exemple:
In[]: s=[’a’, ‘b’, ‘c’, 1, 22, 33]
In[]: s.append(555) # adaugă la sfârșitul listei o nouă valoare
['a', 'b', 'c', 1, 22, 33, 555]

In[]: t= [‘mama’, ‘tata’]


In[]: s.extend(t ) # adaugă la sfârșitul listei o nouă listă
[‘a’, 'b', 'c', 1, 22, 33, 555, ‘mama’, ‘tata’] # lista t rămâne neschimbată

In[]: s=[‘mere’, ‘pere’, ‘prune’]


In[]: s.insert(1, ‘gutui’) # adaugă la o anumita pozitie o nouă valoare
In[]: print(s)
['mere’, ‘gutui’,’pere’, ‘prune’]
Metode pe liste
In[]: help(list) # metodele asociate cu tipul de date list

Căteva exemple:

In[]: s= [‘a’, 'b', 'c', 1, 22, 33, 555, ‘mama’, ‘tata’]


In[]: s.remove(33) # șterge prima apariție a valorii
[‘a’, 'b', 'c', 1, 22, 555, ‘mama’, ‘tata’]

In[]: s.pop() # șterge ultimul element din listă


[‘a’, 'b', ‘c’, 1, 22, 555, ‘mama’]

In[]: s.pop(2) # șterge de la indexul valorii


[‘a’, 'b', 1, 22, 555, ‘mama’]

In[]: s= [‘a’, 'b', 'c', 1, 22, 33, 555, ‘mama’, ‘tata’]


In[]: s.clear()
In[]: print(s)
In[]: []
Metode pe liste

Toate metodele pe liste :

https://docs.python.org/3/tutorial/datastructures.html#more-on-lists

L=[9,6,0,3]
sorted(L) # întoarce lista sortată, dar nu modifică lista originală L

L.sort() # întoarce lista sortată, dar modifică lista originală L=[0,3,6,9]

L.reverse() #modifică lista originală, sortănd invers L=[9,6,3,0]


Mutații , Aliasing , Cloning

Listele sunt obiecte MUTABILE – își modifică conținutul

- listele sunt obiecte încărcate în memorie

- numele de variabile pointează către aceste obiecte

- atunci când lucrați cu liste rețineți că apar efecte secundare !


Aliasing
Aliasing: pot avea multiple variabile care pointează către aceeași lista.

Când modific o variabilă și restul vor fi afectate!


Pointează către aceeași structură!

In[]:madona =[‘muzica’, ‘bogată’, ‘blonda’]


In[]:diva =madona # variabila diva este alias pentru variabila madona
In[]: madona
[‘muzica’, ‘bogată’, ‘blonda’]
In[]:diva
[‘muzica’, ‘bogată’, ‘blonda’]
In[]: diva.append(‘sot’)
In[]: diva
[‘muzica’, ‘bogată’, ‘blonda’, ‘sot’]
În[]:madona
[‘muzica’, ‘bogată’, ‘blonda’, ‘sot’]
Print nu e același lucru cu ==

Dacă două liste se printează identic, nu înseamnă că sunt aceeași structură

In[]: madona =[‘muzica’, ‘bogată’, ‘blondă’]


In[]:diva = [‘muzica’, ‘bogată’, ‘blondă’]
In[]: madona
[‘muzica’, ‘bogată’, ‘blondă’]
In[]:diva
[‘muzica’, ‘bogată’, ‘blondă’]

In[]: diva[2] =‘copii’


In[]: diva
[‘muzica’, ‘bogată’, ‘copii’]
In[]:madona
[‘muzica’, ‘bogat’, ‘blondă’]
Cloning
Cloning: creez o nouă lista, copiind fiecare element.
Se face o copie a listei, am două structuri diferite!

In[]: madona =[‘muzica’, ‘bogată’, ‘blondă’]


In[]:diva = madona[:] # se face o copie a listei

In[]: madona
[‘muzica’, ‘bogată’, ‘blondă’]
In[]:diva
[‘muzica’, ‘bogată’, ‘blondă’]

In[]: diva.append(‘copii’)
In[]: diva
[‘muzica’, ‘bogată’, ‘blondă’, ‘copii’]
In[]:madona
[‘muzica’, ‘bogat’, ‘blondă’]
Sort() și Sorted()
sort() modifică lista și nu întoarce nimic return:None

sorted() nu modifică lista și rezultatul este întors într-o variabilă

In[]: madona =[‘muzica’, ‘bogată’, ‘copii’]


In[]: mm=madona.sort() # modifică lista originală
In[]: mm
In[]:
In[]:madona
[‘bogata’, ‘copii’, ‘muzică’]

In[]: cool=[‘muzica’, ‘bogată’, ‘blondă’]


In[]:cc=sorted(cool) # s-a creat o noua lista
In[]:cc
[‘bogata’, ‘copii’, ‘muzică’]
In[]: cool
[‘muzica’, ‘bogată’, ‘blondă’] # nu modifică lista originală
Liste de Liste de Liste

Atenție la liste imbricate !

In[]: c1 =[‘galben’, ‘portocaliu’]


In[]: c2=[ ‘alb’]
In[]: bb=[c1]
In[]:bb
[[‘galben’, ‘portocaliu’]] # listă cu 1 element care e o listă
In[]: bb.append(c2)
In[]: print(bb)
[[‘galben’, ‘portocaliu’], [‘alb’]]
In[]:c2.append(‘roz’) # s-a adăugat la sfârșitul listei c2 o noua valoare
In[]:print(bb)
[[‘galben’, ‘portocaliu’], [‘alb’, ‘roz’]] # aliasing
Modificări & Iterații
Nu faceți modificări pe lista pe care iterați

def remove_dup(L1, L2): def remove_dup_n(L1, L2):


for e in L1: L1_copy = L1[:] # cloning
if e in L2: for e in L1_copy:
L1.remove(e) if e in L2:
L1.remove(e)
In[]: L1 = [1, 2, 3, 4]
In[]: L2 = [1, 2, 5, 6]
In[]: remove_dup(L1, L2)
In[]:L1
[2,3,4] # de ce nu [3,4] ?

De ce?
• Python folosește un counter intern pentru index-ul din buclă
• L1.remove(e)- modifică lungimea listei, dar Python nu updatează
counter-ul intern
• bucla niciodată nu ajunge la elementul 2
Funcții ca obiecte

Putem folosi funcțiile în același mod în care folosim alte tipuri de date:
numere, liste, etc

De ce?
• funcțiile au / întorc un anumit tip de date
• funcțiile pot fi elelemente într-o alta structură de date : listă
• funcțiile pot apărea în expresii
- atribuire
- argument la o altă funcție

Folosim funcții ca argument când sunt cuplate cu liste !


Folosim funcții în interiorul listelor – higher order programming!
Higher order programming
def applyToEach(L, f):
"""L este o listă, f este o funcție
modificăm L prin înlocuirea fiecărui element, e, al listei L cu f(e)"""

for i in range(len(L)):
L[i] = f(L[i])

# gasește elementul L[i], aplică funcția pe acest element f(L[i]) și


modifică lista L, pentru fiecare element L[i]
In[]: L = [1, -2, 3.4]
In[]: applyToEach(L, abs)
[1, 2, 3.4]
In[]:applyToEach(L, int)
[1, 2, 3]
In[]: applyToEach(L, fact) # funcția factorial
[1, 2, 6]
In[]: applyToEach(L, fib) # funcția fibonacci
[1, 2, 13]
Higher order programming

Invers: aplic o listă de funcții unui element:

def applyFuns(L, x):


for f in L:
print(f(x))

In[]: applyFuns([abs, int, fact, fib], 4)

4
4
24
5
Generalizare -Higher order programming
Python: map – o funcție aplicată pe o colecție de date
Exemplu: map(abs, [1, -2, 3, -4])

Rezultă un obiect în cazul acesta o listă, care este un ‘iterator’


Atenție! Nu se pot folosi index pentru a selecta un element din obiectul
‘iterator’.

In[]: for elt in map(abs, [1, -2, 3, -4]):


print(elt)
[1, 2, 3, 4]

General – n- funcții si n colecții de argumente


In[]: L1 = [1, 28, 36]
In[]: L2 = [2, 57, 9]

In[]: for elt in map(min, L1, L2):


print(elt)
[1, 28, 9]
String, Tuple, Range, Liste

Operații comune:
◦ seq[i] # elementul al i-lea din secvența
◦ len(seq) # lungimea secvenței
◦ seq1 + seq2 # concatenare a două secvențe (nu se poate cu range)
◦ n*seq # secvența care se repetă de n ori (nu se poate cu range)
◦ seq[start:end] # slice-ing în secvență
◦ e in seq #True dacă e conținut în secvență
◦ e not in seq # True dacă e conținut în secvență
◦ for e in seq # iterează peste elementele din secvență

Exact aceleași operații indiferent de tipul de date! Viva Python!

String, Tuple, Range – IMUTABLE


Liste - MUTABILE
Dicționar

Generalizarea listelor:

nume = ['Ana', 'Ion', 'Dan', 'Radu']


note = [10, 9, 10, 7]
hobby= [‘citit’, ‘sport’, ‘muzica’, ‘calatorii’]

Greu de accesat datele asociate:


- listele trebuie sa fi de aceeași dimensiune
- listele se modifică

Vreau să indexez datele direct- și nu doar după un index de tip int

Vreau să folosesc o singura structură, nu liste separate


Dicționar

Colecție de itemi ce reprezintă perechea cheie:valoare, unei chei îi


corespunde o singură valoare – o mapare

Itemii pot reprezenta diferite tipuri de date

Perechile cheie:valoare sunt separate de virgulă , și reprezentate între { }

In[]:note={‘Ana’: 10 , ‘Ion’: 9, ‘Dan’: 10, ‘Radu’: 7}

In[]:d={} # crearea unui dicționar de dimensiune 0


In[]:dd= dict( ) # crearea unui dicționar cu funcția built-in

Dicționarele sunt MUTABILE =se pot modifica


Operații pe Dicționar
In[]:note={‘Ana’: 10 , ‘Ion’: 9, ‘Dan’: 10, ‘Radu’: 7}

Indexarea se face după cheie, se întoarce valoarea asociată cu acea


cheie (nu trebuie sa știu ordinea lor !)

In[]: note[‘Radu’]
7
In[]:note[‘Maria’] # KetError: ‘Maria’ - nu există cheia în dicționar

In[]: len(note) # numărul de itemi din dicționar, întoarce 4

In[]: note[‘Elena’] =6 #adaug un item în dicționar

In[]: ‘Ion’ in note # testez dacă există cheia Ion , întoarce True

In[]: del(note[‘Dan’])
note={‘Ana’: 10 , ‘Ion’: 9, ‘Radu’: 7, ‘Elena’:6}
Metode pe Dicționar

In[]:note={‘Ana’: 10 , ‘Ion’: 9, ‘Dan’: 10, ‘Radu’: 7}

In[] : note.keys() #întoarce un obiect iterabil, tupla tuturor cheilor


dict_keys( ['Ana','Dan', ‘Ion’, 'Radu'])

In[]: note.values() #întoarce un obiect iterabil, tupla tuturor valorilor


dict_values([10,10, 9 7])

Ordinea itemilor nu contează!


Cheie:Valoare în Dicționar

cheile:
• trebuie să fie unice
• să fie de un tip de date immutabil ( int, float, string, tuple,bool )
• ATENȚIE la tipul de date float

valorile:
• pot fi duplicate
• orice tip de date (immutabile sau mutabile)
• pot fi liste, chiar alte dicționare!

Ordinea cheilor sau a valorilor nu contează!

In[]: d = {4:{1:0}, (1,3):"twelve", 'const':[3.14,2.7,8.44]}


Listă vs Dicționar

List vs Dicționar

secvență ordonată de elemente pereche “keys” la “values”

indexarea elementelor dupa index int indexare item cu item,după cheie

indicii au o ordine nicio ordine

indexul este intreg cheile pot fi de orice tip immutabil


Exemple:3 Funcții să cream/ folosim Dicționar

Vrem să analizăm versurile unui cântec:

1) creăm un dicționar - frecvența cuvintelor, o mapare: str: int

2) găsim cuvântul care apare de cele mai multe și de câte ori apare
• utilizăm o listă, în cazul în care există mai mult de un cuvânt
• returnează o tuplă (listă, int) pentru (lista_cuv, frecvența_mare)

3) găsim cuvintele care apar de cel puțin X ori


• utilizatorul alege parametrul "cel puțin X ori"
• returnează o listă de tuple, fiecare tuplă este o (listă, int)
care conține lista de cuvinte ordonate după frecvența lor
• Soluția: Din dicționarul cântecului găsesc cuvântul cel mai frecvent,
apoi îl șterg, repet procedura. Funcționează pentru că dicționarul cântec
este MUTABIL.
Creez Dicționarul

she_loves_you = ['she', 'loves', 'you', 'yeah', 'yeah', 'yeah',


'she', 'loves', 'you', 'yeah', 'yeah', 'yeah',
'she', 'loves', 'you', 'yeah', 'yeah', 'yeah',

'you', 'think', "you've", 'lost', 'your', 'love',


'well', 'i', 'saw', 'her', 'yesterday-yi-yay',
"it's", 'you', "she's", 'thinking', 'of',
'and', 'she', 'told', 'me', 'what', 'to', 'say-yi-yay',

''you', 'know', "it's", 'up', 'to', 'you',


'i', 'think', "it's", 'only', 'fair',
'pride', 'can', 'hurt', 'you', 'too',
'apologize', 'to', 'her', ]
Creez Dicționarul

# versuri –string, șir de caractere separate prin spațiu sau alte caractere

def frecvența_cuvintelor(versuri):
myDict = {}
for cuv in versuri: # iterez pe o listă de string-uri
if cuv in myDict: # iterez pe o cheile din dicționar
myDict[cuv] += 1 # modific valorile asociate cu cheile
else:
myDict[cuv] = 1 # prima dată când găsesc cuv, dau valoarea1
return myDict

In[]: beatles=frecventa_cuvintelor(she_loves_you)

[( [‘you’], 36), ([‘yeah’], 28), ([‘she’], 20), ([‘loves’], 13).....([‘love’], 5)


Creez Dicționarul

In[]: beatles={‘because’: 1,
‘yes’:1,
‘a’:4,
‘ almost’: 1,
‘bad’: 3,
‘ can: 1,
‘can’t’:3,
‘her’:4,
‘hurt’:2
}

Toate cuvintele din cântecul she_loves_you


Folosesc Dicționarul

# am dicționarul și vreau să aflu cele mai comune cuvinte

def cel_mai_comun_cuvânt(dct):
val = dct.values() # iterabil , știu că sunt de tip int
best = max(val) # pot aplica funcții built-in pe iterabil
cuvs = [] # aflu cuvintele care au valoarea best
for k in dct:
if dct[k] == best: # iterez pe cheile din dicționar
cuvs.append(k) # adaug la listă
return (cuvs, best)

In[]:(cuv,best) =cel_mai_comun_cuvânt(beatles)
In[]: cuv
[‘a’, ‘like’, ‘her’, ‘with’]
In[]: best
4
Folosesc Dicționarul

# vreau să aflu cuvintele ce apar mai des

def cuv_dese(dct, minTimes):


rez = [] # creez o listă goală
gata = False
while not gata:
temp = cel_mai_comun_cuvânt(dct)
if temp[1] >= minTimes: # dacă apar mai mult de minTimes
rez.append(temp) # îl adaug la lista rezultat
for w in temp[0]: # șterg cuvântul din dicționar
del(dct[w])
else:
gata = True
return rez

In[]: print(cuv_dese(beatles, 5)) # cuvintele ce apar mai mult de 5 ori


Fibonacci - Ineficient

fib(0)=fib(1)=1
fib(n) = fib(n-1) + fib(n-2)

fib(6) = fib(5)+fib(4)
fib(5) = fib(4)+fib(3)
fib(4) = fib(3)+fib(2)
fib(3) = fib(2)+fib(1)
fib(2) = fib(1)+fib(0)

Recursiv: recalculează aceeași valoare de mai multe ori!

Folosim dicționare: putem salva valorile deja calculate !


Fibonacci– Eficient cu dicționar

def fib_eficient(n, d):


if n in d: # dacă n e deja în dicționar
return d[n] # întorc acea valoare
else:
rez = fib_eficient(n-1, d) + fib_eficient(n-2, d)
d[n] = rez
return rez

In[]: d = {0:1, 1:1} # setez cazurile de bază


In[]: print(fib_eficient(6, d))

Timp de calcul mai mic decât în cazul recursiv!


Fibonacci– Eficient cu dicționar
def fib_eficient(n, d):
global numFibCalls # țin cont de câte ori s-a aplelat functia
numFibCalls + =1
if n in d: # dacă n e deja în dicționar
return d[n] # întorc acea valoare
else:
rez = fib_eficient(n-1, d) + fib_eficient(n-2, d)
d[n] = rez
return rez

In[]: numFibCalls =0
In[]: print(fib_recursiv(12)) # am setat global numFibCalls și în recursiv
In[]: print(‘am apelat de atârea ori’, numFibCalls)

In[]: numFibCalls =0
In[]: d = {0:1, 1:1} # setez cazurile de bază
In[]: print(fib_eficient(12, d))
In[]: print(‘am apelat de atârea ori’, numFibCalls)
Fibonacci– Eficient cu dicționar
In[]: fib_recursiv(12))
In[]: am apelat de atârea ori 287

In[]: print(fib_eficient(12, d))


In[]: am apelat de atârea ori 21

In[]: fib_recursiv(30))
In[]: am apelat de atârea ori 1664079

In[]: print(fib_eficient(30, d))


In[]: am apelat de atârea ori 57

In[]: fib_recursiv(34))
In[]: am apelat de atârea ori 11405773

In[]: print(fib_eficient(34, d))


In[]: am apelat de atârea ori 65
Graf -uri

Un graf in matematică/ informatică este definit ca o rețea formată din


noduri( vertices) care pot sau nu să fie conectate între ele.

Liniile/ calea care conectează două noduri se numește muchie (edges).

Dacă muchiile au o anumită direcție de parcurgere, atunci avem un grafic


direcționat.

Dacă nu sunt specificate direcții, graficul este denumit grafic


nedirecționat.

Principiile grafurilor/teoriei grafurilor sunt importante special în


domeniul Data Sciences adesea folosite pentru a modela problemele
legate de viața reală: rețelele sociale, studiile moleculare în chimie și
biologie, hărți, sistemul de recomandare.
Graf -uri
Implementarea grafurilor folosind dictionare:

graf = { "a" : ["c", "d"],


"b" : ["d", "e"],
"c" : ["a", "e"],
"d" : ["a", "b"],
"e" : ["b", "c"] }
def muchii(graf):
m = []
for nod in graf:
for vecin in graf[nod]:
m.append((nod, vecin))
return m

print(muchii(graf))

[('a', 'c'), ('a', 'd'), ('b', 'd'), ('b', 'e'), ('c', 'a'), ('c', 'e'), ('e', 'b'), ('e', 'c'),
('d', 'a'), ('d', 'b')]
Graf -uri

Puteți face lucruri interesante cu graf-uri:


-să găsiți dacă există o cale între două noduri
-să găsiți calea cea mai scurtă intre două noduri, determinând ciclurile
din graf.

Faimoasa "problemă a vânzătorilor care călătoresc" este, de fapt, găsirea


celei mai scurte rute posibile care vizitează fiecare nod exact o dată și se
întoarce la punctul de plecare.

Uneori, nodurile sau muchiile unui graf au atribuite costuri (weights).

De ex: atribuind un nivel de dificultate pentru a parcurge graful și sunteți


interesat să găsiți calea cea mai ieftină sau cea mai ușoară.
TEMA

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Case Study : Capitolul 13


13.1-13.6
DE CITIT!

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Liste - Capitolul 10
Dicționare -Capitolul 11
Tuple - Capitolul 12
DE CITIT!

Tuple/Liste/Dictionare
https://docs.python.org/3/tutorial/datastructures.html
Curs 5

Limbajul PYTHON
Exemple/Exerciții
While Loop
In programare, traducerea cuvântului while (cât timp) vă spune că e vorba
de un interval de timp, iar loop însemna că ceva se repetă

Când trebuie să implementați o buclă while trebuie să țineți cont că se va


executa o secvență de cod de mai multe ori “while” o condiție este
adevarată.

Aveți nevoie de:


1. cuvântul cheie while
2. o condiție care va fi True sau False
3. un bloc de code pe care să-l executați repetitiv

Folosind bucla while scrieți codul care va afișa:


Multumesc
Multumesc
Multumesc
While Loop

Exemplu de implementare:

#Setez o variabilă:
nr=2

# Condiția de ieșire din buclă


while nr < 5:
print("Multumesc")
# Incrementez valoarea variabilei “nr” cu 1
nr + = 1
While Loop

Putem adăuga conțidii mai complexe în bucla while folosind if/else.

Folosind operatorul % modificați acest code să printeze:

Numărul 2 este par


Numarul 3 este impar
Numarul 4 este par
While Loop

nr = 2

while nr < 5 :
# gasesc restul împărțirii la 2
if nr % 2 == 0:
print(Numărul "+str(nr)+" este par")
else:
print("Numărul "+str(nr)+" este impar")

# Incrementez variabile nr cu 1
nr+ = 1
For Loop
In programare, traducerea pentru for loop este că repetiția execu ției unui
code se va face pentru un anumit număr de ori.

Când trebuie să implementați o buclă for trebuie să țineți cont că se va


executa o secvență de cod de mai multe ori , acest număr de ori find
specificat intr-o secvență (o enumerare ordonată)

Aveți nevoie de:


1. cuvântele cheie for in
2. o variabilă
3. pentru a crea secvența ordonată folosim funcția range()
4. un bloc de code pe care să-l executați repetitiv

for nr in range(3):
print("Multumesc")
For Loop

for nr in range(5,10,2) :
print("Eu sunt : "+str(nr))

Eu sunt: 5
Eu sunt: 7
Eu sunt: 9

Scrieti functia desenez(n) care va printa:


Hint: E o structura simetrică, desenez primele n randuri
*
**
***
****
***
**
*
For Loop
def desenez(n):
for i in range(n):
for j in range(i):
print("* ", end="") # end=”” este pentru a trece la randul următor
print(" ")

for i in range(n,0,-1):
for j in range(i):
print("* ", end="")
print(" ")
desenez(4) # apelul functiei
* 0 # am inlocuit cu print(j, end=””)
** 01
*** 012
**** 0123
*** 012
** 01
* 0
Apel de funcție în altă funcție - decompoziție

Scrieți funcția sum_patrate(x, y, z=10) care calculează suma ariilor unor


pătrate.

Faceți apelul funcției.

Printați rezultatul.
Apel de funcție în altă funcție - decompoziție

def patrat(x):
y=x*x
return y

def sum_patrate(x, y, z=10):


a = patrat(x)
b = patrat(y)
c = patrat(z)
return a + b + c

rez1 = sum_patrate(-3, 5)
rez2 = sum_patrate(-3, 5, 20)
print(rez1,rez2)

# variabile locale, globale, pasarea parametrilor


Strings
# String-urile- secvența de caractere,

# for iterează după fiecare caracter automat. item-by-item :


for ch in "Filme SF":
print(ch)

# for iterează după poziția fiecărui caracter, by-index :


x = "pricomigdale"
for idx in range(len(x)):
print(x[idx])

# while iterează după poziția fiecărui caracter, by-index:


x = "pricomigdale"
pos = 0
while pos < len(x):
print(x[pos])
pos = pos + 1
Strings
# concatenarea string-urilor se face cu operatorul +

def faraVocale(s):
voc = "aeiouAEIOU"
vs = ""
for c in s:
if c not in voc : # în loc de not in cum puteam să mai scriu ?
vs = vs + c
return vs

print(faraVocale("Acum plec la munte"))

Ce se va printa in exemplul următor?


s = "sport"
r = ""
for item in s:
r = item.upper() + r
print(r)
Strings

Scrieți funcția care calculează de câte ori o anumită literă apare intr-un
string.
Strings

def nr_char(text, ch):


nr = 0
for c in text:
if c == ch:
nr = nr + 1
return nr

print(nr_char("abra ca dabra","a"))
Liste

Ce se va printa?

a = [ [4, [True, False], 6, 8], [888, 999] ]


if a[0][1][0]:
print(a[1][0])
else:
print(a[1][1])
Liste și String-uri
s=”Am un sir de caractere”
print(list(s))
['A', 'm', ' ', 'u', 'n', ' ', 's', 'i', 'r', ' ', 'd', 'e', ' ', 'c', 'a', 'r', 'a', 'c', 't', 'e', 'r',
'e']

#Implicit, orice număr de caractere goale este considerat delimitare


s = "Ce mare caldă și albastră și ce saltă albatroșii în Italia "
ws = s.split()
print(ws)
['Ce', 'mare', 'caldă', 'și', 'albastră', 'și', 'ce', 'saltă', 'albatroșii', 'în',
'Italia']

# orice sir de caractere se poate utiliza ca delimitare.


s = "Ce mare caldă și albastră și ce saltă albatroșii în Italia ..."
wal = s.split(‘al’)
print(wal)
['Ce mare c', 'dă și ', 'bastră și ce s', 'tă ', 'batroșii în It', 'ia']
Liste și String-uri
Inversul metodei split() este join().
Alegeți orice șir de caractere ca element de legătură între
elementele din lista

s = ["rosu", "alb", "verde"]


sep = ';'
ss = sep.join(s) # puteam să scriu ss=’;‘.join(s)
print(ss)
roșu;alb;verde

print("***".join(s)) print(‘ ‘.join(s))


roșu***alb***verde rosualbverde

listanr = [1, 2, 3]
ss = ''.join(str(n) for n in listanr)
print(ss)
Liste
Care e diferența între funcțiile extend () și append() ?

extend()- ia un obiect iterabil (o listă, un set, o tuplă sau un șir de


caractere) și adaugă fiecare element din obiectul iterabil unul câte
unul.
x=[1,2,3]
x.extend([4,5])
print(x)
[1,2,3,4,5]

append()- adaugă argumentul său la sfârșitul listei ca un singur


element, ceea ce înseamnă că atunci când funcția append () are un
argument iterabil ca argument, îl va trata ca un singur obiect.
x.append([4,5])
print(x)
[1,2,3,4,5,[4,5]]
Liste
De fiecare dată când aveți nevoie de o funcție care crează și
întoarce o listă folosiți următorul patern:

initializați variabila rezultat la o listă goală


bucla ( for sau while)
creați un element nou
adăugati-l la rezultat folosind funcția append() sau concatenare +
return rezultat

def dublu(lin):
""" întoarce în lista lout - elementele double din lista lin """
lout = []
for v in lin:
elnou = 2 * v
lout.append(elnou)
return lout
Liste

Scrieți funcția care întoarce lista numerelor mai mici ca n,


divizibile cu 3 și cu 7
Liste

def div3_7(n):
""" intoarce lista numerelor mai mici ca n, divizibile cu 3 și 7 """
rez = []
for i in range(2, n):
if i%3 ==0 and i%7 ==0:
rez.append(i)
return rez

rrr=div3_7(118)
[21, 42, 63, 84, 105]
List comprehension
List comprehensions este modalitatea concisă pentru a crea liste.
Sintaxa generală este:

[<expression> for <item> in <sequence> if <condition>]

Clauza if este optională

m = [1,2,3,4,5]
rez = [item * 2 for item in m] # dublu elementelor din lista m

def div3_7(n):
re = [i for i in range(2,n) if i %3 ==0 and i%7 ==0]
return re

r=div3_7(118)
Functii Lambda
List comprehensions este modalitatea concisă pentru a crea liste.
Sintaxa generală este:
[<expression> for <item> in <sequence> if <condition>]

lis= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


[(lambda x: x*x)(x) for x in lis]

Funcțiile lambda sunt funcții anonime construite la timpul de


execuție :
lambda argumente: expresie
Poate avea oricate argumente, dar o singura expresie

Ca și cum am scrie:

f = lambda x: x*x
[f(x) for x in range(10)]
Functii Lambda

Avantajul functiilor lambda sunt evidentiate când folosesc o


funcție anonima în corpul altei funcții.

def mmm(n):
return lambda a : a * n

ddd = mmm(2)

print(ddd(11))
List comprehension

Folosesc funcția count() dacă vreau să număr apariția unui anumit


element intr-o listă:

li= [‘a’,’b’,’b’]
li.count(‘b’) # va fi 2

list= [‘a’,’b’,’b’, ‘c’, ‘b’]


[[x,list.count(x)] for x in set(list)]

# am convertit list – un obiect de tip listă la un obiect de tip set – să


rețin doar elementele unice!
List comprehension

Scrieți funcția care separă o listă în părți egale (rezultă bucăți


egale, succesive din lista originală ):

Exemplu:
X = [1,2,3,4,5,6,7,8,9]
[(1,2,3), (4,5,6), (7,8,9)]

Hint! Folosiți slicing [start:stop:step]


List comprehension

z = list(range(0, 50)) # funcția list crează lista elementelor


nbuc = 5
[z[i:i + nbuc] for i in range(0, len(z), nbuc)]

print(list([z[i:i + nbuc] for i in range(0, len(z), nbuc)]))

[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18,
19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29], [30, 31, 32, 33, 34],
[35, 36, 37, 38, 39],[40, 41, 42, 43, 44], [45, 46, 47, 48, 49]]
List
Folosind functiile zip() și iter() separ o listă în părți egale:

x = [1,2,3,4,5,6,7,8,9]
y = zip(*[iter(x)]*3) # impart lista x în 3
list(y) # folosesc metoda list() să printez rezultatul metodei zip()
[(1,2,3), (4,5,6), (7,8,9)]

iter () este un iterator pe o secvență

[iter (x)] * 3 produce o listă cu trei obiecte list-iterator: fiecare


iterator al listei este un iterator de x

* care este trecut la început în funcția zip () - despachetează


secvența în argumente, astfel încât să trecând același iterator de trei
ori în funcția zip () să extrageți de fiecare dată un element din
iterator.
Ce se întamplă pas cu pas : List
1. Aveți 3 obiecte de tip list iterator, cam așa :
[1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]

2. La primul apel , zip() va extrage un element din list secvențial:


[1][2][3]
Atenție! Obiectul iterator ține cont care este următorul element în
iterație!

3. A doua oară, elementele vor fi adăugate la cele 3 liste pe care le-


ați creat:
[1, 4], [2, 5], [3, 6]

4. Ultimul apel:
[1, 4, 7], [2, 5, 8], [3, 6, 9]

5. Zipping aceste 3 liste împreună:


[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Liste vs Tuple
Tuplele sunt folosite pentru a colecta o secvența ordonata de
elemente (sunt imutabile):

- NU se pot adauga elemente noi unei tuple. Tuplele NU au


metodele: append() sau extend()
- NU se pot elimina elemente dinr-o tuple. Tuples NU au metodele
remove() sau pop()

- Pot sa găsesc elementele intr-o tuple


- Pot sa folosesc operatorul in sa verific dacă un element exista
intr-o tuple.

Astfel dacă definiți un set constant de valori și tot ce vreți să faceți


e să iterați prin acest set, indicat e să folosiți o tuple în loc de o listă
– va fi mai rapid și mai sigur, tuplele conțin date “write-protect”.
Convertesc Liste la Dicționar
l=['one',1,'two', 2, 'three', 3]
Vreau să creez dicționarul {‘one’:1, ‘two’:2, ‘three’:3}

Selectăm folosind slicing [start:stop:step ] și pasăm funcției zip()


Funcția zip() creează tuple de 1 element ale elementelor din listă

p=list(zip(l)) #am folosit list() să pot printa rezultatul funcției zip()


[('one',), (1,), ('two',), (2,), ('three',), (3,)]

d=dict(zip(l[0::2],l[1::2])) # {'one': 1, 'three': 3, 'two': 2}

Dacă lista e mare, și nu știu nr de elemente folosesc funcția iter()


ll=[11, 2, 33, 4, 55, 6, 77]
a=iter(ll)
dd= dict(zip(i,i)) # {11:2, 33:4, 55:6}
Obiectul iterator ține informația unde ești exact în iterație
# print(a) list_iterator object at 0x7f9142f56f28>
Manipularea fișierelor text

Folosim funcția open cu diferite argumente:

fid=open(filename,'r') - deschide un fișier numit filename pentru citire


(read). Funcția open va întoarce o referință la un object de tip fișier.

fid= open(filename,'w') - deschide un fișier numit filename pentru


scriere ( write). Funcția open va întoarce o referință la un object de tip
fișier.

fid.close() - s-a incheiat folosirea obiectului de tip fișier.

Obiectul de tip fișier (fid) are mai multe metode asociate:

readline(n) , readlines(n), read(n), write(sir de caractere)


Manipularea fișierelor text
Metode asociate obiectului de tip fișier (fid) :

readline(n) – returnează linia din fișier - citește toate caracterele până (și
inclusiv ) întâlnește caracterul de linie nouă \n. Dacă n este specificat ca
parametru, numai n caractere vor fi returnate dacă linia este mai lungă
decât n.

readlines(n) -returnează o listă de string-uri, fiecare element din listă


reprezentând o singură linie a fișierului. În cazul în care n nu este
specificat atunci toate liniile de fișier sunt returnate. În cazul în care n este
specificat, doar n caractere sunt citite, dar n este rotunjit, astfel încât o
întreagă linie este returnată.

read(n) - citește și returnează un șir de n caractere, sau întregul fișier ca


un singur șir dacă n nu este specificat.
write(ceva) -adaugă șirul de caractere “ceva” la sfârșitul fișierului. fid
trebuie să se refere la un fișier care a fost deschis pentru scriere.
Manipularea fișierelor text

Fisier de date: CTdata.txt:

Maria Georgescu CT CLE 135 222 1576 6 9 60.8% 74.5


Ion Vasilescu CT TB 291 474 3451 25 6 61.4% 95.9
Mihai Voinescu CT PHI 233 372 3018 21 6 62.6% 100.2
Marius Pop CT HOU 365 574 4370 24 12 63.6% 92.0
Paul Rizea CT SD 357 541 4710 30 13 66.0% 101.8
Mark Harbu CT SEA 266 444 3001 12 17 59.9% 73.2
George Clav CT CAR 157 299 1558 3 9 52.5% 58.4
Diana Florea CT BAL 306 489 3622 25 10 62.6% 93.6
Carmen Tudose CT DEN 293 498 3653 20 9 58.8% 87.5
Manipularea fișierelor text
fid = open("CTdata.txt", "r")
olinie = fid.readline()
olinie
'Maria Georgescu CT CLE\t135\t222\t1576\t6\t9\t60.8%\t74.5\n'

fid = open("CTdata.txt", "r")


lista_linii = fid.readlines()
print(len(lista_linii))
34
print(lista_linii[0:4])
['Maria Georgescu CT CLE\t135\t222\t1576\t6\t9\t60.8%\t74.5\n',
'Ion Vasilescu CT TB\t291\t474\t3451\t25\t6\t61.4%\t95.9\n',
'Mihai Voinescu CT PHI\t233\t372\t3018\t21\t6\t62.6%\t100.2\n',
'Marius Pop CT HOU\t365\t574\t4370\t24\t12\t63.6%\t92.0\n']
Manipularea fișierelor text
fid = open("CTdata.txt", "r")
sir = fid.read()
print(len(sir))
1233
print(sir[:256])
Maria Georgescu CT CLE 135 222 1576 6 9 60.8% 74.5
Ion Vasilescu CT TB 291 474 3451 25 6 61.4% 95.9
Mihai Voinescu CT PHI 233 372 3018 21 6 62.6% 100.2
Marius Pop CT HOU 365 574 4370 24 12 63.6% 92.0
Pau

Rețineți! Trebuie să redeschideți fișierul înainte de fiecare citire, astfel


încât să pornim de la început.
Fiecare fișier are un marker care indică poziția de citire curentă în fi șier.
De fiecare dată când una dintre metodele de citire este apelată markerul
este mutat la caracterul imediat după ultimul caracter returnat
( readline() se mută pe primul caracter al următorului rând din fișier, iar
la readlines () se mută la sfârșitul fișierului
Manipularea fișierelor text

Cum citesc tot fișierul :

fid = open("CTdata.txt", "r")


linie = fid.readline() # variabile linie e de tip str
while linie: # linie goală are doar caracterul \n
val = linie.split() # aplic metoda split() a obiectului str
print('CT ', val[0], val[1], 'are ', val[10] )
linie = fid.readline() # să nu am buclă infinită

fid.close()
Dicționare

Ce se va printa?

zoo = {"maimute":12, "lupi":6, "zebre":23}


zoo["lei"] = zoo["maimute"] + zoo["lupi"]
print(zoo["lei"])
Metode pentru Dicționare

keys (none) - întoarce un obiect iterabil cu cheile din dicționar

values(none) - întoarce un obiect iterabil cu valorile din dicționar

items (none) - întoarce un obiect iterabil cu perechile


(cheie,valoare) din dicționar

get(key) - întoarce valorile asociate cu cheile din dicționar


Exemple Dicționare

cutii = {'mere': 430, 'banane': 312, 'portocale': 525, 'pere': 217}

for ak in cutii.keys():
print("Ptr cheia", ak, "am valoarea", cutii[ak])

ks = list(cutii.keys())
print(ks)

print(list(cutii.values()))
print(list(cutii.items()))

Ce se va printa?
Exemple Dicționare

Definiti un dicționar ale carui chei să fie diverse fructe.

Scrieți functia care va printa valoarea unei anumite chei


Exemple Dicționare
cutii = {'mere': 430, 'banane': 312, 'portocale': 525, 'pere': 217}

def iauv(d)
for k in d:
print(d[k])

Ce se va printa?
iauv(cutii) # ordinea cheilor nu e definita
430
525
312
217

Iterațiile în dicționar implicit se fac asupra cheilor sale (nu e nevoie


sa apelăm metoda keys()).

Cum fac să fie aranjate într-o anumită ordine?


Exemple Dicționare

animale = {"cai":12, "dromaderi":6, "elefanți":23, "bizoni":20}


kl = list(animale.keys())
kl.sort()
print(kl[3])

Ce se va printa?
Exemple Dicționare

total = 0
zoo = {"cai":12, "dromaderi":6, "elefanti":23, "bizoni":20}
for ak in zoo:
if len(ak) > 3:
total = total + zoo[ak]
print(total)

Ce se va printa?
Exemple Dicționare
Scrieți un program care permite utilizatorului să introducă un șir de
caractere, apoi printează un tabel cu literele alfabetului care apar în
șir împreună cu numărul de apariție a fiecărei litere ( se afișează în
ordine alfabetică).

O rulare a programului arătă astfel:


x=”Am acasa doi copii”
a4
c2
d1
i3
m1
o2
p1
s1
Exemple Dicționare
Știți să numărați de câte ori apare o litera într-un cuvânt:

def nr_a(cuv): def nr_toate (text):


alfabet='abcdefghijklmnopqrstuvwxyz'
nr = 0 nrL = {}
for c in cuv: for c in text:
if c == 'a': if c in alfabet:
if c in nrL:
nr = nr + 1 nrL[c] = nrL[c] + 1
return nr

else:
nrL[c] = 1
return nrL

print(nr_a("banana"))
Dicționare

x = input("Introduceți un text x=")

x = x.lower() # convertesc tot șirul de caractere la lowercase

toateLit=nr_toate(x) # îmi creez dicționarul apelând fct nr_toate(x)

lchei = list(toateLit.keys()) # convertesc la lista obiectul iterabil

lchei.sort() # aranjez literele în ordine alfabetică

for c in lchei: # iterez pe lista literelor sortate


print(c, toateLit[c]) # afișez
TEMA

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Case Study : Capitolul 13


13.1, 13.3, 13.4 -de codat
13.2 , 13.5, 13.6 - de citit
Curs 6

Limbajul PYTHON
Exemple/Exerciții
Recursivitate
Exemple while

Scrieți funcția calcul1(n)care va afișa rezultatul

S= 1*2 +2*3+3*4+….n*(n+1)
Exemple while

def calcul1(n):

#calculez s=1*2 +2*3+3*4 +....n+(n+1)

s=0
i=1
while i<=n:
s=s+i*(i+1)
i=i+1
return s
Exemple while

Scrieți funcția calcul2(n)care va afișa rezultatul

S= 1+1*2 +1*2*3+1*2*3*4+….1*2*3...*n
Exemple while

def calcul2(n):

#calculez s=1+1*2 +1*2*3+1*2*3*4 +....1*2*3*4...*n-1*n

s=0
p=1
i=1
while i<=n:
p=p*i
s=s+p
i=i+1
return s
Exemple while

Scrieti functie calcul3(n) care determina numarul de aparitii al ultimei


cifre din numarul n
Exemple while

def calcul3(n):

#calculez numarul de aparitii al ultimei cifre in numarul n


p=0
a=n%10
while n !=0:
c=n%10
n=n//10
if c==a:
p=p+1
return p
Exemple while

Scrieți o funcție care primește ca argumente un string format din mai


multe cuvinte și un număr întreg „n”.
În urma apelării funcției se va determina câte vocale conține al „n-lea”
cuvânt.
Dacă numărul n introdus este mai mare decât numărul total de cuvinte din
acel string, în urma apelării funcției va fi afisat mesajul „ Nr introdus
este mai mare decat limita admisa!Apelati din nou functia”.
Exemple while

def vocale_cuvant_n(string, numar):


t = string.split()
if numar > len(t):
print('Nr introdus este mai mare decat limita admisa!Apelati din nou
functia')
else:
nr_vocale = 0
for v in t[numar-1]:
if v == "a" or v == "e" or v == "i" or v == "o" or == "u":
nr_vocale += 1
return nr_vocale
List Comprehension

Scrieți funcția primInterval()care va afișa toate numerele prime dintr-un


anumit interval [a, b].

Exemplu: fac apelul functiei primInterval(10,50)

11,13,17,19,23,29,31,37,41,43,47
List Comprehension

def primInterval (a, b):


for num in range(a,b + 1):
if num > 1:
for i in range(2,num): #for i in range(2,num):
if num % i == 0: # if num%i ==0:
break # return False
else: # return True
print(num)
List Comprehension

Gasesc lista numerelor mai mici ca 50 care NU sunt prime:

nuprime = [j for i in range(2, 8) for j in range(i*2, 50, i)]

nuprime=[4,6,8...48,6,9,12,15…48,8,12,16,20...48,10,15,20..45,
12,18...14,21,49]

Facem inversa listei nuprime =lista numerelor mai mici ca 50 care SUNT
prime:

prime = [x for x in range(2, 50) if x not in nuprime]

prime
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
Liste

Se dau doua liste ce reprezinta anii si numele persoanelor:

a = [51, 52, 2, 4, 10]

n = ['Paul', 'Maria', 'Emma', 'George', 'Vasile']

Scrieti functia varsta(a,n) care in urma apelului va afisa:

Buna Paul! ai trait pe aceasta planeta 18615 zile!


Buna Maria! ai trait pe aceasta planeta 18980 zile!
Buna Emma! ai trait pe aceasta planeta 730 zile!
Buna George! ai trait pe aceasta planeta 1460 zile!
Buna Vasile! ai trait pe aceasta planeta 3650 zile!
Liste

Se dau doua liste a si n ce reprezinta anii si numele persoanelor:

def varsta(a,n):
for i in range(0, len(n)):
nume = n[i]
ani = a[i]
zile = ani*365
print('Buna ' + nume + '! ai trait pe aceasta planeta ' + str(zile) + '
zile!')

a = [51, 52, 2, 4, 10]

n = ['Paul', 'Maria', 'Emma', 'George', 'Vasile']

varsta(a,n)
Liste

Se da urmatorul text: Invatat e omul care nu termina niciodata de invatat

Folosind functiile buit-in map() si lambda() precum si metodele asociate


tipului de date string creati urmatoarea lista:

['INVATAT', 'invatat', 7]
['E', 'e', 1]
['OMUL', 'omul', 4]
['CARE', 'care', 4]
['NU', 'nu', 2]
['TERMINA', 'termina', 7]
['NICIODATA', 'niciodata', 9]
['DE', 'de', 2]
['INVATAT', 'invatat', 7]
Liste

text = “Invatat e omul care nu termina niciodata de invatat”

cuv =text.split()

rez=map(lambda w: [w.upper(), w.lower(), len(w)], cuv)

# rezultatul functiei map e un obiect iterabil

for i in rez:
print(i)
Liste

Scrieți codul funcției valMediana( L) care îndeplinește următoarea


specificație:

” ” ”Daca lista L are număr impar de elemente întoarce valoarea mediana,


Daca lista L are număr par de elemente întoarce media celor doua valori
mediene “””

Indicații: folosiți metoda sort() și pentru că această metodă modifică lista


trebuie mai intăi să faceti o copie a listei. Pentru a verifica dacă
numarul de elemente din lista este impar trebuie să folositi operatorul
mod pe lungimea listei.
Liste

def findMedian(L):
cc = L[:]
cc.sort()
if len(cc) % 2 == 1:
return cc[len(cc) / 2]
else:
return (cc[len(cc) / 2] + cc[len(cc) / 2 - 1]) / 2
Strings

Scrieți un script în care:


-folosind funcția input() cereți utilizatorului să introducă două cuvinte.
-folosind bucla for programul va printa literele comune din cele doua
cuvinte.

Indicatii: Creați un string gol. Cand gasiti un caracter comun celor doua
cuvinte, verificati daca nu se afla in acest string.
Strings
w1 = input( "Introduceti cuvantul1: " )
w2 = input( "Introduceti cuvantul2: " )
comun = ""
for litera in w1:
if (litera in w2) and (litera not in comun):
comun += litera
if comun == "":
print( "Nu sunt litere comune" )
else:
print( "Cuvintele au aceste litere in comun", comun )

s1 = " portocala "


s2 = " banana "
for litera in s1 :
if litera in s2 :
print ( s1 , "si", s2 , " au comun ", litera )
Strings

Aveti urmatorul fisier fructe.txt:

mere 10 15 20 30 40
pere 23 16 19 22
caise 8 22 17 14 32 17 24 21 2 9 11 17
gutui 12 28 21 45 26 10
prune 14 32 25 16 89

Scrieti scriptul care va afisa valoarea min si max pentru fiecare fruct
Strings

f = open("fructe.txt", "r")

for line in f:
items = line.split()
print(items[0], "min", min(items[1:]), "max", max(items[1:]))

f.close()
Recursivitate

Recursivitatea este o metodă de rezolvare a problemelor

Metoda recursivă implică împărțirea unei probleme în subprobleme mai


mici și mai mici, până când se ajunge la o problemă suficient de mică
încât poate fi rezolvată trivial - cazul de bază

Recursivitatea implică o funcție care se apelează pe sine.

Recursivitatea ne permite să scriem soluții elegante la problemele care ar


putea fi altfel foarte dificil de programat.
Recursivitate

Scrieți funcția sumI -iterativ, care calculează suma elementelor numerice a


unei liste:

L=[2, 11, 40, 5, 65, 44, 33]


Recursivitate

Varianta iterativă:

def sumI(numL):
s=0
for i in numL:
s=s+i
return s

L=[2, 11, 40, 5, 65, 44, 33]


print(sumI(L))
Recursivitate
Scrieți funcția sumR -recursiv, care calculează suma elementelor numerice
a unei liste:

L=[2, 11, 40, 5, 65, 44, 33]

Presupuneți ca nu aveți bucle for sau while – împar țiti problema în


subprobleme mai mici până ajungeți la cazul de bază:

s= (2+(11+(40+(5+ (65+(44+33))))))
s= (2+(11+(40+(5 +(65+77)))))
s= (2+(11+(40+(5+142))))
s= (2+(11+(40+147)))
s= (2+(11+187))
s= (2+198)
s= 200

s= primul element din lista + restul elemtelor din listă


Recursivitate

Varianta recursivă:

def sumR(numL):
if len(numL) == 1:
return numL[0]
else:
return numL[0] + sumR(numL[1:])

L=[2, 11, 40, 5, 65, 44, 33]


print(sumR(L))

Câte apeluri recursive sunt efectuate când se calculează suma listei ?


3 Legi ale Recursivității

1. Un algoritm recursiv trebuie să aibă un caz de bază (când se oprește).

2.Un algoritm recursiv trebuie să își schimbe starea și să avanseze spre


cazul de bază.

3.Un algoritm recursiv trebuie să se apeleze pe sine însuși - recursiv.


Recursivitate

Scrieți funcția nr2str care să convertească un număr întreg de la un șir de


caractere într-o anumită bază între binar și hexazecimal.

Exemplu: nr2str(10, 2) va fi 1010

Aplicați cele 3 reguli :


1. Reduceți numărul inițial la o serie de numere cu o singură cifră – cazul
de bază

2. Algoritmul care schimbă starea să vă apropiați de cazul de bază.

3.Concatenați șirurile de caractere de o singură cifră împreună pentru a


forma rezultatul final.
Recursivitate
def nr2str(n,baza):
convs = "0123456789ABCDEF"
if n < baza:
return convs[n]
else:
return nr2str(n//baza,baza) + convs[n%baza]

Atenție! dacă schimbăm ordinea concatenării, obtineam sirul scris invers!

print(nr2tr(1453,16))
5AD

print(nr2tr(1453,2))
10110101101

print(nr2tr(1453,9))
1884
Fractali - Recursivitate Grafică

Fractalii provin dintr-o ramură a matematicii - au multe în comun cu


recursivitatea.

Fractali – aceeași formă de bază la diferite niveluri de mărire

Exemplu: un copac este format dintr-un trunchi care are un alt copac care
ii crește pe dreapta și un alt copac care ii crește pe stanga -aplicăm
recursivitate în ambele direcții – recursivitate dublă
Fractali - Recursivitate Grafică

import turtle

def tree(t, branchLen):


if branchLen > 5: # cazul de baza, ramura nu e prea mică
t.fd(branchLen)
t.rt(20)
tree(t,branchLen-15) # apel recursiv, ramura din dreapta
t.lt(40)
tree(t,branchLen-15) # apel recursiv, ramura din stânga
t.rt(20)
t.bk(branchLen)

Cum se vor desena următoarele apeluri? Se vor face simetric? Simultan?


tree(t,25)
tree(t,50)
Fractali - Recursivitate Grafică

Fac apelul funcției tree(t, branchLen)

def main():
t = turtle.Turtle()
myscr = turtle.Screen() # creez o fereastra grafică
t.lt(90)
t.pu()
t.bk(100)
t.pd()
t.color("green")
tree(t,75)
myscr.exitonclick() # inchid fereastra grafică

main()
Fractali - Recursivitate Grafică

Apelul funcției tree(t, 75)


Fractali - Recursivitate Grafică
Sierpinski Triangle – recursivitate triplă
Desenez un triunghi, apoi unind mijloacele fac alte 4, repet procedura
pentru cele 3 noi triunghiuri create (ignor pe cel din mijloc)
Gradul fractalului – cazul de bază – de cate ori repet procedura
Fractali - Recursivitate Grafică

import turtle

def triunghi(t, pct, color):


t.fillcolor(color)
t.pu()
t.goto(pct[0][0],pct[0][1])
t.pd()
t.begin_fill()
t.goto(pct[1][0],pct[1][1])
t.goto(pct[2][0],pct[2][1])
t.goto(pct[0][0],pct[0][1])
t.end_fill()

def getMid(p1,p2):
return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]) / 2)
Fractali - Recursivitate Grafică
def sierpinski(t, pct, grad):
colormap = ['blue','red','green','white','yellow', 'violet','orange']
triunghi(t, pct, colormap[grad])
if grad > 0:
sierpinski(t, [pct[0],
getMid(pct[0], pct[1]),
getMid(pct[0], pct[2])],
grad-1)
sierpinski(t, [pct[1],
getMid(pct[0], pct[1]),
getMid(pct[1], pct[2])],
grad-1)
sierpinski(t,[pct[2],
getMid(pct[2], pct[1]),
getMid(pct[0], pct[2])],
grad-1)
Fractali - Recursivitate Grafică

def main():
t = turtle.Turtle()
myscr = turtle.Screen()
myPct = [[-100,-50],[0,100],[100,-50]]
sierpinski(t, myPct, 3)
myscr.exitonclick()

main()
Curs - Turtle

Limbajul PYTHON
CaseStudy - Turtle
Modulul Turtle

Acest modul Turtle vă introduce în studiul funcțiilor prin intermediul


graficii permițând să creați imagini utilizând grafica Turtle.

Modulul Turtle este inclus în majoritatea versiunilor Python.


Modulul Turtle

Verificăm dacă modulul Turtle este instalat (in ipython sau python).

Când executați următorul cod, ar trebui să se creeze o nouă fereastră care


conține o săgeată mică ce reprezintă o broasca țestoasă.

In[]: import turtle


In[]: bob=turtle.Turtle()

Modulul turtle (cu literă mică 't') oferă o metodă numită Turtle (cu 'T'),
care creează un obiect Turtle, pe care îl atribuim unei variabile numită bob
Modulul Turtle

Creăm un fișier mypolygon.py în Spyder în care scriem următoarele linii


de code:

import turtle
bob = turtle.Turtle()
print(bob)
turtle.mainloop()

Apelul de funcție print(bob) va afișa :

<turtle.Turtle object at 0xb7bfbf4c> - ceea ce înseamnă ca bob


“pointează” către un obiect de tip Turtle ce se află în modulul turle

Metoda mainloop() ține fereastra deschisă, pentru ca utilizatorul să facă


ceva.
Modulul Turtle

De exemplu, pentru a-l muta pe bob înainte (forward), apelăm metoda fd()

bob.fd (100)

Metoda fd () este asociată cu obiectul bob.

Apelarea unei metode este ca și cum ați face o cerere: cereți lui bob să
meargă înainte.

Argumentul metodei fd() este o distanță în pixeli, astfel încât dimensiunea


reală depinde dimensiunea ecranului vostru.
Modulul Turtle

Alte metode pe care le puteți apela:


bk() -- să se mute înapoi (backward)
lt() -- pentru a se întoarce la stânga (left turn)
rt() – pentru a se întoarce la dreapta (right turn)

Argumentele metodelor lt() și rt() sunt unghiuri în grade.


Modulul Turtle
De asemenea, fiecare obiect Turtle are un pix, care este fie în jos sau în
sus. În cazul în care pixul este în jos, Turtle desenează un traseu atunci
când se mișcă.

Metodele:

pu() -- "pix în sus" (pen up)


pd() -- "pix în jos" (pen down)

Pentru a desena un unghi drept, adaugăm aceste linii la code (după crearea
obiectului bob și înainte de a apela metoda mainloop):

bob.fd (100)
bob.lt (90)
bob.fd (100)

După executarea acestui program, ar trebui să vedeți cum bob se muta


către est și apoi spre nord, desenând două segmente de linii.
Modulul Turtle

După executarea acestui program, ar trebui să vedeți cum bob se muta


către est și apoi spre nord, desenând două segmente de linii.

Modificați programul pentru a desena un pătrat folosind apelurile


metodelor fd() si lt().
Modulul Turtle
Vreți să scrieți așa:

bob.fd (100)
bob.lt (90)

bob.fd (100)
bob.lt (90)

bob.fd (100)
bob.lt (90)

bob.fd (100)

Folosind bucla for și funcția range(), rescriem acest cod:


Modulul Turtle

Rescriem așa:

for i in range(4):
bob.fd(100)
bob.lt(90)

Această versiune de cod este puțin diferită de precedentul cod deoarece


aceasta face o altă întoarcere după desenarea ultimei laturi a pătratului.

Rândul suplimentar durează mai mult timp, dar simplifică codul dacă
facem același lucru de fiecare dată în buclă.

Acestă versiunea are de asemenea efectul de a păstra “broscu ța” în pozi ția
inițială, în direcția de plecare.
Modulul Turtle
Modulul Turtle
import turtle
t=turtle.Turtle()

t.speed(10)
t.pencolor(“red”)
d= 200
u=90
for i in range(1,50):
t.fd(d)
t.lt(u)
d=d-3
t.pu() # t.up() pen up
t.fd(300) # t.goto(x,y)
t.pd() # t.down() pen down

Repet codul de mai sus….

Daca setez u=140 cum va arata?


Încapsulare

Avantaj: re-utilizam acest cod, fără a copia codul de mai multe ori !

Încapsulăm cod-ul într-o funcție.

Scrieți funcția square care desenează un pătrat.

Funcția primește ca argument parametrul t, ce reprezinta o broască


țestoasă.

Scrieți apelul de funcție în care broasca țestoasă este bob și apoi executați
programul din nou.
Încapsulare
def square(t):
for i in range(4):
t.fd(100)
t.lt(90)

import turtle
bob=turtle.Turtle()
square(bob)

Ideea este că t poate fi orice broasca țestoasă, nu numai bob.

Dacă creăm un nou obiect, o nouă broască țestoasă numită alice, putem
apela funcția square din nou :

alice = turtle.Turtle()
square(alice)
Generalizare

Avantaj: adăugarea unor noi parametrii la o funcție.

Adăugați funcției square un alt parametru, numit length.

Modificați corpul funcției pentru acest nou parametru și modificați și


apelul funcției pentru a furniza acest al doilea argument.

Rulați programul din nou.

Testați programul cu o gamă de valori pentru length.


Generalizare

def square(t, length):


for i in range(4):
t.fd(length)
t.lt(90)

import turtle
bob=turtle.Turtle()
square(bob, 100)

Generalizare: adăugarea unui nou parametru la o funcție, în loc de pătrat


desenez orice poligon regulat
Generalizare

Creați o nouă funcție numită polygon care va desena un poligon regulat


cu n-fețe (modificăm funcția square prin adăugarea unui alt parametru n=
unghiurile exterioare ale unui poligon regulat cu n-fețe sunt 360 /n grade)

Faceți apelul funcției polygon()

Folositi metodele pu(), fd(), bk() si pd() pentru a desena pe ecran la altă
pozitie.
Generalizare

def polygon(t, n, length):


angle = 360 / n
for i in range(n):
t.fd(length)
t.lt(angle)

import turtle
bob=turtle.Turtle()
polygon(bob, 8, 70)

Atenție! Ordinea argumentelor contează

Indicat să făceam așa: def polygon(t, n=6, length=50)

Ce se afișa la acest apel al funcției:


polygon(bob)
Generalizare

Scrieți o funcție numită circle, care desenează un cerc prin apelarea


funcției polygon – un poligon regulat cu mai multe laturi este o bună
aproximare a unui cerc, de exemplu n=50.

Funcția circle primește ca parametrii o broască țestoasă, t, și raza, r.

Testați funcția cu o gamă de valori ale lui r.

Sugestie: faceti import math pentru a putea folosi pi.


Circumferința cercului este 2*math.pi *r , iar lungimea laturii poligonului
este length = circumferință \ n.

Folositi metodele pu(), fd(), bk() si pd() pentru a desena pe ecran la altă
pozitie.
Generalizare
import math

def circle(t, r):


circumference = 2 * math.pi * r
n = 50
length = circumference / n
polygon(t, n, length)

import turtle
bob=turtle.Turtle()
circle(bob, 100)

Care este limitarea acestei funcții ?


Generalizare
n este constant – pentru cercuri cu raza mare, n este prea mare, pentru
cercuri cu raza mica n e prea mic.

Daca-l dau pe n ca parametru de intrare, utilizatorul va avea mai mult


control, dar interfața funcției nu mai este clară (trebuie să știu detaliile din
implementarea funcției). Aleg n sa depindă de circumferința cercului:

import math
def circle(t, r):
circumference = 2 * math.pi * r
n = int(circumference / 3) + 1
length = circumference / n
polygon(t, n, length)

import turtle
bob=turtle.Turtle()
circle(bob, 100)
Refactorizare

Când am scris funcția circle() am reușit să reutilizez funcția polygon().

Modificând funcția circle() vreau să creez o noua funcție numită arc().

Funcția arc() primește un parametru suplimentar, unghiul. Unghiul este în


unități de grade, astfel încât atunci când unghiul = 360, funcția arc() ar
trebui să deseneze un cerc complet.

Dar arcul nu este la fel de cooperant- nu putem folosi poligonul sau cercul
să trasăm un arc de cerc.

O alternativă este să începem cu o copie a poligonului și să o transformăm


în arc
Refactorizare
Rezultatul ar putea arăta astfel:

def arc(t, r, angle):


arc_length = 2 * math.pi * r * angle / 360
n = int(arc_length / 3) + 1
step_length = arc_length / n
step_angle = angle / n

for i in range(n):
t.fd(step_length)
t.lt(step_angle)

Partea de cod cu for seamana cu funcția polygon(), dar are imi trebuie alti
parametrii de intrare. Am putea să generalizăm funcția polygon() și să
adaugam un nou parametru de intrare angle, dar funcția o sa devină mai
apropiată de o polylinie, nu de un polygon.
Refactorizare

Rezultatul ar putea arăta astfel:

def polyline(t, n, length, angle):


for i in range(n):
t.fd(length)
t.lt(angle)

Acum rescriem funcțiile polygon() și arc() folosind funcția polyline() :

def polygon(t, n, length):


angle = 360.0 / n
polyline(t, n, length, angle)
Refactorizare

def arc(t, r, angle):


arc_length = 2 * math.pi * r * angle / 360
n = int(arc_length / 3) + 1
step_length = arc_length / n
step_angle = float(angle) / n
polyline(t, n, step_length, step_angle)

În final, putem rescrie funcția circle() to folosind funcția arc() :

def circle(t, r):


arc(t, r, 360)
Refactorizare

Refactorizarea este acest proces de rearanjarea a unui program pentru


facilitarea reutilizării codului și pentru îmbunătățirea interfețelor –
apelurile de funcții căt mai intuitive.

De exemplu în acest caz, am observat că există un cod similar în funcția


arc() și în funcția polygon(), așa că am "transfotmat-o" în funcția polyline.

Dacă am fi planificat înainte, am fi scris mai întâi funcția polyline și să


evităm refactorizarea, dar de multe ori nu știm suficiente detalii de la
începutul unui proiect, pentru a proiecta inteligent toate interfețele.

Odată ce începeți codarea, înțelegeți mai bine problema.

Uneori refactorizarea este un semn că ați învățat ceva.


TEMA

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

TEMA: din carte, de la Capitolul 4 :


exercitiul 4.2
exercițiul 4.3
exercițiul 4.4 - vă scriți numele
Referinte

https://docs.python.org/3/library/turtle.html

python -m turtledemo

https://www.geeksforgeeks.org/turtle-programming-python/

https://stackoverflow.com/questions/14713037/python-turtle-set-
start-position
Curs 7

Limbajul PYTHON
Programare Orientată pe Obiect
(OOP)
Programare Orientată pe Obiecte(OOP)
Python: crearea de obiecte și clase de obiecte definite de utilizator

Avantajele OOP:

 Abstractizarea datelor - crearea de pachete (clase) și interconectarea


lor prin interfețe bine definite (împachetarea datelor împreună cu
metodele care lucrează pe aceste date)

 Implică dezvoltarea divide-and-conquer


- implementarea și testare fiecărei clase în parte
- crește modularitate și reduce complexitatea

Ușurința de reutilizare a codului


- mai multe module, fiecare clasă are un mediu separat (nicio coliziune pe
numele funcțiilor etc)
- moștenirea permite definirea unor subclase care redefinesc sau extind
comportamentul unui clase “părinte”
Obiecte

Diferite tipuri de data în Python:

(int) 123 (float) 3.14159 (string) ‘Maria’ (tuple) ( 1, 44, (‘a’,’b’))


(liste) [1, 5, 7, 11, 13] dicționare {"A": “albină”, "B":”balaur” }

Orice obiect are:


• un tip de date asociat
• o reprezentare internă a datelor
• un set de funcții/metode asociate

Orice tip de date folosim este o instanță a unui tip de obiect:

• 123 este o instanță a unui obiect int


• a = ‘Vasile’ este o instanță a unui obiect string
Obiecte
Orice dată în Python este un obiect și are un tip asociat

Obiectele sunt de fapt abstractizarea datelor :

• reprezentare internă
prin atributele datelor
• interfața pentru interacțiunea cu alte obiecte
prin funcții/metode ce ascund implementarea

Putem crea instanțe noi ale obiectelor

Putem distruge obiectele:


• explicit folosind comanda del sau
• doar “uităm” de ele – Python are un sistem pentru obiectele inacesible
numit “garbage collection”
Obiecte
Obiectivul: să creăm propriul nostru object de un anumit tip de date
Exemplu: [1,2,3,4] – este o instanță a unui obiect de tip listă

Care este reprezentarea internă a unei liste?


L =1 x→ 2 x → 3 x→4 x
Listă de celule interconectate ? Trebuie să știu?

Care este interfața unei liste ? Cum manipulăm listele?


L[i], L[i:j], L[i,j,k],
len(), min(), max(), del(L[i])
L.append(),L.extend(),L.count(),L.index(),L.insert(),L.pop(), L.remove(),
L.reverse(), L.sort()

Nu mă preocup de reprezentarea internă ci doar de interfață


Clase

Trebuie să faceți distincție între crearea unei class și folosirea clasei –


crearea unei instanțe a obiectului clasă

Definirea (crearea) unei clase implică:


• definirea unui nume pentru această clasă
• definirea unor atribute pentru această clasă
(cineva a scris niște linii de code pentru implementarea clasei list)

Folosirea clasei implică:


• crearea unor noi instanțe a obiectului clasă
• aplicarea unor operații pe aceste instanțe
(L=[1,2] , len(L) - se operează pe această instanța L, nu pe toată clasa)
Definirea unei clase

Definirea claselor începe cu cuvântul cheie class urmat de numele clasei, un


set de paranteze care conțin (object) urmat de două puncte :

class Coordonate (object) :


definesc atributele

Indentarea codului pentru a indica care desclarații fac parte din defini ția
clasei

Cuvântul object inseamnă că Coordonate este un obiect Python și moștenește


toate toate atributele:
- Coordonate este o subclasă (fiu) a lui object
- object este o superclasă (părinte) a lui Coordonate
Ce sunt Atributele ?

Date și metode/funcții ce “aparțin” unei clase

• date
- ne gândim la date ca la alte obiecte care compun clasa
( pentru un set de coordonate am nevoie de 2 numere)

• metode
- ne gândim la metode/funcțiile care se potrivesc numai cu acestă clasă
(pot să definesc distanța dintre 2 obiecte Coordonate, dar această distan ța
nu are niciun sens între 2 obiecte listă, sau string)
Definirea unei instanțe a unui obiect
Definesc o instanță a unui obiect cu o metoda specială numită
_ _init_ _ care va inițializa unele atribute ale obiectului:

class Coordonate(object):
def __init__(self, x, y):
self.x = x
self.y = y

_ _init_ _ se va apela implicit când se creează obiectul Coordonate

parametrul self este “un pointer” la instanța curenta a clasei se foloseste


pentru a accesa variabilele ce apartin clasei

parametrul self nu trebuie sa fie obligatoriu numit self, puteti sa-i dati
orice nume, dar trebuie sa fie primul parametru in orice metoda a
clasei

x,y atribute de tip date, asociate cu fiecare obiect Coordonate


Crearea unei instanțe a unui obiect
De fiecare dată cand scriem numele obiectului (Coordonate) se crează o
nouă instanță a obiectului:
De fiecare dată se apelează metoda __init__ și se fac legăturile cu
atributele obiectului( datele).

c=Coordonare(3,4)
print(c.x)

Operatorul “.” este folosit pentru a accesa orice atribut al obiectului

origine=Coordonate(0,0)
print(origine.x)

Atributele de tip date ale unei instanțe se numesc variabilele instanței.

Am scris corect? Unde este self?

Python automat include self intr-o instanta pe care am creat-o, nu e


nevoie sa-l scriu (self “pointează” catre noua instanță).
Metode
Metodele sunt atribute procedurale ale clasei - funcționează doar în clasă
în care au fost definite

Când apelăm o metoda a obiectului clasă, Python întodeauna pasează


obiectul actual ca prim parametru, de aceea convenția este să folosim
self ca numele primului argument al tuturor metodelor.

class Coordinate(object):
def __init__(self, x, y):
self.x = x
self.y = y
def distanta(self, pct): # calculez distanța între două puncte
dif_x = (self.x-pct.x)**2
dif_y = (self.y-pct.y)**2
return (dif_x + dif_y)**0.5

În afară de self și notația cu punct pentru a accesa datele, metodele se


comportă ca oricare funcție (primesc parametrii, fac operații, întorc valori)
Cum apelez Metodele clasei
Vreau sa calculez distanța între punctul c și origine:

stilul conventional echivalent cu

c = Coordonate(3,4) c = Coordonate(3,4)
origine = Coordonate(0,0) origine = Coordonate(0,0)
print(c.distanta(origine)) print(Coordonate.distanta(c, origine))

Dacă folosesc numele unei instanțe a obiectului apelez așa:


numele instanței (c).numele metodei(parametrii fără self)

Dacă folosesc numele obiectului -clasei apelez așa:


numele clasei(Coordonate).numele metodei(toți parametrii)

c.distanta moștenește metoda distanta din definiția clasei, si automat


folosește c ca prim argument
Coordonate.distanta, din clasa Coordonate, se apelează metoda distanta
și avem nevoie de valorile tuturor argumentelor
Cum printez reprezentarea unui obiect
In[]: c = Coordonate(3,4)
In[]: print(c)
<__main__.Coordonate object at 0x7fa918510488>

In mediul __main__ c este o instanță a obiectului Coordonate care se află


la acesta locație în memorie

Imi definesc o noua metodă a clasei numită __str__

Vreau să printez obiectul clasă, Python automat cheama metoda __str__


(metoda __str__ e asociată cu orice obiect Python: liste, tuple, dictionare)

Voi decideți (scrieți codul) cum să se printeze un anumit obiect.


Cum scrieți codul pentru metoda __str__ a obiectului Coordonate ?
In[]: print(c)
<3, 4>
Cum printez reprezentarea unui obiect

class Coordonate(object):
def __init__(self, x, y):
self.x = x
self.y = y
def distanta(self, pct):
dif_x = (self.x-pct.x)**2
dif_y = (self.y-pct.y)**2
return (dif_x + dif_y)**0.5
def __str__(self):
return "<" + str(self.x) + "," + str(self.y) + ">"

In []: c = Coordonate(3,4)
In []: print(c)
<3,4>
Reprezentarea unui obiect
Vreau să verific tipul unui obiect :
In []: print(type(c))
<class __main__.Coordinate>

c este o instanță a unei clase, iar tipul clasei este Coordonate. (__main__
precizează mediul Python unde a fost definită, mediul cu care eu
interacționez)

Are sens sa aflu ce este Coordonate ? De ce tip este?


In []: print(Coordonate, type(Coordonate))
<class __main__.Coordonate> <type 'type'>

Pot sa fac o instanța a unui obiect, pot s-o printez, să aflu informa ții
despre ea.

Metoda built-in isinstance() asociată cu orice obiect Python - verifică


daca este o instanța a unui obiect.
In []: print(isinstance(c, Coordinate))
True
Metode Magic
Metodele magic – adauga diverse functionalitati claselor

Toate clasele Python built-in au aceste metode

>>> dir(int) – listeaza toate metodele definite in clasa int


['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__',
'__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__',
'__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__',
'__getnewargs__', '__gt__', '__hash__', '__index__', '__init__',
'__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__',
'__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__',
'__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__',
'__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__',
'__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__',
'__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__',
'__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__',
'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator',
'real', 'to_bytes']
Metode Magic

Toate metodele magice au dublu underscores înainte/după

Metoda magica __add__ este apelata cand adun doua numere folosind
operatorul +

>>> num=10
>>> num + 5
15
De fapt ceea ce se apeleaza e:
>>> num.__add__(5)
15

Metodele magice nu sunt apelate direct, ci sunt folosite pentru a fi


suprascris comportamentul diversilor operatori Python
Metode Magic
https://docs.python.org/3/reference/datamodel.html#basic-customization

La fel ca metoda __str__ care printează obiectul, putem rescrie toate


aceste metode buit-in, să creăm noi metode, care să fie specifice claselor
noastre : +, -, ==, <, >, len(), print, și multe altele

Trebuie sa definim metodele magice cu dublu underscores înainte/după:

__add__(self, other) self + other


__sub__(self, other) self - other
__eq__(self, other) self == other
__lt__(self, other) self < other
__len__(self) len(self)
__str__(self) print(self)…

Cum îmi redefinesc metoda __sub__ - diferență între 2 coordonate ?


Operatori speciali __sub__
Cum îmi redefinesc metoda __sub__ diferența între 2 puncte?

O adaug la definiția clasei:

class Coordonate(object):
def __sub__(self, other):
return Coordonate(self.x-other.x, self.y-other.y)

In[]: c=Coordonate(5,10)
In[]: origine=Coordonate(0,0)
In[]: c-origine
<__main__.Coordonate at 0x11935f9e8 > # “-”este o metoda a clasei
In[]: x= c-origine
<5,10> # așa se printrează ptr că eu am definit metoda __str__

Pot să am metode care întorc:


-simple valori sau
- o nouă instanță a obiectului (a clasei create) return Coordonate(...)
Exemplu: obiect fractie

Vreau să creez un obiect nou - fractie care să reprezinte un număr ca o


fractie.

Care sunt atributele obiectului?

date(reprezentarea internă, am nevoie de 2 numere întregi)


• numărător
• numitor

metode – interfața, cum interacționez cu obiectul fractie


• print
• add, subtract
• convertesc la un număr float - să pot face operații standard
Definirea clasei fractie - Pas1
Scriu funcția __init__ și rescriu metoda __str__, cea care printează
obiectul

class fractie(object):
def __init__(self, num, numi):
self.num = num
self.numi = numi
def __str__(self):
return str(self.num) + ' / ' + str(self.numi)

In []: jumatate = fractie(1,2)


In []: print(jumatate)
1/2
In []: douatreimi = fraction(2,3)
In []: print(douatreimi)
2/3
Definirea clasei
Important!

Nu vrem să manipulăm în mod direct reprezentarea internă a unui obiect.


Exemplu : class Coordonate(object)
def distanta(self, pct):
dif_x = (self.x-pct.x)**2
dif_y = (self.y-pct.y)**2
return (dif_x + dif_y)**0.5
Dorim să se separe utilizarea obiectului de ceea ce este în interiorul
acestuia.

De aceea în orice obiect creez metode de tipul Get()


def getX(self): def getY(self):
return self.x return self.y

În orice moment vreau să accesez ceva dintr-un obiect, folosesc metoda


“care îmi permite să scot afară acea parte a obiectului”.
dif_x = (self.getX()-pct.x)**2 dif_y = (self.getY()-pct.x)**2
Definirea clasei fractie - Pas2
Adaug metodele care îmi permit să accesez atributele de tip date:

class fractie(object):
def __init__(self, num, numi): # nu am mai rescris să am loc pe slide
def __str__(self): # nu am mai rescris să am loc pe slide
def getNumarator(self):
return self.num
def getNumitor(self):
return self.numi

In[]: jumatate.getNumarator()
1
jumatate este o instanță a obiectului fractie, cu operatorul “.” accesez
metoda getNumarator() - nu am self ca parametru, deoarece jumatate este
o instanța, se pasează automat self.

In[]: fractie.getNumitor(douatrimi)
3
nume_clasa.nume_metoda(instanța) – am ca argument instanța obiectului
Definirea clasei fractie - Pas3
Adaug noi metode, rescriu metodele: __add__ și __sub__
class fractie(object):
def __init__(self, num, numi): # nu am mai rescris să am loc pe slide
def __str__(self): # nu am mai rescris să am loc pe slide
def getNumarator(self): # nu am mai rescris să am loc pe slide
def getNumitor(self): # nu am mai rescris să am loc pe slide
def __add__(self, other):
numNew = other.getNumitor() * self.getNumarator() \
+ other.getNumarator() * self.getNumitor()
numiNew = other.getNumitor() * self.getNumitor()
return fractie(numNew, numiNew)
def __sub__(self, other):
numNew = other.getNumitor() * self.getNumarator() \
- other.getNumarator() * self.getNumitor()
numiNew = other.getNumitor() * self.getNumitor()
return fractie(numNew, numiNew)

\ înseamnă că se continuă instrucțiunea pe rândul următor


Ambele metode întorc o instanță a obiectului fractie
Testez - Pas3

Testez metodele __add__ și __sub__ (folosesc operatorii “+ “ și “ - “):

In[]: jumatate = fractie(1,2)


In[]: douatreimi = fractie(2,3)
In[]: s = jumatate + douatreimi
In[]: print(s)
7/6
In[]:treipepatru=fractie(3,4)
In[]:dif=douatreimi – treipepatru
In[]:print(dif)
-1 / 12
Definirea clasei fractie - Pas3

Adaug o nouă metodă – convertesc la float


class fractie(object):
def __init__(self, num, numi): # nu am mai rescris să am loc pe slide
def __str__(self): # nu am mai rescris să am loc pe slide
def getNumarator(self): # nu am mai rescris să am loc pe slide
def getNumitor(self): # nu am mai rescris să am loc pe slide
def __add__(self, other): # nu am mai rescris să am loc pe slide
def __sub__(self, other): # nu am mai rescris să am loc pe slide
def convert(self):
return self.getNumarator() / self.getNumitor()

In[]:dif.convert() # reprezentarea pe float


-0.0833333333333
Exemplu obiect intSet

Vreau să creez un obiect nou intSet -care reprezintă o colecție de intregi


• inițial setul este gol
• un intreg apare doar o data în set

Care sunt atributele obiectului ?

date – reprezentarea internă


• folosesc o listă pentru a stoca elementele unui set

medode - interfața, cum interacționez cu obiectul intSet


• insert(e) – inserez un intreg e in set dacă nu se află în set
• member(e) – verific dacă intregul e este in set, întorc True altfel False
• remove(e) – sterg intregul e din set, altfel eroare dacă nu e prezent
Definirea clasei intSet
class intSet(object):
def __init__(self): # nu am alți parametrii, inițial e goală listă
self.vals = [] # inițializez variabila vals- numele meu intern ptr set
def insert(self, e):
if not e in self.vals: # verific dacă elementul e este deja în listă
self.vals.append(e) # folosesc metoda listelor append()
def member(self, e): # folosesc proprietatea o lista e iterabilă
return e in self.vals
def remove(self, e):
try:
self.vals.remove(e) # folosesc metoda listelor remove()
except: #dau mesaj de eroare dacă șterg un element care nu există
raise ValueError(str(e) + ' nu e in lista')
def __str__(self):
self.vals.sort() # folosesc metoda listelor sort()
rez = ' '
for e in self.vals: # pentru fiecare element din lista sortată
rez = rez + str(e) + ',' # il adaug, și mai adaug ,
return '{' + rez[:-1] + '} # rez[:-1] – fără ultima virgulă
Testez obiectul intSet

Soluția cu list comprehension pentru metoda __str__

def __str__(self):
""" intoarce reprezentarea a lui self ca strig """
self.vals.sort()
return '{' + ','.join([str(e) for e in self.vals]) + '}'
Testez obiectul intSet
In []: s = intSet()
In []: print(s)
{}
In []: s.insert(3)
In []: s.insert(4)
In []:s.insert(3)
In []:print(s)
{3, 4}
In []: s.member(3)
True
In []: s.member(6)
False
In []: s.remove(3)
In []: s.insert(6)
In []: print(s)
{4,6}
In []: s.remove(3)
ValueError: 3 nu e in lista
De ce OOP ?

Mimăm viața reală

Gruparea obiectelor care au:


• caracteristici comune
• proceduri care operează pe aceste atribute

Folosește abstractizarea pentru a face distincție între modul de


implementare a unui obiect vs modul de utilizare a obiectului

Permite construirea de nivele de abstractizare a obiectelor care


moștenesc comportamente din alte clase de obiecte

Permite crearea propriile noastre clase de obiecte deasupra claselor de


bază din Python
Implementare Claselor vs Utilizarea Claselor

Scriu code din două perspective diferite:

implementarea unui nou tip utilizarea noului tip de obiect:


de obiect - o clasă:

definirea clasei crearea instanțelor de tipul obiectului

definirea datelor facem diverse operații pe obiect


(ce este obiectul)

definirea metodelor
(cum folosesc obiectul)
Definirea Clasei vs Instanța Clasei

clasa este un type instanța este un obiect particular


type Coordonate
class Coordinate(object): c = Coordonate(1,2)

clasa este definită generic valorile datelor variază intre
instanțe
- folosim self să ne referim la c1 = Coordinate(1,2)
orice instanță când definim c2 = Coordinate(5,6)
clasa c1 și c2 au valorile datelor diferite
pentru că sunt obiecte diferite

clasa defineste datele si instanța are structura unei clase


metodele comune tuturor
instanțelor
Grupuri de Obiecte

Grupuri de obiecte

atribute data
- cum putem reprezenta un obiect cu ajutorul datelor?
- ce este obiectul?
ptr o coordonată: valorile x și y
pentru un animal: vârsta, nume

atribute procedurale (comportament/operații/metode)


- cum putem interacționa cu acest obiect?
- ce face acest obiect?
ptr o coordonată: aflu distanța între două obiecte
ptr un animal: scot un sunet
Definesc Clasa (recapitulare)
class Animal(object):
def __init__(self, age):
self.age = age # animale toate au o vârstă
self.name = None # inițial nu au nume, o sa le adaug

definirea clasei class, nume (clasa părinte -moștenește de la object)


definirea metodei __init__ care crează instanțe pentru clasa Animal
(self- variabila cu care se face referire la o instanță a clasei , alte
variabile de care am nevoie)

Pot defini legături pentru atributele date care sunt transmise – age
dar pot defini, de asemenea, și alte legături cu atribute date pe care am de
gând să le folosesc pe plan intern, le voi seta separat –name

Metoda __init__ va crea o legătura pentru ambele atribute age și name la


fiecare instanțiere.

a=Animal(3) # instanță a clasei Animal cu vârsta 3, age e variabila


definită, nu name
Definesc Clasa (recapitulare)
class Animal(object):
def __init__(self, age):
self.age = age
self.name = None
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage): # schimb variabila age
self.age = newage
def set_name(self, newname=""): # schimb variabila name
self.name = newname # în loc de None pun un șir gol
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)

Funcțiile get(getters) și set(setters) trebuiesc folosite în afara clasei, pentru


a accesa atributele dată !
Instanța Clasei (recapitulare)
Creez o instanță a obiectului Animal:

In[]: a = Animal(3)
In[]: print(a)
In[]: animal: None:3
In[]: a.set_name(“Trump”)
In[]: print(a)
In[]: animal: Trump:3
In[]: a.get_age()
In[]: 3

Notația cu punct . este folosită pentru a accesa atributele (date și metode)


a.age # accesez direct datele
a.get_age() # chem metoda care îmi permite acesul la date

Este recomandat să folosiți getters și setters pentru a accesa atributele


dată pentru a face diferența între reprezentarea internă și accesarea
reprezentării interne.
Informație ascunsă !
Cel care a creat definița clasei poate schimba atributele data- numele
variabilelor:

class Animal(object):
def __init__(self, age):
self.years = age
def get_age(self):
return self.years

Dacă accesati atributele dată în afara clasei și în definiția clasei ele sunt
schimbate - > erori

Recomand ca în afara clasei, să folosiți getters și setters, folosiți


a.get_age() NU a.age !

• un bun stil de a programa


• ușurința de a întreține codul
• preveniți erori
Python nu ascunde informația !

Python permite accesarea datelor, din afara definiției clasei


print(a.age)

Python permite să scrieți/modificați date, din afara definiției clasei


a.age = 'nemuritor'

Python permite să creați atribute dată pentru o instanță, din afara


definiției clasei
a.size = "mic"

Chiar dacă puteți nu o faceți!

Este recomandat să NU faceți nici una dintre aceste abordări!


Self și alte argumente
self este determinat de o instanța, este argumentul pasat la instanțiere
• metoda: def __init__(self, age) crează self și-l pasează automat ca
argument
a = Animal(3) # fără self pentru că este furnizat automat

• metod: def get_age(self)


apelăm metoda așa a.get_age()

sau altă alternativă Animal.get_age(a)

argumentele“default” sunt folosite dacă nu sunt specificate alte valori


• for the method: def set_name(self, newname="")

argumentul de default este folosit aici a.set_name()

argumentul pasat este folosit aici a.set_name("Ronny")


Excepții
Ori de câte ori apare o eroare de execuție se creează un obiect excepție.

Se oprește rularea codului la acel punct și Python printrază traceback-ul-


mesajul care descrie excepția faptului care a avut loc.

In[]: print(55/0)
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

In[]: a = []
In[]: print(a[5])
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
IndexError: list index out of range

Mesajul de eroare,ultima linie are două părți: tipul de eroare : și despre


specificul erorii .
Excepții
Uneori dorim să executăm o operațiune care ar putea duce la o excep ție,
dar NU vrem ca programul să se oprească.

Ne putem ocupa de această excepție folosind instrucțiuni care "wrap" o


regiune de cod.

De exemplu, am putea cere utilizatorului numele unui fișier și apoi să


încercăm să-l deschidem. În cazul în care fișierul nu există, nu vrem ca
programul să se oprească, vrem să se ocupe de excepție:

filename = input("Introduceți numele fisierului: ")


try:
f = open(filename, "r")
except:
print("Nu există niciun fisier cu numele", filename)

Instrucțiunea try are trei clauze distincte, introduse de cuvintele cheie


try ... except ... finally.Oricare din clauzele except sau finally pot fi omise
Creăm propriile Excepții
În cazul în care programul nostru detectează o condiție de eroare, putem
ridica o excepție.

Exemplu:verifică dacă numărul introdus de utilizator este pozitiv:

def vârsta():
v = int(input("Introduceți vârsta dvs: "))
if v < 0:
# Creăm o instanță a unei excepții
my_error = ValueError(str(v) +’ nu e vârsta potrivită’)
raise my_error
return v

În acest caz excepția este un obiect built-in ValueError care incapsuleaza


informația specifică despre eroare.

Lista completă a excepțiilor built-in:


https://docs.python.org/3/library/exceptions.html
Clauza finally a unei declarații try
Finally: “curăță” toate resursele pe care le-am folosit
import turtle
def show_poly():
try:
win = turtle.Screen() # creăm o fereastra
gigi = turtle.Turtle()
n = int(input("Cate laturi vrei sa aibă poligonul?")) # exceptia sir de caractere
angle = 360 / n
for i in range(n): # Deseneaza poligonul
gigi.fd(10)
gigi.lt(angle)
finally:
win.bye() # Inchide fereastra
show_poly() # crează o fereastră și desenează un poligon
show_poly() # crează o fereastră și desenează un poligon
show_poly() # crează o fereastră și desenează un poligon

Chiar dacă avem o excepție, vrem să inchidem fereastra – clauza finally


se va executa întotdeauna
Observați că excepția nu este tratată- nu există clauză except programul se
blochează dar, cel puțin fereastra va fi închisă înainte de a se bloca!
DE CITIT!

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Clase și Obiecte- Capitolul 15


Clase și Funcții -Capitolul 16
DE CITIT!

Clase și Obiecte : Data model


https://docs.python.org/3.3/reference/datamodel.html
Bibliografie

Eric Grimson, and John Guttag. 6.00 Introduction to Computer


Science and Programming. Fall 2008. Massachusetts Institute of
Technology: MIT OpenCourseWare, https://ocw.mit.edu. License:
Creative Commons BY-NC-SA.
Curs 8

Limbajul PYTHON
Programare Orientată pe Obiect
(OOP) - Ierarhii
OOP
Clasele -template-uri pentru a crea
propriile obiecte

Cream instante pentru aceste


tempate-uri pe care putem aplica
operatii si metode
Ierarhii de Obiecte
Crearea de obiecte și clase de obiecte definite de utilizator

O proprietate importantă a claselor este aceea că putem crea ierarhii de


clase

Grupăm comportamente comune – instanțe ale aceluiași obiect – creăm


subclase cu comportamente diferite

Animal

Cats Rabbits People

Students
Ierarhii de Obiecte

clasa părinte Animal


(superclasa)
Animal

Cats Rabbits People

clase copii
(subclase)
Cats, Rabbits, People, Students
Students
• moștenesc toate datele și comportamentul
clasei parinte
• adaugă mai multă informație (date)
• adaugă mai multă interacțiune (metode)
•comportament diferit, suprascriu interacțiunile clasei părinte(alte metode)
Ierarhii – Clasa Părinte
class Animal(object):
def __init__(self, age):
self.age = age # variabila unei instanțe
self.name = None # variabila unei instanțe
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage):
self.age = newage
def set_name(self, newname=""):
self.name = newname
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)
Ierarhii

class Cat(Animal): # moștenește toate atributele de la clasa Animal


def speak(self): # adaug caracteristici noi -metode noi
print("meow”)
def __str__(self): # suprascriu metoda __str__din clasa Animal
return "cat:"+str(self.name)+":"+str(self.age)

Am adăugat functionalitate noua cu metoda speak()

• instanța de tip Cat poate fi apelată cu noua metodă


• instanța de tip Animal va da eroare dacă este apelată cu noua metodă

__init__ nu lipsește , se folosește versiunea de la clasa Animal


Utilizarea Ierarhiilor
In []: jelly = Cat(1) # jelly - instanța clasei Cat
In []: jelly.get_name()
In []: jelly.set_name('JellyBelly')
In []: jelly.get_name()
Out[]:’JellyBelly’
In []: print(jelly) # __str__ din clasa Cat
cat:JellyBelly:1

In []:print(Animal.__str__(jelly)#apelez __str__din clasa Animal ptr jelly


Animal:JellyBelly:1

In []: bob = Animal(1) # bob - instanța clasei Animal


In []: print(bob)
animal:None:1

In []: bob.set_name()
In []: print(bob) # apelez metodele clasei ptr a seta atribute
animal::1
Ierarhii

class Cat(Animal):
def speak(self):
print("meow”)
def __str__(self):
return "cat:"+str(self.name)+":"+str(self.age)

class Rabbit(Animal):
def speak(self):
print(”meep”)
def __str__(self):
return ”rabbit:"+str(self.name)+":"+str(self.age)
Utilizarea Ierarhiilor

In []: jelly = Cat(1)


In []: bob = Animal(1)
In []: peter = Rabbit(5)
In []: jelly.speak() # metoda speak() din clasa Cat
meow

In []: peter.speak() # metoda speak() din clasa Rabbit


meep

Acelasi nume de funcție - speak() asociat în clase diferite

In []: bob.speak()
AttributeError: 'Animal' object has no attribute 'speak'
Utilizarea Ierarhiilor

subclasa poate avea metode cu același nume ca ale superclasei

subclasă poate avea metode cu același nume ca și altele subclase

o instanță a unei clase, caută numele metodei în definiția curentă a clasei

în cazul în care nu a fost găsită, caută numele metodei în ierarhie


( părinte, apoi bunic, și așa mai departe)

se utilizează prima metodă din ierarhia cu aceeași denumire a metodei


căutate
Utilizarea Ierarhiilor
class Person(Animal): # părinte clasa Animal
def __init__(self, name, age):
Animal.__init__(self, age) # chem constructorul clasei Animal
Animal.set_name(self, name) # chem metoda clasei Animal
self.friends = [] # adaug noi atribute de tip date
def get_friends(self):
return self.friends
def add_friend(self, fname):
if fname not in self.friends:
self.friends.append(fname)
def speak(self):
print("hello”)
def age_diff(self, other):
diff = self.get_age() - other.get_age() #diff = self.age - other.age
if self.age > other.age:
print(self.name, "is", diff, "years older than", other.name)
else:
print(self.name, "is", -diff, "years younger than", other.name)
def __str__(self):
return "person:"+str(self.name)+":"+str(self.age)
Utilizarea Ierarhiilor

In []: elena = Person(‘Elena’, 45)

In []: ion = Person(‘Ion’, 55)

In []: elena.speak()
Hello

In []: elena.age_diff(ion) # folosesc metoda age_diff asociată cu o


# instanță a clasei Person
Elena is 10 years younger than Ion

In []: Person.age_diff(ion,elena) #folosesc clasa Person să atribui metoda


Ion is 10 years older than Elena
Utilizarea Ierarhiilor
import random # să pot accesa funcții din modulul random

class Student(Person): # clasa parinte Person


def __init__(self, name, age, major=None):
Person.__init__(self, name, age) # mostenesc si de la clasa Person
self.major = major # adaug atribute/date noi
def change_major(self, major):
self= major
def speak(self):
r = random.random() # metoda random() intoarce un nr intre [0,1)
if r < 0.25:
print("i have homework”)
elif 0.25 <= r < 0.5:
print("i need sleep”)
elif 0.5 <= r < 0.75:
print("i should eat”)
else:
print("i am playing computer games”)
def __str__(self): # suprascriu metoda __str__ din clasa Person
return "student:"+str(self.name)+":"+str(self.age)+":"+str(self.major)
Utilizarea Ierarhiilor
In []: elena = Person(‘Elena’, 45)

In []: fred = Student(‘Fred’, 18, ‘Course VI’)

In []: print(fred)
student:Fred:18:Course VI

In []: fred.speak()
i have homework

In []: fred.speak() # apelez metoda speak() de mai multe ori


i have homework

In []: fred.speak() # comportament diferit datorita metodei random()


i am playing computer games

In []: fred.speak()
i should eat
Variabilele Instanțelor vs Variabilele Claselor

am vazut variabilele unei introduc noțiunea variabilele unei clase,


instanțe aparțin clasei

specifice unei instanțe definite în interiorul clasei, dar în afara


oricărei metode a clasei, în afara metodei
__init__

se crează ptr fiecare instanța, sunt împărțite-shared între toate obiectele/


aparțin unei instanței instanțele unei clase

folosesc numele generic de


variabilă self, în interiorul
clasei
Ierarhii – Clasa Părinte
class Animal(object):
def __init__(self, age):
self.age = age # variabila unei instanțe
self.name = None # variabila unei instanțe
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage):
self.age = newage
def set_name(self, newname=""):
self.name = newname
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)
Variabilele Claselor și subclasa Rabbit

subclasa moștenește toate atributele de tip date și toate metodele de


la clasa părinte

class Rabbit(Animal): # părinte clasa Animal


tag = 1 # variabila clasei Rabbit
def __init__(self, age, parent1=None, parent2=None):
Animal.__init__(self, age)
self.parent1 = parent1
self.parent2 = parent2
self.rid = Rabbit.tag # variabila instanței rid = variabila clasei
Rabbit.tag += 1 # incrementez variabila clasei

tag folosit pentru a avea un id unic ptr fiecare noua instan ță a clasei
Rabbit
Variabilele Claselor și subclasa Rabbit
class Rabbit(Animal):
tag = 1 # variabila clasei Rabbit
def __init__(self, age, parent1=None, parent2=None):
Animal.__init__(self, age)
self.parent1 = parent1
self.parent2 = parent2
self.rid = Rabbit.tag
Rabbit.tag += 1
def get_rid(self):
return str(self.rid).zfill(3)
def get_parent1(self):
return self.parent1
def get_parent2(self):
return self.parent2

zfill() metoda aplicata pe stringuri, adaugă 0 în față – 001în loc de 1

Avem acces și la metodele get_name() și get_age() moștenite de la


părinte, clasa Animal
Utilizarea Ierarhiilor

In[]: peter = Rabbit(2)

In[]: peter.set_name(‘Peter’)

In[]: hopsy = Rabbit(3)

In[]: hopsy.set_name(‘Hopsy’)

In[]: cotton = Rabbit(1, peter, hopsy)


cotton.set_name(‘Cottontail’)

In []: print(cotton)
animal:Cottontail:1

In []: print(cotton.get_parent1())
animal:Peter:2
Definirea propriei metode: __add__

definesc operatorul + între două instanțe ale clasei Rabbit

#din clasa Rabbit: __init__(self, age, parent1=None, parent2=None)

def __add__(self, other):


# întoarce un obiect de același type - clasa Rabbit
return Rabbit(0, self, other) # age=0, self = parent1, other=parent2

• vreau: r4 = r1 + r2
r1 și r2 sunt instanțe ale clasei Rabbit
r4 este o nouă instanța a clasei Rabbit cu age 0
Exemplu de utilizare

In []: peter = Rabbit(2)

In []: peter.set_name(‘Peter’)

In []: hopsy = Rabbit(3)

In []: hopsy.set_name(‘Hopsy’)

In []: mopsy = peter + hopsy

In []: mopsy.set_name('Mopsy')

In []: print(mopsy.get_parent1())
animal:Peter:2

In []: print(mopsy.get_parent2())
animal:Hopsy:3
Definirea propriei metode: __eq__

Decidem că doi iepuri sunt egali dacă ei au aceeași doi părinți


(compar doua variabile booleane)

def __eq__(self, other):


parents_same = self.parent1.rid == other.parent1.rid \
and self.parent2.rid == other.parent2.rid
parents_opposite = self.parent2.rid == other.parent1.rid \
and self.parent1.rid == other.parent2.rid
return parents_same or parents_opposite

Comparăm id pătinților -rid sunt unice (deoarece este o variabila a clasei)

Atenție: dacă comparăm obiecte ( self.parent1==other.parent1 ) se va


apela metoda __eq__ și vom obține mesajul de eroare: AttributeError -
NU PUTEM COMPARA INSTANȚELE DIRECT!
Exemplu de utilizare
In []: peter = Rabbit(2)

In []: peter.set_name(‘Peter’)

In []: hopsy = Rabbit(3)

In []: hopsy.set_name(‘Hopsy’)

In []:cotton = Rabbit(1, peter, hopsy) # creat direct, prin instanțiere


cotton.set_name(‘Cottontail’)

In[]: mopsy = peter + hopsy # creat cu operatorul +


mopsy.set_name('Mopsy')

In []: print(mopsy == cotton)


True

Am folosit variabila clasei să obțin informații asociate cu instanțele


obiectelor
Sumar Clase și OOP

Incapsulează obiecte care împart:


• atribute comune și
• metode/proceduri care operează pe aceste atribute

Folosim abstractizarea ptr a face distincție între cum să implementăm un


obiect vs cum să folosim obiectul

Construim nivele de abstractizare ale claselor de obiecte, care moștenesc


comportamentul de la alte clase de obiecte

Creăm propriile clase de obiecte, peste obiectele claselor de bază Python


Exemplu : utilizarea ierarhilor de clase

Creăm o aplicație care va organiza datele oamenilor:

Începem cu un obiect Person

clasa Person:

# date
nume, data nașterii

# metode ce vor accesa datele


get last_name
sort by last_name
get age
Exemplu : construim clasa Person

import datetime

class Person(object):
def __init__(self, name): # crează o persoană cu numele name
self.name = name
self.birthday = None
self.lastName = name.split(' ')[-1]
def getLastName(self): # intoarce self's last name
return self.lastName
def __str__(self): # întoarce self's name
return self.name
def setBirthday(self,month,day,year): # setez data nașterii
self.birthday = datetime.date(year,month,day)

# metoda split() pe stringuri, separă într-o listă de stinguri, după spațiu


‘ ‘ și am selectat ultimul element din listă
# metoda date() din modulul datetime
Exemplu : construim clasa Person

import datetime

class Person(object):
def __init__(self, name): # crează o persoană cu numele name
def getLastName(self): # intoarce last name
def __str__(self): # întoarce name
def setBirthday(self,month,day,year): # setez data nașterii

def getAge(self): # întoarce vârsta curenta convertită în zile


if self.birthday == None:
raise ValueError
return (datetime.date.today() - self.birthday).days

def __lt__(self, other): # sortez lexicografic după lastName


if self.lastName == other.lastName:
return self.name < other.name
return self.lastName < other.lastName
Exemplu : construim baza de date Person

In[]: p1 = Person('Mark Zuckerberg')


In[]: p1.setBirthday(5,14,84)
In[]: p2 = Person('Drew Houston')
In[]: p2.setBirthday(3,4,83)
In[]: p3 = Person('Bill Gates')
In[]: p3.setBirthday(10,28,55)
In[]: p4 = Person('Andrew Gates')
In[]: p5 = Person('Steve Wozniak')

In[]:print(p1)
Out: 'Mark Zuckerberg'
Exemplu : sortare de date Person

# Vreau să sortez persoanele, creez lista persoanelor:


personList = [p1, p2, p3, p4, p5]

personList.sort()
for e in personList: for e in personList:
print(e) print(e)
Mark Zuckerberg Andrew Gates
Drew Houston Bill Gates
Bill Gates Drew Houston
Andrew Gates Steve Wozniak
Steve Wozniak Mark Zuckerberg
Exemplu : utilizarea ierarhilor de clase

Creăm o aplicație care va organiza datele oamenilor:

Avem clasa Person:


# date
nume, data nașterii
# metode ce vor accesa datele
get last_name
sort by last_name
get age

Adăugăm clasa UPBPerson: Person


# date
asignăm ID number
# metode ce vor accesa datele
get ID number
sortăm după ID number
Utilizarea ierarhilor de clase

class UPBPerson(Person): # moștenește de la clasa Person


nextIdNum = 0 # nextIDnumber – variabila clasei

def __init__(self, name):


Person.__init__(self, name) # initializez atributele din cls Person
self.idNum=UPBPerson.nextIdNum # atribut unic
UPBPerson.nextIdNum += 1 # asignez următorul ID number
fiecărei noi instanțe
def getIdNum(self):
return self.idNum

def __lt__(self, other): # sortez folosind ID number, nu numele!


return self.idNum < other.idNum

def speak(self, blablabla):


return (self.getLastName() + " says: " + blablabla)
Vizualizarea ierarhilor de clase

Ce se întămplă când creez o instanța UPBPerson ?

Person:
__init__
getLastName
__lt__

UPBPerson:
nextIDNum: 0(1) name, birthday, lastName, nextIDNum=0
__init__
__lt__

Creez o nouă instanță UPBPerson, nextIDNum va fi 1, etc


Exemplu utilizare
In[]:m3 = UPBPerson('Mark Zuckerberg')
In[]:Person.setBirthday(m3,5,14,84)
In[]:m2= UPBPerson('Drew Houston')
In[]:Person.setBirthday(m2,3,4,83)
In[]:m1 = UPBPerson('Bill Gates')
In[]:Person.setBirthday(m1,10,28,55)

In[]:UPBPersonList = [m1, m2, m3] # in ordine inversă in listă


print(m1.speak(‘hello!’))

UPBPersonList.sort()
for e in UPBPersonList: for e in UPBPersonList:
print(e) print(e)
Bill Gates Mark Zuckerberg
Drew Houston Drew Houston
Mark Zuckerberg Bill Gates
# sortat după name # sortat după idNum
Exemplu utilizare ierarhii
In[]:p1 = UPBPerson(‘Eric’) # name=Eric, birthday, lastName, idNum=0
In[]:p2=UPBPerson(‘John’) # name=John, birthday, lastName, idNum=1
In[]:p3=UPBPerson(‘John’) # name=John, birthday, lastName, idNum=2
In[]: p4 = Person(‘John’) # name=John, birthday, lastName

In[]: p1 < p2 # compar instanțe UPBPerson, compar după idNum


True
# p1 < p2 se va converti in p1.__lt__(p2) care aplică metoda asociată cu
type lui p1

In[]: p4 < p1
False
# p4 < p1 echivalent cu p4.__lt__(p1), care aplică metoda asociată cu
type lui p4, clasa Person (compară după name)

In[]: p1 < p4 # compar instanțe UPBPerson (idNum) și Person (name) !


Attribute Error
Cum se compară ?

clasa UPBPerson are propria metodă __lt__

metoda “shadows” metoda din clasa Person - Python va vedea această


versiune a metodei __lt__ NU CEA DIN CLASA Person atunci când
comparăm obiecte UPBPerson
Exemplu : utilizarea ierarhilor de clase
Avem clasa Person:
# date
nume, data nașterii
# metode ce vor accesa datele
get last_name
sort by last_name
get age

Adăugăm clasa UPBPerson: Person


# date
asignăm ID number
# metode ce vor accesa datele
get ID number
sortăm după ID number

Adăugăm clasele UG, Grad: UPBPerson


UG- undergraduate students : classYear
Grad-graduate students
Exemplu : utilizarea ierarhilor de clase

class UG(UPBPerson):
def __init__(self, name, classYear):
UPBPerson.__init__(self, name)
self.year = classYear # year=data atribut
def getClass(self):
return self.year
def speak(self, blablabla):
return UPBPerson.speak(self, " Dude, " + blablabla)

class Grad(UPBPerson):
pass # cuvânt cheie nu avem nimic de declarat

def isStudent(obj):
return isinstance(obj,UG) or isinstance(obj,Grad)
Exemplu : utilizarea ierarhilor de clase

In[]: s1= UG('Matt Damon', 2017)


In[]: s2= UG('Ben Affleck', 2017)
In[]: s3= UG('Lin Manuel Miranda', 2018)
In[]: s4= Grad('Leonardo di Caprio')

In[]:print(s1)
Out: 'Matt Damon'
In[]: print(s1.getClass())
Out: 2017
In[]: print(s1.speak('unde este berea?'))
Out: Damon says: Dude, unde este berea?

In[]:print(s2.speak(‘Habar nu am!’))
Out: Affleck says: Habar nu am!
Principiul Substituției
Person

Ierarhia de clase:
UPBPerson

UG Grad

Vreau să adaug o noua clasă:


class TransferStudent(UPBPerson):
pass

Trebuie să rescriu următoarea metodă:


def isStudent(obj):
return isinstance(obj,UG) or isinstance(obj,Grad)
Principiul Substituției

class Student(UPBPerson): # creez o superclasă - acoperă toate cazurile


pass
class UG(Student):
def __init__(self, name, classYear):
UPBPerson.__init__(self, name)
self.year = classYear
def getClass(self):
return self.year
def speak(self, blablabla):
return UPBPerson.speak(self, " Dude, " + blablabla)
class Grad(Student):
pass
class TransferStudent(Student):
pass
def isStudent(obj):
return isinstance(obj,Student)
Principiul Substituției
Person

Ierarhia de clase:
UPBPerson

Student

UG Transfer
Grad

Principiul Substituției:

Comportamentul/caracteristicile importante ale unei superclase trebuie să


fie suportate de toate subclasele
Exemplu : utilizarea ierarhilor de clase

Avem clasa Person:


clasa UPBPerson: Person
clasa Student: UPBPerson
clasele UG, Grad, TransferStrudent: Student

Vreau să modific comportamentul unei clase, fără să modific în altă clasă

Adăugăm clasa Professor : UPBPerson


Exemplu : o nouă clasa de obiecte

class Professor(UPBPerson):
def __init__(self, name, department):
UPBPerson.__init__(self, name)
self.department = department # setez un atribut nou
def speak(self, bla):
new = 'In course' + self.department + ' we say ’
return UPBPerson.speak(self, new + bla) # moștenită
def lecture(self, topic):
return self.speak('it is obvious that ' + topic) # propria metodă

faculty=Professor(‘ Doctor Who’, ‘six’)


Exemplu : o nouă clasa de obiecte

In[]:print(m1.speak('hello')) # metoda speak() din clasa UPBPerson


Gates says: hello

In[]:print(s1.speak('hello')) #metoda speak() din clasa UG


Damon says:Dude, hello

In[]: print(faculty.speak('hello')) #metoda speak() din clasa Professor


Who says: In course six we say hello

In[]: print(faculty.lecture('hello'))
Who says: In course six we say it is obvious that hello

# metoda lecture folosește metoda speak() din clasa Professor


Modularitate

Izolez metode în clase – mai ușor să modific comportamentul

◦ dacă schimb comportamentul unei metode din clasa UPBPerson,


modificarea se moștenește de către toate subclasele clasei UPBPerson

def speak(self, bla):


return (self.name + " says: " + bla)

◦ dacă schimb comportamentul unei metode dintr-o clasa mai jos in


ierarhie UG

def speak(self, bla):


return UPBPerson.speak(self, " Yo Bro, " + bla)
Exemplu Modularitate

Am schimbat metoda speak() din clasa UPBPerson – afectează toate


subclasele care o folosesc

In[]:print(m1.speak('hi there'))
Mark Zuckerberg says: hi there

In[]:print(s1.speak('hi there'))
Matt Damon says:Dude, hi there

In[]: print(faculty.speak('hi there'))


Doctor Who says: In course six we say hi there

In[]: print(faculty.lecture('hi there'))


Doctor Who says: In course six we say it is obvious that hi there
Exemplu Modularitate

Am schimbat metoda speak() din clasa UG – afectează doar clasa care


folosește metoda

In[]:print(m1.speak('hi there'))
Mark Zuckerberg says: hi there

In[]:print(s1.speak('hi there'))
Matt Damon says:Yo Bro, hi there

In[]: print(faculty.speak('hi there'))


Doctor Who says: In course six we say hi there

In[]: print(faculty.lecture('hi there'))


Doctor Who says: In course six we say it is obvious that hi there
Exemplu: Catalog

Creez o clasă care include instanțele altor clase în ea

Concept : vreau să creez un Catalog – să construiesc o structură de date


pentru notele studenților

Incapsulez date și proceduri într-o singură structură, astfel încât


utilizatorul s-o manipuleze fără să știe detaliile interne
Exemplu: Catalog
Vreau să fac o mapare între studenti și lista notelor

class Grades(object):
def __init__(self): # creez un catalog gol
self.students = [] # lista studentilor
self.grades = {} # dicționar :mapez idNum -> lista notelor
self.isSorted = True # true daca self.students este sortată

def addStudent(self, student): # adaug un student in catalog


if student in self.students:
raise ValueError('Duplicate student')
self.students.append(student) # metoda listelor - append()
self.grades[student.getIdNum()] = [] # creez un loc in dicționar
self.isSorted = False

# am o lista de studenti si o lista de note


# dictionarul va fi accesat prin idNum
Exemplu: Catalog
class Grades(object):
def addGrade(self, student, grade): #adaug o notă la lista notelor
studentului
try:
self.grades[student.getIdNum()].append(grade)
except KeyError:
raise ValueError('Student not in grade book')

def getGrades(self, student): # intoarce lista notelor unui student


try:
return self.grades[student.getIdNum()][:]
except KeyError:
raise ValueError('Student not in grade book’)

# indexez in dictionar folosind idNum, intorc lista notelor


# adaug o notă folosind metoda listelor append()
# intoarc o copie a notelor studentului [:]
Exemplu: Catalog

class Grades(object):
def allStudents(self): # intorc lista studentilor din catalog
if not self.isSorted:
self.students.sort()
self.isSorted = True
return self.students[:]

# intorc o copie a listei studentilor


Folosesc Catalogul !

def gradeReport(course): # presupun ca course este de type grades


report = []
for s in course.allStudents(): # metoda allStrudents() intoarce lista
tot = 0.0
numGrades = 0
for g in course.getGrades(s): #ptr fiecare nota g, fiecarui student s
tot += g
numGrades += 1
try:
average = tot/numGrades
report.append(str(s) + '\'s mean grade is ' + str(average))
except ZeroDivisionError:
report.append(str(s) + ' has no grades')
return '\n'.join(report) # inserez \n dupa fiecare element din lista
Folosesc Catalogul !
Creez baza de date cu studenți:

ug1 = UG('Matt Damon', 2018) # undergraduate student


ug2 = UG('Ben Affleck', 2019)
ug3 = UG('Drew Houston', 2017)
ug4 = UG('Mark Zuckerberg', 2017)
g1 = Grad(’Bill Gates’) # graduate student
g2 = Grad(’Steve Wozniak')

Creez catalogul, adaugând studenții în ordine arbitrară (am idNum!):

six00 = Grades()
six00.addStudent(g1)
six00.addStudent(ug2)
six00.addStudent(ug1)
six00.addStudent(g2)
six00.addStudent(ug4)
six00.addStudent(ug3)
Rulez exemplul Catalog !

six00.addGrade(g1, 100) six00.addGrade(g1, 90)


six00.addGrade(g2, 25) six00.addGrade(g2, 45)
six00.addGrade(ug1, 95) six00.addGrade(ug1, 80)
six00.addGrade(ug2, 85) six00.addGrade(ug2, 75)
six00.addGrade(ug3, 75)

print(gradeReport(six00)) print(gradeReport(six00))

Matt Damon's mean grade is 95.0 Matt Damon's mean grade is 87.5
Ben Affleck's mean grade is 85.0 Ben Affleck's mean grade is 80.0
Drew Houston's mean grade is 75.0 Drew Houston's mean grade is 75.0
Mark Zuckerberg has no grades Mark Zuckerberg has no grades
Bill Gates’s mean grade is 100.0 Bill Gates’s mean grade is 95.0
Steve Wozniak’s mean grade is 25.0 Steve Wozniak’s mean grade is 35.0
Folosesc Catalogul !

Printez lista tuturor studentilor sortați după idNum

for s in six00.allStudents():
print(s)

ASA NU! (chiar daca Python permite)


for s in six00.students:
print(s)

Se expune reprezentarea interna!

Dacă ar fi să schimb modul în care doresc să reprezint catalogul,


ar trebui să schimb doar metodele din acel obiect, nu metodele externe
care folosesc catalogul !
Comentarii pe exemplul Catalog

Separarea datelor de folosirea datelor

Acces prin metodele asociate la obiectul catalog

Această versiune este ineficientă – pentru a obține lista tuturor


studentilor se crează o copie internă a listei

Dacă manipulăm lista fără să schimbam structura internă, ce facem când


avem 100,000 studenți ?

Folosim obiectul Python GENERATORS


DE CITIT!

Allen Downey :Think Python - How to Think Like a Computer


Scientist, 2nd Edition, Version 2.2.18 Green Tea Press

Clase și Metode- Capitolul 17


Mosteniri- Capitolul 18
DE CITIT!

Clase și Obiecte : Data model


https://docs.python.org/3.3/reference/datamodel.html
TEMA!

Clase și Obiecte : Capitolul 18 exercițiul 18.3


Bibliografie

Eric Grimson, and John Guttag. 6.00 Introduction to Computer


Science and Programming. Fall 2008. Massachusetts Institute of
Technology: MIT OpenCourseWare, https://ocw.mit.edu. License:
Creative Commons BY-NC-SA.
Curs 3

Librarii/Pachete Python
NumPy
Pachete & Python

De ce este Python prima optiune in prelucrarea datelor stiintifice?

IPython oferă o modalitate convenabilă de a face calcule științifice


interactiv cu Python.

IPython nu oferă caracteristici științifice de calcul per se, ci o


interfață interactivă pentru accesarea diferitelor biblioteci externe
pentru a rezolva diverse probleme din domeniile Data Mining, Data
Exploration, Machine Learning, Deep Learninig, Matematica,
Visualizare si altele asemenea.

Împreună, aceste instrumente oferă un cadru pentru calculul științific


ce poate concura cu instrumentele comerciale folosite pe scară largă în
comunitatea din domeniul științific - Matlab sau Mathematica.
Top 13 pachete Python

1. NumPy - Matematica
2. Pandas – Data Exploration si Vizualizare
3. Matplotlib - Vizualizare
4. SciPy – Matematica, Inginerie
5. Plotly - Vizualizare
6. Seaborn - Vizualizare
7. BeautifulSoup - Data Mining
8. Scrappy - Data Mining
9. Scikit Learn - Machine Learning (ML) - invatare automata
10. PyCaret – Machine Learning ( ML)
11. TensorFlow – Deep Learning (DL) - invatare profunda
12. Keras – Deep Learning (DL)
13. PyTorch – Deep Learning (DL)
Pachete & Python

NumPy oferă calcul optimizat al structurilor de date n-dimensionale

Panda oferă structuri de date pentru date sub formă de tabele care provin
din seturi de date reale.

Matplotlib permite desenarea figurilor grafice, astfel încât să se


vizualizeze interactiv orice formă de date și posibilitatea de a face figuri
de calitate pentru articolele științifice.

SciPy se bazează pe NumPy si oferă o largă varietate de algoritmi


științifici (de prelucrare a semnalului, optimizare, imagistic, etc),
combina domeniile matematica, stiinta si inginerie – echivalentul free a
Matlab-ului.
Pachete & Python

Plotly - unul dintre cele mai bune instrumente de vizualizare a datelor


disponibile, construit deasupra bibliotecii de vizualizare D3.js, HTML și
CSS. Este creat folosind Python și cadrul Django.

Seaborn - mulți oameni de știință preferă Seaborn fata de Matplotlib


datorită interfeței sale de nivel înalt pentru a desena grafice statistice
atractive și informative.

BeautifulSoup - bibliotecă de analiză care permite web scraping –


extragerea datelor din documente HTML și XML.

Scrapy - permite large scale web scraping – extragerea datelor de pe site-


uri web, procesarea și stocarea în structura și formatul preferat.
Pachete ML/ DL & Python
SciKit - instrumente eficiente pentru învățarea automată și modelarea
statistică, inclusiv clasificarea, regresia, gruparea și reducerea
dimensionalității.

PyCaret – efectuați experimente de învățare automată de la inceput la


sfarsit, atribuirea valorilor lipsă, codificarea datelor pe categorii,
extragerea caracteristicilor, reglarea hiperparametrilor.

TensorFlow - include instrumente, biblioteci și resurse pentru a construi


aplicații bazate pe invatare automata (ML) & invatare profunda (DL).

Keras - este un API pentru DL scris în Python, care rulează deasupra


platformei TensorFlow. A fost dezvoltat cu accent pe activarea
experimentării rapide.

PyTorch -cel mai bun framework pentru DL scris în Python,oferă


flexibilitate și viteză maximă, ecosistem robust, instruire distribuita,
suport in cloud.
NumPy- Numeric Python

NumPy este baza pentru majoritatea calculelor stiintifice din Python,


este pachetul esential pentru Machine learning (ML) and
Deep Learning (DL).

NumPy este pachetul de calcul optimizat al structurilor de date n-


dimensionale

NumPy prevede instrumente pentru integrarea propriului cod cu


cod C , C ++ și Fortran.
Module din Numpy

Algebra Lineara (linalg)

Transformata Fourier Discreta (fft)

Numere Random (random)

Distributii Discrete

Distributii Continue
Funcții din Numpy.linalg

diag - returnează elementele de pe diagonala (or off-diagonal) unei


matrici patratice ca un 1D array, sau converteste un 1D array intro
matrice patratica cu zero in afara diagonalei
dot – multiplicarea matricilor
trace – calculeaza suma elementelor de pe diagonala matricei
det - calculeaza determinantul unei matrici
eig - calculeaza eigenvalues and eigenvectors unei matrici patratice
inv - calculeaza inversa unei matrici patratice
pinv - calculeaza Moore-Penrose pseudo-inverse unei matrici patratice
qr - calculeaza decompozitia QR
svd - calculeaza Singular Value Decomposition (SVD)
solve – rezolva un sistem linear Ax = b ptr x, A este o matrice patratica
lstsq - calculeaza solutia least-squares pentru y = Xb
Import NumPy
Pachetele trebuiesc instalate și apoi importate în scripturi/fișiere sau
folosite interactiv în Python:

In[]: import numpy sau așa In[]: from numpy import *


# folosesc asa:
x= numpy.array([1,2,3]) #array este o funcție din pachetul numpy

In[]: import numpy as np #pot să redenumesc un pachet:


# folosesc asa:
x=np.array([1,2,3])
print(np.__version__) # aflu numarul versiunii numpy

In[]: from numpy import array #import doar o funcție dintr-un pachet
# folosesc doar asa:
x=array([1,2,3])

In[]: %pylab #NumPy și Matplotlib sunt încărcate cu namespace-ul


np pentru numpy și plt pentru matplotlib.pyplot
NumPy - array

Obiectul de bază din NumPy este matricea omogenă n-dimensională

array =bloc de date organizat în mai multe dimensiuni

array – un alt tip de date adăugat în Python de NumPy,


clasa acestui obiect este ndarray alias array.

array – folosit pentru a reprezenta vectori, matrici, alte structuri n-dim

array – au dimensiune fixă dată la construirea lor

Elementele arrays sunt toate de acelasi tip de date ( scriem cod mai
simplu și mai eficient) stocate într-un boc continnuu de memorie
Întreg în Python ?
x=10000 # tipul de date este inferat dinamic

Codul sursa ne arata pentru integer(long) urmatoarea structura:

struct _longobject {
long ob_refcnt;
PyTypeObject *ob_type;
size_t ob_size;
long ob_digit[1]; }

ob_refcnt - a reference count - aloca si dealoca memoria
• ob_type - encodează tipul variabilei
• ob_size - specifică dimensiunea următoarelor date membre
• ob_digit - conține valoarea întreagă actuală ce reprezintă variabila

Această informație suplimentară din structura întregului în Python este


ceea care permite Python să fie codificat atât de liber și de dinamic.
Întreg în Python ?

Un număr întreg în C este o etichetă pentru o poziție în memorie ai căror


octeți codifică o valoare întreagă.

Un întreg în Python este un pointer către o poziție în memorie care


conține toate informațiile despre obiectul Python, inclusiv octeții care
conține valoarea întregului.

Toate aceste informații suplimentare din tipurile Python au un cost, care


devine deosebit de evident în structuri care combină multe dintre aceste
obiecte. - > necesitatea NumPy - ndarray
Listă în Python ?

Lista permite diverse tipuri de date- fiecare element este un obiect Python

In[]: L = [True, "2", 3.0, 4]


In[]: [ type(item) for item in L]

Out[]: [bool, str, float, int]

La nivel de implementare:

Lista conține un pointer către un bloc de pointeri, fiecare dintre ei


indicând la rândul său un obiect Python complet.

Array conține un singur pointer către un bloc continuu de date.


Listă în Python ?

NumPy arrays nu permit tipuri de date diferite, dar sunt mult


mai eficiente pentru stocarea și manipularea datelor.
Tipuri de data standard - NumPy
NumPy

Manipularea datelor in Python sinonim cu manipularea arrays în NumPy

Atributele obiectului ndarray


Determinarea dimensiunii, formei, a tipurilor de date ale arrays si a
consumului de memorie
Indexarea
Obținerea și setarea valorilor individuale ale elementelor
Slicing
Obținerea și setarea subarrays
Reshaping
Schimbarea formei unei array de date
Joining and Splitting
Combinarea mai multor arrays și împărțirea arrays în mai multe părți
Atributele obiectului ndarray
ndim
numărul de axe (dimensiuni) a unui array = rank
shape
dimensiunile unui array – acesta este o tuplă indicând lungimea în
fiecare dimensiune. Ptr o matrice cu n linii și m coloane, atributul shape
va fi (n,m).
size
numărul total de elemente, este egal cu produsul elementelor din shape.
dtype
un obiect ce descrie tipul de elemente din array.
data
un buffer ce conține toate elementele din array. Nu folosim acest
atribut, deoarece elementele din array se vor accesa prin indexare
itemsize :dimensiunea în bytes a fiecărui element (4 – int32,
8 – float64)

In[]: help(array) # accesez help-ul


In[]: lookfor(‘cuvantcheie’) # accesez help-ul
Atributele obiectului ndarray
In[]: %pylab
In[]: poz=rand(10000000, 2) # generează 10000000 numere in [0,1]

In[]: type(poz)
numpy.ndarray

In[]: poz.dtype
dtype('float64')

In[]: poz.ndim
2
In[]: poz.shape
(10000000, 2)

In[]: poz.itemsize
8 # calculez bite size - float64
2 dimension ndarray
Indexarea liniilor si coloanelor de la 0
Cum creăm un array

Sunt mai multe moduri în care putem crea un numpy array:

- pe baza listelor sau tuplelor

- folosind funcții dedicate ( arange, linspace, zeros, ones etc.)

- citim date din fișiere


Creăm un array cu liste
Folosim liste ca argumente în funcția array() :
In[]: %pylab
In[]: v = array([1,2,3,4,5]) sau v = array([1, 2, 3, 4, 5], dtype=int32)
In[]: v
array([1,2,3,4,5])

In[]: M = array([ (1, 2), (3, 4) ]) - listă în listă 2D array


In[]: M
array([[1, 2], # default NumPy afișează ordinarea pe linii (C-order)
[3, 4]])

v și M sunt obiecte de tipul ndarray.


In[]: type(v) , type(M) # numpy.ndarray
In[]: v.shape sau M.shape # TypeError: 'tuple' object is not callable
In[]: shape (v) sau shape(M) # (5,) (2,2)

Pentru a crea n-dim array, trebuie să scriem liste în liste de liste cu n


nivele de recursivitate.
numpy.ndarray

numpy.ndarray arată foarte asemănator cu o listă (sau lista imbricată).

De ce să nu utilizăm pur și simplu liste pentru calcule în loc să creăm un


nou tip de date - array?

Listele sunt foarte generale, pot conține orice fel de obiecte, sunt scrise
în mod dinamic. Listele nu oferă suport pentru funcții matematice.

numpy array sunt omogene și statice (tipul elementelor este determinat


atunci când sunt create). Sunt eficiente din pct de vedere al memoriei și
suportă aplicarea rapidă a funcțiilor matematice
numpy.ndarray
In[]:a = array([[1, 2], [3, 4], [5, 6]])
Out[]: array([[1, 2],
[3, 4],
[5, 6]])
Fiecare din listele interioare devine un rand in array
Toate listele interioare trebuie sa fie de aceeasi dimensiune
Putem face calcule cu un array 2D ca si cum am folosi 1D array
In[]: b = 3 * a + 2
Out[]: print b
[[ 5 8]
[11 14]
[17 20]]
Toate calculele sunt facute “element cu element” (asemanator cu Matlab)
inclusiv inmultirea se va face a.*a si nu multiplicare de matrici
In[]:print a * a
Out[]:[[ 1 4]
[ 9 16]
[25 36]]
Creăm un array cu liste

Putem defini explicit tipul unui array când îl creăm folosind atributul
dtype ca argument:

In []: M = array([[1, 2], [3, 4]], dtype=complex)


In []: M
array([[ 1.+0.j, 2.+0.j],
[ 3.+0.j, 4.+0.j]])

Tipurile de date ce pot fi folosite : int, float, complex, bool, string, object

Putem defini explicit dimensiunea pe biti a tipurilor de date:


int64, int16, float128, complex128.
Exemplu
Un array de numere complexe initializat cu valorea 6

In[]: d = np.full((3, 3), 6, dtype = 'complex')


In[]: d
array ([[ 6.+0.j 6.+0.j 6.+0.j]
[ 6.+0.j 6.+0.j 6.+0.j]
[ 6.+0.j 6.+0.j 6.+0.j]])

Flatten() metoda de a obtine o copie a unui array colapsat intr-o singura


dimensiune. Valoarea default este ‘C’ (pentru aranjarea pe linii).
Folositi ‘F’ pentru aranjarea pe coloane.

In[]: f=d.flatten()
In[]: f
array([ 6.+0.j, 6.+0.j, 6.+0.j, 6.+0.j, 6.+0.j, 6.+0.j, 6.+0.j,
6.+0.j, 6.+0.j])
Cum creăm un array cu funcții
Pentru date de dimensiuni mari, nu este practic să le inițializăm manual,
folosind liste.

Putem folosi diverse funcții: arange, linspace, rand, randn, zeros,


zeros_like, ones, ones_like, empty, empty_like, fromfunction, fromfile

In[]: x = arange(0, 10, 1) # argumente: start, stop, step in [start, stop)

In[]: linspace(0, 10, 25) # generează 25 de valori in [start, stop]

In[]: random.rand(5,5) # generează o matrice de 5 linii și 5 coloane


de numere random –distribuite uniform în [0,1]

In[]: random.randn(5,5) # generează o matrice de 5 linii și 5 coloane


de numere random -distribuite Gaussian în [0,1]
In[]: random.randint(15,35, size=(3,4) ) # generează o matrice de 3 linii
și 4 coloane de numere random
distribuite în intervalul [15,35]
Cum creăm un array cu folosind funcții
In[]: zv=zeros(5) # am creat vectorul de 5 elemente 0
In[]: zv
Array ([0., 0., 0., 0., 0.]) # default nr in virgulă mobilă - float

In[]: zm= zeros ( (3,3) ) # trebuie să scriem tupla ( lin,col)


In[]: zm # am creat matricea cu 3 linii si 3 coloane de 0
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])

Funcțiile ones() și eye() - crează matricea de 1 și matricea Identitate

In[]: diag([1,2,3]) sau # matricea cu diagonala principală [1,2,3]


In[] :diag([1,2, 3] , k=1) #offset fața de diagonala principală dat de k
array ([[0, 1, 0, 0],
[0, 0, 2, 0],
[0, 0, 0, 3],
[0, 0, 0, 0]])
Cum creăm un array folosind funcții
import numpy as np

In[]:a=np.array([[1, 2],[3, 4]])


In[]: array([[1,2],
[3,4]])
arange – genereaza numere in intervalul [strat: stop)
In[]: a.arange(1,5 ) # nu pot folosi functia arange pe un array creat
Out: AttributeError: 'numpy.ndarray' object has no attribute 'arange'

In[]: b= np.arange(6) # fct arange asemanatoare cu fct range din Matlab


Out: array([0,1, 2, 3,4,5])
In[]: x = b.reshape((2, 3))
In[]: x
array([[0, 1, 2],
[3, 4, 5]])
In[]: np.zeros_like(x) In[]: zeros((3,1))
Out[]:array([[0, 0, 0], Out[]: array([[0],
[0, 0, 0]]) [0],
[0]])
Cum creăm un array din fișiere
De obicei fișierele de date sunt în unul din aceste formate (CSV) comma-
separated values sau (TSV) tab-separated values

# comanda head afișeaza primele 10 linii dintr-un fisier


In[]: !head stkdate.dat
1800 1 1 -6.1 -6.1 -6.1 1
1800 1 2 -15.4 -15.4 -15.4 1
1800 1 3 -15.0 -15.0 -15.0 1
1800 1 4 -19.3 -19.3 -19.3 1

Formatul datelor: an,luna,zi, temp_med/zi, minima, maxima, locația.

Funcțiile genfromtxt(), loadtxt() și fromfile() permit încărcarea datelor


din fișiere text sau fișiere binare și să le convertească NumPy array.

In[]: dd=genfromtxt('stkdate.dat', names= True, dtype=None)


– permite gestiunea valorilor lipsa
In[]: dd.shape
(643, 7) # 643 de linii si 7 coloane
Cum creăm un array din fișiere
In[]: d=loadtxt('stkdate.dat', delimiter=’,’, skiprows =1, usecols=[0,2])
In[]: d
array([[ 1.80000000e+03, 1.00000000e+00, 1.00000000e+00,
-6.10000000e+00, -6.10000000e+00, -6.10000000e+00,
1.00000000e+00],
[ 1.80000000e+03, 1.00000000e+00, 2.00000000e+00,
-1.54000000e+01, -1.54000000e+01, -1.54000000e+01,
1.00000000e+00],
[ 1.80000000e+03, 1.00000000e+00, 1.00000000e+01,
-9.50000000e+00, -9.50000000e+00, -9.50000000e+00,
1.00000000e+00]])

Similar, funcția fromstring() permite fie text ASCII cu valori separate


de orice delimitator fie date binare de orice tip:

In []:x = fromstring('1 2 5 10', dtype=int, sep=' ')


In[]: x
array([ 1, 2, 5, 10])
Salvăm un array în fișier
Pentru salvarea arrays (datelor) în fișiere avem funcțiile save() – pentru
formatul binar și savetxt() – pentru formatul text
In[]: M = rand(3,3)
In[]: M
array([[ 0.77872576, 0.40043577, 0.66254019],
[ 0.60410063, 0.4791374 , 0.8237106 ],
[ 0.96856318, 0.15459644, 0.96082399]])
In[]: savetxt("mtx.csv", M)
In[]:!cat mtx.csv # comanda cat afisează conținutul fișierului mtx.csv
7.787257639287014088e-01 4.004357670697732408e-01 6.625401863466899854e-01
6.041006328761111543e-01 4.791373994963619154e-01 8.237105968088237473e-01
9.685631757740569281e-01 1.545964379103705877e-01 9.608239852111523094e-01

In[]: savetxt("mtx.csv", M, fmt='%.5f') # fmt specifică formatul


In[]: !cat mtx.csv
0.77873 0.40044 0.66254
0.60410 0.47914 0.82371
0.96856 0.15460 0.96082
Manipularea arrays
Indexarea, Slicing și Iterarea- permit selecția unei porțiuni din array

1D array – indexarea, slicing si iterarea exact ca în listele Python


Indexarea începe de la 0.

In[]: a = arange(10)**3
In[]: a
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
In[]: a[0], a[2], a[-1] # primul, al treilea si ultimul element
(0, 8, 729)
In[]: a[2:5] # a[start: stop: step] echivalent cu a[start, stop) default step 1
array([ 8, 27, 64])
In[]: a[2:5] =55 # atribuim valoarea 55 pentru toate elementele din interval
array([ 0, 1, 55, 55, 55, 125, 216, 343, 512,729])
In[]: a[:6:2] = -1000 # echivalent cu a[0:6:2] = -1000
In[]: a
array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])
In[]: a[ : :-1] # în ordine inversă a
array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])
Manipularea arrays
In[]: a = linspace(10, 100, num=10)
Out[]: print(a)
[ 10. 20. 30. 40. 50. 60. 70. 80. 90. 100.]

Array slices = fragment/poza (views):


- daca a este un numpy array, si b = a[m:n:s], b este un “view” a array-ului a
- a si b sunt obiecte distincte (i.e., id(a) != id(b))
- a si b ambele sunt de type numpy.ndarray
- a si b impart aceleasi date:b e doar o restrictie a datelor pe care le putem
vedea din a
- orice schimbare a datelor in b de asemenea afecteaza datele din a
b = a[:4]
b[0] = 1000
print(a)
[ 1000. 20. 30. 40. 50. 60. 70. 80. 90. 100.]
Retineti!
Ptr o lista L, z=L[:], z este o copie a elementelor din L, z este o noua lista,
care poate fi manipulata fara sa afecteze elementele din L
Ptr un array a, b=a[:], b este un “view” ale datelor din a, orice schimbare a
datelor prin acest view va schimba datele si din a.
Manipularea arrays
Multidimensional arrays 2D, 3D... pot avea un index pe axa. Indexii sunt
tuple de întregi.

Dacă indecsii sunt mai putini ca nr axelor se întoarce array de dimensiune N-1

In[]: x=diag(arange(3))
x
array([[0, 0, 0],
[0, 1, 0],
[0, 0, 2]])
In[]: x[1,1] # linia 1, coloana 1 (primul indice linia, al doilea coloana)

In[]: x[1] # întoarce întreaga linie


array([0, 1, 0])
In[]: x[-1] # întoarce ultima linie

In[] : x[1, :] # întoarce prima linie array([0, 1, 0])

In[]: x[:, 1] # întoarce prima coloană array([0, 1, 0])


Manipularea arrays
Ptr 2D arrays se aplica aceeasi logica ca la1D arrays:

In[]: b = array([[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34]])
Out[]:print(b)
[[11 12 13 14]
[21 22 23 24]
[31 32 33 34]]

In[]: print (b[:, 1]) # [12 22 32]

In []: print( b[0, :]) # [11 12 13 14]

In[]: print (b[:, :2]) # primele doua coloane

In[]: print (b[:, ::2]) # coloana 0 si 2

In[]: print (b[1:, ::2]) # putem selecta simultan pe linii si coloane


Out[]: [[21 23]
[31 33]]
Manipularea arrays
Pentru n-dim arrays: expresia x[i] este tratată ca i urmat de atâtea instanțe : necesare să
reprezinte axele. NumPy permite și notația cu puncte … așa x[i,...].
Punctele (...) reprezintă nr de coloane necesare pentru a indexa tuple.
De examplu, dacă x este de rank 5 (i.e., avem 5 axe):

x[1,2, . . .] este echivalent cu x[1, 2, : ,: ,:]


x[. . . ,3] este echivalent cu x[: ,: ,: ,: , 3]
x[4, . . . , 5, :] este echivalent cu x[4, : , : , 5, : ].

In[]: m = np.array( [[[ 0, 1, 2], # 3D array (stacked 2 arrays 2D)


... [ 10, 12, 13]],
... [[100,101,102],
... [110,112,113]]])
In[]: m.shape
(2, 2, 3)
In[]: m[1,...] # echivalent cu m[1, : , :] sau m[1]
array([[100, 101, 102],
[110, 112, 113]])
In[]: m[… , 2] # echivalent cu m[: , : , 2]
array([[ 2, 13],
[102, 113]])
Indexare booleană

Rezultatul va fi un array( ….. , dtype=bool)

Se poate aplica pe arrays de orice dimensiune.


Indexare booleană

In []: B = array([n for n in range(5)])


In[]:B
Out[]:array([0, 1, 2, 3, 4])

Mask index: este un Numpy array de data tip boolean.

Elementul este selectat (True) sau nu (False) depinzând de valoarea indexului


din mask de la poziția fiecărui element:

In []: msk = array([True, False, True, False, False])


In[]:B[msk]
Out[]:array([0, 2])

In []: ms = array([1,0,1,0,0], dtype=bool) # obtin același lucru


In[]:B[ms]
Out[]:array([0, 2])
Indexare booleană
Această caracteristică este utilă când folosim operatori de compara ție pentru a
selecta elementele dintr-un array,:
In []: x = arange(0, 10, 0.5)
In[]: x
array([ 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. , 6.5, 7. ,
7.5, 8. , 8.5, 9. , 9.5])
In []: masca = (5 < x) * (x < 7.5)
In[]:masca
array([False, False, False, False, False, False, False, False, False,
False, False, True, True, True, True, False, False, False,
False, False], dtype=bool)
In []: x[masca]
array([ 5.5, 6. , 6.5, 7. ])

Masca de indecsi poate fi convertita la poziția indecsilor cu functia where():


In []: indici = where(masca)
In[] : indici
(array([11, 12, 13, 14]), )
In []: x[indici] # această indexare este echivalenta cu x[masca]
array([ 5.5, 6. , 6.5, 7. ])
Indexare booleană
Care este diferența dintre cuvintele cheie and și or și operatorii & și | pe ?

Când folosim unul versus celălalt?

and și or verifică adevărul sau falsitatea întregului obiect


& și | se referă la operațiile pe biți din fiecare obiect

În Python, toate numerele întregi diferite de zero se vor evalua drept True:

In[]: bool(42), bool(0) In[]: bin(42)


Out[]: (True, False) Out[]: '0b101010'
In[]: bool(42 and 0) In[]: bin(59)
Out[]: False Out[]: '0b111011'
In[]: bool(42 or 0) In[]: bin(42 & 59)
Out[]: True Out[]: '0b101010'
In[]: bin(42 | 59)
Out[]: '0b111011'
Funcții de Indexare
Functia take() este similară cu metoda de indexarea:
In []: v = arange(-3,3)
In[]:v
array([-3, -2, -1, 0, 1, 2])
In []: ind = [1, 3, 5]
v[ind] # exemplul anterior de indexare
array([-2, 0, 2])

In []: v.take(ind)
Array([-2, 0, 2])

In []: take([-3, -2, -1, 0, 1, 2], ind) # fct take() aplicată pe liste și alte obiecte
Array([-2, 0, 2])

Functia choose() construieste un array, alegând elemente din alte arrays:

In []: care = [1, 0, 1, 0]


In[]: deunde = [[-2,-2,-2,-2], [5,5,5,5]]
In[]:choose(care, deunde)
array([ 5, -2, 5, -2])
Reshape arrays
Formatul unui Numpy array se poate modifica fără să copiem structura datelor
shape – numărul de elemente pe fiecare axă:

In[]: b = arange(12).reshape(4,3) # 2D array


In[]: b
array([[ 0 1 2],
[ 3 4 5],
[ 6 7 8],
[ 9 10 11]])
In[]: z=b.reshape(1,12) # z=b.reshape(2, -1) se decide automat nr coloanelor
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])
In[]: c = arange(24).reshape(2,3,4) # 3D array
In[]: c
array([[[ 0 1 2 3],
[ 4 5 6 7],
[ 8 9 10 11]],

[[12 13 14 15],
[16 17 18 19],
[20 21 22 23]]])
Exercițiu

In[]: c = arange(24).reshape(2,3,4) # 3D array

In[]: print(c)

Cum va arata acest 3D array?


Exercițiu

In[]: c = arange(24).reshape(2,3,4) # 3D array


In[]: c
array([[[ 0 1 2 3],
[ 4 5 6 7],
[ 8 9 10 11]],

[[12 13 14 15],
[16 17 18 19],
[20 21 22 23]]])

1. Care este comanda sa afisez 18?

2. Care este comanda sa afisez:


array([[ 18,19],
[22,23]])
3. Care este comanda sa afisez:
array([[ 13,17, 21],
[14,18, 22]])
Răspuns

1. Care este comanda sa afisez 18?

c[1,1,2]

2. Care este comanda sa afisez:


array([[ 18,19],
[22,23]])

c[1,1:,2:]

3. Care este comanda sa afisez:


array([[ 13,17, 21],
[14,18, 22]])

c[1, :, [1,2]]
Exerciții
In[]: %pylab

1. Ce va afisa urmatoarea comanda ?

In[]: x=np.arange(3*4*5*6).reshape(3,4,5,6) ?

2. Ce va afisa urmatoarea comanda ?

In[]: x=np.arange(3*4*5*6)[ : : -1] ?

3. Ce va afisa urmatoarea comanda ?

In[]: x=np.arange(3*4*5*6)[ : : -1].reshape(5,4,6,3) ?

4. Ce va afisa urmatoarea comanda ?


In[]” x.ravel() # rearanjeaza toate elementele pe o singura linie
Stacking arrays
Functiile repeat(), tile(), concatenate(), vstack() - pentru dimensiuni diferite
Parametrul axis=0 se refera la linii si axis=1 se refera la coloane

In[]:a = array([[1, 2], [3, 4]])


In[]:b = array([[5, 6]])
In[]: repeat(a, 3) # repetă fiecare element de 3 ori
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])
In[]: tile(a, 3)
array([[1, 2, 1, 2, 1, 2],
[3, 4, 3, 4, 3, 4]])
In[]: concatenate((a, b), axis=0) similar cu vstack((a,b))
array([[1, 2],
[3, 4],
[5, 6]])
In[]: concatenate((a, b.T), axis=1) similar cu hstack((a,b.T))
array([[1, 2, 5],
[3, 4, 6]])

b.T – transpusa lui b


Funcții universale -ufuncs

Calculele repetate asupra elementelor matricilor pot fi foarte rapide cand sunt
utilizate operațiuni vectorizate

În NumPy vectorizarea este implementată prin aceste func ții universale –


ufuncs – mult mai eficiente decat folosirea buclelor

In[]: x = np.arange(9).reshape((3, 3))


In[]: 2 ** x
Out[]: array([[ 1, 2, 4],
` [ 8, 16, 32],
[ 64, 128, 256]])
Funcții universale -ufuncs

Alte exemple de ufuncs: trigonometrice, exponentiale, logatitmice, etc


Ufuncs
Comportamentul default este element cu element pe vector sau matrice:
In[]:v = arange(0, 5)
In[]:v * 2 , v + 2
array([0, 2, 4, 6, 8]), array([2, 3, 4, 5, 6])
In[]: v * v # sau așa v**2 , element cu element
array([ 0, 1, 4, 9, 16])
In[]: A= array([[ 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14],
[20, 21, 22, 23, 24],
[30, 31, 32, 33, 34],
[40, 41, 42, 43, 44]])
In[]: A +2, A*2
In[]: A*A # element cu element
In[]: A*v # multiplicare element cu element fiecare linie
array([[ 0, 1, 4, 9, 16],
[ 0, 11, 24, 39, 56],
[ 0, 21, 44, 69, 96],
[ 0, 31, 64, 99, 136],
[ 0, 41, 84, 129, 176]])
Ufuncs
Functia dot( x,y) multiplica cele 2 argumente: matrice -matrice, matrice-vector
In[]:dot(A, A)
array([[ 300, 310, 320, 330, 340],
[1300, 1360, 1420, 1480, 1540],
[2300, 2410, 2520, 2630, 2740],
[3300, 3460, 3620, 3780, 3940],
[4300, 4510, 4720, 4930, 5140]])
In []: dot(A, v) #array([ 30, 130, 230, 330, 430])
In[]: dot(v, v) # 30
Facem cast obiectului array la tipul matrice sa putem folosi operatorii
aritmetici standard +, - , * pe matrici.
In[]: M = matrix(A)
In[]:vv = matrix(v).T # fac vectorul coloană
vv
matrix([[0],
[1],
[2],
[3],
[4]])
Ufuncs
Functia dot( x,y) multiplica cele 2 argumente: matrice -matrice, matrice-vector
In[]:dot(A, A)
array([[ 300, 310, 320, 330, 340],
[1300, 1360, 1420, 1480, 1540],
[2300, 2410, 2520, 2630, 2740],
[3300, 3460, 3620, 3780, 3940],
[4300, 4510, 4720, 4930, 5140]])
In []: dot(A, v) #array([ 30, 130, 230, 330, 430])
In[]: dot(v, v) # 30
Facem cast obiectului array la tipul matrice sa putem folosi operatorii
aritmetici standard +, - , * : M*M, M*c, c.T*c, c+M*c, inv(a), det(A)
In[]: M = matrix(A)
In[]:c = matrix(v).T # fac vectorul coloană folosind transpusa
c
matrix([[0],
[1],
[2],
[3],
[4]])
Ufuncs - argumentul out

Argumentul out – specifică unde va fi stocat rezultatul

In[]: x = arange(5)
In[]: y = zeros(10)
In[]: power(2, x, out=y[::2])
print(y)
[1. 0. 2. 0. 4. 0. 8. 0. 16. 0.]

Daca scriam y[::2] = 2 ** x se crea un array temporar ptr 2**x si apoi se


copia in y – mult mai lent
Ufuncs - argumentul axis
Argumentul axis - specifică dimensiunea arrays de-a lungul căreia se
calculează restrăns axis=0 – din fiecare coloană axis=1 – din fiecare
linie
Procesăm date
Formatul datelor: an,luna,zi, temp_med/zi, minima, maxima, locația.
In[]: !head stkdate.dat
1800 1 1 -6.1 -6.1 -6.1 1
1800 1 2 -15.4 -15.4 -15.4 1
1800 1 3 -15.0 -15.0 -15.0 1
1800 1 4 -19.3 -19.3 -19.3 1

In[]: data=genfromtxt('stkdate.dat')
In[]: data.shape
(643, 7) # 643 de linii si 7 coloane
In[]: mean(data[:,3]) # calculez temp medie in ultimii ani
In[]: std(data[:,3]), var(data[:,3]) # deviatia standard si varianta
In[]: data[:,3].min() , data[:,3].max() # valoarea min si max

Atenție!
In Python avem funcțiile buit-in sum(), min(), max() etc
În NumPy avem alte funcții sum(), min(), max() etc care operează mult
mai rapid si tin cont de dimensiunile arrays
Ufuncs - scipy.special

O altă sursă excelentă pentru ufuncs mai specializate și obscure este


submodulul scipy.special.

In[]: from scipy import special


# Gamma functions (generalized factorials) and related functions

In[]:x = [1, 5, 10]


In[]: print("gamma(x)=", special.gamma(x))
Out[]:gamma(x) = [ 1.00000000e+00 2.40000000e+01 3.62880000e+05]

In[]: print("ln|gamma(x)| =", special.gammaln(x))


Out[]:ln|gamma(x)| = [ 0. 3.17805383 12.80182748]

In[]: print("beta(x, 2)=", special.beta(x, 2))


Out[]:beta(x, 2)= [ 0.5 0.03333333 0.00909091]
Broadcasting
Un alt mijloc de vectorizare – broadcasting - set de reguli pentru
aplicarea ufunc-urilor binare (adunare, scădere, multiplicare etc.) pe
arrays de diferite dimensiuni.
In[]: a = array([0, 1, 2]) In[]: M = ones((3, 3))
In[]: a+5 In[]: M+5

In[]: a = arange(3)
In[]:b = arange(3)[: , newaxis]
In[]:print(a)
Out[]:[0 1 2]
In[]:print(b)
Out[]:[[0]
[1]
[2]]
In[]: a + b
Out[]: array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
Broadcasting
Reguli Broadcasting

NumPy urmează un set de reguli pentru a determina broadcasting


între două arrays:

• Regula 1: Dacă cele două arrays diferă prin numărul lor de


dimensiuni, forma (shape) celui cu mai puține dimensiuni este
padded cu 1 incepând din stânga sus.

• Regula 2: Dacă forma celor două arrays nu se potrivește în nicio


dimensiune, array-ul cu forma egală cu 1 în acea dimensiune este
extinsă pentru a se potrivi cu cealaltă formă.

• Regula 3: Dacă cele două arrays diferă în orice dimensiune și


niciuna dintre ele nu este egală cu 1, se semnealează eroare.
Reguli Broadcasting
Exemplu: adunarea unui 2D array cu un 1D array

In[]: M = np.ones((2, 3)) #M.shape = (2, 3)


In[] :a = np.arange(3) # a.shape = (3,)

Aplicam regula1 adăugăm 1 la stânga, devine a.shape -> (1, 3)


Aplicăm regula 2, extindem dimensiunile :
M.shape -> (2, 3)
a.shape -> (2, 3)

In[]: M+a
Out[]: array([[ 1., 2., 3.],
[ 1., 2., 3.]])
Sortarea
import numpy as np

def my_sort(x):
for i in range(len(x)):
swap = i + np.argmin(x[i:]) # găsesc valoarea minimă
(x[i], x[swap]) = (x[swap], x[i])
return x

In[]: x = np.array([2, 1, 4, 3, 5])


In[]: my_sort(x)
Out[]: array([1, 2, 3, 4, 5])

NU așa ! Foarte lent algoritmul mai ales ptr foarte multe valori
NU folosim nici funcțiile sort() și sorted() de la liste!

Folosim algoritmii implementati în NumPy np.sort (alg quick-sort)


Sortarea
import numpy as np

In[]: x = np.array([2, 1, 4, 3, 5])


In[]: y = np.sort(x) # făra să-l modificăm pe x
Out[]: y
array([1, 2, 3, 4, 5])

In[]: x.sort() # sortez modificăndu-l pe x


In[]: print(x)
[1 2 3 4 5]

Funcția argsort() întoarce indicii elementelor sortate


In[]: x = np.array([2, 1, 4, 3, 5])
In[]: i = np.argsort(x)
In[]: print(i)
[1 0 3 2 4]
Bibliografie

http://www.numpy.org/
http://jrjohansson.github.io/computing.html
Curs 10

Matplotlib
Librarie grafică & Python
Matplotlib – librărie ce permite desenarea figurilor grafice 2D și 3D în
Python, astfel încât să se vizualizeze interactiv orice formă de date și
posibilitatea de a face figuri de calitate pentru articolele științifice.

Avantaje:

Ușor de învățat

Suport pentru texte și etichete formatate LATEX

Control sporit al fiecărui element dintr-o figură, inclusiv dimensiunea
figurii și DPI (dots per inch)- toate aspectele figurii pot fi controlate
prin programare

Salvarea figurilor de calitate în diverse formate PNG, PDF, SVG, EPS,
și PGF.

GUI pentru a explora în mod interactiv cifre și pentru generarea
figurilor
Import Matplotlib
Libraria Matplotlib trebuie importată în scripturi/fișiere sau folosită
interactiv în IPython

pyplot - MATLAB-like API furnizată de matplotlib.


In[]: %pylab #NumPy și Matplotlib sunt încărcate cu
namespace-ul np pentru numpy și plt pentru
matplotlib.pyplot
sau așa:
In[]: from pylab import *
# folosesc așa:
In[]:plot([1,2,3,4]) # doar apelez funcțiile din librărie
In[]:show()

sau așa:
In[]: import numpy as np
In[]: import matplotlib.pyplot as plt
# folosesc asa:
x= plt. subplots() #subplot este o funcție din pachetul pyplot
Exemple
Majoritatea funcțiilor din MATLABsunt implementate în modulul pyplot

Scriem în linia de comandă în IPython:

In[]: % pylab
In[]: x=linspace(0, 5, 10)
In[]: y= x**2
In[]: plot(x,y, ‘r’)
In[]: xlabel('x')
In[]: ylabel('y',fontsize=’large’)
In[]:title('orice text')

Accesăm help-ul:
In[]: ylabel?

Se afișează automat graficul, intr-o fereastră.


Exemple
In scripturi scriem:

from pylab import *


x=linspace(0, 5, 10)
y= x**2
figure()
plot(x,y)
xlabel('x')
ylabel('y')
title('title')
show()

Dupa fiecare apel de funcție plot() trebuie să apelăm funcția show()


pentru a se afișa figura
Exemple

Seturi de date multiple:

In[]: x=linspace(0, 2*pi, 50)

In[]: plot(x, sin(x), x, sin(2*x))

Implicit se desenează in albastru


Figure Menu Bar
Exemple

Setăm diferite tipuri de linii și markeri.

In[]: x=linspace(0, 2*pi, 50)

In[]: plot(x, sin(x), ‘bo-’,


x, sin(2*x), ‘r-^’)

Accesăm help-ul:
In[]: plot?
Exemplu - subplot()
Multiple plots – împărțim fereastra de desenare:
subplot(nr de linii, nr de coloane, poziția activă)

x=array([1,2,3,2,1])
y=array([1,3,2,3,1])
subplot(1,2,1)
plot(x, ‘c--’)
subplot(1,2,2)
plot(y, ‘g*-’)

#șterge plot-ul curent


clf()
#închide plot-ul curent
close()
close(‘all’)

Accesăm help-ul:
In[]: subplot?
Matplotlib

Matplotlib va crea vizualizările cu comanda plot ();

Nu se va afisa vizualizarea până ce nu vom apela comada show().

Putem ajusta continuu vizualizarea cu cod suplimentar, până când suntem


gata să o finalizăm.

Dacă nu salvăm o referință la figura noastră, odată ce este afi șată, va


trebui să o recreăm pentru a modifica ceva.

Acest lucru se datorează faptului că referința a fost distrusă pentru a


elibera resurse din memorie.
API Orientată -obiect

Recomand învățarea și utilizarea API orientată-obiect a librariei Matplotlib.


Avantajul real al acestei abordări devine evident atunci când se creează figuri
avansate, când o figură conține mai multe figuri/subplot-uri.

Ideea principală este de a avea obiecte pe care se pot aplica funcții și acțiuni
și niciun obiect sau program nu ar trebui să fie global (cum este API
MATLAB).

Pentru a utiliza API-ul orientat pe obiect folosim clasa Figure - dar în loc să
creăm o nouă figură globală, păstrăm o referință la instanța figurii create
(într-o variabilă) și în ea vom folosi metode (exemplu: creăm noi axe cu
metoda add_axes)
Exemplu API Orientată -obiect
import matplotlib.pylab as plt
x=linspace(0,5,10)
y=x**2
fig = plt.figure()
# stânga, jos, lațime, înălțime (range 0 to 1)
axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # axe principale
axes2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # axe insert

# figura principală
axes1.plot(x, y, 'r')
axes1.set_xlabel('x')
axes1.set_ylabel('y')
axes1.set_title('title')

# insert
axes2.plot(y, x, 'g')
axes2.set_xlabel('y')
axes2.set_ylabel('x')
axes2.set_title('insert title')
fig.show()
Exemplu figsize, DPI, savefig
Matplotlib permite ca DPI raportul de aspect și dimensiunea figurii să fie
specificate când obiectul figure este creat, folosind argumentele figsize și
dpi.
figsize este o tuplă (lățimea , înălțimea) figurii în inch,
dpi este dots-per-inch (pixel per inch).

Creăm figura 800x400 și 100 puncte pe inch:


fig = plt.figure(figsize=(8,4), dpi=100)

Aceleași argumente pot fi plasate și functiei subplot():


fig, axes = plt.subplots(figsize=(12,3))
axes.plot(x, y, 'r')
fig.show()

Salvăm figura într-un fișier folosind metoda clasei Figure:


fig.savefig("filename.png")
fig.savefig("filename.pdf", dpi=200)
Exemplu Legend
API Matlab: apelăm metoda legend a obiectului axes pasând o listă /
tuple de texte pentru curbele definite anterior:

ax.legend(["curve1", "curve2", "curve3"])


Această variantă e predispusă la erori și inflexibilă în cazul în care
curbele sunt adăugate sau îndepărtate din figură (rezultând într-o curbă
marcată în mod greșit).

O metodă mai bună este utilizarea argumentului label funcției plot, iar
apoi folosim metoda legend fără argumente pentru a adăuga legenda
figurii:

ax.plot(x, x**2, label="curve1")


ax.plot(x, x**3, label="curve2")
ax.legend(loc=2) # loc specifică unde să fie plasată legenda
# l oc =0 – plasare automată, loc=2 colt stănga sus etc.
Exemplu Legend

fig, ax = subplots()
ax.plot(x, x**2, label="y = x**2")
ax.plot(x, x**3, label="y = x**3")
ax.legend(loc=2)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title')
Formatarea textului:LaTeX, font size, font family

Matplotlib are un bun suport pentru LaTeX.


Trebuie să utilizăm semnul $ ptr a încapsula LaTeX orice text (legendă,
titlu, etichetă, etc.) : "$ y = x ^ 3 $".

Atenție! În LaTeX, folosim frecvent backslash în comenzi, de exemplu


\ alfa pentru a produce simbolul α. Dar, backslash-ul are deja un în țeles
în șirurile Python (the escape code character). Pentru a evita aceata
trebuie să folosim siruri de caractere de text brute "raw" care sunt
prefixate cu un "r", cum ar fi r "\ alpha", sau r '\ alfa' în loc de '\ alfa' .

ax.plot(x, x**2, label=r"$y = \alpha^2$")


ax.plot(x, x**3, label=r"$y = \alpha^3$")
ax.set_xlabel(r'$\alpha$', fontsize=18)
ax.set_ylabel(r'$y$', fontsize=18)
Formatarea textului:LaTeX, font size, font family
Putem schimba global dimensiunea fontului și familia de fonturi, care se
aplică tuturor elementelor de text dintr-o figură (etichete,axe,titluri,
legende etc.):

# Updatăm parametrii de configurare din matplotlib:


matplotlib.rcParams.update({'font.size': 18, 'font.family': 'serif'})

O alegere bună ptr fonturile globale - STIX fonts:


matplotlib.rcParams.update({'font.size':18,'font.family':
'STIXGeneral', 'mathtext.fontset': 'stix'})

Sau putem seta ca matplotlib să folosească LaTeX ptr orice element text
din figură:
matplotlib.rcParams.update({'font.size': 18, 'text.usetex': True})

# restaurăm condițiile
matplotlib.rcParams.update({'font.size': 12, 'font.family': 'sans',
'text.usetex': False})
Formatarea textului:LaTeX, font size, font family

Serif STIR
LaTeX
Setare culorilor

x=linspace(0,5,20)

# MATLAB style
ax.plot(x, x**2, 'b.-') # dots line albastra
ax.plot(x, x**3, 'g--') # dashed line verde

Putem defini culori prin nume sau codurile hexa RGB(folosim #) și


optional sertăm si valoarea alpha ca argument:

ax.plot(x, x+1, color="red", alpha=0.5) # rosu transparent


ax.plot(x, x+2, color="#1155dd") # RGB hex code - albastrui
ax.plot(x, x+3, color="#15cc55") # RGB hex code - verzui
Setare stilurilor de linii
Pentru grosimea liniei folosim ca argument cuvintele cheie linewidth sau
lw, iar pentru stilul liniei linestyle sau ls :

x=linspace(0,5,20)

fig, ax = subplots(figsize=(12,6))
ax.plot(x, x+1, color="blue", linewidth=0.25)
ax.plot(x, x+2, color="blue", lw=0.50)

# linestype options ‘-‘, ‘--’, ‘-.’, ‘:’, ‘steps’


ax.plot(x, x+5, color="red", lw=2, linestyle='-')
ax.plot(x, x+6, color="red", lw=2, ls='-.')
ax.plot(x, x+7, color="red", lw=2, ls=':')

# custom dash
line, = ax.plot(x, x+8, color="black", lw=1.50)
line.set_dashes([5, 10, 15, 10]) # formatul: line length, space length, ...
Setare stilurilor de markers

# simboluri de markers posibili: '+', 'o', '*', 's', ',', '.', '1', '2', '3', '4', ...
ax.plot(x, x+ 9, color="green", lw=2, ls='--', marker='+')
ax.plot(x, x+10, color="green", lw=2, ls='--', marker='o')
ax.plot(x, x+11, color="green", lw=2, ls='--', marker='s')
ax.plot(x, x+12, color="green", lw=2, ls='--', marker='1')

#dimensiunea și culoarea markerilor:


ax.plot(x, x+13, color="purple", lw=1, ls='-', marker='o', markersize=2)
ax.plot(x, x+14, color="purple", lw=1, ls='-', marker='o', markersize=4)
ax.plot(x, x+15, color="purple", lw=1, ls='-', marker='o', markersize=8,
markerfacecolor="red")
ax.plot(x, x+16, color="purple", lw=1, ls='-', marker='s', markersize=8,
markerfacecolor="yellow",markeredgewidth=2,
markeredgecolor="blue")
Setare stilurilor de linii și markers
Controlul axelor
Putem controla range-ul, gradațiile (ticks), plasarea etichetelor,
dimensiunea fontului pentru axe.
Range-ul axelor: aplicăm obiectului axis metodele set_ylim și set_xlim
sau axis('tight') ptr setarea automată:

x=linspace(0,5,20)
fig, axes = subplots(3, 1, figsize=(12, 4))
axes[0].plot(x, x**2, x, x**3)
axes[0].set_title("default axes ranges")

axes[1].plot(x, x**2, x, x**3)


axes[1].axis('tight')
axes[1].set_title("tight axes")

axes[2].plot(x, x**2, x, x**3)


axes[2].set_ylim([0, 60])
axes[2].set_xlim([2, 5])
axes[2].set_title("custom axes range")
Axe logaritmice

Scala axelor este setată separat folosind metodele set_xscale și


set_yscale care acceptă un parametru cu valoarea "log" :

x=linspace(0,5,20)
fig, axes = plt.subplots(2, 1, figsize=(10,4))
axes[0].plot(x, x**2, x, exp(x))
axes[0].set_title("Normal scale")

axes[1].plot(x, x**2, x, exp(x))


axes[1].set_yscale("log")
axes[1].set_title("Logarithmic scale (y)")
Controlul gradațiilor axelor
Gradațiile (ticks) axelor: aplicăm obiectului axis metodele set_xticks
și set_yticks care ambele iau o listă de valori pentru unde să fie plasate.
Metodele set_xticklabels și set_yticklabels oferă o listă de etichete
personalizate de text pentru fiecare locație:

x=linspace(0,5,20)
fig, ax = subplots(figsize=(10, 4))
ax.plot(x, x**2, x, x**3, lw=2)

ax.set_xticks([1, 2, 3, 4, 5])
ax.set_xticklabels([r'$\alpha$',r'$\beta$',
r'$\gamma$', r'$\delta$', r'$\epsilon$'],
fontsize=18)

yticks = [0, 50, 100, 150]


ax.set_yticks(yticks)
ax.set_yticklabels(["$%.1f$" % y for y in yticks], fontsize=18)
# etichete formatate LaTeX
Spațierea gradațiilor axelor
# distanța între axele x și y axis și numerele de pe axe
matplotlib.rcParams['xtick.major.pad'] = 5
matplotlib.rcParams['ytick.major.pad'] = 5

x=linspace(0, 5, 10)
fig, ax = subplots(1, 1)

ax.plot(x, x**2, x, exp(x))


ax.set_yticks([0, 50, 100, 150])

ax.set_title("label and axis spacing")

# spațiere între eticheta axei și numerele de pe axa


ax.xaxis.labelpad = 5
ax.yaxis.labelpad = 5

ax.set_xlabel("x")
ax.set_ylabel("y")
Grid
x=linspace(0,5,10)
fig, axes = subplots(1, 2, figsize=(10,3))
# default grid
axes[0].plot(x, x**2, x, x**3, lw=2)
axes[0].grid(True) # afișează gridul
# custom grid
axes[1].plot(x, x**2, x, x**3, lw=2)
axes[1].grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
Axe duale – funcțiile twinx și twiny

Curbe cu unități de masură diferite pe același grafic


x=linspace(0,5,10)
fig, ax1 = subplots()
ax1.plot(x, x**2, lw=2, color="blue")
ax1.set_ylabel(r"area $(m^2)$",
fontsize=18, color="blue")
for label in ax1.get_yticklabels():
label.set_color("blue")

ax2 = ax1.twinx()
ax2.plot(x, x**3, lw=2, color="red")
ax2.set_ylabel(r"volume $(m^3)$", fontsize=18, color="red")
for label in ax2.get_yticklabels():
label.set_color("red")
Axa x și y sunt 0

fig, ax = subplots()

ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')

ax.xaxis.set_ticks_position('bottom')
# setez poziția lui x la x=0
ax.spines['bottom'].set_position(('data',0))

ax.yaxis.set_ticks_position('left')
# setez poziția lui y la y=0
ax.spines['left'].set_position(('data',0))

xx = linspace(-0.75, 1., 100)


ax.plot(xx, xx**3);
Adnotarea Textului – funcția text
from matplotlib import style
style.use(“fivethirtyeight”) # pentru un anumir style al liniilor
x=linspace(0,5,10)
fig, ax = subplots()
ax.plot(xx, xx**2, xx, xx**3)
ax.text(0.15, 0.2, r"$y=x^2$", fontsize=20, color="blue")
ax.text(0.65, 0.1, r"$y=x^3$", fontsize=20, color="green")
Figuri cu multiple subplots
Se pot adăuga axe manual folosind fig.add_axes sau folosind sub-figure
layout manager subplots, subplot2grid, sau gridspec:

subplot2grid
fig = figure()
ax1 = subplot2grid((3,3), (0,0), colspan=3)
ax2 = subplot2grid((3,3), (1,0), colspan=2)
ax3 = subplot2grid((3,3), (1,2), rowspan=2)
ax4 = subplot2grid((3,3), (2,0))
ax5 = subplot2grid((3,3), (2,1))
fig.tight_layout()
Figuri cu multiple subplots
gridspec
import matplotlib.gridspec as gridspec
fig = figure()
gs = gridspec.GridSpec(2, 3, height_ratios=[2,1], width_ratios=[1,2,1])
for g in gs:
ax = fig.add_subplot(g)
fig.tight_layout()
Obiectele Figure si Axes
Rețineți cu plt.<func>() avem acces doar la ultimul obiect Figure

Dacă salvăm referințele la obiectele noastre Figure, putem lucra cu


oricare dintre ele, indiferent de momentul în care au fost create.

Figure si Axes au metode cu nume similare sau identice ce cele pyplot.

In[]: fig = plt.figure(figsize=(8, 8))


In[]: gs = fig.add_gridspec(3, 3)
In[]: stanga_sus = fig.add_subplot(gs[0, 0])
In[]: mijloc_stanga = fig.add_subplot(gs[1, 0])
In[]: dreapta_sus = fig.add_subplot(gs[:2, 1:])
In[]: jos = fig.add_subplot(gs[2,:])
In[]: fig.savefig('orice.png')

Faceți-vă bunul obicei să închideți figurile să nu risipiți resurse.

In[]: plt.close('all')
rcParams
Este important să vă simțiți confortabili să lucrați cu obiectele Figure și
Axes, acestea permite un control mai fin al vizualizărilor.

Valorile implicite ale setărilor din Matplotlib sunt în rcParams ceea ce


înseamnă că putem să suprascriem cu ușurință ceea ce ne dorim pentru
sesiunea noastră și recuperăm valorile implicite când repornim
sesiunea noastră IPython.

Sunt peste 300 de setări:

In[]: import random


In[]: rcparams_list = list(rcParams.keys())
In[]: random.seed(20) # să fie repeatabile
In[]: random.shuffle(rcparams_list)
In[]: sorted(rcparams_list[:20])
['animation.bitrate', rcParams
'animation.writer',
'axes.formatter.limits',
'axes.prop_cycle',
'boxplot.meanprops.markersize',
'boxplot.showfliers',
'image.origin',
'keymap.quit',
'lines.dashed_pattern',
'mathtext.tt',
'patch.linewidth',
'pdf.use14corefonts',
'ps.useafm',
'text.kerning_factor',
'webagg.address',
'xtick.bottom',
'xtick.top',
'ytick.color',
'ytick.major.left',
'ytick.minor.width']
rcParams

Sunt foarte multe setări pe care le puteți face.

Verificăm valorile implicite pentru figsize

In[]: rcParams['figure.figsize']
[6.4 , 4.8 ]

Schimbăm valorile pentru sesiunea curentă de lucru:


In[]: rcParams['figure.figsize'] = (300, 10)

La sfârșit revenim la setările implicite:

In[]: rcdefaults()
Diverse 2D plots
Scatter - identificăm corelații și posibile
relații neliniare între variabile

x=rand(200)
y=rand(200)
size=rand(200)*30
color=rand(200)
scatter(x,y,size,color)
colorbar()

fill_between
fig, axes = subplots(1,1, figsize=(12,3))
axes.fill_between(x, x**2, x**3,
color="green", alpha=0.5)
axes.set_title("fill_between")
Polar plots

# polar plot folosește metoda add_axes cu argumentul polar


fig = figure()
ax = fig.add_axes([0.0, 0.0, .6, .6], polar=True)
t = linspace(0, 2 * pi, 100)
ax.plot(t, t, color='blue', lw=3)
Pandas plots
Structurile Series și DataFrame au funcția plot( argumente).

Argumentul ‘kind’ determină tipul plot-ului (line, bar , scatter, etc) și ce


alte argumente vor fi necesare desenării
In[]: %pylab
In[]: import pandas as pd
In[]: fb = pd.read_csv('fb_stock_prices_2018.csv',index_col='date',
parse_dates=True )
In[]:fb.plot( kind='line', y='open', figsize=(10, 5), style='-b',
legend=False, title='Bla bla bla')
Pandas plots
Conținutul fișierului fb_stock_prices_2018.csv :
Pandas plots
Argumentele funcției plot() :
Pandas plots
Putem selecta o listă de coloane pentru a le desena și stiliza individual.
In[]: %pylab
In[]: import pandas as pd
In[]: fb = pd.read_csv('data/fb_stock_prices_2018.csv',index_col='date',
parse_dates=True )
In[]:fb.first('1W').plot( y=['open', 'high', 'low', 'close'],
style=['o-b', '--r', ':k', '.-g'] ).autoscale() # adaugă spațiu între date și
axes
Pandas plots
Vrem să vizualizăm relația dintre variabile – folosim scatter plot - care ne
arată valoarea variabilei y la diferite valori ale variabilei x.

Analizând datele stocului Facebook, observăm că în zilele cu


volum mare tranzacționat par a fi corelate cu scăderi mari ale prețului
acțiunilor.

In[]: fb.assign( max_abs_change=fb.high – fb.low).plot( kind='scatter',


x='volume', y='max_abs_change')
Pandas plots
Apare să fie o relație, dar nu putem observa că e liniară.

Trebuie să facem logaritmul (log) volumului si avem opțiunile:


• Creăm o nouă coloană care este log volumului folosind np.log() .
• Folosiți o scară logaritmică pentru axa x pasând logx=True la plot()
sau apelarea metodei xscale('log') .
Cel mai simplu schimbăm modul în care ne afișăm datele
In[]: fb.assign( max_abs_change=fb.high – fb.low).plot( kind='scatter',
x='volume', y='max_abs_change', logx=True)
Pandas plots
Panda plot() are trei argumente pentru scala logaritmică: logx / logy
pentru reglarea unei singure axe și loglog pentru setarea ambelor axe.

O problemă cu plot-urile scatter este dificil să discerneți concentrația


de puncte dintr-o zonă dată sunt reprezentate unele peste celelalte.
Folosim argumentul alfa pentru a controla transparența punctelor - ia
valori de la 0 la 1, unde 0 este complet transparent și 1 este complet opac.

In[]: fb.assign( max_abs_change=fb.high – fb.low).plot( kind='scatter',


x='volume', y='max_abs_change', logx=True, alpha=0.25)
Pandas plots
Folosim box plots pentru a vizualiza outliers- potențialele valori aberante

In[]: fb.iloc[:,:4].plot( kind='box', title='Facebook Prices Box Plot' )


In[]: ylabel('price ($)')

Pierdem unele informații , nu mai avem ideea densității punctelor, cu box


plot, ne concentrăm pe 5 numere:
Pandas plots
Putem apela metoda boxplot() după apelarea groupby() .
Examinăm cum se schimbă boxplot atunci când le calculăm pe baza
volumului tranzacționat:

In[]:fb.assign(volume_bin=pd.cut(fb.volume, 3, labels=['low', 'med',


'high'])).groupby('volume_bin').boxplot(column=['open','high', 'low',
'close'], layout=(1, 3), figsize=(12, 3))
Modul pandas.plotting

Panda are un modul numit plotting cu grafice speciale pe care le putem


folosi pe datele noastre.

Funcția scatter_matrix() permite vizualizarea pentru fiecare combinație


de variabile din date - diagrame de dispersie pentru fiecare combinație
ale coloanelor din fișierul de date fb;

Funcția scatter_matrix() des folosită în machine learning pentru a vedea


care variabile ar putea fi utile în construirea unui model.

In[]:from pandas.plotting import scatter_matrix


In[]: scatter_matrix(fb, figsize=(10, 10))
Modul pandas.plotting
In[]:from pandas.plotting import scatter_matrix
In[]: scatter_matrix(fb, figsize=(10, 10), diagonal='kde')
kde – kernel distribution function
3D plots - suprafețe

Pentru a folosi 3D in matplotlib, trebuie creată o instanță a clasei Axes3D


Axele 3D se adaugă la fel ca în 2D sau mai convenabil, prin pasarea
cuvăntul cheie projection='3d' ca argument la metodele add_axes sau
add_subplot

%pylab
from mpl_toolkits.mplot3d import Axes3D

x,y=mgrid[-5: 5 : 35j , 0: 10: 35j]


z=x*sin(x)*cos(0.25*y)
fig=figure()
ax=Axes3D(fig)
ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.jet)
3D plots - suprafețe

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = Axes3D(fig)
X = np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=plt.cm.hot)
ax.contourf(X, Y, Z, zdir='z', offset=-2, cmap=plt.cm.hot)
ax.set_zlim(-2,2)
plt.show()
3D plots- suprafețe

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
# `ax` e o instanță 3D deoarece cuvântul cheie projection='3d' e
argument al metodei add_subplot

ax = fig.add_subplot(1,1, 1, projection='3d')
X = np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=plt.cm.summer)


plt.show()
3D plots – curbe parametrice

%pylab
from mpl_toolkits.mplot3d import Axes3D

t=linspace(0,30, 1000)
x,y,z=[ t*cos(t), t*sin(t), t]
fig = figure()
ax = fig.gca(projection='3d')
ax.plot(x, y, z)
Mayavi - mlab
Mayavi2 oferă vizualizarea interactivă a datelor 3D:

- interfață de utilizator bogată în dialoguri pentru a interacționa cu


toate datele și obiectele de vizualizare.

- o interfață simplă de scripting în Python, inclusiv gata de a utiliza


funcționalitatea 3D de vizualizare similară cu Matlab sau
matplotlib (folosind mlab), sau o interfață de programare orientată
pe obiecte.

- exploatează VTK fără a vă forța să-l învățăți

In[]: from mayavi import mlab


Mayavi - mlab
Ipyvolume in Jupyter
Ipyvolume bibliotecă Python pentru vizualizarea volumelor și gliphs
3d (scatter plot 3d) în notebook-ul Jupyter, cu configurare și efort
minim.

In prezent este în etapa pre-1.0.

O analogie bună ar fi: IPyvolume sunt pentru 3Darray , ceea ce e în


matplotlib ptr 2D array.

Instalare:
pip install ipyvolume # folosind comanda pip

conda install -c conda-forge ipyvolume # in Conda/Anaconda

Puteți citi mai multe aici :

https://ipyvolume.readthedocs.io/en/latest/?badge=latest
SEABORN

Seaborn este o bibliotecă de vizualizare Python bazată pe


matplotlib.

Acesta oferă o interfață la nivel înalt pentru desenarea graficelor


statistice într-un mod atractiv.

https://seaborn.pydata.org/

https://python-graph-gallery.com/
DASH
Dash este un frameworkPython pentru construirea aplicațiilor web.

Este scris în Flask, Plotly.js și React.js fără a fi nevoie de javascript

Are elemente moderne cum ar fi dropdowns, sliders, graphs etc

Dash este foarte potrivit pentru construirea aplicațiilor de vizualizare a


datelor ce pot fi redate în browserul web.

pip install dash==0.29.0 # The core dash backend


pip install dash-html-components==0.13.2 # HTML components
pip install dash-core-components==0.36.0 # Supercharged components
pip install dash-table==3.1.3 # Interactive DataTable component (new!)

Puteți citi mai multe aici :

https://dash.plot.ly/
Aplicația exportă în mod dinamic date din Google Finance într-un
DataFrame Pandas.
Codul sursa:
https://gist.github.com/chriddyp/3d2454905d8f01886d651f207e2419f0
BASHPLOTLIB
Bashplotlib este instrument in linie de comandă pentru realizarea
ploturilor de bază în terminal.

Scris exclusiv în Python, este util pentru a vizualiza datele atunci


când utilizatorii nu au acces la GUI.

Se instaleaza așa:
pip install bashplotlib

Sau direct de la sursa:


git clone git@github.com:glamp/bashplotlib.git
cd bashplotlib
python setup.py install

Puteți citi mai multe aici:


https://github.com/glamp/bashplotlib
BASHPLOTLIB
Bashplotlib este instrument in linie de comandă pentru realizarea
ploturilor de bază în terminal.

Scris exclusiv în Python, este util pentru a vizualiza datele atunci


când utilizatorii nu au acces la GUI.

Se instaleaza așa:
pip install bashplotlib

Sau direct de la sursa:


git clone git@github.com:glamp/bashplotlib.git
cd bashplotlib
python setup.py install

Puteți citi mai multe aici:


https://github.com/glamp/bashplotlib
BASHPLOTLIB

hist --file data/exp.txt


Bibliografie

http://matplotlib.org

http://docs.enthought.com/mayavi/mayavi/

https://seaborn.pydata.org

https://ipyvolume.readthedocs.io/en/latest/?badge=latest

https://dash.plot.ly/
Bibliografie

How to Create Animated Graphs in Python (with matplotlib):


https://towardsdatascience.com/how-to-create-animated-graphs-in-
Python-bb619cc2dec1

• Intro to Animations in Python (with plotly):


https://plot.ly/python/animations/

• 5 Python Libraries for Creating Interactive Plots:


https://mode.com/blog/ython-interactive-plot-libraries/
Curs 11

PANDAS
Pandas & Python

Procesarea datelor este o componentă foarte importantă în fluxul de lucru


științific sau nu.

Librăria Pandas este cea care trebuie utilizată când doriți să faceți
manipularea și analiza datelor heterogene în Python.

Pandas a fost inițial construită ca o extensie librăriei NumPy:

“ is an open source, BSD-licensed library providing high-performance,


easy-to-use data structures and data analysis tools for the Python
programming language”

Structurile de date pe care le oferă librăria Pandas sunt rapide, flexibile și


sunt concepute special pentru a face mult mai ușor analiza datelor din
lumea reală - Finanțe, Neuroștiințe, Economie, Statistică, Publicitate,
Analiză Web...
Data Science

1 Formulezi o întrebare – experiența, ideea unui expert

2 Faci de rost de date – baze de date, fișiere CSV/Excel

3. Explorezi datele – Pandas, NumPY

4. Modelezi datele – Pandas, sk-learn

5. Comunici rezultate – Matplotlib, d3.js – vizualizări intereactive


Ce oferă Pandas ?
Instrumente rapide pentru citirea și scrierea datelor între structurile de date din
memorie și diferite formate: CSV, fișiere text, Excel, baze de date SQL și formatul
HDF5;

Alinierea inteligentă a datelor și gestionarea integrată a datelor lipsă: obținerea alinierii


automate bazate pe etichete și manipularea ușoară a datelor dezordonate

Reformarea și pivotarea flexibilă a seturilor de date;

Divizarea inteligentă a etichetelor (slicing), indexarea ‘fantezistă’ și subdivizarea


seturilor mari de date;

Îmbinare (merging) și asociere de înaltă performanță (joining) a seturilor de date;

Hierarchical axis indexing- modalitate intuitivă de a lucra cu date înalte


dimensionale într-o structură de date cu dimensiuni reduse;

Time series-functionality: generarea datelor bazate pe timp, conversia frecvenței,


statistici pe intervale de timp, regresii liniare pe intervale de timp, schimbarea datei și
întârzierea. Se pot crea offset-uri de timp specifice domeniului și intrați în serii de timp
fără a pierde date;
Pandas & IPython
Pachetele trebuiesc instalate și apoi importate în scripturi/fișiere sau
folosite interactiv în IPython.

Anaconda are instalat pachetul Pandas.

In Ipython încarc pachetul Pandas si explorez help-ul :

In[]: import pandas as pd

In[]: pd.__version__

In[]: pd?

In[]: pd.<TAB>
Pandas

Obiectele Pandas sunt versiuni îmbunătățite ale obiectului NumPy


ndarray în care rândurile și coloanele sunt identificate cu etichete decât
simpli indici întregi.

Structurile fundamentale din Pandas sunt: Series, DataFrame si Index


Pandas – Data Structure: Series
Series:
- 1D array object capabil să conțină orice tip de date NumPy
- are asociat un index= array de date de tip etichete

In[]: import pandas as pd

In[]: obj=pd.Series([-3, 4, -6, -8])


In []: obj
0 -3 # coloana din stanga reprezinta indexul
1 4 # coloana din dreapta reprezinta valorile
2 -6
3 -8
dtype: int64

Deoarece nu am specificat un anumit index pentru date, implicit este


creat indexul format din numerele întregi de la 0 la N - 1 (unde N este
lungimea datelor)
Series

In[]:help(pd.Series) # accesez help-ul


In[]: pd.Series? # accesez help-ul scurtat

Puteți obține secvența valorilor array-ului si a index-ului obiectului


Series prin accesarea atributelor values, respectiv index:

In []: obj.values
Out[]: array([-3, 4, -6, -8]) # NumPy array

In []: obj.index
Out[]: RangeIndex(start=0, stop=4, step=1) # array-like
Series
In[]:from pandas import Series
In []: obj2 = Series( [4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
In []: obj2
Out[]:
d 4
b 7
a -5
c 3
dtype:int64

In []: obj2.index
Out[]: Index(['d', 'b', 'a', 'c'], dtype='object')

In[]: obj2.index[1:]
Out[]: Index([b, a, c], dtype=object)

In[]: list(data.items())
Out[]:[('d', 4), ('b', 7), ('a', -5), ('c', 3)]
Series

Indexul unei structuri Series deja create poate fi alterat prin atribuire
(assignment):

In []: obj2.index = ['Bob', 'Steve', 'Jeff', 'Ryan']


In []: obj2
Out[]:
Bob 4
Steve 7
Jeff -5
Ryan 3

Ce va afișa urmatoarea comandă?

In[]: data = Series( 5, index=[100, 200,300,400])


Series
Comparând cu un NumPy array, putem folosi valorile din index:

In []: obj2 = Series( [4, 7, -5, 3], index=['d', 'b', 'a', 'c'])

In []: obj2['a'] #selectăm o valoare


Out[]: -5

In []: obj2['d'] = 6 # setăm o nouă valoare

In[]: obj2[‘e’] =8 #extindem cu o noua valoare

In []: obj2[['c', 'a', 'd']] # selectăm anumite valori


Out[]:
c3
a -5
d6
Series
Comparând cu un NumPy array, putem folosi valorile din index:

In []: data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])

Out[]:
a 0.25
b 0.50
c 0.75
d 1.00
dtype: float64

In []: data['a':'c'] #slicing pe index

sau asa

In[]: data[0:2] #slicing pe index


Series
Operațiile din NumPy array: filtrarea cu un array boolean, multiplicarea,
sau aplicarea funcțiilor matematice, vor păstra ‘index-value link’:

In[]: import numpy as np

In []: obj2 In []: obj2[obj2 > 0]


Out[]: Out[]:
d6 d6
b7 b7
a -5 c3
c3

In []: obj2 * 2 In []: np.exp(obj2)


Out[]: Out[]:
d 12 d 403.428793
b 14 b 1096.633158
a -10 a 0.006738
c6 c 20.085537
Series
O altă modalitate de a interpreta o structură Series este aceea a unui
dicționar ordonat de lungime fixă, care mapează perechile
ckeie: valoare la index:valoare

Dacă avem date conținute intr-un dicționar Python, putem crea o


structură Series pasând dicționarul:

In []: sdata ={'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah':


5000}
In []: obj3 = Series(sdata)
In []: obj3
Out[]:
Ohio 35000
Oregon 16000
Texas 71000
Utah 5000

Ce observați?
Series

Când pasați un dicrionar, indexul din structura Series rezultată va avea


cheile din dicționar în ordine sortată:

In []: state = ['California', 'Ohio', 'Oregon', 'Texas']


In []: obj4 = Series(sdata, index=state)
In []: obj4
Out[]:
California NaN
Ohio 35000
Oregon 16000
Texas 71000

Valorile găsite în sdata au fost plasate în locațiile corespunzătoare, dar


din moment ce nici o valoare pentru "California" nu a fost găsită, pandas
adaugă NaN pentru a marca valorile lipsă.
Series
In []: state = ['California', 'Ohio', 'Oregon', 'Texas']
In []: obj4 = Series(sdata, index=state)
In []: obj4
Out[]:
California NaN
Ohio 35000
Oregon 16000
Texas 71000

Accesăm metodele isnull() sau notnull() pentru a detecta datele lipsă:

In []: obj4.isnull()
Out[]:
California True
Ohio False
Oregon False
Texas False
Series
O caracteristică critică a structurilor Series pentru multe aplicații este
aceea că se aliniază automat, datele indexate în operațiile aritmetice:

In []: obj3 In []: obj4


Out[]: Out[]:
Ohio 35000 California NaN
Oregon 16000 Ohio 35000
Texas 71000 Oregon 16000
Utah 5000 Texas 71000

In []: obj3 + obj4


Out[]:
California NaN
Ohio 70000
Oregon 32000
Texas 142000
Utah 5000
Pandas – Data Structure: DataFrame
DataFrame: structură tabelară/ spreadsheet/ grid rectangular/colecție
ordonată de coloane

- 2D array object capabil să conțină orice tip de date NumPy


- are asociate un index= array de date de tip etichete și coloane

Fiecare rând al grilei corespunde măsurătorilor sau valorilor unei


instanțe, în timp ce fiecare coloană este un vector care con ține date
pentru o anumită variabilă.

DataFrame are atât un index de rând, cât și un index de coloană.

Pentru a crea o structură DataFrame folosim funcția DataFrame() căruia


îi pasăm argumentele de date, index si columns
Cum creăm DataFrame ‘Gol’
In[] :df = DataFrame(np.nan,
index=list('abcde'),
columns=['x','y','z', 10, 20])
In[]: df
x y z 10 20
a NaN NaN NaN NaN NaN #np.nan este de tip ‘float’
b NaN NaN NaN NaN NaN
c NaN NaN NaN NaN NaN
d NaN NaN NaN NaN NaN
e NaN NaN NaN NaN NaN

Ce vor afișa urmatoarele comenzi?

In[]:df = DataFrame(index=range(0,4),columns=['A'], dtype='int')


In[]:df
In[]:df.index
In[]:df.columns
Cum creăm DataFrame
Diferite moduri de a construi un DataFrame:
- generalizare NumPy ndarray ca input în constructorul DataFrame:

In[]: my2d = np.array([[1, 2, 3], [4, 5, 6]])


In[]: print(pd.DataFrame(my2d))
Out[]:
0 1 2
0 1 2 3
1 4 5 6

- folosind dicționar de liste cu lungime egală ca input în DataFrame :

In[]: my_dict = {1: ['1', '3'], 2: ['1', '2'], 3: ['2', '4']}


In[]: print(DataFrame(my_dict))
Out[]:
1 2 3
0 1 1 2
1 3 2 4
Cum creăm DataFrame
- folosind DataFrame ca input în constructorul DataFrame:
In[]: my_df = DataFrame(data=[4,5,6,7], index=range(0,4), columns=['A'])
In[]: print(DataFrame(my_df))
Out[]:
A
0 4
1 5
2 6
3 7
- folosind Series ca input în constructorul DataFrame: :
In[]:my_series = Series({"United Kingdom":"London", "India":"New
Delhi", "United States":"Washington", "Belgium":"Brussels"})
In[]: print(DataFrame(my_series))
Out[]:
Belgium Brussels
India New Delhi
United Kingdom London
United States Washington
Cum creăm DataFrame
- folosind dicționare imbricate ca input în constructorul DataFrame:
In[]: my_dictdict = {'Nevada': {2001: 2.4, 2002: 2.9},
...'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
In[]: d2 =DataFrame(my_dictdict)
Out[]: d2
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6

- folosind dicționar de Series ca input in constructorul DataFrame:


In []: pdata = {'Ohio': d2['Ohio'][:-1], 'Nevada': d2['Nevada'][:2]}
In []: DataFrame(pdata)
Out[]:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
Tipuri de date posibile în constructorul DataFrame

2D ndarray - o matrice de date


dict of arrays, liste sau tuple - fiecare secvență devine o coloană în
DataFrame( toate secvențele trebuie să aibă aceeași lungime)
NumPy record array - tratată ca "dict of arrays"
dict of Series - fiecare valoare devine o coloană. Indicii din fiecare serie
sunt uniți pentru a forma indexul rezultat
dict of dicts - fiecare dict interior devine o coloană. Cheile sunt unite
pentru a forma indexul rezultat ca în "dict of Series ".
list of dict sau Series - fiecare articol devine un rând în DataFrame.
Uniunea cheilor dict sau indexurile Seriei devin etichetele coloanelor din
DataFrame
list of lists or tuples - tratate ca "2D ndarray"
Un alt DataFrame - indecsii DataFrame sunt utilizați
NumPy MaskedArray - Ca și cazul "2D ndarray", cu excepția valorilor
mascate ce devin NaN în DataFrame-ul rezultat
DataFrame
Cel mai comun este cu dicționar de liste sau din NumPy arrays:

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],


'year': [2000, 2001, 2002, 2001, 2002],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}
frame = DataFrame(data)

In []: frame
Out[]:
pop state year
0 1.5 Ohio 2000
1 1.7 Ohio 2001
2 3.6 Ohio 2002
3 2.4 Nevada 2001
4 2.9 Nevada 2002

Indexul e asignat automat, iar coloanele sunt sortate


Cum creăm DataFrame

Se poate specifica o anumită secvență de coloane:

In []: DataFrame(data, columns=['year', 'state', 'pop'])


Out[]:
year state pop
0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9
Cum creăm DataFrame
Asemănător cu structurile Series, dacă se pasează o coloană care nu se
regăsește in data, va apărea cu valori NaN:

In []: frame2 = DataFrame(data, columns=['year', 'state', 'pop', 'debt'],


index=['one', 'two', 'three', 'four', 'five'])

In []: frame2
Out[]:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 NaN
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 NaN
five 2002 Nevada 2.9 NaN

In [23]: frame2.columns
Out[23]: Index(['year', 'state', 'pop', 'debt'], dtype='object')
Cum creăm DataFrame

Coloanele pot fi modificate prin alocare. De exemplu, coloana "debt"


goale ar putea să i se atribuie o valoare scalară sau o serie de valori:

In []: frame2['debt'] = 16.5 In[]:frame2['debt'] = np.arange(5)

In []: frame2 In []: frame2


Out[]: Out[]:
year state pop debt year state pop debt
one 2000 Ohio 1.5 16.5 one 2000 Ohio 1.5 0
two 2001 Ohio 1.7 16.5 two 2001 Ohio 1.7 1
three 2002 Ohio 3.6 16.5 three 2002 Ohio 3.6 2
four 2001 Nevada 2.4 16.5 four 2001 Nevada 2.4 3
five 2002 Nevada 2.9 16.5 five 2002 Nevada 2.9 4
Selecția unei coloane DataFrame
Selectarea unei coloane dintr-o structură DataFrame poate fi prin notație
ca de dicționar sau prin atribut, iar ceea ce rezultă este o structură Series:

In []: frame2['state'] In []: frame2.year


Out[]: Out[]:
one Ohio one 2000
two Ohio two 2001
three Ohio three 2002
four Nevada four 2001
five Nevada five 2002
Name: state, dtype: object Name: year, dtype: int64
Cum selectăm index și columns în DataFrame
In[]: df = DataFrame(data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), index=
[2, 'A', 4], columns=[48, 49, 50])
In []: df
48 49 50
2 1 2 3
A 4 5 6
4 7 8 9

df.loc[] df.iloc[]

-loc selectează pe baza etichetei indexului - explicit


df.loc [2], caută valorile care au un index etichetat 2 – selecteză linia
-iloc selectează pe baza poziției din index - implicit
df.iloc [2], caută valorile care se află la indexul "2"

loc() si iloc() se aplica si la Series


Cum selectăm index și columns în DataFrame
df=
48 49 50
2 1 2 3
A 4 5 6
4 7 8 9

Ce se va selecta in următoarele comenzi ?

df.loc[‘A’]
df.iloc[1]
df.iloc[:, 2]
df.loc[:, 49]
df.iloc[-2:]
df.loc[‘A’:, 49] # conteaza ordinea pentru :
df.iloc[:1, :2]
Cum selectăm anumite valori din DataFrame
In[]: df = DataFrame(data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), index=
[2, 'A', 4], columns=[48, 49, 50])
df=
48 49 50
2 1 2 3
A 4 5 6
4 7 8 9
df.at[] df.iat[]
-at va funcționa la fel ca loc, dar mai rapid (indicele nu este intreg)
df.at[‘A’, 49]
-iat va funcționa la fel ca iloc (indicele doar valori intregi)
df.iat[2,1]

In[]: df['sum']=df[48]+df[49] # adaug o noua coloana sum


Reindex in Series
Reindex - crearea unui nou obiect cu datele conforme cu un nou index
(rearanjează datele, introduce NaN daca valoarea unui index nu exista).

obj = Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])


In []: obj
d 4.5
b 7.2
a -5.3
c 3.6
objre = obj.reindex(['a', 'b', 'c', 'd', 'e'])
In []: objre
a -5.3
b 7.2
c 3.6
d 4.5
e NaN

obj.reindex(['a', 'b', 'c', 'd', 'e'], fill_value=0) #valoarea indexului e va fi 0


Reindex in Series
In[]: A = pd.Series([2, 4, 6], index=[0, 1, 2])
In[]: A
0 2
1 4
2 6
dtype: int64

In[]: B = pd.Series([1, 3, 5], index=[1, 2, 3])


In[]: B Out[]: 1 1
2 3
3 5
In[]: A + B dtype: int64
Out[]: 0 NaN
1 5.0
2 9.0
3 NaN
dtype: float64
Reindex in DataFrame
Reindex în DataFrame poate altera index (linie), columns, sau ambele.
In []: frame = DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'c', 'd'],
columns=['Ohio', 'Texas', 'California'])

In []: frame
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8

In[]: frame2 = frame.reindex(['a', 'b', 'c', 'd'])


Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0
Reindex in DataFrame
Reindex pe linie si coloana:

In[]: states = ['Texas', 'Utah', 'California']

In[]: frame.reindex(index=['a', 'b', 'c', 'd'],columns=states)


Texas Utah California
a 1.0 NaN 2.0
b NaN NaN NaN
c 4.0 NaN 5.0
d 7.0 NaN 8.0

In[]: frame.reindex(index=['a', 'b', 'c', 'd'],columns=states, fill_value=0)


Out[]:
Texas Utah California
a 1 0 2
b 0 0 0 # NaN a devenit valoarea 0
c 4 0 5
d 7 0 8
Reindex in DataFrame
In[]: rng = np.random.RandomState(42)
In[]: A = pd.DataFrame(rng.randint(0, 20, (2, 2)), columns=list('AB'))
In[]:A
A B
0 6 19
1 14 10
In[]: B = pd.DataFrame(rng.randint(0, 10, (3, 3)), columns=list('BAC'))
In[]: B
B A C
0 7 4 6
1 9 2 6
2 7 4 3
In[]: A+B # indicii sunt aranjati si sortati
Out[]:
A B C
0 10.0 26.0 NaN # inlocuim valorile NaN cu 0
1 16.0 19.0 NaN # A.add( B, fill_value=0 )
2 NaN NaN NaN
Operații in Series & DataFrame
Similare cu operațiile intre 2D si 1D NumPy arrays
Se aplica regulile de Broadcasting (row-wise by default)

In[]:A = rng.randint(10, size=(3, 4)) Out[]: array([[7, 7, 2, 5],


In[]: A-A[0] [4, 1, 7, 5],
Out[]: array([[ 0, 0, 0, 0], [1, 4, 0, 9]])
[-3, -6, 5, 0],
[-6, -3, -2, 4]])

In[]: df = pd.DataFrame(A, columns=list('QRST'))


In[]: df – df.iloc[0]
Out[]: # pe linii by default
Q R S T
0 0 0 0 0
1 -3 -6 5 0
2 -6 -3 -2 4

In[]: df.subtract(df['R'], axis=0) # pe coloane


Operații in Series & DataFrame
Operațiile pe datele din Pandas vor menține întotdeauna contextul datelor
- păstrarea și alinierea indicilor și coloanelor
Pandas elimina erorile care ar putea apărea atunci când lucrați cu date
heterogene și / sau greșit aliniate în NumPy arrays.

In[]: df = pd.DataFrame(A, columns=list('QRST'))


Out[]: Q R S T
0 7 7 2 5
1 4 1 7 5
2 1 4 0 9
In[]: hf = df.iloc[0, ::2] Out[]: Q 7
S 2
In []: df - hf
Out[]:
Q R S T
0 0.0 NaN 0.0 NaN
1 -3.0 NaN 5.0 NaN
2 -6.0 NaN -2.0 NaN
Drop() - Șterge linii din Series

Apelăm metoda drop()

In []: obj = Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])


In []: new_obj = obj.drop('c')
Out[]:
a0
b1
d3
e4
In []: obj.drop(['d', 'c'])
Out[]:
a0
b1
e4
Drop() - Șterge linii din DataFrame

Apelăm metoda drop()

In[]: data = DataFrame(np.arange(16).reshape((4, 4)),


index=['Ohio', 'Colorado', 'Utah', 'NY'],
columns=['one', 'two', 'three', 'four'])

In []: data.drop(['Colorado', 'Ohio'])

one two three four


Utah 8 9 10 11
NY 12 13 14 15
Drop - Șterge linii/coloane din DataFrame
Apelăm metoda drop(axes, inplace)
- axes este 0 dacă indică linii și 1 când este folosit pentru coloane.
- inplace=True pentru a șterge linia/coloana fără să reasociați DataFrame
In[]:df
48 49 50
2 1 2 3
A 4 5 6
4 7 8 9
In[]: df.drop('A', axis=0, inplace=True) # df.drop(df.index[1])
Out[]:
48 49 50
2 1 2 3
4 7 8 9
In[]:df.drop(df.columns[[1]], axis=1)
Out[]:
48 50
2 1 3
A 4 6
4 7 9
Date lipsă
In[]: df1 = DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
index=['Ohio', 'Texas', 'Colorado'])
b c d
Ohio 0.0 1.0 2.0
Texas 3.0 4.0 5.0
Colorado 6.0 7.0 8.0
In[]:df2 = DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
In[]: df1+df2
b c d e
Colorado NaN NaN NaN NaN
Ohio 3.0 NaN 6.0 NaN
Oregon NaN NaN NaN NaN
Texas 9.0 NaN 12.0 NaN
Utah NaN NaN NaN NaN
Date lipsă

Ne referim la datele lipsă ca valorile null, NaN, sau NA.

In general, în alte limbaje se aplică una din aceste doua strategii:

- folosim o mască care va indica valorile lipsă : masca ar putea fi o matrice


booleană complet separată sau ea poate implica însușirea unui bit în
reprezentarea datelor pentru a indica local starea nulă a unei valori.

- alegem o valoare “santinelă” care va indica valorile lipsă: valoarea


“santinelă” ar putea fi o convenție specifică datelor, indicarea unei valori
întregi lipsă cu –9999 sau un tipar de biți rar, sau o convenție mai global
indicarea unei valori cu virgulă mobilă lipsă cu NaN.
Date lipsă

Pandas folosește santinelele -valorile nule deja existente in Python:


valoarea NaN și obiectul Python None convertindu-le între ele.

In Pandas datele string sunt stocate ca object dtype


Metode pentru NaN

Metode pentru ștergerea, îndepărtarea sau înlocuirea valorilor NaN sau null in
structurile de date Pandas.

isnull() - generează o mască cu valori booleene care indică valorile lipsă.

Notnull()- inversa / negarea lui isnull().

dropna() - întoarce o versiune filtrată a datelor lipsă (se poate seta câte)

fillna() - întoarce o copie a datelor cu completarea datele lipsă cu o anumită


valoare sau folosind o metodă de interpolare, cum ar fi "ffill" sau "bfill".
Metode pentru NaN - dropna()

In[]: data = Series([1, NAN, 3.5, NAN, 7])


In[]: data.dropna() sau așa data[data.notnull()]

In[]:df = DataFrame([[1., 6.5, 3.], [1., NAN, NAN], [NAN, NAN, NAN],
[NAN, 6.5, 3.]])

Nu putem sterge doar o valoare NaN ci un întreg rând sau coloană

în DrataFrame:
In[]: df.dropna() # șterge orice linie ce conține cel puțin o valoare NAN

#șterge doar coloanele ce conțin cel puțin o valoare NAN

In[]: data.dropna(axis=1) # sau axis=”columns”


Metode pentru NaN - dropna()

In[]:df = DataFrame([[1., 6.5, 3. , NAN],


[1., NAN,. 3, NAN],
[NAN, NAN, NAN, NAN],
[NAN, 6.5, 3., 4]])
Se pierd date.
Dorim să eliminăm liniile si coloanele cand toate valorile sunt NaN.

In[]: df.dropna(how=’all’) # șterge doar liniile ale caror valori sunt toate NAN
In[]: data.dropna(axis=1, how='all') # sau axis=”columns”

Parametrul thresh permite să se specifice un nr minim de valori nenule care să


se păstreze:
In[]: df.dropna(axis='rows', thresh=3)
Metode pentru NaN - fillna()
fillna() - întoarce o copie a datelor cu valorile NAN înlocuite
method -by default 'ffill' – forward -fill
axis - axis pe care se inlocuieste, default axis=0
inplace- modifică obiectul fara sa produca o copie
limit - numarul maximn de perioade consecutive de inlocuit

0 1 2
0 -0.252457 NaN NaN
1 -0.337905 NaN NaN
2 1.690233 NaN 0.515731
3 0.371090 NaN 1.397751
4 -2.188474 -0.391753 -0.042306
5 0.144136 -0.866658 -0.507123
6 -0.138193 0.540594 0.856762

In[]: df.fillna(0) # înlocuim valorile NaN cu 0


In[]: df.fillna({1: 0.5, 2: ‘Bravo’)
In[]: df.fillna(method='ffill') # înlocuim valorile NaN cu valoarea precedentă
In[]: df.fillna(method='ffill', limit=2)
Replace

In[]: df2 = DataFrame(np.arange(20).reshape((4, 5)), columns=list('abcde'))


a b c d e
0 0 1 2 3 4
1 5 6 7 8 9
2 10 11 12 13 14
3 15 16 17 18 19

In[]: df2.replace([1,11,19], [22,44,99])


a b c d e
0 0 22 2 3 4
1 5 6 7 8 9
2 10 44 12 13 14
3 15 16 17 18 99
Aplicarea funcțiilor și mapping

In []: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'),


index=['Utah', 'Ohio', 'Texas', 'Oregon'])
b d e
Utah -0.211437 -0.129002 1.529442
Ohio -0.070834 -2.389602 -1.539794
Texas -1.374791 0.242112 -0.826041
Oregon -0.630577 0.772946 0.364634

In[]: a=np.abs(frame) # toate funcțiile element-wise din NumPy

In []: f = lambda x: x.max() - x.min()

In []: frame.apply(f) # se aplica pe 1D arrays pe linii sau pe coloane


b 1.303957 # frame.apply(f, axis=1)
d 3.162548 # frame.sum() sau frame.sum(axis=1) – fără apply
e 3.069237
dtype: float64
Aplicarea funcțiilor și mapping

In []: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'),


index=['Utah', 'Ohio', 'Texas', 'Oregon'])
b d e
Utah -0.211437 -0.129002 1.529442
Ohio -0.070834 -2.389602 -1.539794
Texas -1.374791 0.242112 -0.826041
Oregon -0.630577 0.772946 0.364634

In[]:format = lambda x: '%.2f' % x # funcțiile element-wise din Python

In []: frame.applymap(format) # folosim funcția applymap deoarece


b d e # Series au o functie map()
Utah -0.21 -0.13 1.53
Ohio -0.07 -2.39 -1.54
Texas -1.37 0.24 -0.83
Oregon -0.63 0.77 0.36
Concatenare

Concatenarea obiectelor Series și DataFrame este similara cu concatenarea


NumPy arrays - np.concatenate()

import numpy as np

In[]: x = [1, 2, 3]
In[]: y = [4, 5, 6]
In[]: z = [7, 8, 9]
In[]: np.concatenate([x, y, z])
Out[]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])

In[]: x = [[1, 2],


[3, 4]]
In[]: np.concatenate([x, x], axis=1)
Out[]: array([[1, 2, 1, 2],
[3, 4, 3, 4]])
Pandas - concat()
Concatenarea în Pandas cu diferite opțiuni
pd.concat(objs, axis=0, join='outer', ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False,
sort = False)

In[]: df1
A B
1 A1 B1
2 A2 B2

In[]: df2
A B A B
3 A3 B3 1 A1 B1
4 A4 B4 2 A2 B2
3 A3 B3
In[]: print(pd.concat([df1, df2])) 4 A4 B4

Concatenarea obj Series și DataFrame se face row-wise (axis=0 ) -default


axis =1 sau axis=’col ’ - coloana
Pandas – concat() - duplicarea indicilor
O diferență importantă între np.concatenate și pd.concat este că în Pandas
concatenarea păstrează indicii, chiar dacă rezultatul va avea indici duplica ți!

In[]: df1
A B
1 A1 B1
2 A2 B2

In[]: df2
A B A B
1 A3 B3 1 A1 B1
2 A4 B4 2 A2 B2
1 A3 B3
In[]: print(pd.concat([df1, df2])) 2 A4 B4

ignore_index = True, ignorăm indexul și concatenarea va crea un nou index


întreg pentru rezultat. ([0,1,2,3])

In[]: print(pd.concat([x, y], ignore_index=True))


Pandas – concat() - multi index key

O altă alternativă este să utilizați opțiunea keys pentru a specifica o etichetă


pentru date - rezultatul va fi o serie indexată ierarhic

In[]: df1
A B
0 A1 B1
1 A2 B2

In[]: df2
A B A B
0 A3 B3 x 0 A1 B1
1 A4 B4 1 A2 B2
y 0 A3 B3
In[]: print(pd.concat([df1, df2], keys=['x', 'y'])) 1 A4 B4

Rezultatul este un obiect DataFrame indexat multiplu.


Pandas – concat() - join
Concatenarea obiectelor DataFrame cu nume de coloane partajate.
In[]: df5 In[] : df6
A B C B C D
1 A1 B1 C1 3 B3 C3 D3
2 A2 B2 C2 4 B4 C4 D4

In[]: print(pd.concat([df5, df6]) A B C D


1 A1 B1 C1 NaN
2 A2 B2 C2 NaN
3 NaN B3 C3 D3
4 NaN B4 C4 D4
Implicit, intrările pentru care nu sunt disponibile date sunt completate cu NaN.
join= ‘outer’ # reuniune de coloane sau
In[]: print(pd.concat([df5, df6], join='inner')) # intersecția coloanelor
B C
1 B1 C1
2 B2 C2
3 B3 C3
4 B4 C4
Pandas – index ierarhic

De multe ori este util să stochezi date cu dimensiuni superioare - adică date
indexate cu mai mult de una sau două dimensiuni.

Pandas oferă obiecte Panel și Panel4D care gestionează în mod nativ date
tridimensionale și patru dimensiuni.

Un mod mult mai comun în practică este acela de a folosi indexarea ierarhizată
(sau multi-indexare) pentru a încorpora mai multe niveluri de index în cadrul
unui index unic.

În acest fel, datele de dimensiuni superioare pot fi reprezentate compact


în cadrul obiectelor Series unidimensionale și DataFrame bidimensionale.
Pandas – multi-index

De multe ori este util să stochezi date cu dimensiuni superioare - adică date
indexate cu mai mult de una sau două dimensiuni.

Pandas oferă obiecte Panel și Panel4D care gestionează în mod nativ date
tridimensionale și patru dimensiuni.

Un mod mult mai comun în practică este acela de a folosi indexarea ierarhizată
(sau multi-indexare) pentru a încorpora mai multe niveluri de index în cadrul
unui index unic.

În acest fel, datele de dimensiuni superioare pot fi reprezentate compact


în cadrul obiectelor Series unidimensionale și DataFrame bidimensionale.
Pandas – multi-index
Exemplu NU ASA:
In[]: index = [('California', 2000), ('California', 2010),
('New York', 2000), ('New York', 2010),
('Texas', 2000), ('Texas', 2010)]
In[]: populatia = [33871648, 37253956, 18976457, 19378102, 20851820,
25145561]
p = pd.Series(populatia, index=index)
Out[]: p
(California, 2000) 33871648
(California, 2010) 37253956
(New York, 2000) 18976457
(New York, 2010) 19378102
(Texas, 2000) 20851820
(Texas, 2010) 25145561

Dacă vrem să selectăm toate valorile din 2010 – e complicat si foarte lent:
In[] : p[[i for i in p.index if i[1] == 2010]]

Trebuie să creăm un multi-index !


Pandas – multi-index
Indexarea bazată pe tuple este un multi-index, iar tipul MultiIndex ne oferă
tipul de operă țiuni pe care dorim să le avem.
Creăm un index multiplu din tuple așa:

In[]: index = [('California', 2000), ('California', 2010), ('New York', 2000),


('New York', 2010), ('Texas', 2000), ('Texas', 2010)]

In[]: populatia = [33871648, 37253956, 18976457, 19378102, 20851820,


25145561]
In[]: in = pd.MultiIndex.from_tuples(index)
In[]: p = pd.Series(populatia, index=in)
Out[]: # primele 2 coloane multi-index în coloama a treia datele
California 2000 33871648
2010 37253956
New York 2000 18976457
2010 19378102
Texas 2000 20851820
2010 25145561
In[]: p[:, 2010] # accesăm datele din al doilea index – slicing
Metode de creare a unui multi-index
Pasăm o listă de două sau mai multe - index arrays to the constructor:
In[]: df = pd.DataFrame(np.random.rand(4, 2),
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
columns=['data1', 'data2'])
Out[]: df
data1 data2
a 1 0.080027 0.699456
2 0.684682 0.621030
b 1 0.988411 0.274719
2 0.034877 0.678340

Pasăm un dicționar de tuple ca chei - Pandas va recunoașteți automat acest


lucru ca un MultiIndex:

In[]: data = {('California', 2000): 33871648, ('California', 2010): 37253956,


('Texas', 2000): 20851820, ('Texas', 2010): 25145561, ('New York', 2000):
18976457, ('New York', 2010): 19378102}
In[]: pd.Series(data)
Explicit multi-index constructor

Folosim metodele din clasa MultiIndex:

In[]: pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'], [1, 2, 1, 2]])


Out[]:
MultiIndex([('a', 1),
('a', 2),
('b', 1),
('b', 2)],) sau așa

In[]:pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b', 2)]) sau așa

In[]: pd.MultiIndex.from_product([['a', 'b'], [1, 2]]) # produs Cartesian


Multi-index constructor
Într-un DataFrame, rândurile și coloanele sunt complet simetrice. Coloanele
pot avea și mai multe niveluri de indici:

In[]: index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],


names=['year', 'visit'])
In[]: columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'], ['HR',
'Temp']], names=['subject', 'type'])
In[]: data = np.round(np.random.randn(4, 6), 1)
In[]:data[:, ::2] *= 10
In[]: data += 37
In[]: health_data = pd.DataFrame(data, index=index, columns=columns)
Out[]: health_data
subject Bob Guido Sue
type HR Temp HR Temp HR Temp
year visit
2013 1 40.0 37.7 45.0 38.1 35.0 35.6 #4D data
2 43.0 34.9 37.0 36.8 23.0 37.7 In[]: health_data['Guido']
2014 1 57.0 37.3 22.0 37.6 32.0 36.3
2 32.0 36.8 23.0 38.2 50.0 36.3
Multi-index constructor
Într-un DataFrame, coloanele sunt indexate by default și de multi-index:
In[]: health_data['Guido', 'HR']
Out[]:
year visit
2013 1 45.0
2 37.0
2014 1 22.0
2 23.0

Putem folosi funcțiile loc , iloc și ix - ca în cazul unui singur index

In[]: health_data.iloc[:2, :2]


Out[]:
subject Bob
type HR Temp
year visit
2013 1 40.0 37.7
2 43.0 34.9
Multi-index constructor
Într-un DataFrame, coloanele sunt indexate by default și de multi-index:
In[]: health_data['Guido', 'HR']
Out[]:
year visit
2013 1 45.0
2 37.0
2014 1 22.0
2 23.0

Putem folosi funcțiile loc , iloc și ix - ca în cazul unui singur index

In[]: health_data.iloc[:2, :2]


Out[]:
subject Bob
type HR Temp
year visit
2013 1 40.0 37.7
2 43.0 34.9
Citirea datelor din format text

Input/Output : citirea fișierelor text (sau .csv)și a altor formate de pe disc


(Excel Matlab), încărcarea datelor din baze de date (MySQL) și interac țiunea
cu surse de lucru - API-urile web.

Funcțiile din Pandas pentru citirea datelor tabulare în structura DataFrame:

read_csv - încărcă date delimitate de virgulă dintr-un fișier, sau de la un URL

read_table - încărcă date delimitate de ‘\t’ dintr-un fișier, sau de la un URL

read_fwf - încărcă date in fixed-width column format (fără delimitator)

read_clipboard - versiune a lui read_table, încarcă date din clipboard – folosit


pentru conversia tabelelor din pagini web
Citirea datelor din format text

In[]: import pandas as pd


In[]: df = pd.read_csv('tex1.csv') sau in[]: pd.read_table('tex1.csv', sep=',')

In []: df
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo

In[]: df = pd.read_csv('tex1.csv', , nrows=2) # incarc doar primele 2 linii


In[]: pd.read_csv('yourFile', parse_dates=True)

In[]: url = 'https://archive.ics.uci.edu/ml/machine-learning-


databases/wine/wine.data'
In[]: dddd = pd.read_csv(url)
Citirea datelor din format text
Vrem ca index să fie coloana ‘message’:

In[]: df=pd.read_csv('tex1.csv', index_col='message')


In []: df
a b c d
message
hello 1 2 3 4
world 5 6 7 8
Foo 9 10 11 12

Dacă avem un fisier tex2.csv care arată asa:

A B C\n',
'aaa -0.264438 -1.026059 -0.619500\n',
'bbb 0.927272 0.302904 -0.032399\n',
'ccc -0.264273 -0.386314 -0.217601\n',
'ddd -0.871858 -0.348382 1.100491\n'

In[]: result = pd.read_table('tex2.csv', sep='\s+') # pentru a elimina whitespaces


Scrierea datelor in format text
Datele mele arată așa :
rez=
something a b c d message
0 one 1 2 3 4 NaN
1 NaN 5 6 NaN 8 world
2 three 9 10 11 12 foo

In[] import pandas as pd


In[]: rez.to_csv('out.csv')
Out[]:
,something,a,b,c,d,message
0,one,1,2,3,4,
1,two,5,6,,8,world
2,three,9,10,11.0,12,foo

In[]: rez.to_csv('my.csv', sep='\t') # delimitate de tab


In[]: rez.to_csv('my.csv', sep='\t', encoding='utf-8')

http://pandas.pydata.org/pandas-docs/stable/generated/
pandas.DataFrame.to_csv.html
Scrierea datelor in format Excel

In[]: import pandas as pd


In[]: writer = pd.ExcelWriter('myDataFrame.xlsx')
df.to_excel(weiter, 'DataFrame')
writer.save()

http://pandas.pydata.org/pandas-docs/stable/generated/
pandas.DataFrame.to_excel.html

http://pandas.pydata.org/pandas-docs/stable/io.html
Bibliografie

https://pandas.pydata.org/

https://www.youtube.com/watch?v=9d5-Ti6onew

https://tutswiki.com/pandas-cookbook/chapter1/

S-ar putea să vă placă și