Une rapide introduction
CentraleSupélec
Module SPM-INF-002:
Evaluation par les rendus des TPs, limite 1 semaine après le TP.
Scientific python lectures aborde :
L’écosystème python est très riche en librairies scientifiques :
Python est un langage interprété
“à la volée”: cpython peut créer un fichier .pyc qui contient le code compilé (bytecode).
Les interpréteurs
Mauvais code
range explicité en mémoire \(\neq\) paresseusefor en pythonl[i] pourrait être problématique mais les listes python sont des tableaux et pas des listes chaînéesMauvais code
Fondamentalement différent
built-in sum, déjà compiléeInstructions vectorielles
CPython ne supporte pas les instructions vectorisées,gcc ou clang, réorganisent le code pour exploiter les instructions SIMDUn environnement virtuel est :
Il permet la reproductibilité (Benureau and Rougier 2018), avec le contrôle des packages installés et de leurs versions.
Différentes options :
venv (inclus dans Python depuis la version 3.3) : python package manager, ne permet pas de changer de version de pythonanaconda, miniconda, mamba, micromamba, etc… : system package manager. Permet de changer de version de python, installer des dépendances autres que des packages python (e.g. C++, Java, Rust, …), mais parfois un peu lourduv https://docs.astral.sh/uv : python package manager, flexible sur la version de python, ne permet d’installer que des paquets pythonPour installer micromamba (en utilisateur normal):
Pour construire un environnement virtual local dédié :
Pour installer des dépendances :
On peut exporter, recharger, etc.. un environnement.
Pour installer uv (en utilisateur normal) :
Pour construire un environnement virtuel local dédié :
Pour installer des dépendances :
Warning
Vous devez utiliser un Integrated Development Environment (IDE)
Suggestion : utilisez Visual Studio Code + tout son environnement de plugins qui permet :
VS Code https://code.visualstudio.com/docs/languages/python
=> Démo avec Venv
On peut également travailler avec des notebooks jupyter.
Fonctionnalités :
Voir ces exemples à essayer dans le navigateur, e.g. le simulateur de l’attracteur de Lorentz
int, float, bool, setstr, tuplelistdicttype(x), tester le type isinstance(x, letype), connaître les méthodes applicables dir(x)x = 3, walrusSur les listes
Faire attention aux constructions du type :
Complexité en temps en \(O(|L|^2)\) pour une liste chaînée. Complexité en temps en \(O(|L|)\) pour un tableau.
Les générateurs
Une construction de liste par compréhension est exécutée à la déclaration.
Pour économiser de la mémoire (e.g. permettant de représenter d’énormes structures), on peut utiliser des générateurs
Sur les fonctions
Définition d’une fonction : def fname():
On peut utiliser un nombre variable d’arguments positionnels *args et/ou nommés **kwargs (kwargs = keyword arguments). Voir Python args and kwargs.
Les lambda fonctions allègent la déclaration des fonctions :
Voir https://www.pythoncheatsheet.org/cheatsheet/decorators
@CountCallNumber
def say_hi(name"):
print("Hi ! My Name is "+ name)
class CountCallNumber:
def __init__(self, func):
self.func = func
self.call_number = 0
def __call__(self, *args, **kwargs):
self.call_number += 1
print("This is execution number " + str(self.call_number))
return self.func(*args, **kwargs)Loguer avec print: Pour déboguer un programme, on utilise bien souvent des print(...)
Que l’on peut combiner avec des f-strings :
Que l’on peut combiner avec des options de formattage :
Loguer avec logging : On peut aussi utiliser des loggers du module logging :
import logging
logging.basicConfig(format="%(levelname)s - %(asctime)s: %(message)s",
datefmt= "%d/%m/%y %H:%M:%S", level=logging.INFO)
logging.info("Une info")
logging.warning("Un warning")
logging.error("Une erreur")
logging.basicConfig(filename="monlog.log",
format="%(levelname)s - %(asctime)s: %(message)s",
datefmt= "%d/%m/%y %H:%M:%S", level=logging.INFO)De manière générale sur un texte en ASCII :
Lecture, avec un with statement:
Ecriture :
ATTENTION C’est une très mauvaise idée de stocker en ASCII des données numériques.
La valeur “1.23439949959” en ASCII occupe \(13 \times 8 = 104 bits\). Alors qu’un flottant peut occuper \(64\), \(32\), \(16\) bits.
Si ce sont des tableaux de valeurs, utilisez numpy.savez.
Vous pouvez aussi construire votre propre format de fichier et utiliser fh.seek(), fh.read(num_bytes), struct.pack(), struct.unpack(). Voir [struct](https://docs.python.org/3/library/struct.html]
Sur des formats spécifiques yaml, xml, json, utilisez des parsers écrit pour le yaml, xml, json
Problème Simuler un système d’Ising (Sethna (2006), Hayes (2000)). Moment magnétique des atomes arrangés sur une grille régulière \(\sigma_i \{-1, 1\}\). Pour une grille finie, il existe un nombre fini de configurations mais avec des dynamiques intriguantes.
La probabilité d’occuper un état du système \(\sigma\) est \(P(\sigma) = \frac{1}{Z}\exp(-\beta E(\sigma))\), avec
\[ E(\sigma) = -\sum_{i < j} J_{i,j} \sigma_i \sigma_j \]
Il existe des transitions de phase, en particulier pour \(\beta \approx 2.269\).
Voir la description complète du système sur https://tutos.metz.centralesupelec.fr/TPs/Intro-Python/index.html#an-example-project-simulating-the-ising-model
Concept fondamental de programmation orientée objet (OOP).
On peut construire une instance d’un objet :
On dispose également de beaucoup de méthodes spéciales : __lt__, __add__, __str__, …
On peut construire une hiérarchie de classes, on doit alors appeler le constructeur de la classe mère.
Problème Écrire un évaluateur d’expressions mathématiques.
class BinaryOp(Expression):
def __init__(self, fun, left, right):
self.fun = fun
self.left, self.right = left, right
def __call__(self, env):
return self.fun(self.left(env), self.right(env))
class Add(BinaryOp):
def __init__(self, left, right):
super().__init__(lambda x, y: x+y, left, right)
def __str__(self):
return f"({self.left} + {self.right})"
class Multiply(BinaryOp):
def __init__(self, left, right):
super().__init__(lambda x, y: x*y, left, right)
def __str__(self):
return f"({self.left} * {self.right})"Exemple
On décompose le projet en \(3\) classes :
ConsoleIsingGUIUn programme peut déclencher plusieurs fils d’exécutions, les threads. Ici :
Cela permet à la simulation de continuer à tourner même si l’utilisateur n’interagit pas.
import threading
class Simulator(threading.Thread):
def __init__(self, lattice_size, kT):
threading.Thread.__init__(self)
self.lock = threading.RLock()
self.spins = np.random.random((lattice_size, lattice_size)) > 0.5
def run(self):
while self.is_kept_alive():
if self.running:
self.step()
def stop(self):
with self.lock:
self.keeps_alive = False
def get_spins(self):
with self.lock:
return self.spins.copy()Au lieu des threads, on aurait pu utiliser du multiprocessing.
Installation (grâce au setup.py)
Exécution
Le projet est packagé avec setuptools.
La python packaging authority (pypa) maintient un tutorial sur le packaging, voir https://setuptools.pypa.io/en/latest/userguide/quickstart.html
Le packaging peut se faire avec un setup.py :
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="Ising",
version="1.0",
author="CentraleSupelec",
author_email="Jeremy.Fixcentralesupelec.fr",
description='A simulator of the Ising model with '
'the Metropolis algorithm',
long_description=long_description,
long_description_content_type="text/markdown",
url='http://tutos.metz.centralesupelec.fr/Intro_Python',
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
install_requires=['numpy', 'scipy', 'PyQt5', 'matplotlib', 'opencv-python-headless']
)Mais pourrait aussi utiliser pyproject.toml.
Et vous pourriez déposer votre librairie sur pypi, ou conda.
Numpy : Numerical Python
NumPy is the fundamental package for scientific computing in Python. It is a Python library that provides a multidimensional array object, various derived objects (such as masked arrays and matrices), and an assortment of routines for fast operations on arrays, including mathematical, logical, shape manipulation, sorting, selecting, I/O, discrete Fourier transforms, basic linear algebra, basic statistical operations, random simulation and much more.
Plusieurs approches pour créer un tableau Numpy :
np.zeros, np.ones, np.arange, np.linspace, np.random.rand, etc.np.loadtxt, np.genfromtxt, np.load, etc.# Tableau 1D de taille (5, )
np.array([1, 2, 3, 4, 5])
# Tableau 2D de taille (2, 3)
np.array([[1, 2, 3], [4, 5, 6]])
# Tableau 2D de taille (3, 4) rempli de zéros
np.zeros((3, 4))
# Tableau 6D de taille (2, 3, 4, 5, 6, 7) rempli de uns
np.ones((2, 3, 4, 5, 6, 7))
# Tableau 1D de 0 à 10 par pas de 2
np.arange(0, 10, 2)
# Tableau 1D de taille (5, ) avec des valeurs entre 0 et 1
np.linspace(0, 1, 5)
# Tableau 2D de taille (3,3) avec des valeurs aléatoires entre 0 et 1
np.random.rand(3, 3)Les tableaux ont :
On accède aux éléments :
start:stop:step,Pour l’efficacité en mémoire et temps:
On peut le vérifier avec l’attribut base
a = np.array([[1, 2, 3], [4, 5, 6]])
# Accès par index
# Premier élément de la première ligne
a[0, 0] # 1
# Accès par slicing
# Deuxième ligne
# Equivalent à a[1]
a[1, :] # [4 5 6]
# Première colonne
a[:, 0] # [1 4]
# Toutes les lignes, colonnes d'indice pair
a[:, ::2] #
a[:, ::2] = 0 # Modifie a
# Masquage booléen : fancy indexing
mask = a > 3 # np.array de dtype bool
b = a[mask] # [4 5 6]
b[0] = 10 # Ne Modifie pas aNe jamais faire
mais utiliser l’ellipsis :
On peut modifier la forme d’un tableau par :
np.reshape : retourne une vue du tableau avec une nouvelle forme,np.flatten ou np.ravel : retourne une copie ou une vue du tableau aplati (1D),np.transpose : retourne une vue du tableau transposé,np.pad : ajoute une bordure autour du tableau.np.expand_dims, x = x[np.newaxis, :] : ajoute une nouvelle dimension.On peut combiner/diviser plusieurs tableaux par :
np.concatenate : concatène plusieurs tableaux le long d’un axe,np.stack : empile plusieurs tableaux le long d’un nouvel axe,np.hstack et np.vstack : concatène horizontalement ou verticalement plusieurs tableaux.np.split, np.hsplit, np.vsplit : divise un tableau en plusieurs sous-tableaux.On peut permuter les éléments d’un tableau par :
np.transpose, np.swapaxesnp.rollLes opérations arithmétiques sont appliquées élément par élément +, ‘/’, -, *. Il existe également les opérations logiques.
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([[10, 20, 30], [40, 50, 60]])
# Addition
c = a + b # [[11 22 33] [44 55 66]]
# Soustraction
d = b - a # [[9 18 27] [36 45 54]]
# Multiplication terme à terme
e = a * b # [[10 40 90] [160 250 360]]
# Division terme à terme
f = b / a # [[10. 10. 10.] [10. 10. 10.]]Attention : * est le produit terme à terme, pas le produit matriciel. Le produit matriciel est obtenu par np.matmul ou simplement @
Problème Simuler le système de réaction-diffusion de GrayScott dont l’évolution des champs spatialisés \(u(x,t)\), \(v(x,t)\), \(x\in \mathbb{R}^2\) est régie par :
\[ \begin{eqnarray*} \frac{\partial u}{dt}(x,t) &=& D_u \nabla^2 u(x,t) - u(x,t) v^2(x,t) \\ & &+ F(1-u(x,t))\\ \frac{\partial v}{dt}(x,t) &=& D_u \nabla^2 v(x,t) + u(x,t) v^2(x,t) \\ && - (F+k) v(x,t) \end{eqnarray*} \]
def step(ut_1, vt_1, Du, Dv, F, k):
uvv = ut_1 * vt_1**2
lu = laplacian(ut_1)
lv = laplacian(vt_1)
ut[...] = ut_1 + dt * (Du * lu - uvv + F*(1-ut_1))
vt[...] = vt_1 + dt * (Dv * lv + uvv - (F + k) * vt_1)
def laplacian(src):
"""
u is a 2D nd array
"""
padded_src = np.pad(src, pad_width=1, mode="constant") # zero padding src
laplacian = (
0
+ padded_src[0:-2, 1:-1]
+ 0
+ padded_src[1:-1, 0:-2]
- 4 * padded_src[1:-1, 1:-1]
+ padded_src[1:-1, 2:]
+ 0
+ padded_src[2:, 1:-1]
+ 0
)
return laplacian[1:-1, 1:-1]avec \(\nabla^2\) le laplacien discret :
\[ \nabla^2 u(x,y) = \frac{u(x-\delta, y) + u(x+\delta, y) + u(x, y-\delta) + u(x, y+\delta) - 4 u(x,y)}{\delta^2} \]
Les fonctions Mathématiques s’appliquent sur tout les éléments, e.g. np.exp, np.sin, np.log, etc mais également les puissances.
e.g. Calcul d’une gaussienne centrée en \(x=2\) avec écart-type \(\sigma=0.5\)
On peut également calculer (selon un axis=..):
np.sum,np.min, np.max, np.argmin, np.argmax,np.mean \(\frac{1}{N} \sum_i X[..., i, ...]\), l’écart type np.std \(\sqrt{\frac{1}{N} \sum_i (X[..., i, ...] - \mu_i)^2}\)np.cov \(\sum_i (x_i - \mu)^T (x_i - \mu)\)Le broadcasting est un mécanisme qui permet d’effectuer des opérations entre des tableaux de formes différentes.
Numpy “étend” automatiquement les dimensions des tableaux pour qu’ils soient compatibles pour l’opération.
De la documentation sur le broadcasting
La règle : des opérations sur deux tableaux sont applicables si les dimensions sont compatibles.
Les dimensions sont compatibles si, 1) les tableaux ont le même nombre de dimensions, 2) partant de la droite (des dimensions)
Sinon vous obtiendrez l’exception ValueError: operands could not be broadcast together
Par exemple, on souhaite calculer la valeur d’une fonction gaussienne \(f(x)\) centrée en \(\mu\in\mathbb{R}^2\), sur une grille régulière centrée sur \(\mu\) :
\[ \forall x \in \mathbb{R}^2, f(x) = \frac{1}{\sigma \sqrt{2\pi}} \exp(\frac{- \|x - \mu\|_2^2}{2\sigma^2}) \]
if __name__ == '__main__':
mu = np.array([1, 2])
sigma = 0.3
step = 0.1
Nsteps = 10
X = gaussian(mu, sigma, step, Nsteps)
def gaussian(mu: np.ndarray, sigma: float, step: float, Nsteps: int) -> np.ndarray:
# Generate the regular grid
# (-N, -(N-1), ..., -1, 0, 1, ..., N-1, N) * step
intsteps = (np.arange(Nsteps) + 1)
deltasteps = np.concatenate([-intsteps[::-1], [0], intsteps]) * step
# Generate the steps centered on mu
# mu is (2, ), we use broadcasting on mu and deltasteps to generate
# mu (2, 1) + deltasteps(1, M) => mu + deltasteps is (2, M)
mu_delta = mu[:, np.newaxis] + deltasteps[np.newaxis, :]
# Generate the meshgrid with this values
# X and Y are of shape (2*Nsteps + 1, 2*Nsteps + 1)
X, Y = np.meshgrid(mu_delta[0], mu_delta[1])
# Concatenate the X, Y matrices
# xy is (( (2*Nsteps + 1) * (2*NSteps + 1), 2)
xy = np.concatenate((X.ravel()[:,np.newaxis], Y.ravel()[:, np.newaxis]), axis=1)
# Compute tha gausian values
# To compute the distance between the grid points is computed between xy and mu
# To broadcast, we do : xy (M, 2) - mu(1, 2)
dist = ((xy - mu[np.newaxis, :])**2).sum(axis=1)
values = 1.0 / (2.0 * sigma * np.sqrt(2.0 * np.pi)) * np.exp(- dist / (2.0 * sigma**2))
# Values is of shape (( (2*Nsteps + 1) * (2*NSteps + 1), ) that we reshape as
# (2*Nsteps + 1, 2*Nsteps + 1)
values = values.reshape((2*Nsteps + 1, 2*Nsteps + 1))
return values
Numpy supporte bien évidemment toutes les routines utiles en algèbre linéaire, dans le sous-package np.linalg:
np.dotnp.linalg.trace,np.linalg.inv, du déterminant np.linalg.detnp.norm - norme \(p\) pour un vecteur \((\sum_i x_i^p)^{1/p}\), Frobenius pour une matrice,np.linalg.eignp.linalg.cholesky \(X = L L^T\) (L triangulaire inférieure), SVD np.linalg.svd \(X = U S V^T\)Moindres carrés régularisés Soit \(n, m \in \mathbb{N}\), \(A\in \mathbb{R}^{n,m}\), \(b\in \mathbb{R}^{n}\), trouver \(x \in \mathbb{R}^m\) qui minimise :
\[ argmin_x J(x) = \|A x - b\|_2^2 = \sum_{i=0}^{n-1} (\sum_{j=0}^{m-1} a_{i,j}x_j - b_i)^2 \]
En fonction du range de \(A\), il peut exister une infinité de solution. Si \(A\) est singulière, une solution est obtenue en résolvant le problème des moindres carrés régularisés \(argmin_x J(x) + \lambda \|x\|_2^2\) , \(\lambda \in \mathbb{R}^+\) :
\[ x = (A^T A + \lambda \mathbf{I})^{-1} A^T b \]
Avec numpy :
import numpy as np
def rls(A: np.ndarray, b: np.ndarray, lbd: float) -> np.ndarray:
n, m = A.shape
inv = np.linalg.inv(A.T@A + lbd * np.eye(m))
x = np.dot(inv@A.T, b)
return x
if __name__ == '__main__':
n, m = 3, 4
A, b = np.random.random((n, m)), np.random.random((n,))
lbd = 0.001
x = rls(A, b, lbd)
print(x)Analyse en composantes principales Soit \(N\) vecteurs \(x_i \in \mathbb{R}^d\), trouver le vecteur \(w_0 \in \mathbb{R}^d\) et \(r\) vecteurs de projections \(w_j\) qui minimisent l’erreur de reconstruction :
\[ \begin{align} \min_{\{w_0, w_1, ..w_r\} \in \mathbb{R}^d} \sum_{i=0}^{N-1} \left|x_i - (w_0 + \sum_{j=1}^r (w_j^T (x_i-w_0))w_j )\right|_2^2 \end{align} \]
sujet à la contrainte \(\forall i, j \geq 1, w_i^T w_j = \delta_{i,j}\).
Algorithme:
Implémentation naïve https://github.com/rougier/ML-Recipes
class PCA:
def fit(self, X):
'''
Performs the PCA of X and stores the principal components.
The datapoints are supposed to be stored in the row vectors of X.
It keeps only the n_components projection vectors, associated
with the n_components largest eigenvalues of the covariance matrix X.T X
'''
# Center the datapoints
self.centroid = np.mean(X, axis=0)
# Computes the covariance matrix
sigma = np.dot((X - self.centroid).T, X - self.centroid)
# Compute the eigenvalue/eigenvector decomposition of sigma
eigvals, eigvecs = np.linalg.eigh(sigma)
# Note :The eigenvalues returned by eigh are ordered in ascending order.
# Stores the n_components eigenvectors/eigenvalues associated
# with the largest eigen values
#self.eigvals = eigvals[-self.n_components:]
#self.eigvecs = eigvecs[:, -self.n_components:]
# Stores all the eigenvectors/eigenvalues
# Useful for later computing some statistics of the variance we keep
self.eigvals = eigvals
self.eigvecs = eigvecs
def transform(self, Z):
'''
Uses a fitted PCA to transform the row vectors of Z
Remember the eigen vectors are ordered by ascending order
Denoting Z_trans = transform(Z),
The first component is Z_trans[:, -1]
The second component is Z_trans[:, -2]
...
'''
return np.dot(Z - self.centroid, self.eigvecs[:, -self.n_components:])La librairie pandas est développée pour l’analyse de données.
Site du projet https://pandas.pydata.org/
Cheat sheet, Data Wrangling with pandas
Les très bons Getting started tutorials
Autres ressources : Workshop pandas de Stéfanie Molin et son livre Hands-On Data Analysis with Pandas: A Python data science handbook for data collection, wrangling, analysis, and visualization
Installation
Avec uv :
Avec pip :
Beaucoup d’exemples par la suite viennent du guide utilisateur Pandas.
une série peut être indexée, slicée, etc.. comme un tableau numpy avec iloc, et comme un dictionnaire avec [..] ou loc()
une DataFrame est une collection de séries de données (comme une base de données)
d = {
"one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
"two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
"three": pd.Series([1.0, 2.0 ], index=["b", "d"])
}
df = pd.DataFrame(d)
print(df)
one two three
a 1.0 1.0 NaN
b 2.0 2.0 1.0
c 3.0 3.0 NaN
d NaN 4.0 2.0
df.loc['a',['one', 'three']] # pd.Seriesdf.columns, à leurs types df.dtypes, à l’index df.indexdf["four"] = df["one"] + df["two"], supprimer del df["one"]On peut construire une DataFrame à partir de dictionnaires, tableaux numpy, … ou de fichiers :
# Dictionnaire des colonnes
d = {
"one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
"two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
"three": pd.Series([1.0, 2.0 ], index=["b", "d"])
}
df = pd.DataFrame(d)
# Liste des lignes
df = pd.DataFrame([{"one": 1, "two": 2}, {"one": 5, "two": 10, "three": 20}])
# Fichier CSV
df = pd.read_csv("fichier.csv", delimiter=";")df.info(), df.describe(), df.head()df.describe(), (unique, top, freq) df.describe(include='category')datetime), des données catégorielles categories, des données numériques, il faut fixer les typesdef read_data(filepath: str):
df = pd.read_csv(filepath, delimiter=";")
return df
def fix_datatypes(df):
cat_columns = ['code_station', 'libelle_station', ...]
for c in cat_columns:
df[c] = df[c].astype('category')
df["date_observation"] = pd.to_datetime(df["date_observation"])
df = read_data("ecoulements.csv")
fix_datatypes(df)Important fixer les types des séries permet de profiter de toutes les fonctionnalités associées (e.g. catégories, temps)
Sur une dataframe, on peut :
df["Age"].median()min, max, mean, …) ou arbitraires df.agg({"Age": ["min", "max", "median", "skew"]})Les statistiques peuvent être calculées sur des sous-groupes formés par groupby:
titanic = pd.read_csv("data/titanic.csv")
# On peut regrouper les données par catégories et calculer
# des statistiques sur les sous-groupes
titanic = pd.read_csv("titanic.csv")
grouped = titanic[["Sex", "Pclass", "Age", "Fare"]].groupby(["Sex", "Pclass"])
print(grouped.size())
print(grouped.mean()) PassengerId Survived Pclass ... Fare Cabin Embarked
0 1 0 3 ... 7.2500 NaN S
1 2 1 1 ... 71.2833 C85 C
2 3 1 3 ... 7.9250 NaN S
Sex Pclass
female 1 94
2 76
3 144
male 1 122
2 108
3 347
dtype: int64
Age Fare
Sex Pclass
female 1 34.611765 106.125798
2 28.722973 21.970121
3 21.750000 16.118810
male 1 41.281386 67.226127
2 30.740707 19.741782
3 26.507589 12.661633Partant d’une représentation empilée (stacked), on peut réarranger les données avec la méthode pivot ou pivot_table (permet d’agréger) :
df = pd.DataFrame(
{
"A": ["one", "one", "two", "three"] * 6,
"B": ["bidule", "machin", "truc"] * 8,
"C": ["foo", "foo", "foo", "bar", "bar", "bar"] * 4,
"D": np.random.randn(24),
"E": np.random.randn(24),
"F": [datetime.datetime(2013, i, 1) for i in range(1, 13)]
+ [datetime.datetime(2013, i, 15) for i in range(1, 13)],
}
)
print(df)
pivoted = pd.pivot_table(df, values="D", index=["A", "B"], columns=["C"])
print(pivoted)
print(pivoted.loc[("one", "bidule"), "bar"]) A B C D E F
0 one bidule foo 0.029577 0.050984 2013-01-01
1 one machin foo -0.442099 -0.106008 2013-02-01
2 two truc foo 1.953053 1.265677 2013-03-01
3 three bidule bar -1.299982 0.762093 2013-04-01
4 one machin bar 0.652348 -0.323386 2013-05-01
5 one truc bar 1.433064 -1.818466 2013-06-01
6 two bidule foo 2.291547 -0.087507 2013-07-01
7 three machin foo -1.203084 -0.089323 2013-08-01
8 one truc foo 1.022026 -1.088463 2013-09-01
9 one bidule bar -0.433758 2.892209 2013-10-01
...
C bar foo
A B
one bidule 0.243642 -0.317265
machin -0.183342 -0.318656
truc 0.942341 1.991506
three bidule -0.641189 NaN
machin NaN -1.362425
truc -1.088678 NaN
two bidule NaN 1.272628
machin -0.686480 NaN
truc NaN 1.024224
0.24364165912866229pivot retourne une erreur en cas de doublons (moins flexible). pivot_table agrège les doublons (e.g. défaut : aggfunc='mean').
On peut joindre plusieurs dataframes par des clés, e.g. :
Ces tableaux peuvent être joints, par exemple pour lister les observations réalisées par un opérateur.
pd.merge dans les cheatsheets pandasPandas est interfacée avec matplotlib pour le tracé d’histogrammes (hist), nuages de points (scatter), séries (plot, même avec le temps !)
Les fonctions de tracé conduisent à des tracés indépendants pour des données groupées groupby.
Matplotlib : Numerical Python
Adapting the requirements laid out by John Hunter Matplotlib should:
Quelques concepts clés pour fixer les idées :
On suppose disposer de données \(x_i\) qui peuvent être étiquetées \(y_i\)
Apprentissage supervisé :
Etant donnés des paires \((x_i, y_i) \sim P_{X, Y}\), apprendre une fonction \(f_\theta\) (un prédicteur) qui minimise le risque réel :
\[ R_r(f) = \int_{X \ times Y} \mathcal{L}(y, f_\theta(x)) p_{X, Y}(x,y) dx dy \]
\[ R_e(f) = \sum_{i=0}^{N-1} \mathcal{L}(y_i, f_\theta(x_i)) \]
Question : comment prédire l’étiquette \(y\) associée à des \(x\) étant données des paires d’échantillons \((x_i, y_i)\)
Attention nous ne pourrons pas introduire tout les concepts liés au machine learning
Scikit-learn est une librairie python qui offre:
Le tout étant pipelinable et disposant d’une interface commune.
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# Chargement des données
iris = load_iris()
X, y = iris.data, iris.target
# Construction d'un pli d'entrainement et un pli de test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Construction du prédicteur et entrainement
clf = make_pipeline(StandardScaler(), KNeighborsClassifier(3))
clf.fit(X_train, y_train)
# Evaluation du risque sur un pli de test
score = clf.score(X_test, y_test)
# Inférence sur le pli de test
y_test_pred = clf.transform(X_test)
Produced with quarto