Notebook originel: exam22_corrige.ipynb
Examen Science des données🔗
Date: 08 décembre 2022
Durée: 2 h
[1]:
import numpy as np
import matplotlib.pyplot as plt
[2]:
%matplotlib notebook
Consignes🔗
Créez un répertoire Exam22_NOM_Prenom dans lequel vous travaillerez, et où seront stockés
le notebook jupyter d’analyse (
nom_prenom.ipynb, copié à partir de ce notebook énoncé sans l’extension.orig), que vous complèterez progressivement en ajoutant une ou plusieurs cellules en dessous de chaque question;le package python en développement, selon les instructions ci-dessous.
Vous devez alors mettre ce répertoire sous contrôle git sur un projet dédié sur le serveur https://gitlab.in2p3.fr, et en transmettre l’adresse à y.copin@ipnl.in2p3.fr.
Si, par manque de temps ou de connaissance, vous ne pouvez pas créer un dépôt git, envoyez par mail le notebook et/ou le package (sous forme d’un fichier tar ou zip) à y.copin@ipnl.in2p3.fr (cette solution sera pénalisée dans la notation).
Tests🔗
Ce notebook inclut des tests unitaires vous permettant de tester a minima le code du notebook. Utilisez ces tests pour guider votre développement et vérifier vos résultats. Ne modifiez pas (a fortiori, n’effacez pas) ces tests.
Régression linéaire🔗
Soit un jeu de données de \(N\) points \(\{(x_i, y_i)\}\). La régression linéaire
est définie par
où
Le coefficient de corrélation linéaire est défini par:
Sans détailler les calculs, quelles équations ont conduit à ces estimateurs?
Dans le notebook, écrire une fonction
linregress_1D(x, y)qui, en utilisant uniquement la fonctionnp.mean, retourne \(\hat{a}\), \(\hat{b}\) et \(r\) pour des vecteurs (1D) d’entrée \(x\) et \(y\).
[3]:
def linregress_1D(x, y):
"""
Régression linéaire `y = a + b x`.
:param np.ndarray x: vecteur des variables explicatives
:param np.ndarray y: vecteur des variables expliquées
:return: `a` (ordonnée à l'origine), `b` (pente), `r` (coefficient de corrélation)
>>> x = np.arange(10)
>>> linregress_1D(x, 3 + 2 * x)
(3.0, 2.0, 1.0)
>>> linregress_1D(x, 3 + 2 * x + np.round(np.sin(x * 10), 3))
(2.8803090909090923, 2.0317757575757573, 0.9925669351010524)
"""
xbar = np.mean(x)
ybar = np.mean(y)
varx = np.mean((x - xbar)**2)
vary = np.mean((y - ybar)**2)
covxy = np.mean((x - xbar)*(y - ybar))
b = covxy / varx
a = ybar - b * xbar
r = covxy / (varx * vary)**0.5
return a, b, r
Tester explicitement sur le cas
>>> x = np.arange(10)
>>> y = 3. + 2. * x + np.round(np.sin(x * 10), 3)
et vérifier le résultat avec scipy.stats.linregress.
[4]:
n, a, b = 10, 3., 2.
x = np.arange(n)
y = a + b * x
y += np.round(np.sin(x * 10), 3)
[5]:
res = aa, ba, ra = linregress_1D(x, y)
print(res)
(2.8803090909090923, 2.0317757575757573, 0.9925669351010524)
[6]:
from scipy.stats import linregress
res = linregress(x, y)
print(res)
LinregressResult(slope=2.031775757575758, intercept=2.8803090909090905, rvalue=0.9925669351010524, pvalue=1.323642496012334e-08, stderr=0.08807677394385144, intercept_stderr=0.47020130379248715)
Faire une figure illustrant les points de données \(\{(x, y)\}\) (
scatter) et la régression linéaire (plot). Ajouter un label sur chacun des axes, un titre à la figure et une légende.
[7]:
fig, ax = plt.subplots()
ax.scatter(x, y, c='k')
ax.plot(x, a + b * x, label=f"Input: a={a}, b={b}")
ax.plot(x, aa + ba * x, label=f"Analytic: a={aa:.2f}, b={ba:.2f} (r={ra:.3f})")
ax.set(xlabel='x', ylabel='y')
ax.legend()
[7]:
<matplotlib.legend.Legend at 0x7f7a291bf130>
Detrending🔗
Le detrending permet de retirer une composant linéaire d’un jeu de données \(\{y_i\}\):
Écrire une fonction
detrend_1D(y)retournant \(\tilde{y}\) (càd \(y\) duquel a été soustrait sa composante linéaire) et \(r\).
[8]:
def detrend_1D(y):
"""
Soustraction de la composante linéaire du vecteur (1D) `y`:
.. math::
\tilde{y}_i = y_i - (\hat{a} + \hat{b} i)
:param np.ndarray y: vecteur d'entrée
:return: $\tilde{y}
>>> x = np.arange(5)
>>> detrend_1D(1 + 2 * x)
array([0., 0., 0., 0., 0.])
>>> detrend_1D(x**0.5)
array([-0.28284271, 0.24395221, 0.18496069, 0.02959285, -0.17566304])
"""
i = np.arange(len(y))
a, b, r = linregress_1D(i, y)
return y - (a + b * i)
Tester et faire une figure sur le vecteur
yprécédent.
[9]:
ytilde = detrend_1D(y)
[10]:
fig, ax = plt.subplots()
ax.scatter(range(len(y)), ytilde, c='k')
ax.plot(0 * y)
ax.set(xlabel='i', ylabel=r'$\tilde{y}$');
Généralisation multi-dimensionnelle🔗
Écrire la function de régression linéaire
linregress(x, y, axis)pour retourner le résultat de la régression sur les tableauxxetyde rang arbitraire, le long de la dimensionaxis. La fonction doit retourner 3 tableauxa,betrde même rang que les tableaux d’entrée.Par example:
>>> x = np.arange(3*4).reshape((3, 4)) >>> y = 3 + 2 * x >>> linregress(x, y, axis=0) (array([[3., 3., 3., 3.]]), array([[2., 2., 2., 2.]]), array([[1., 1., 1., 1.]])) >>> linregress(x, y, axis=1) (array([[3.], [3.], [3.]]), array([[2.], [2.], [2.]]), array([[1.], [1.], [1.]])) >>> linregress(x, y, axis=2) Traceback (most recent call last): ... AxisError: axis 2 is out of bounds for array of dimension 2
[11]:
def linregress(x, y, axis=None):
"""
Régression linéaire `y = a + b x` le long de la dimension `axis`.
:param np.ndarray x: vecteur des variables explicatives
:param np.ndarray y: vecteur des variables expliquées
:param int axis: dimension le long de laquelle réaliser la régression linéaire
:return: `a` (ordonnée à l'origine), `b` (pente), `r` (coefficient de corrélation)
>>> x = np.arange(3*4).reshape((3, 4))
>>> y = 3 + 2 * x
>>> linregress(x, y, axis=0) # doctest: +NORMALIZE_WHITESPACE
(array([[3., 3., 3., 3.]]),
array([[2., 2., 2., 2.]]),
array([[1., 1., 1., 1.]]))
>>> linregress(x, y, axis=1) # doctest: +NORMALIZE_WHITESPACE
(array([[3.], [3.], [3.]]),
array([[2.], [2.], [2.]]),
array([[1.], [1.], [1.]]))
>>> linregress(x, y, axis=2)
Traceback (most recent call last):
...
numpy.AxisError: axis 2 is out of bounds for array of dimension 2
"""
if axis is None:
return linregress_1D(np.ravel(x), np.ravel(y))
xbar = np.mean(x, axis=axis, keepdims=True)
ybar = np.mean(y, axis=axis, keepdims=True)
varx = np.mean((x - xbar)**2, axis=axis, keepdims=True)
vary = np.mean((y - ybar)**2, axis=axis, keepdims=True)
covxy = np.mean((x - xbar)*(y - ybar), axis=axis, keepdims=True)
b = covxy / varx
a = ybar - b * xbar
r = covxy / (varx * vary)**0.5
return a, b, r
[12]:
x = np.arange(3*4).reshape((3, 4))
y = 3 + 2 * x
linregress(x, y, axis=0)
[12]:
(array([[3., 3., 3., 3.]]),
array([[2., 2., 2., 2.]]),
array([[1., 1., 1., 1.]]))
[13]:
linregress(x, y, axis=1)
[13]:
(array([[3.],
[3.],
[3.]]),
array([[2.],
[2.],
[2.]]),
array([[1.],
[1.],
[1.]]))
De même, écrire la fonction
detrend(y, axis). Vous pouvez utiliser les lignes suivantes pour générer un indice courant le long de la dimensionaxis:shape = np.ones_like(np.shape(y)) shape[axis] = -1 i = np.arange(np.shape(y)[axis]).reshape(shape)
[14]:
def detrend(y, axis=None):
"""
Soustraction de la composante linéaire `y - (a + b i)` le long de la dimension `axis`.
:param np.ndarray y: tableau d'entrée
:param int axis: dimension le long de laquelle soustraire la composante linéaire
:return: le tableau sans sa composante linéaire
>>> x = np.arange(3*4).reshape((3, 4))
>>> y = 3 + 2 * x
>>> detrend(y, axis=0) # doctest: +NORMALIZE_WHITESPACE
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> detrend(y, axis=1) # doctest: +NORMALIZE_WHITESPACE
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> detrend(y, axis=2)
Traceback (most recent call last):
...
IndexError: index 2 is out of bounds for axis 0 with size 2
"""
if axis is None:
return detrend_1D(np.array(y).ravel()).reshape(np.shape(y))
shape = np.ones_like(np.shape(y))
shape[axis] = -1
i = np.arange(np.shape(y)[axis]).reshape(shape)
a, b, r = linregress(i, y, axis=axis)
return y - (a + b * i)
[15]:
x = np.arange(3*4).reshape((3, 4))
y = np.arange(3).reshape(-1, 1) + 2 * np.arange(1, 4).reshape(-1, 1) * x
y = 3 + 2 * x
print(y, '\n', detrend(y, axis=0), sep='')
[[ 3 5 7 9]
[11 13 15 17]
[19 21 23 25]]
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
Ajouter des options suivantes à la fonction
detrend(voir scipy.signal.detrend pour un modèle):type='linear'pour soustraire la composante linéaire \(a + b\,i\) (type='linear'), la composante affine \(b\,i\) (type='affine'), ou uniquement la composante constante \(a\) (type='constant');overwrite=Falsepour réaliser la soustraction sur un nouveau tableau (overwrite=False) ou sur place (overwrite=True)
Si vous n’avez pas réussi à stocker votre package sous
git, envoyer le notebook à y.copin@ipnl.in2p3.fr.
Packaging🔗
Extraire les 4 fonctions précédentes (
linregress_1D,detrend_1D,linregressetdetrend) et les inclure dans un fichierdetrend.py. Il constituera le module de votre package.Documenter (succinctement) le module et les fonctions par des docstrings.
Créer un package
Exam22_Nom_Prenomconstitué du seul moduledetrend.py, avec l’arborescence suivante:Exam22_Nom_Prenom/ ├── detrend_nom_prenom │ ├── detrend.py │ └── __init__.py ├── LICENSE ├── README ├── setup.cfg └── setup.py
avec les fichiers suivants (à adapter à votre contexte):
Ajouter un répertoire
doc/pour configurer et générer la documentation Sphinx du package (voir documentation pyyc). Y inclure votre notebook (sousdoc/notebooks/) dans une section dédiée.Si vous n’avez pas réussi à stocker votre package sous
git, générer un tarball des sources de votre package (duquel vous aurez effacé les répertoires_build/,dist/s’ils existent),$ tar cvzf Exam22_NOM_Prenom.tgz Exam22_NOM_Prenom/
et envoyer le tarball résultant à nouveau à y.copin@ipnl.in2p3.fr.
Tests🔗
Tests unitaires🔗
[16]:
import unittest
class TestNotebook(unittest.TestCase):
def test_linregress_1D(self):
x = np.arange(10)
self.assertEqual(linregress_1D(x, 3 + 2 * x), (3.0, 2.0, 1.0))
self.assertAlmostEqual(linregress_1D(x, 3 + 2 * x + np.round(np.sin(x * 10), 3)),
(2.8803090909090923, 2.0317757575757573, 0.9925669351010524))
def test_detrend_1D(self):
x = np.arange(5)
np.testing.assert_allclose(detrend_1D(1 + 2 * x), [0., 0., 0., 0., 0.])
np.testing.assert_allclose(
detrend_1D(x**0.5),
[-0.28284271, 0.24395221, 0.18496069, 0.02959285, -0.17566304])
def test_linregress(self):
x = np.arange(3*4).reshape((3, 4))
y = 3 + 2 * x
np.testing.assert_allclose(
linregress(x, y, axis=0),
([[3., 3., 3., 3.]],
[[2., 2., 2., 2.]],
[[1., 1., 1., 1.]]))
np.testing.assert_allclose(
linregress(x, y, axis=0),
([[3., 3., 3., 3.]],
[[2., 2., 2., 2.]],
[[1., 1., 1., 1.]]))
np.testing.assert_allclose(
linregress(x, y, axis=1),
([[3.], [3.], [3.]],
[[2.], [2.], [2.]],
[[1.], [1.], [1.]]))
with self.assertRaises(np.AxisError):
linregress(x, y, axis=2)
def test_detrend(self):
x = np.arange(3*4).reshape((3, 4))
y = 3 + 2 * x
np.testing.assert_allclose(detrend(y, axis=0), 0.)
np.testing.assert_allclose(detrend(y, axis=1), 0.)
with self.assertRaises(IndexError):
detrend(y, axis=2)
unittest.main(argv=[''], verbosity=2, exit=False)
test_detrend (__main__.TestNotebook) ... ok
test_detrend_1D (__main__.TestNotebook) ... ok
test_linregress (__main__.TestNotebook) ... ok
test_linregress_1D (__main__.TestNotebook) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.004s
OK
[16]:
<unittest.main.TestProgram at 0x7f7a26f52bf0>
Doctests🔗
[17]:
import doctest
doctest.testmod(verbose=True)
Trying:
x = np.arange(3*4).reshape((3, 4))
Expecting nothing
ok
Trying:
y = 3 + 2 * x
Expecting nothing
ok
Trying:
detrend(y, axis=0) # doctest: +NORMALIZE_WHITESPACE
Expecting:
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
ok
Trying:
detrend(y, axis=1) # doctest: +NORMALIZE_WHITESPACE
Expecting:
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
ok
Trying:
detrend(y, axis=2)
Expecting:
Traceback (most recent call last):
...
IndexError: index 2 is out of bounds for axis 0 with size 2
ok
Trying:
x = np.arange(5)
Expecting nothing
ok
Trying:
detrend_1D(1 + 2 * x)
Expecting:
array([0., 0., 0., 0., 0.])
ok
Trying:
detrend_1D(x**0.5)
Expecting:
array([-0.28284271, 0.24395221, 0.18496069, 0.02959285, -0.17566304])
ok
Trying:
x = np.arange(3*4).reshape((3, 4))
Expecting nothing
ok
Trying:
y = 3 + 2 * x
Expecting nothing
ok
Trying:
linregress(x, y, axis=0) # doctest: +NORMALIZE_WHITESPACE
Expecting:
(array([[3., 3., 3., 3.]]),
array([[2., 2., 2., 2.]]),
array([[1., 1., 1., 1.]]))
ok
Trying:
linregress(x, y, axis=1) # doctest: +NORMALIZE_WHITESPACE
Expecting:
(array([[3.], [3.], [3.]]),
array([[2.], [2.], [2.]]),
array([[1.], [1.], [1.]]))
ok
Trying:
linregress(x, y, axis=2)
Expecting:
Traceback (most recent call last):
...
numpy.AxisError: axis 2 is out of bounds for array of dimension 2
ok
Trying:
x = np.arange(10)
Expecting nothing
ok
Trying:
linregress_1D(x, 3 + 2 * x)
Expecting:
(3.0, 2.0, 1.0)
ok
Trying:
linregress_1D(x, 3 + 2 * x + np.round(np.sin(x * 10), 3))
Expecting:
(2.8803090909090923, 2.0317757575757573, 0.9925669351010524)
ok
6 items had no tests:
__main__
__main__.TestNotebook
__main__.TestNotebook.test_detrend
__main__.TestNotebook.test_detrend_1D
__main__.TestNotebook.test_linregress
__main__.TestNotebook.test_linregress_1D
4 items passed all tests:
5 tests in __main__.detrend
3 tests in __main__.detrend_1D
5 tests in __main__.linregress
3 tests in __main__.linregress_1D
16 tests in 10 items.
16 passed and 0 failed.
Test passed.
[17]:
TestResults(failed=0, attempted=16)
[ ]:
Cette page a été générée à partir de
exam22_corrige.ipynb.