Auteur: Joseph Salmon joseph.salmon@umontpellier.fr
On va illustrer la notion de quantile sur des lois classiques en affichant la densité, la fonction de répartion et la fonction quantile associée. On observera ainsi l'impact du centrage (en: centering) et de la mise à l'échelle (en: mise à l'échelle).
La liste exhaustive des lois disponibles sous scipy
se trouve ici: https://docs.scipy.org/doc/scipy/reference/stats.html
Ce notebook est inspiré des posts suivants:
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import ipywidgets # ipywidgets>=7.5
%matplotlib widget
def make_box_layout():
return ipywidgets.Layout(
border='solid 1px black',
margin='0px 10px 10px 0px',
padding='5px 5px 5px 5px',
)
def keep_no_param_distribution():
distributions = stats._continuous_distns._distn_names
distributions_0 = []
for i, name in enumerate(distributions):
dist = getattr(stats, name)
if not dist.shapes or len(dist.shapes)==0:
distributions_0.append(name)
distributions_0_val = [getattr(stats.distributions, string) for string in distributions_0 ]
distributions_0_dict = dict(zip(distributions_0, distributions_0_val))
return distributions_0_dict
# inspired by: https://stackoverflow.com/questions/30453097/getting-the-parameter-names-of-scipy-stats-distributions
distributions_0_dict = keep_no_param_distribution()
class RandomWidgetQuantiles(ipywidgets.HBox):
def __init__(self):
super().__init__()
output = ipywidgets.Output()
output0 = ipywidgets.Output()
self.xranges = (-6, 6) # Bornes d'observation
self.yranges = (0, 0.5) # Bornes d'observation
self.x = np.linspace(self.xranges[0], self.xranges[1], num=400)
self.quantiles_value = 0.5
self.mu, self.sigma = 0, 1
self.distribution = distributions_0_dict['norm']
self.size = 5
self.initial_color = '#1a60e1'
# self.jitter = 0.10
self.params = dict(
color=self.initial_color, alpha=0.50, linewidth=0.2, edgecolor="black"
)
self.fontsize = 4
with output:
self.fig, self.ax = plt.subplots(2, 2, sharey='row', sharex='col',
num='Quantiles et fonctions de répartition',
constrained_layout=True, figsize=(4, 4))
with output0:
self.fig0, self.ax0 = plt.subplots(1, 1,
num='Densité',
constrained_layout=True, figsize=(3, 1.5))
self.fig0.canvas.toolbar_visible = False
self.fig.canvas.toolbar_visible = False
self.pdf, = self.ax0.plot(self.x,
self.distribution.pdf(
self.x,
loc=self.mu, scale=self.sigma),
self.initial_color)
self.ax0.set_xlim(self.xranges)
self.area = self.ax0.fill_between(self.x, 0, self.distribution.pdf(
self.x, 0, 1), where=self.x <= self.quantiles_value, alpha=0.25, color=self.initial_color)
self.quantile_bar = self.ax0.axvline(
x=self.quantiles_value, c='k', ls="--", lw=2)
self.ticks = np.arange(self.xranges[0], self.xranges[1] + 0.1, step=2.)
self.mytext = self.ax[0, 1].text(np.min(self.x), np.mean(self.x), "Quantile d'ordre {:.2f} : \n {:.2f}".format(self.quantiles_value, self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)))
self.ax[0, 1].axis('Off')
self.diago, = self.ax[1, 0].plot(
[-0.1, 1.1], [-0.1, 1.1], self.initial_color)
self.ax[1, 0].set_xlim((-0.1, 1.1))
self.ax[1, 0].set_ylim((-0.1, 1.1))
self.ax[1, 0].set_xticks([0, 0.25, 0.5, 0.75, 1])
self.ax[1, 0].set_yticks([0, 0.25, 0.5, 0.75, 1])
self.points_q10, = self.ax[1, 0].plot(np.array([self.quantiles_value, self.quantiles_value, 1.1]), np.array([
1.1, self.quantiles_value, self.quantiles_value]), '--', color='k')
self.quantiles_range = self.distribution.cdf(
self.x,
loc=self.mu, scale=self.sigma)
self.cdf, = self.ax[1, 1].plot(self.x,
self.quantiles_range,
self.initial_color)
self.ax[1, 1].set_xlabel(r'Fonction de répartition')
self.ax[1, 1].set_xlim(self.xranges)
self.ax[1, 1].set_ylim((-0.1, 1.1))
self.ax[1, 1].set_xticks(self.ticks)
self.points_q11, = self.ax[1, 1].plot(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]), np.array([self.quantiles_value, self.quantiles_value]), '--', color='k')
self.points_cdf, = self.ax[1, 1].plot(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), self.quantiles_value, 'o', color=self.initial_color)
self.ax[0, 0].set_xlabel(r'Quantiles')
self.ax[0, 0].set_ylim(self.xranges)
self.ax[0, 0].set_xticks([0, 0.25, 0.5, 0.75, 1])
self.ppf, = self.ax[0, 0].plot(self.quantiles_range, self.x)
self.ax[0, 0].set_ylim(self.xranges)
self.ax[0, 0].set_yticks(self.ticks)
self.points_q00, = self.ax[0, 0].plot(np.array([self.quantiles_value, self.quantiles_value]), np.array([self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), self.xranges[0]]), '--', color='k')
self.points, = self.ax[0, 0].plot(self.quantiles_value, self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), 'o', color=self.initial_color)
self.fig.canvas.toolbar_position = 'bottom'
# define widgets
style = {'description_width': '100px'}
layout = {'width': '300px'}
mu_slider = ipywidgets.FloatSlider(
value=0, min=-2, max=2, step=0.1, description='$\mu$',
style=style, layout=layout)
sigma_slider = ipywidgets.FloatSlider(
value=1, min=0.5, max=3, step=0.1, description='$\sigma$',
style=style, layout=layout)
color_picker = ipywidgets.ColorPicker(
value=self.initial_color,
description='Couleur', style=style, layout=layout)
quantile_slider = ipywidgets.FloatSlider(
value=self.quantiles_value, min=0.001, max=0.999, step=0.001,
description="Quantile d'ordre ", style=style, layout=layout)
text_distribution = ipywidgets.Dropdown(
options=list(distributions_0_dict),
value='norm',
description='Distribution', style=style, layout=layout)
int_xrange_slider = ipywidgets.FloatRangeSlider(
value=self.xranges,
min=-5, max=5, step=0.1,
description="Zoom en x", style=style, layout=layout)
int_yrange_slider_pdf = ipywidgets.FloatRangeSlider(
value=(self.yranges),
min=-1, max=3, step=0.1,
description="Zoom en y", style=style, layout=layout)
controls = ipywidgets.VBox([
mu_slider,
sigma_slider,
text_distribution,
color_picker,
int_xrange_slider,
int_yrange_slider_pdf,
quantile_slider,
output0
])
controls.layout = make_box_layout()
out_box = ipywidgets.Box([output])
output.layout = make_box_layout()
# A Afficher
mu_slider.observe(self.update_mu, 'value')
sigma_slider.observe(self.update_sigma, 'value')
color_picker.observe(self.line_color, 'value')
text_distribution.observe(self.update_text_distribution, 'value')
int_xrange_slider.observe(self.update_xrange_slider, 'value')
int_yrange_slider_pdf.observe(self.update_yrange_slider_pdf, 'value')
quantile_slider.observe(self.update_quantile_slider, 'value')
self.children = [controls, output]
def update_mu(self, change):
"""Evolution with the mu parameter."""
self.mu = change.new
self.quantiles_range = self.distribution.cdf(
self.x,
loc=self.mu, scale=self.sigma)
self.pdf.set_ydata(self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma))
self.cdf.set_ydata(self.distribution.cdf(
self.x, loc=self.mu, scale=self.sigma))
self.ppf.set_xdata(self.quantiles_range)
self.points.set_ydata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.points_cdf.set_xdata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.points_q11.set_xdata(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]))
self.points_q11.set_ydata(
np.array([self.quantiles_value, self.quantiles_value]))
self.points_q00.set_xdata(
np.array([self.quantiles_value, self.quantiles_value]))
self.points_q00.set_ydata(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]))
self.area.remove()
self.quantile_bar.remove()
self.mytext.set_text("Quantile d'ordre {:.2f} : \n {:.2f}".format(self.quantiles_value, self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)))
self.area = self.ax0.fill_between(self.x, 0, self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma), where=self.x <= self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), alpha=0.25, color=self.initial_color)
self.quantile_bar = self.ax0.axvline(
x=self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), c='k', ls="--", lw=2)
self.fig.canvas.draw()
def update_sigma(self, change):
"""Evolution with the sigma parameter."""
self.sigma = change.new
self.quantiles_range = self.distribution.cdf(
self.x,
loc=self.mu, scale=self.sigma)
self.pdf.set_ydata(self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma))
self.cdf.set_ydata(self.distribution.cdf(
self.x, loc=self.mu, scale=self.sigma))
self.ppf.set_xdata(self.quantiles_range)
self.points.set_ydata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.points_cdf.set_xdata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.mytext.set_text("Quantile d'ordre {:.2f} : \n {:.2f}".format(self.quantiles_value, self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)))
self.points_q11.set_xdata(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]))
self.points_q11.set_ydata(
np.array([self.quantiles_value, self.quantiles_value]))
self.points_q00.set_xdata(
np.array([self.quantiles_value, self.quantiles_value]))
self.points_q00.set_ydata(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]))
self.area.remove()
self.quantile_bar.remove()
self.area = self.ax0.fill_between(self.x, 0, self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma), where=self.x <= self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), alpha=0.25, color=self.initial_color)
self.quantile_bar = self.ax0.axvline(
x=self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), c='k', ls="--", lw=2)
self.fig.canvas.draw()
def update_quantile_slider(self, change):
"""Evolution with the quantile parameter."""
self.quantiles_value = change.new
self.ppf.set_xdata(self.quantiles_range)
self.points.set_ydata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.points.set_xdata(self.quantiles_value)
self.points_cdf.set_xdata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.points_cdf.set_ydata(self.quantiles_value)
self.mytext.set_text("Quantile d'ordre {:.2f} : \n {:.2f}".format(self.quantiles_value, self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)))
self.points_q11.set_xdata(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]))
self.points_q11.set_ydata(
np.array([self.quantiles_value, self.quantiles_value]))
self.points_q00.set_xdata(
np.array([self.quantiles_value, self.quantiles_value]))
self.points_q00.set_ydata(np.array([self.xranges[0], self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)]))
self.points_q10.set_xdata(
np.array([self.quantiles_value, self.quantiles_value, 1.1]))
self.points_q10.set_ydata(
np.array([1.1, self.quantiles_value, self.quantiles_value]))
self.area.remove()
self.quantile_bar.remove()
self.area = self.ax0.fill_between(self.x, 0, self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma), where=self.x <= self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), alpha=0.25, color=self.initial_color)
self.quantile_bar = self.ax0.axvline(
x=self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), c='k', ls="--", lw=2)
self.fig.canvas.draw()
def line_color(self, change):
self.initial_color = change.new
self.pdf.set_color(self.initial_color)
self.cdf.set_color(self.initial_color)
self.ppf.set_color(self.initial_color)
self.area.set_color(self.initial_color)
self.params['color'] = self.initial_color
self.points_cdf.set_color(self.initial_color)
self.points.set_color(self.initial_color)
self.diago.set_color(self.initial_color)
self.fig.canvas.draw()
def update_xrange_slider(self, change):
self.xranges = change.new
self.ax[1, 1].set_xlim(self.xranges)
self.ax[0, 0].set_ylim(self.xranges)
self.ax0.set_xlim(self.xranges)
self.mytext.set_position((self.xranges[0], np.mean(self.xranges)))
self.fig.canvas.draw()
def update_yrange_slider_pdf(self, change):
self.yranges_pdf = change.new
self.ax0.set_ylim(self.yranges_pdf)
self.fig.canvas.draw()
def update_text_distribution(self, change):
self.distribution = distributions_0_dict[change.new]
self.quantiles_range = self.distribution.cdf(
self.x,
loc=self.mu, scale=self.sigma)
self.pdf.set_ydata(self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma))
self.cdf.set_ydata(self.distribution.cdf(
self.x, loc=self.mu, scale=self.sigma))
self.ppf.set_xdata(self.quantiles_range)
self.points.set_ydata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.points_cdf.set_xdata(self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma))
self.mytext.set_text("Quantile d'ordre {:.2f} : \n {:.2f}".format(self.quantiles_value, self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma)))
self.area.remove()
self.quantile_bar.remove()
self.area = self.ax0.fill_between(self.x, 0, self.distribution.pdf(
self.x, loc=self.mu, scale=self.sigma), where=self.x <= self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), alpha=0.25, color=self.initial_color)
self.quantile_bar = self.ax0.axvline(
x=self.distribution.ppf(
self.quantiles_value, loc=self.mu, scale=self.sigma), c='k', ls="--", lw=2)
self.fig.canvas.draw()
RandomWidgetQuantiles()
RandomWidgetQuantiles(children=(VBox(children=(FloatSlider(value=0.0, description='$\\mu$', layout=Layout(widt…