Implementation
This page describes the internal flow of histogram and the order in which validation, bin selection, and plotting additions are applied. Unlike Overview, the goal here is not to repeat the parameter list, but to show how the function actually builds the histogram.
The following snippets are taken from the current src/mespy/plot_utils.py implementation. Private helpers are mentioned only to clarify the flow; complete details are documented in _as_float_vector, _validate_axis_limits, _validate_figsize, _validate_decimals, _style_context, and standard_deviation.
Execution sequence
The implementation follows this sequence:
Converts
xinto a one-dimensional, finitefloat64vector with_as_float_vector("x", x).Validates
decimalsas a readable non-negative integer.Enters
_style_context(style)to apply the requested style and make behaviour consistent across notebooks.If
xlimorylimare provided, it validates them as pairs of finite values.If
ax is None, it creates a new figure withplt.subplots(...); otherwise it reuses the figure associated with the provided axis.Builds
hist_range_tupleifhist_rangeis provided, and checks that it satisfiesxmin < xmax.If
bin_widthis provided, it checks that it is positive, enforcesbins == "auto", and explicitly builds the bin edges withnp.arange(...).Prepares
hist_kwargs, addingbar_colorandedgecoloronly when they were passed explicitly.Calls
ax.hist(...)to draw the histogram and retrievebin_edges.If requested, it uses
bin_edgesto set x-axis ticks and labels, then applies any requested rotation.Computes the arithmetic mean and standard deviation of the sample.
If enabled, it adds the mean line and the
+- 1 sigmaband.Sets labels, title, grid, legend, axis limits, optional saving, and finally returns
(fig, ax).In IPython/Jupyter environments,
_style_contextexplicitly displays the new figures before leaving the context manager.
Input validation, style, and figure setup
values = _as_float_vector("x", x)
decimals = _validate_decimals(decimals)
with _style_context(style):
if xlim is not None:
xlim = _validate_axis_limits(
xlim,
name="xlim",
min_label="xmin",
max_label="xmax",
)
if ylim is not None:
ylim = _validate_axis_limits(
ylim,
name="ylim",
min_label="ymin",
max_label="ymax",
)
if ax is None:
import matplotlib.pyplot as plt
subplots_kwargs: dict = {"constrained_layout": True}
if figsize is not None:
subplots_kwargs["figsize"] = _validate_figsize(figsize)
if dpi is not None:
subplots_kwargs["dpi"] = dpi
fig, ax = plt.subplots(**subplots_kwargs)
else:
fig = ax.get_figure()
This first block sets the function’s input contract.
xis not passed directly to Matplotlib: it is first normalized intovalues, which must be a one-dimensional, non-empty numeric array without non-finite values.decimalsis validated immediately and remains available both for x-axis ticks and for the textual labels of the mean and band._style_context(style)centralizes three distinct cases:style=Noneuses the currentrcParams,style="mespy"loads themespy.mplstylefile, and any other string is passed toplt.style.context(...).xlimandylimare checked before any drawing happens. If they are present, they must be valid pairs of finite numbers.figsizeanddpiare passed toplt.subplots(...)only when they are explicitly provided. If the user passesax, the function does not create a new figure and reusesax.get_figure().constrained_layout=Trueis part of the standard behaviour whenhistogramcreates the figure on its own.In IPython/Jupyter notebooks, the same context manager also handles explicit display of new figures to avoid awkward interactions with
plt.style.context(...).
Range and bin construction
hist_range_tuple: tuple[float, float] | None = None
if hist_range is not None:
hist_range_tuple = _validate_axis_limits(
hist_range,
name="hist_range",
min_label="xmin",
max_label="xmax",
)
xmin, xmax = hist_range_tuple
if xmin >= xmax:
raise ValueError("Serve xmin < xmax in 'hist_range'")
else:
xmin = float(np.min(values))
xmax = float(np.max(values))
if bin_width is not None:
if bin_width <= 0:
raise ValueError("'bin_width' deve essere > 0.")
if bins != "auto":
raise ValueError(
"'bins' e 'bin_width' sono mutualmente esclusivi. Usane uno solo"
)
start = np.floor(xmin / bin_width) * bin_width
stop = np.ceil(xmax / bin_width) * bin_width
bins = np.arange(start, stop + bin_width, bin_width)
Qui la funzione decide il dominio numerico su cui costruire l’istogramma.
Se
hist_rangee presente, viene validato con lo stesso helper dei limiti degli assi, ma con un controllo aggiuntivo: deve valere strettamentexmin < xmax.Se
hist_rangenon e presente,xminexmaxvengono ricavati direttamente dai dati validati invalues.bin_widthnon modifica solo la larghezza dei bin: cambia proprio la strategia di costruzione, perche i bordi vengono generati esplicitamente connp.arange(...).Il calcolo di
startestopusaflooreceil, quindi i bin vengono allineati a multipli interi dibin_widthche coprano tutto l’intervallo considerato.
Calling ax.hist(...) and formatting the x axis
hist_kwargs: dict = {
"bins": bins,
"range": hist_range_tuple if bin_width is None else None,
"density": False,
"alpha": hist_alpha,
"label": label,
}
if bar_color is not None:
hist_kwargs["color"] = bar_color
if edgecolor is not None:
hist_kwargs["edgecolor"] = edgecolor
_, bin_edges, _ = ax.hist(values, **hist_kwargs)
fmt = f".{decimals}f"
if show_bin_ticks:
ax.set_xticks(bin_edges)
ax.set_xticklabels([f"{edge:{fmt}}" for edge in bin_edges])
if tick_rotation != 0:
ax.tick_params(axis="x", rotation=tick_rotation)
Questo e il punto in cui il grafico viene davvero disegnato.
La funzione usa
density=False, quindi l’istogramma e sempre a conteggi, non a densita normalizzata.bar_colorededgecolornon hanno un default hardcoded nella funzione: vengono inoltrati a Matplotlib solo se l’utente li passa. Altrimenti decide lo stile attivo.ax.hist(...)restituiscebin_edges, che diventano la sorgente ufficiale per i tick dell’asse x quandoshow_bin_ticks=True.Le etichette dei bordi non vengono calcolate in modo indipendente: vengono formattate direttamente dai
bin_edgesprodotti da Matplotlib.decimalscontrolla la rappresentazione testuale dei bordi tramitefmt = f".{decimals}f", ma la stessa formattazione verra riusata anche per media e banda.tick_rotatione indipendente dashow_bin_ticks: se diverso da zero, la rotazione viene comunque applicata all’asse x.
Mean, band, and plot finalization
mu = float(np.mean(values))
sigma = standard_deviation(values, ddof=ddof)
if show_mean:
ax.axvline(
mu,
color=mean_color,
linestyle="--",
linewidth=1.2,
label=rf"${mean_symbol} = {mu:{fmt}}$",
)
if show_band:
ax.axvspan(
mu - sigma,
mu + sigma,
color=band_color,
alpha=band_alpha,
label=rf"$\pm 1\sigma = {sigma:{fmt}}$",
)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel if ylabel is not None else "Conteggi")
title_kwargs: dict = {}
if title_fontsize is not None:
title_kwargs["fontsize"] = title_fontsize
if title_pad is not None:
title_kwargs["pad"] = title_pad
ax.set_title(title, **title_kwargs)
if not show_grid:
ax.grid(False)
elif grid_alpha is not None:
ax.grid(True, axis="y", alpha=grid_alpha)
if show_legend:
legend_kwargs: dict = {}
if legend_fontsize is not None:
legend_kwargs["fontsize"] = legend_fontsize
if legend_loc is not None:
legend_kwargs["loc"] = legend_loc
ax.legend(**legend_kwargs)
if xlim is not None:
ax.set_xlim(xlim)
if ylim is not None:
ax.set_ylim(ylim)
if save_path is not None:
savefig_kwargs: dict = {"bbox_inches": "tight"}
if dpi is not None:
savefig_kwargs["dpi"] = dpi
fig.savefig(save_path, **savefig_kwargs)
return fig, ax
L’ultima parte aggiunge gli elementi statistici e completa la configurazione dell’asse.
La media
mue calcolata connp.mean(values), mentresigmaviene delegata astandard_deviatione quindi dipende daddof.show_meaneshow_bandsono indipendenti: si puo mostrare solo la linea, solo la banda, entrambe oppure nessuna delle due.mean_colorandband_coloralways remain explicit per-call overrides; they are not recovered fromrcParams.La legenda della media usa
mean_symbol, mentre la banda viene etichettata come+- 1 sigmain forma matematica.Se
ylabeleNone, la funzione usa il default esplicito"Conteggi".Title and legend receive extra keyword arguments only when the user has provided them; in all other cases, the active style configuration remains in effect.
The grid follows three distinct branches:
show_grid=Falseturns it off explicitly,show_grid=Truewithgrid_alpha is Noneleaves it to the active style, andshow_grid=Truewith an explicitgrid_alphaapplies a grid on theyaxis with that opacity.Saving happens at the end with
bbox_inches="tight";dpiis passed tosavefig(...)only if the user explicitly requested it.
Important interactions between parameters
Alcune combinazioni di parametri definiscono il comportamento pratico piu importante della funzione.
bin_widthebinssono mutualmente esclusivi, salvo il caso default in cuibins == "auto".hist_rangeha due ruoli diversi: sebin_widthe assente, viene passato aax.hist(..., range=...); sebin_widthe presente, viene usato per determinarexminexmaxda cui costruire i bordi dei bin.style=Noneuses the currentrcParams,style="mespy"uses the package style, and any other string is passed straight through Matplotlib.bar_color,edgecolor,title_fontsize,title_pad,legend_fontsize,legend_loc, andgrid_alphaoverride the style only when they are notNone.show_bin_ticks=Trueusa ibin_edgesrestituiti da Matplotlib, non una ricostruzione separata fatta damespy.ddofinfluisce solo sul calcolo disigmae quindi sulla banda+- 1 sigma; non cambia i conteggi dell’istogramma.ylabel=Nonenon lascia l’asse senza etichetta: produce sempre"Conteggi".figsizeonly has an effect whenax is None;dpican instead affect both figure creation and saving, but always only when it is explicitly provided.show_gridnon attiva una griglia completa sul piano, ma solo una griglia orizzontale legata all’asseyquando la funzione interviene direttamente.save_pathsalva sempre la figura finale, anche quandoaxe stato passato dall’esterno e la figura non e stata creata dentrohistogram.In notebooks,
_style_contextshows only the new figures created inside the block and then closes them, to avoid double automatic display.
Commented example
from mespy import histogram
fig, ax = histogram(
x,
bin_width=0.5,
hist_range=(-2, 3),
style="mespy",
show_mean=True,
show_band=True,
decimals=2,
)
In questo esempio:
hist_rangedefinisce l’intervallo numerico da cui ricavarexminexmaxbin_width=0.5forza la costruzione esplicita dei bordi dei bin e quindi esclude l’uso dibinsdiverso da"auto"style="mespy"explicitly requests the package style for that single callla linea della media e la banda
+- 1 sigmavengono aggiunte dopo il disegno dell’istogrammaif x-axis ticks are enabled, they are shown using the bin edges formatted with 2 decimal places