{ "cells": [ { "cell_type": "code", "execution_count": 9, "id": "15236ca4", "metadata": {}, "outputs": [], "source": [ "%matplotlib widget" ] }, { "cell_type": "code", "execution_count": 10, "id": "f1c8e058", "metadata": {}, "outputs": [], "source": [ "# Heavy lifting\n", "import numpy as np\n", "import scipy\n", "from scipy.optimize import curve_fit\n", "\n", "# Disegnini\n", "import matplotlib.pyplot as plt\n", "import matplotlib.patches as patches\n", "from matplotlib.widgets import RectangleSelector\n", "\n", "# Tabelle\n", "import ipysheet\n", "import ipywidgets as widgets\n", "from IPython.display import display\n", "\n", "# Estrazione parametri\n", "import ast\n", "import re\n", "\n", "# Funzione di Helper molto comoda\n", "def curve_fit_xy(fn, xs, ys, uxs, uys, p0, n_iter=7, **kwargs):\n", " \"\"\"\n", " Esegue curve_fit tenendo conto degli errori su entrambi gli assi.\n", "\n", " Algoritmo:\n", " 1. Parte da p0.\n", " 2. Calcola sigma_eff = sqrt(uy² + (df/dx · ux)²) con la derivata numerica valutata nei parametri correnti.\n", " 3. Rifà il fit con sigma=sigma_eff.\n", " 4. Ripete n_iter volte (di solito 3-7 bastano).\n", "\n", " Se uxs è tutto zero ricade nel classico curve_fit con solo sigma=uys.\n", " Se anche uys è tutto zero, usa curve_fit senza sigma (come prima).\n", " \"\"\"\n", " ha_ux = np.any(uxs != 0)\n", " ha_uy = np.any(uys != 0)\n", "\n", " popt = np.asarray(p0, dtype=float)\n", "\n", " for _ in range(n_iter):\n", " if ha_ux:\n", " dx = 1e-6\n", " dfdx = (fn(xs + dx, *popt) - fn(xs - dx, *popt)) / (2 * dx)\n", " sigma_eff = np.sqrt(uys**2 + (dfdx * uxs)**2)\n", " elif ha_uy:\n", " sigma_eff = uys.copy()\n", " else:\n", " sigma_eff = None\n", "\n", " if sigma_eff is not None:\n", " sigma_eff = np.where(sigma_eff == 0, 1e-12, sigma_eff) # evita /0\n", "\n", " popt, pcov = curve_fit(fn, xs, ys, p0=popt, sigma=sigma_eff, absolute_sigma=(sigma_eff is not None), **kwargs)\n", "\n", " return popt, pcov\n", "\n", "\n", "# Funzione principale\n", "def fit_interattivo(x_data, y_data, ux_data=None, uy_data=None, scala_barre=1):\n", " \"\"\"\n", " Strumento interattivo di fit su dati (x, y) con supporto opzionale per le barre di errore.\n", "\n", " Può essere chiamato in due modi:\n", "\n", " 1) Solo dati grezzi:\n", " fit_interattivo(x, y)\n", "\n", " 2) Con array di errori espliciti:\n", " fit_interattivo(x, y, ux_data=ux, uy_data=uy)\n", "\n", " Parametri\n", " ----------\n", " x_data : array-like — valori asse x\n", " y_data : array-like — valori asse y\n", " ux_data : array-like o None — incertezze su x (opzionale)\n", " uy_data : array-like o None — incertezze su y (opzionale)\n", " scala_barre : float — fattore moltiplicativo per la lunghezza delle\n", " barre di errore nel grafico (default 1)\n", "\n", " Ritorna\n", " -------\n", " rect_selector : RectangleSelector\n", " Tieni il riferimento vivo nella cella del notebook:\n", " rs = fit_interattivo(x, y)\n", "\n", " Note sul fit\n", " ------------\n", " - Gli errori su x vengono propagati iterativamente in sigma_eff:\n", " sigma_eff = sqrt(uy² + (df/dx · ux)²)\n", " e il fit viene ripetuto fino a convergenza (5 iterazioni di default).\n", " - p0 (punto di partenza) può essere specificato nel campo apposito\n", " dell'interfaccia (valori separati da virgola). Se lasciato vuoto\n", " si usa 0.5 per tutti i parametri.\n", " \"\"\"\n", " x_data = np.asarray(x_data, dtype=float)\n", " y_data = np.asarray(y_data, dtype=float)\n", " ux_data = np.asarray(ux_data, dtype=float) if ux_data is not None else np.zeros_like(x_data)\n", " uy_data = np.asarray(uy_data, dtype=float) if uy_data is not None else np.zeros_like(y_data)\n", "\n", " ha_errori = not (np.all(ux_data == 0) and np.all(uy_data == 0))\n", "\n", " assert len(x_data) == len(y_data), \"x_data e y_data devono avere la stessa lunghezza\"\n", " assert len(ux_data) == len(x_data), \"ux_data deve avere la stessa lunghezza di x_data\"\n", " assert len(uy_data) == len(y_data), \"uy_data deve avere la stessa lunghezza di y_data\"\n", "\n", " # Stato interno\n", " selected_mask = np.ones(len(x_data), dtype=bool)\n", " fit_history = []\n", " ultimo_fit_dati = {}\n", "\n", "\n", " # Grafico Principale\n", " fig, ax = plt.subplots(figsize=(9, 5))\n", " plt.subplots_adjust(bottom=0.12)\n", "\n", " sc_all = ax.scatter(x_data, y_data,\n", " c='steelblue', s=25, alpha=0.5, zorder=2, label='tutti i punti')\n", "\n", " if ha_errori:\n", " eb_all = ax.errorbar(x_data, y_data,\n", " xerr=scala_barre * ux_data, yerr=scala_barre * uy_data,\n", " fmt='none', ecolor='steelblue', elinewidth=0.8, capsize=2, alpha=0.4, zorder=2)\n", " else:\n", " eb_all = None\n", "\n", " sc_sel = ax.scatter([], [], c='tomato', s=45, alpha=0.9, zorder=3, label='selezionati')\n", "\n", " if ha_errori:\n", " eb_sel = ax.errorbar([], [],\n", " xerr=[], yerr=[],\n", " fmt='none', ecolor='tomato', elinewidth=1.2, capsize=3, alpha=0.85, zorder=3)\n", " else:\n", " eb_sel = None\n", "\n", " line_fit, = ax.plot([], [], color='darkorange', lw=2.5, zorder=4, label='fit')\n", " fit_text = ax.text(0.02, 0.97, '', transform=ax.transAxes, fontsize=10, color='darkorange',\n", " va='top', ha='left', bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.7))\n", " box_patch = patches.Rectangle((0, 0), 0, 0,\n", " edgecolor='tomato', facecolor='tomato', alpha=0.08,\n", " linewidth=1.5, linestyle='--', zorder=1)\n", " ax.add_patch(box_patch)\n", " ax.set_xlabel('x')\n", " ax.set_ylabel('y')\n", " ax.legend(loc='upper right', fontsize=9)\n", " ax.grid(True, alpha=0.3)\n", "\n", "\n", " # Grafico Residui\n", " fig_res, (ax_res1, ax_res2) = plt.subplots(1, 2, figsize=(12, 4), gridspec_kw={'wspace': 0.35})\n", " fig_res.suptitle('Analisi dei residui — ultimo fit', fontsize=12, fontweight='bold', y=1.02)\n", " for ax_r in (ax_res1, ax_res2):\n", " ax_r.axhline(0, color='#444', lw=1, linestyle='--', zorder=1)\n", " ax_r.grid(True, alpha=0.25)\n", " ax_res1.set(xlabel='x', ylabel='residuo (y - ŷ)', title='Grafico 1 — Residui su x')\n", " ax_res2.set(xlabel='ŷ', ylabel='residuo (y - ŷ)', title='Grafico 2 — Residui su ŷ')\n", " plt.tight_layout()\n", "\n", " #######################################################################\n", " #VERA UI\n", " #######################################################################\n", " func_input = widgets.Text(\n", " value='a * x + b',\n", " description='f(x) =',\n", " layout=widgets.Layout(width='420px')\n", " )\n", " # campo p0 (utile per funzioni trascendenti)\n", " p0_input = widgets.Text(\n", " value='',\n", " description='p0 (opz.):',\n", " placeholder='es. 1.0, 0.5, 3.14 — lascia vuoto per usare 0.5',\n", " layout=widgets.Layout(width='480px'),\n", " style={'description_width': 'initial'}\n", " )\n", "\n", " fit_btn = widgets.Button(description='Esegui fit', button_style='primary')\n", " clear_btn = widgets.Button(description='Reset selezione', button_style='warning')\n", " clear_hist_btn = widgets.Button(description='Reset storia fit', button_style='danger')\n", " show_data_btn = widgets.Button(description='Mostra dati selezionati', button_style='info')\n", "\n", " scala_slider = widgets.FloatSlider(\n", " value=scala_barre, min=0.1, max=scala_barre * 2, step=0.1,\n", " description='Scala barre:',\n", " style={'description_width': 'initial'},\n", " layout=widgets.Layout(width='520px')\n", " ) if ha_errori else None\n", "\n", " sel_label = widgets.Label(value=f'Selezionati: {len(x_data)} / {len(x_data)}')\n", " out_log = widgets.Output(layout=widgets.Layout(border='1px solid #ddd', padding='8px', min_height='80px'))\n", "\n", "\n", " # HELPER: parsing e costruzione della funzione di fit\n", " NOMI_RISERVATI = {\n", " 'x', 'np', 'numpy', 'math', 'pi', 'e', 'inf',\n", " 'True', 'False', 'None',\n", " 'exp', 'log', 'sin', 'cos', 'sqrt', 'tan',\n", " 'tanh', 'sinh', 'cosh',\n", " 'abs', 'min', 'max'\n", " }\n", "\n", " def estrai_parametri(expr):\n", " expr = re.sub(r'^[a-zA-Z_]\\s*=\\s*', '', expr.strip())\n", " try:\n", " tree = ast.parse(expr, mode='eval')\n", " nomi = {n.id for n in ast.walk(tree) if isinstance(n, ast.Name)}\n", " return sorted(nomi - NOMI_RISERVATI)\n", " except SyntaxError:\n", " return []\n", "\n", " def costruisci_funzione(expr, params):\n", " ns = {\n", " 'np': np,\n", " 'exp': np.exp, 'log': np.log,\n", " 'sin': np.sin, 'cos': np.cos, 'tan': np.tan,\n", " 'sinh': np.sinh, 'cosh': np.cosh, 'tanh': np.tanh,\n", " 'sqrt': np.sqrt,\n", " 'pi': np.pi, 'e': np.e, 'inf': np.inf,\n", " }\n", " exec(f\"def _f(x, {', '.join(params)}):\\n return {expr}\", ns)\n", " return ns['_f']\n", "\n", " def leggi_p0(k):\n", " raw = p0_input.value.strip()\n", " if not raw:\n", " return np.ones(k) * 0.5\n", " try:\n", " vals = [float(v) for v in raw.replace(';', ',').split(',')]\n", " if len(vals) != k:\n", " print(f\"p0 ha {len(vals)} valori ma il modello ha {k} parametri \"\n", " f\"fallback: uso 0.5 per tutti.\")\n", " return np.ones(k) * 0.5\n", " return np.array(vals)\n", " except ValueError:\n", " print(\"p0 non parsabile — fallback: uso 0.5 per tutti.\")\n", " return np.ones(k) * 0.5\n", "\n", " # HELPER: aggiornare le barre di errore\n", " def aggiorna_errorbar(eb, xs, ys, uxs, uys, scala):\n", " n = len(xs)\n", " if n == 0:\n", " for lc in eb[2]:\n", " lc.set_segments([])\n", " for cap in eb[1]:\n", " cap.set_data([], [])\n", " return\n", "\n", " segs_x = [np.array([[xs[i] - scala*uxs[i], ys[i]], [xs[i] + scala*uxs[i], ys[i]]]) for i in range(n)]\n", " segs_y = [np.array([[xs[i], ys[i] - scala*uys[i]], [xs[i], ys[i] + scala*uys[i]]]) for i in range(n)]\n", "\n", " if len(eb[2]) >= 2:\n", " eb[2][0].set_segments(segs_x)\n", " eb[2][1].set_segments(segs_y)\n", "\n", " caps = eb[1]\n", " cap_x_lo = np.array([[xs[i] - scala*uxs[i], ys[i]] for i in range(n)])\n", " cap_x_hi = np.array([[xs[i] + scala*uxs[i], ys[i]] for i in range(n)])\n", " cap_y_lo = np.array([[xs[i], ys[i] - scala*uys[i]] for i in range(n)])\n", " cap_y_hi = np.array([[xs[i], ys[i] + scala*uys[i]] for i in range(n)])\n", " if len(caps) >= 4:\n", " caps[0].set_data(cap_x_lo[:, 0], cap_x_lo[:, 1])\n", " caps[1].set_data(cap_x_hi[:, 0], cap_x_hi[:, 1])\n", " caps[2].set_data(cap_y_lo[:, 0], cap_y_lo[:, 1])\n", " caps[3].set_data(cap_y_hi[:, 0], cap_y_hi[:, 1])\n", "\n", "\n", " # HELPER: selezione rettangolare\n", " def on_select(eclick, erelease):\n", " nonlocal selected_mask\n", " if eclick.xdata is None or erelease.xdata is None:\n", " return\n", "\n", " x0, x1 = sorted([eclick.xdata, erelease.xdata])\n", " y0, y1 = sorted([eclick.ydata, erelease.ydata])\n", "\n", " new_mask = (\n", " (x_data >= x0) & (x_data <= x1) &\n", " (y_data >= y0) & (y_data <= y1)\n", " )\n", "\n", " # Ctrl premuto → aggiunge alla selezione esistente, altrimenti sostituisce\n", " ctrl_held = eclick.key in ('ctrl', 'control')\n", "\n", " if ctrl_held:\n", " selected_mask = selected_mask | new_mask\n", " else:\n", " selected_mask = new_mask\n", "\n", " xs_s = x_data[selected_mask]\n", " ys_s = y_data[selected_mask]\n", " pts = np.column_stack([xs_s, ys_s]) if xs_s.size > 0 else np.empty((0, 2))\n", " sc_sel.set_offsets(pts)\n", "\n", " if ha_errori and eb_sel is not None:\n", " scala = scala_slider.value if scala_slider else 1.0\n", " aggiorna_errorbar(eb_sel, xs_s, ys_s,\n", " ux_data[selected_mask],\n", " uy_data[selected_mask],\n", " scala)\n", "\n", " box_patch.set(xy=(x0, y0), width=x1-x0, height=y1-y0, visible=True)\n", " sel_label.value = f'Selezionati: {int(selected_mask.sum())} / {len(x_data)}'\n", " fig.canvas.draw_idle()\n", "\n", " # HELPER: cambio scala barre d'errore\n", " def on_scala_change(change):\n", " scala = change['new']\n", " aggiorna_errorbar(eb_all, x_data, y_data, ux_data, uy_data, scala)\n", " xs_s = x_data[selected_mask]\n", " ys_s = y_data[selected_mask]\n", " aggiorna_errorbar(eb_sel, xs_s, ys_s,\n", " ux_data[selected_mask],\n", " uy_data[selected_mask],\n", " scala)\n", " fig.canvas.draw_idle()\n", "\n", " if ultimo_fit_dati:\n", " d = ultimo_fit_dati\n", " aggiorna_residui(d['xs'], d['ys'], d['y_pred'],\n", " d['uxs'] * scala, d['uys'] * scala)\n", "\n", " # HELPER: Fa la super analisi dei modelli\n", " def stampa_confronto_modelli(history):\n", " best_AICc = min(h['AICc'] for h in history)\n", " delta = np.array([h['AICc'] - best_AICc for h in history])\n", " pesi = np.exp(-delta / 2); pesi /= pesi.sum()\n", " W = 72\n", "\n", " print(f\"\\n{'═'*W}\")\n", " print(f\" CONFRONTO TRA MODELLI ({len(history)} fit eseguiti)\")\n", " print(f\"{'═'*W}\")\n", " print(f\"\\n {'#':<3} {'Funzione':<26} {'par':>4} {'n':>5} \"\n", " f\"{'R²':>7} {'RMSE':>9} {'AICc':>9} {'ΔAICc':>7}\")\n", " print(f\" {'─'*3} {'─'*26} {'─'*4} {'─'*5} \"\n", " f\"{'─'*7} {'─'*9} {'─'*9} {'─'*7}\")\n", " for i, (h, d) in enumerate(zip(history, delta)):\n", " marker = ' ◀' if d < 1e-6 else ' '\n", " print(f\" {i+1:<3} {h['expr']:<26} {h['k']:>4} {h['n']:>5} \"\n", " f\"{h['R2']:>7.4f} {h['RMSE']:>9.4g} \"\n", " f\"{h['AICc']:>9.2f} {d:>7.2f}{marker}\")\n", " print(f\"\\n ◀ = migliore secondo AICc | ΔAICc < 2 equivalente | > 10 da scartare\")\n", "\n", " print(f\"\\n{'─'*W}\")\n", " print(f\" PESI DI AKAIKE\")\n", " print(f\"{'─'*W}\")\n", " for i, (h, w) in enumerate(zip(history, pesi)):\n", " bar = '█' * max(1, int(w * 40)) + '░' * (40 - max(1, int(w * 40)))\n", " print(f\"\\n #{i+1} y = {h['expr']}\")\n", " print(f\" {w*100:5.1f}% {bar}\")\n", "\n", " best_i = int(np.argmin(delta))\n", " bh, bw = history[best_i], pesi[best_i]\n", " print(f\"\\n{'═'*W}\")\n", " print(f\" VERDETTO — modello preferito: #{best_i+1} → y = {bh['expr']}\")\n", " print(f\" R²={bh['R2']:.4f} RMSE={bh['RMSE']:.4g} AICc={bh['AICc']:.2f} peso={bw*100:.1f}%\")\n", " if bw > 0.9: print(\" ✓ Scelta netta.\")\n", " elif bw > 0.6: print(\" ~ Scelta abbastanza chiara — considera anche il modello con ΔAICc minore.\")\n", " else: print(\" ! Modelli equivalenti — scegli il più semplice.\")\n", " print(f\"{'═'*W}\\n\")\n", "\n", "\n", "\n", " #################################################################\n", " # CALLBACK: funzioni che sono effettivamente chiamate dai pulsanti\n", "\n", " #############################################\n", " # CALLBACK: aggiornamento grafici dei residui\n", " def aggiorna_residui(xs, ys, y_pred, uxs, uys):\n", " residui = ys - y_pred\n", " sigma = residui.std(ddof=1)\n", "\n", " for ax_r in (ax_res1, ax_res2):\n", " ax_r.cla()\n", " ax_r.axhline(0, color='#444', lw=1, linestyle='--', zorder=1)\n", " ax_r.grid(True, alpha=0.25)\n", "\n", " ax_res1.set(xlabel='x', ylabel='residuo (y - ŷ)', title='Grafico 1 — Residui su x')\n", " ax_res1.axhspan(-sigma, sigma, color='steelblue', alpha=0.12, zorder=0, label='±1σ')\n", " ax_res1.axhline( sigma, color='steelblue', lw=1.2, linestyle=':', zorder=2)\n", " ax_res1.axhline(-sigma, color='steelblue', lw=1.2, linestyle=':', zorder=2)\n", "\n", " dentro = np.abs(residui) <= sigma\n", " fuori = ~dentro\n", "\n", " if ha_errori:\n", " ax_res1.errorbar(xs[dentro], residui[dentro],\n", " xerr=uxs[dentro], yerr=uys[dentro],\n", " fmt='o', color='steelblue', ecolor='gray',\n", " elinewidth=0.8, capsize=2, markersize=4, alpha=0.8,\n", " zorder=3, label=f'entro ±1σ ({dentro.sum()})')\n", " if fuori.any():\n", " ax_res1.errorbar(xs[fuori], residui[fuori],\n", " xerr=uxs[fuori], yerr=uys[fuori],\n", " fmt='D', color='tomato', ecolor='gray',\n", " elinewidth=0.8, capsize=2, markersize=4, alpha=0.9,\n", " zorder=4, label=f'fuori ±1σ ({fuori.sum()})')\n", " else:\n", " ax_res1.scatter(xs[dentro], residui[dentro], c='steelblue', s=30,\n", " alpha=0.75, zorder=3, label=f'entro ±1σ ({dentro.sum()})')\n", " if fuori.any():\n", " ax_res1.scatter(xs[fuori], residui[fuori], c='tomato', s=40,\n", " alpha=0.9, zorder=4, marker='D',\n", " label=f'fuori ±1σ ({fuori.sum()})')\n", "\n", " pct = dentro.mean() * 100\n", " ax_res1.text(0.98, 0.97, f'{pct:.1f}% entro ±1σ\\nσ = {sigma:.4g}',\n", " transform=ax_res1.transAxes, ha='right', va='top', fontsize=9,\n", " bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.75))\n", " ax_res1.legend(fontsize=8, loc='lower right')\n", "\n", " ax_res2.set(xlabel='ŷ', ylabel='residuo (y - ŷ)', title='Grafico 2 — Residui su ŷ')\n", " if ha_errori:\n", " ax_res2.errorbar(y_pred, residui,\n", " yerr=uys,\n", " fmt='o', color='darkorange', ecolor='gray',\n", " elinewidth=0.8, capsize=2, markersize=4,\n", " alpha=0.75, zorder=3)\n", " else:\n", " ax_res2.scatter(y_pred, residui, c='darkorange', s=30,\n", " alpha=0.75, edgecolors='none', zorder=3)\n", "\n", " if len(y_pred) > 2:\n", " idx = np.argsort(y_pred)\n", " coeff = np.polyfit(y_pred[idx], residui[idx], 1)\n", " ax_res2.plot(y_pred[idx], np.polyval(coeff, y_pred[idx]),\n", " color='gray', lw=1.5, linestyle='--', alpha=0.7,\n", " label=f'tendenza lineare (slope={coeff[0]:.3g})')\n", " ax_res2.legend(fontsize=8)\n", "\n", " ultima_expr = fit_history[-1]['expr'] if fit_history else '?'\n", " fig_res.suptitle(f'Analisi dei residui — ultimo fit: y = {ultima_expr}',\n", " fontsize=11, fontweight='bold', y=1.02)\n", " fig_res.canvas.draw_idle()\n", "\n", "\n", "\n", " ######################\n", " # CALLBACK: esegui fit\n", " def esegui_fit(_):\n", " out_log.clear_output(wait=True)\n", " with out_log:\n", " try:\n", " expr = func_input.value.strip()\n", " params = estrai_parametri(expr)\n", " if not params:\n", " print(\"Nessun parametro trovato. Usa lettere come a, b, c ...\")\n", " return\n", "\n", " xs = x_data[selected_mask]\n", " ys = y_data[selected_mask]\n", " uxs = ux_data[selected_mask]\n", " uys = uy_data[selected_mask]\n", " n, k = len(xs), len(params)\n", "\n", " if n < k + 1:\n", " print(f\"Troppo pochi punti ({n}) per {k} parametri.\")\n", " return\n", "\n", " fn = costruisci_funzione(expr, params)\n", " p0 = leggi_p0(k)\n", "\n", " # Fit con propagazione errori su x e y\n", " popt, pcov = curve_fit_xy(fn, xs, ys, uxs, uys, p0=p0, n_iter=7, maxfev=20_000 )\n", " perr = np.sqrt(np.diag(pcov))\n", " y_pred = fn(xs, *popt)\n", "\n", " # aggiorna grafico principale\n", " x_fit = np.linspace(x_data.min(), x_data.max(), 500)\n", " line_fit.set_data(x_fit, fn(x_fit, *popt))\n", " label_expr = expr\n", " for p, v in zip(params, popt):\n", " label_expr = re.sub(rf'\\b{p}\\b', f'{v:.3g}', label_expr)\n", " fit_text.set_text(f'y = {label_expr}')\n", " fig.canvas.draw_idle()\n", "\n", " # statistiche\n", " ss_res = np.sum((ys - y_pred) ** 2)\n", " ss_tot = np.sum((ys - ys.mean()) ** 2)\n", " r2 = 1 - ss_res / ss_tot\n", " rmse = np.sqrt(ss_res / n)\n", " GdL = max(n - k, 1)\n", " var_res = ss_res / GdL\n", " p_value = scipy.stats.chi2.sf(ss_res, df=GdL)\n", " log_lik = -n / 2 * (np.log(2 * np.pi * ss_res / n) + 1)\n", " AIC = 2 * k - 2 * log_lik\n", " BIC = k * np.log(n) - 2 * log_lik\n", " AICc = AIC + (2 * k * (k + 1)) / max(n - k - 1, 1)\n", "\n", " fit_history.append({\n", " 'expr': expr, 'k': k, 'n': n,\n", " 'R2': r2, 'RMSE': rmse, 'varianza_residuali': var_res,\n", " 'AIC': AIC, 'AICc': AICc, 'BIC': BIC,\n", " })\n", "\n", " # stampa risultati\n", " print(f\"Modello #{len(fit_history)} — fit su {n} punti\\n\")\n", " print(f\"{'Parametro':<12} {'Valore':>14} {'± Errore Std':>14}\")\n", " print(\"─\" * 42)\n", " for p, v, e in zip(params, popt, perr):\n", " print(f\"{p:<12} {v:>14.6g} {e:>14.6g}\")\n", " print(f\"\\nR² = {r2:.6f}\")\n", " print(f\"RMSE = {rmse:.6g}\")\n", " print(f\"χ² rid(σ²=1)= {var_res:.6g} (varianza residua)\")\n", " print(f\"p-value = {p_value:.4g} (GdL={GdL})\")\n", " print(f\"log L = {log_lik:.4f}\")\n", " print(f\"AIC = {AIC:.4f}\")\n", " print(f\"AICc = {AICc:.4f} ← usa questo se n/k < 40\")\n", " print(f\"BIC = {BIC:.4f}\")\n", " print(f\"Punti usati = {n} / {len(x_data)} | k={k} | GdL={GdL}\")\n", " print(f\"p0 usato = [{', '.join(f'{v:.4g}' for v in p0)}]\")\n", "\n", " scala = scala_slider.value if scala_slider else 1.0\n", "\n", " if len(fit_history) > 1:\n", " stampa_confronto_modelli(fit_history)\n", "\n", " ultimo_fit_dati.update(dict(xs=xs, ys=ys, y_pred=y_pred, uxs=uxs, uys=uys))\n", " aggiorna_residui(xs, ys, y_pred, uxs * scala, uys * scala)\n", "\n", " except Exception as err:\n", " print(f\"Errore: {err}\")\n", "\n", "\n", "\n", " #################\n", " # CALLBACK: reset\n", " def reset_selezione(_):\n", " nonlocal selected_mask\n", " selected_mask = np.ones(len(x_data), dtype=bool)\n", " sc_sel.set_offsets(np.empty((0, 2)))\n", " if ha_errori and eb_sel is not None:\n", " aggiorna_errorbar(eb_sel, np.array([]), np.array([]),\n", " np.array([]), np.array([]), 1.0)\n", " box_patch.set(visible=False)\n", " line_fit.set_data([], [])\n", " fit_text.set_text('')\n", " sel_label.value = f'Selezionati: {len(x_data)} / {len(x_data)}'\n", " fig.canvas.draw_idle()\n", " out_log.clear_output()\n", "\n", " def reset_storia(_):\n", " fit_history.clear()\n", " out_log.clear_output()\n", " with out_log:\n", " print(\"Storia dei fit azzerata.\")\n", "\n", " ###############\n", " # CALLBACK: tabella dati\n", " def mostra_dati_selezionati(_):\n", " out_log.clear_output(wait=True)\n", " with out_log:\n", " xs = x_data[selected_mask]\n", " ys = y_data[selected_mask]\n", " uxs = ux_data[selected_mask]\n", " uys = uy_data[selected_mask]\n", " if len(xs) == 0:\n", " print(\"Nessun punto selezionato.\")\n", " return\n", " print(f\"Dati selezionati: {len(xs)} punti\\n\")\n", " if ha_errori:\n", " headers = ['x', 'y', 'ux', 'uy']\n", " data = [xs, ys, uxs, uys]\n", " else:\n", " headers = ['x', 'y']\n", " data = [xs, ys]\n", "\n", " sheet = ipysheet.sheet(rows=len(xs), columns=len(headers), column_headers=headers)\n", "\n", " for i, col in enumerate(data):\n", " ipysheet.column( i, [f'{v:.17g}' for v in col], sheet=sheet )\n", "\n", " display(sheet)\n", "\n", "\n", "\n", "\n", "\n", " ########\n", " # LAYOUT\n", "\n", " # BUINDING\n", " fit_btn.on_click(esegui_fit)\n", " clear_btn.on_click(reset_selezione)\n", " clear_hist_btn.on_click(reset_storia)\n", " show_data_btn.on_click(mostra_dati_selezionati)\n", "\n", " rect_selector = RectangleSelector(ax, on_select,\n", " useblit=False, button=[1], minspanx=0, minspany=0,\n", " spancoords='data', interactive=False,\n", " state_modifier_keys={'move': '', 'clear': '', 'square': '', 'center': ''})\n", " fig.canvas.mpl_connect('button_press_event', lambda e: None)\n", "\n", " if ha_errori and scala_slider is not None:\n", " scala_slider.observe(on_scala_change, names='value')\n", "\n", " # Mostrare il tutto\n", " residuals_out = widgets.Output()\n", " with residuals_out:\n", " display(fig_res.canvas)\n", "\n", " fila_pulsanti = widgets.HBox(\n", " [func_input, fit_btn, clear_btn, clear_hist_btn, show_data_btn]\n", " )\n", " fila_opzioni = widgets.HBox([scala_slider]) if ha_errori and scala_slider else widgets.HBox([])\n", " fila_p0 = widgets.HBox([p0_input])\n", "\n", " display(widgets.VBox([\n", " fila_pulsanti,\n", " fila_opzioni,\n", " fila_p0,\n", " sel_label,\n", " fig.canvas,\n", " out_log,\n", " residuals_out,\n", " ]))\n", "\n", " return rect_selector" ] }, { "cell_type": "code", "execution_count": 11, "id": "702017cc", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipykernel_2932/4231667320.py:155: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n", " plt.tight_layout()\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "ce8f54016b6b401798deb59937bb5ccd", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Text(value='a * x + b', description='f(x) =', layout=Layout(width='420px')), But…" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b73aa6e4169743f69fe0415c6de69b7b", "version_major": 2, "version_minor": 0 }, "image/png": "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AGyqzuoAMD+maUqSYrGYpXEYhqF4PC6fzyenk3kGu2Dc7YcxtyfG3Z4Yd/uppDHPX9vmr3VROiSEVS4ej0uS2tvbLY4EAAAAKK54PK5QKGR1GDXNYZJ2VzXDMNTb26uGhgY5HA7L4ojFYmpvb9fevXsVDAYtiwPlxbjbD2NuT4y7PTHu9lNJY26apuLxuFpbWy1frax1rBBWOafTqba2NqvDmBAMBi0/gKD8GHf7YcztiXG3J8bdfiplzFkZLA/SbQAAAACwKRJCAAAAALApEkIUhdfr1U033SSv12t1KCgjxt1+GHN7YtztiXG3H8bcnigqAwAAAAA2xQohAAAAANgUCSEAAAAA2BQJIQAAAADYFAkhAAAAANgUCSGK4rvf/a6WL18un8+ns846S88//7zVIaGEbrnlFr3rXe9SQ0ODmpubddlll2nnzp1Wh4Uy+vrXvy6Hw6ENGzZYHQpK7K233tKVV16pcDgsv9+vU045RX/84x+tDgslksvldOONN6qzs1N+v1/HHXecvvzlL4sahLXlySef1KWXXqrW1lY5HA498MADk75vmqa++MUvasmSJfL7/brwwgu1a9cua4JFyZEQYt7uu+8+bdy4UTfddJNefPFFnXrqqbrooos0MDBgdWgokU2bNum6667Ts88+q0ceeUSZTEZ//ud/rrGxMatDQxm88MILuvPOO7VmzRqrQ0GJHThwQOeee67cbrcefvhh/ed//qe++c1vauHChVaHhhK59dZbdccdd+j222/XK6+8oltvvVXf+MY39J3vfMfq0FBEY2NjOvXUU/Xd73532u9/4xvf0Le//W19//vf13PPPacFCxbooosuUjKZLHOkKAfaTmDezjrrLL3rXe/S7bffLkkyDEPt7e361Kc+peuvv97i6FAOg4ODam5u1qZNm3TeeedZHQ5KaHR0VO985zv1ve99T1/5yld02mmn6bbbbrM6LJTI9ddfrz/84Q966qmnrA4FZfLBD35QLS0t+tGPfjTx2N/8zd/I7/frpz/9qYWRoVQcDofuv/9+XXbZZZIOrg62trbqs5/9rP7n//yfkqRoNKqWlhbdfffd+shHPmJhtCgFVggxL+l0Wps3b9aFF1448ZjT6dSFF16oZ555xsLIUE7RaFSStGjRIosjQaldd911+sAHPjDpM4/a9atf/UpnnHGGLr/8cjU3N6urq0v/+3//b6vDQgmdc845evTRR/Xqq69Kkv70pz/p97//vS6++GKLI0O5dHd3q6+vb9JxPhQK6ayzzuLarkbVWR0AqtvQ0JByuZxaWlomPd7S0qIdO3ZYFBXKyTAMbdiwQeeee65OPvlkq8NBCf3sZz/Tiy++qBdeeMHqUFAmr7/+uu644w5t3LhRn//85/XCCy/of/yP/yGPx6Orr77a6vBQAtdff71isZhWr14tl8ulXC6nr371q7riiiusDg1l0tfXJ0nTXtvlv4faQkIIYF6uu+46vfzyy/r9739vdSgoob179+rTn/60HnnkEfl8PqvDQZkYhqEzzjhDX/va1yRJXV1devnll/X973+fhLBG/fznP9c999yje++9VyeddJK2bt2qDRs2qLW1lTEHahRbRjEvTU1Ncrlc6u/vn/R4f3+/Fi9ebFFUKJdPfvKTevDBB/X444+rra3N6nBQQps3b9bAwIDe+c53qq6uTnV1ddq0aZO+/e1vq66uTrlczuoQUQJLlizRiSeeOOmxE044QT09PRZFhFL73Oc+p+uvv14f+chHdMopp+iqq67SZz7zGd1yyy1Wh4YyyV+/cW1nHySEmBePx6PTTz9djz766MRjhmHo0Ucf1dq1ay2MDKVkmqY++clP6v7779djjz2mzs5Oq0NCib3vfe/Ttm3btHXr1on/zjjjDF1xxRXaunWrXC6X1SGiBM4999wpLWVeffVVLVu2zKKIUGqJREJO5+TLQ5fLJcMwLIoI5dbZ2anFixdPuraLxWJ67rnnuLarUWwZxbxt3LhRV199tc444wydeeaZuu222zQ2NqZrrrnG6tBQItddd53uvfde/fu//7saGhom7ikIhULy+/0WR4dSaGhomHKP6IIFCxQOh7l3tIZ95jOf0TnnnKOvfe1r+vCHP6znn39eP/jBD/SDH/zA6tBQIpdeeqm++tWvqqOjQyeddJK2bNmib33rW/r4xz9udWgootHRUb322msTX3d3d2vr1q1atGiROjo6tGHDBn3lK1/RypUr1dnZqRtvvFGtra0TlUhRW2g7gaK4/fbb9Y//+I/q6+vTaaedpm9/+9s666yzrA4LJeJwOKZ9/K677tL69evLGwwsc/7559N2wgYefPBB3XDDDdq1a5c6Ozu1ceNGfeITn7A6LJRIPB7XjTfeqPvvv18DAwNqbW3VRz/6UX3xi1+Ux+OxOjwUyRNPPKF169ZNefzqq6/W3XffLdM0ddNNN+kHP/iBRkZG9O53v1vf+9739I53vMOCaFFqJIQAAAAAYFPcQwgAAAAANkVCCAAAAAA2RUIIAAAAADZFQggAAAAANkVCCAAAAAA2RUIIAAAAADZFQggAAAAANkVCCAAAAAA2RUIIAAAAADZFQggAAAAANkVCCADAUQwODmrx4sX62te+NvHY008/LY/Ho0cffdTCyAAAmD+HaZqm1UEAAFDJHnroIV122WV6+umntWrVKp122mn6y7/8S33rW9+yOjQAAOaFhBAAgAJcd911+t3vfqczzjhD27Zt0wsvvCCv12t1WAAAzAsJIQAABRgfH9fJJ5+svXv3avPmzTrllFOsDgkAgHnjHkIAAAqwe/du9fb2yjAM7dmzx+pwAAAoClYIAQA4hnQ6rTPPPFOnnXaaVq1apdtuu03btm1Tc3Oz1aEBADAvJIQAABzD5z73Of3yl7/Un/70JwUCAb33ve9VKBTSgw8+aHVoAADMC1tGAQA4iieeeEK33XabfvKTnygYDMrpdOonP/mJnnrqKd1xxx1WhwcAwLywQggAAAAANsUKIQAAAADYFAkhAAAAANgUCSEAAAAA2BQJIQAAAADYFAkhAAAAANgUCSEAAAAA2BQJIQAAAADYFAkhAAAAANgUCSEAAAAA2BQJIQAAAADYFAkhAAAAANgUCSEAAAAA2BQJIQAAAADYFAkhAAAAANgUCSEAAAAA2BQJIQAAAADYFAkhAAAAANgUCSEAAAAA2BQJIQAAAADY1P8Hv/vSYw6dymEAAAAASUVORK5CYII=", "text/html": [ "\n", "