import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt
import seaborn as sns
import arviz as az
Appendice K — Per liberarvi dai terrori preliminari
Fornisco qui la traduzione del capitolo Per liberarvi dai terrori preliminari di Calculus made easy.
Il terrore preliminare, che impedisce a molti studenti anche solo di tentare di imparare l’analisi, può essere abolito una volta per tutte semplicemente affermando qual è il significato – in termini di buon senso – dei due simboli principali che sono usati nell’analisi matematica.
Questi terribili simboli sono:
\(d\) che significa semplicemente “un po’ di”. Quindi \(\operatorname{d}\!x\) significa un po’ di \(x\); o \(\operatorname{d}\!u\) significa un po’ di \(u\). I matematici pensano che sia più educato dire “un elemento di” invece di “un po’ di”. Fai come ti pare. Ma scoprirai che questi piccoli pezzi (o elementi) possono essere considerati indefinitamente piccoli.
\(\int\) che è semplicemente una S allungata, e può essere chiamata (se volete) “la somma di”. Quindi \(\int \operatorname{d}\!x\) significa la somma di tutti i pezzettini di \(x\); oppure \(\int \operatorname{d}\!t\) significa la somma di tutti i pezzettini di \(t\). I matematici chiamano questo simbolo “l’integrale di”. Ora qualsiasi sciocco può vedere che se \(x\) è considerato come composto da tanti piccoli pezzetti, ognuno dei quali è chiamato \(\operatorname{d}\!x\), se li sommi tutti insieme ottieni la somma di tutti i \(\operatorname{d}\!x\), (che è la stessa cosa dell’insieme di \(x\)). La parola “integrale” significa semplicemente “il tutto”. Se pensi alla durata di un’ora, puoi (se vuoi) pensarla come suddivisa in 3600 piccoli pezzetti chiamati secondi. L’insieme dei 3600 pezzetti sommati fa un’ora. Quando vedrete un’espressione che inizia con questo simbolo terrificante, d’ora in poi saprete che è stato messo lì semplicemente per darvi l’istruzione che ora dovete eseguire (se potete) l’operazione di sommare tutti i piccoli pezzetti che sono indicati dai simboli che seguono.
È tutto.
Verifichiamo con una simulazione. Importiamo le librerie necessarie.
%config InlineBackend.figure_format = 'retina'
# Initialize random number generator
= 8927
RANDOM_SEED = np.random.default_rng(RANDOM_SEED)
rng "arviz-darkgrid")
az.style.use(="colorblind") sns.set_theme(palette
Nella cella seguente definisco la funzione di densità gaussiana:
\[ f(x; \mu, \sigma) = {1 \over {\sigma\sqrt{2\pi} }} \exp \left\{-\frac{(x - \mu)^2}{2 \sigma^2} \right\}. \]
def gaussian(x, mu, sigma):
return (
1 / (sigma * np.sqrt(2 * np.pi)) * np.exp((-1 * (x - mu) ** 2) / (2 * sigma**2))
)
Definisco i parametri della funzione e creo un grande numero di valori \(x\) nell’intervallo [-10, 10]. Il vettore fx
contiene l’ordinata della funzione per ciascuno dei punti x_range
che sono stati definiti.
# Parametri della distribuzione
= 0
mu = 1
sigma
# Limiti dell'integrale
= -10
a = 10
b = 10000
n
# Generazione dei valori della funzione
= np.linspace(a, b, n)
x_range = gaussian(x_range, mu, sigma)
fx # fx = (1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-0.5 * ((x_range - mu) / sigma) ** 2)
= plt.plot(x_range, fx, "b-") _
Creo ora una funzione che approssima l’integrale facendo semplicemente la somma dei prodotti dell’ordinata della funzione moltiplicati per \(\Delta x\), ovvero, nel caso presente, 20 / 10000.
def integral_approximation(f, a, b, n):
= (b - a) / n
delta return np.sum(delta * f)
Sappiamo che la funzione di densità ha un’area unitaria. Usiamo la funzione precedente per calcolare l’intergrale della funzione nell’intervallo [-10, 10].
= integral_approximation(fx, a, b, n)
approx approx
0.9999000000000001
Usiamo ora l’approssimazione di SciPy.
# Scipy approximation
integrate.quad(lambda x: 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp((-1 * (x - mu) ** 2) / (2 * sigma**2)),
a,
b, )
(1.0, 8.671029987439099e-10)
È noto che il 95% dell’area sottesa dalla curva della distribuzione normale standardizzata è contenuta nell’intervallo compreso tra -1.96 e 1.96. Per replicare questo risultato, iniziamo usando la funzione approssimata.
= -1.96
a = 1.96
b = 10000
n = np.linspace(a, b, n)
x_range = gaussian(x_range, mu, sigma)
fx
# Our integral approximation function
def integral_approximation(f, a, b, n):
= (b - a) / n
delta return np.sum(delta * f)
= integral_approximation(fx, a, b, n)
approx approx
0.9499321151989195
Confrontiamo il risultato ottenuto utilizzando l’approssimazione con quello calcolato tramite la libreria SciPy.
# Scipy approximation
integrate.quad(lambda x: 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp((-1 * (x - mu) ** 2) / (2 * sigma**2)),
a,
b, )
(0.9500042097035591, 1.0474096492701335e-11)
È noto che l’area della curva della distribuzione normale compresa tra più e meno una deviazione standard dalla media corrisponde a circa il 68% del totale. Per riprodurre questo risultato usiamo la nostra approssimazione:
= -1.0
a = 1.0
b = 10000
n = np.linspace(a, b, n)
x_range = gaussian(x_range, mu, sigma)
fx
# Our integral approximation function
def integral_approximation(f, a, b, n):
= (b - a) / n
delta return np.sum(delta * f)
= integral_approximation(fx, a, b, n)
approx approx
0.6826696157194765
Confrontiamo il risultato ottenuto utilizzando l’approssimazione con quello calcolato tramite la libreria SciPy.
# Scipy approximation
integrate.quad(lambda x: 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp((-1 * (x - mu) ** 2) / (2 * sigma**2)),
a,
b, )
(0.682689492137086, 7.579375928402476e-15)
In sintesi, per calcolare l’integrale di una funzione di densità in un intervallo, è possibile procedere suddividendo l’area sotto la curva in intervalli di larghezza costante. In ogni intervallo, si moltiplica l’ordinata della funzione per la larghezza dell’intervallo. Si sommano poi questi prodotti per tutti gli intervalli. Una tale somma darà un’idea dell’area sottesa dalla curva e quindi dell’integrale della funzione di densità nell’intervallo considerato.
K.1 Introduzione ai logaritmi
Aggiungo alcune nozioni di base sui logaritmi. Il logaritmo è una funzione matematica che risponde alla domanda: “quante volte devo moltiplicare un certo numero (chiamato”base”) per ottenere un altro numero?” Matematicamente, questo è espresso come:
\[ \log_b(a) = x \iff b^x = a \]
Ad esempio, \(\log_2(8) = 3\) perché \(2^3 = 8\).
Nel contesto dei logaritmi, i valori molto piccoli (compresi tra 0 e 1) diventano più grandi (in termini assoluti) e negativi quando applichiamo una funzione logaritmica. Questo è utile per stabilizzare i calcoli, specialmente quando lavoriamo con prodotti di numeri molto piccoli che potrebbero portare a problemi di underflow.
Per esempio: - \(\log(1) = 0\) - \(\log(0.1) = -1\) - \(\log(0.01) = -2\) - \(\log(0.001) = -3\)
Come si può vedere, i valori assoluti dei logaritmi crescono man mano che il numero originale si avvicina a zero.
Una delle proprietà più utili dei logaritmi è che consentono di trasformare un prodotto in una somma:
\[ \log_b(a \times c) = \log_b(a) + \log_b(c) \]
Questa proprietà è estremamente utile in calcoli complessi, come nella statistica bayesiana, dove il prodotto di molte probabilità potrebbe diventare un numero molto piccolo e causare problemi numerici.
Un’altra proprietà utile dei logaritmi è che un rapporto tra due numeri diventa la differenza dei loro logaritmi:
\[ \log_b\left(\frac{a}{c}\right) = \log_b(a) - \log_b(c) \]
Anche questa proprietà è molto utilizzata in matematica, specialmente in situazioni in cui è necessario normalizzare i dati.
In sintesi, i logaritmi sono strumenti potenti per semplificare e stabilizzare i calcoli matematici. Essi consentono di lavorare più agevolmente con numeri molto grandi o molto piccoli e di trasformare operazioni complesse come prodotti e divisioni in somme e differenze, rendendo i calcoli più gestibili e meno inclini a errori numerici.
K.2 Watermark
%load_ext watermark
%watermark -n -u -v -iv -w -m
Last updated: Thu Feb 29 2024
Python implementation: CPython
Python version : 3.11.8
IPython version : 8.22.1
Compiler : Clang 16.0.6
OS : Darwin
Release : 23.3.0
Machine : x86_64
Processor : i386
CPU cores : 8
Architecture: 64bit
matplotlib: 3.8.3
seaborn : 0.13.2
numpy : 1.26.4
arviz : 0.17.0
scipy : 1.12.0
Watermark: 2.4.3