import numpy as np
Sessió 2
Paquets Científics
Els documents d’aquest curs contenen adaptacions de diferents materials que s’han anat utilitzant a altres cursos i conté aportacions de varis membres del Departament de Matemàtiques de la UAB.
El paquet Numpy
Python
és un llenguatge de programació interpretat. Això vol dir que hi ha un programa base, l’intèrpret de Python
, que analitza i executa les instruccion que nosaltres programem directament a partir del codi font. Això permet, per exemple, poder tenir un entorn com el Jupyter Notebook
, on anem veient en temps real el resultat de l’execució del codi Python
.
Un altre aspecte on Python
és flexible, també per facilitar la feina del programador, és en els tipus de les variables. Per exemple, una llista
o un diccionari
poden contenir elements de diferents tipus (int
, float
, string
, etc).
Això fa que, normalment, un programa escrit en Python
trigui més en executar-se que un programa equivalent escrit en un llenguatge compilat i fortament tipat (com C
o C++
).
Per millorar l’eficiència dels llenguatges escrits en Python
, molts dels paquets que contenen funcions que podrien trigar en executar-se estan escrits en C
. Llavors, aquests paquets escrits en C
contenen una interfície que es pot utilitzar desde Python
, de forma que podem tenir la comoditat d’escriure un programa en Python
aprofitant l’eficiència de C
.
Un dels paquets més importants de Python
que permet fer càlculs numèrics de forma eficient és Numpy
.
Per utilitzar el paquet Numpy
primer l’hem d’importar. És molt comú importar el paquet Numpy
assignant-li l’abreviatura np
.
El tipus de variable amb que el treballa Numpy
és l’array
. Els arrays
son generalitzacions de matrius, on podem tenir valors organitzats en diferents dimensions. Per exemple, un vector és un array
d’una dimensió i una matriu, amb valors organitzats en files i columnes, és un array
amb dues dimensions. Els arrays
a Numpy poden tenir un número de dimensions arbitrari, i cada dimensió pot tenir un número de components també arbitrari.
Per crear arrays
de Numpy
es pot fer directament a través de llistes de Python
.
= np.array([1, 2, 3])
a a
Podem obtenir llavors el número de components de cada dimensió de l’array (en aquest cas només una dimensió):
a.shape
Per crear matrius (o arrays
de vàries dimensions) es poden utilitzar llistes encaixades:
= np.array([[1,2,3], [4,5,6]])
a print(a)
a.shape
Numpy
proporciona diverses funcions per generar arrays
amb valors predeterminats:
= np.zeros(shape=(2, 3))
a a
= np.ones(shape=(3, 2))
a a
= np.eye(5)
a a
En cas necessari es poden canviar les dimensions d’un array
i els valors de cada component es reorganitzen automàticament.
= np.arange(15)
a a
3, 5) a.reshape(
5, 3) a.reshape(
Els arrays de Numpy
de més de dues dimensions es poden generar de forma anàloga, però la visualització per pantalla és menys interpretable.
= np.arange(30).reshape(5, 3, 2)
a a
Per accedir als valors continguts en un array es pot fer servir un slicing
equivalent al de les llistes
de Python
però en cada component.
= np.arange(15).reshape(5, 3)
a 1:3, 1:] a[
Una opció que pot ser molt útil és indexar un array
amb valors True
i False
.
= np.arange(15).reshape(5, 3)
a = (a % 4 == 0)
idx idx
a[idx]
Els arrays de Numpy
es poden operar component a component amb els operadors habituals de Python
.
= np.ones(shape=(5,3))
a = np.arange(15).reshape(5, 3)
b +b a
= np.ones(shape=(5,3))
a = np.arange(15).reshape(5, 3)
b -b a
En el cas en que les dimensions dels arrays no siguin iguals, Numpy
intenta fer broadcasting
, de forma que els valors dels arrays es repeteixen les vegades que siguin necessàries per igualar les dimensions.
= np.arange(3)
a = np.array(5)
b print("a:\n{}\n".format(a))
print("b:\n{}\n".format(b))
print("a + b:\n{}\n".format(a+b))
= np.ones(shape=(3,3))
a = np.arange(3)
b print("a:\n{}\n".format(a))
print("b:\n{}\n".format(b))
print("a + b:\n{}\n".format(a+b))
= np.ones(shape=(3,3))
a = np.arange(3).reshape(3,1)
b print("a:\n{}\n".format(a))
print("b:\n{}\n".format(b))
print("a + b:\n{}\n".format(a+b))
= np.arange(3).reshape(3,1)
a = np.arange(3)
b print("a:\n{}\n".format(a))
print("b:\n{}\n".format(b))
print("a + b:\n{}\n".format(a+b))
Numpy
ens permet també calcular diversos estadístics donat un array
.
= np.arange(15).reshape(3,5)
a a
print("Mitjana de tots els elements: {}".format(a.mean()))
print("Mitjana dels elements de cada columna: {}".format(a.mean(axis=0)))
print("Mitjana dels elements de cada fila: {}".format(a.mean(axis=1)))
print("Suma de tots els elements: {}".format(a.sum()))
print("Suma dels elements de cada columna: {}".format(a.sum(axis=0)))
print("Suma dels elements de cada fila: {}".format(a.sum(axis=1)))
Els arrays
de Numpy
es poden concatenar indicant la dimensió que es vol utilitzar. Per una matriu, seria triar si es volen concatenar horitzontalment o verticalment.
= np.array([[1, 2], [3, 4]])
a = np.array([[5, 6], [7, 8]])
b = np.concatenate((a, b), axis=0)
c
print("a:\n{}\n".format(a))
print("b:\n{}\n".format(b))
print("Contatenació eix 0:\n{}\n".format(c))
= np.array([[1, 2], [3, 4]])
a = np.array([[5, 6], [7, 8]])
b = np.concatenate((a, b), axis=1)
c
print("a:\n{}\n".format(a))
print("b:\n{}\n".format(b))
print("Contatenació eix 1:\n{}\n".format(c))
Donat un array
que correspongui a una matriu, podem utilitzar Numpy
per generar la matriu transposada.
= np.arange(15).reshape(3, 5)
a a
np.transpose(a)
És important observar que si utilitzem la notació de Python
per multiplicar dos arrays
de Numpy
, la multiplicació es farà component a component. En particular, aquesta operació no correspon al producte habitual de matrius. Per calcular el producte de matrius s’ha de demanar específicament a Numpy
.
= np.arange(15).reshape(3, 5)
a # Diferent d'utilitzar l'operació * (producte component a component) np.matmul(a, np.transpose(a))
Una altra operació típica amb matrius que podem fer amb Numpy
és calcular la inversa.
= np.array([[1, 1, 1], [1, 1, 2], [1, 2, 3]])
a a
np.linalg.inv(a)
Podem utilitzar el càlcul d’inversa d’una matriu per resoldre sistemes d’equacions lineals amb Numpy
. Per exemple, considerem el següent sistema:
\[x + y + z = 1\] \[x + y + 2z = 0\] \[x + 2y + 3z = -1\]
Llavors, podem calcular
= np.array([[1, 1, 1], [1, 1, 2], [1, 2, 3]])
a = np.array([1, 0, -1]).reshape(3, 1) b
np.matmul(np.linalg.inv(a), b)
El paquet Pandas
El paquet Numpy
ens permet treballar amb arrays
de dades, i en particular amb matrius, de forma molt eficient, però els arrays
no tenen gaire informació de context, en particular no tenim cap índex a les files ni cap nom que ens permeti distingir les columnes.
El paquet Pandas
precisament afegeix aquesta informació de context (índex i noms de columnes) als arrays
de Numpy
.
Per utilitzar el paquet Pandas
primer s’ha d’importar, i és molt habitual importar el paquet Pandas
assignant-li l’abreviatura pd
.
import pandas as pd
El tipus de variable principal de Pandas
és el DataFrame
, que es correspon amb el que seria una matriu
de dades, on cada fila i cada columna tenen un nom assignat.
Per crear un DataFrame
de Pandas
ho podem fer a partir de llistes de Python
, o a partir d’arrays
de Numpy
o a partir de diccionaris.
= pd.DataFrame(np.ones(shape=(3,3)))
df df
= {"id": [0, 1, 2], "nom": ["a", "b", "c"], "edat": [20, 21, 22]}
d = pd.DataFrame(d)
df df
També es pot crear un DataFrame
de Pandas
a partir d’un índex i una llista de columnes i omplir-lo amb un valor per defecte.
= pd.DataFrame(0, index=np.arange(5), columns=["id", "nom", "edat"])
df df
Pandas
ofereix moltes utilitats per treballar amb índex
definits a partir de dates.
= pd.date_range("2024-01-01", "2024-12-31", freq="D")
date_range = pd.DataFrame(0, index=date_range, columns=["id", "nom", "edat"])
df df
Amb els mètodes set_index
i reset_index
es pot fixar quina columna ha de ser l’índex del DataFrame
.
= pd.date_range("2024-01-01", "2024-12-31", freq="D")
date_range = pd.DataFrame(0, index=date_range, columns=["id", "nom", "edat"])
df df.reset_index()
= pd.date_range("2024-01-01", "2024-12-31", freq="D")
date_range = pd.DataFrame(0, index=date_range, columns=["id", "nom", "edat"])
df = df.reset_index()
df = df.rename(columns={"index": "data"})
df "id") df.set_index(
Per accedir als valors d’un DataFrame
, Pandas
implementa dos mètodes, loc
i iloc
, que permeten fer un slicing
similar al de Numpy
.
El mètode loc
selecciona els registres a partir del valor de l’índex, mentre que el mètode iloc
fa la selecció a partir de la posició dels registres.
= pd.date_range("2024-01-01", "2024-12-31", freq="D")
date_range = pd.DataFrame(0, index=date_range, columns=["id", "nom", "edat"])
df df
"2024-06-10":"2024-06-17"] # Observeu que Pandas inclou el registre corresponent a la última data especificada df.loc[
161:167] # Mentre que si accedim amb iloc, Pandas no inclou el registre corresponent a la última posició especificada df.iloc[
Per accedir a les columnes ho podem fer directament espeficiant el nom de la columna entre claudàtors.
"id"] df[
Si volem accedir a més d’una columna, hem de posar entre claudàtors una llista de noms, de forma que, si ho escribim directament, queden dos claudàtors junts.
= ["nom", "edat"]
llista_columnes df[llista_columnes]
"nom", "edat"]] df[[
Els mètodes loc
i ìloc
permeten també filtrar a la vegada una sèrie de files i columnes.
"2024-01-01", "nom"] df.loc[
També podem fer servir la notació per extreure files i columnes en l’assignació de nous valors, igual que fèiem amb les llistes de Python
i els arrays
de Numpy
.
"2024-01-01", "nom"] = "Toni"
df.loc[ df
Per afegir columnes al DataFrame
simplement hem d’assignar un valor a cada fila i anomenar la nova columna.
"num"] = range(len(date_range))
df[ df
El mètode loc
accepta que li passem una sèrie de valors True
i False
, on els valors True
indiquen les files que volem extreure.
= (df["num"] % 100 == 0)
idx idx
df.loc[idx]
Aquest tipus de selecció s’acostuma a escriure directament en una línia, sense definir explícitament la variable idx
.
"num"] % 100 == 0] df.loc[df[
De forma anàloga a Numpy
, Pandas
també proporciona mètodes per extreure estadístiques d’un DataFrame
.
= pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=["A", "B", "C", "D"])
df df
df.describe()
df.mean()
sum() df.
El paquet Matplotlib
El paquet Matplotlib
és un dels paquest amb els que es poden generar visualitzacions en Python
. Per utilitzar-ho, importarem el submòdul pyplot
, que s’acostuma a abreviar com plt
.
import matplotlib.pyplot as plt
El paquet Matplotlib
està molt integrat amb Numpy
, pel que norlament es fan servir arrays per indicar a Matplotlib
les dades que volem visualitzar.
Per visualitzar la gràfica d’una funció \(f\) només hem d’indicar els valors de \(x\) que volem fer servir acompanyats dels valors \(f(x)\). Matplotlib
s’encarregarà llavors d’unir els valors que li hem donat amb una linia.
= np.linspace(0, 8*np.pi, 100)
x = np.sin(x) y
plt.plot(x, y) plt.show()
Podem modificar fàcilment atributs de la visualització, com el color o el gruix de linia.
="orange", linewidth=5)
plt.plot(x, y, color plt.show()
El paquet Matplotlib
ens permet també mostrar diferents gràfiques en una mateixa visualització. La forma més simple de fer-ho és repetir la crida a la instrucció plot
.
= np.linspace(0, 8*np.pi, 100)
x = np.sin(x)
y1 = np.cos(x) y2
plt.plot(x, y1)
plt.plot(x, y2) plt.show()
Per tenir clar quina gràfica fa referència a cada funció podem afegir una etiqueta a cada plot
i llavors demanar a Matplotlib
que afegeixi una llegenda a la visualització.
="sinus")
plt.plot(x, y1, label="cosinus")
plt.plot(x, y2, label
plt.legend() plt.show()
Matplotlib
ens permet generar visualitzacions de diferents tipus. Per exemple, si volem mostrar directament els punts sense cap linia que els uneixi podem utilitzar el mètode scatter
.
="sinus")
plt.scatter(x, y1, label="cosinus")
plt.scatter(x, y2, label
plt.legend() plt.show()