Per liberarvi dai terrori preliminari#
Fornisco qui la traduzione del primo capitolo di Calculus made easy.
Il terrore preliminare, che impedisce alla maggior parte dei ragazzi di quinta 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.
# Dependencies
import numpy as np
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import arviz as az
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[1], line 5
3 import scipy.integrate as integrate
4 import matplotlib.pyplot as plt
----> 5 import arviz as az
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/arviz/__init__.py:33
27 super()._log(level, msg, *args, **kwargs)
30 _log = Logger("arviz")
---> 33 from .data import *
34 from .plots import *
35 from .plots.backends import *
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/arviz/data/__init__.py:2
1 """Code for loading and manipulating data structures."""
----> 2 from .base import CoordSpec, DimSpec, dict_to_dataset, numpy_to_data_array
3 from .converters import convert_to_dataset, convert_to_inference_data
4 from .datasets import clear_data_home, list_datasets, load_arviz_data
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/arviz/data/base.py:11
8 from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
10 import numpy as np
---> 11 import xarray as xr
13 try:
14 import ujson as json
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/xarray/__init__.py:3
1 from importlib.metadata import version as _version
----> 3 from xarray import testing, tutorial
4 from xarray.backends.api import (
5 load_dataarray,
6 load_dataset,
(...)
10 save_mfdataset,
11 )
12 from xarray.backends.zarr import open_zarr
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/xarray/testing/__init__.py:1
----> 1 from xarray.testing.assertions import ( # noqa: F401
2 _assert_dataarray_invariants,
3 _assert_dataset_invariants,
4 _assert_indexes_invariants_checks,
5 _assert_internal_invariants,
6 _assert_variable_invariants,
7 _data_allclose_or_equiv,
8 assert_allclose,
9 assert_chunks_equal,
10 assert_duckarray_allclose,
11 assert_duckarray_equal,
12 assert_equal,
13 assert_identical,
14 )
16 __all__ = [
17 "assert_allclose",
18 "assert_chunks_equal",
(...)
22 "assert_identical",
23 ]
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/xarray/testing/assertions.py:8
5 from typing import Union
7 import numpy as np
----> 8 import pandas as pd
10 from xarray.core import duck_array_ops, formatting, utils
11 from xarray.core.coordinates import Coordinates
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pandas/__init__.py:46
43 # let init-time option registration happen
44 import pandas.core.config_init # pyright: ignore[reportUnusedImport] # noqa: F401
---> 46 from pandas.core.api import (
47 # dtype
48 ArrowDtype,
49 Int8Dtype,
50 Int16Dtype,
51 Int32Dtype,
52 Int64Dtype,
53 UInt8Dtype,
54 UInt16Dtype,
55 UInt32Dtype,
56 UInt64Dtype,
57 Float32Dtype,
58 Float64Dtype,
59 CategoricalDtype,
60 PeriodDtype,
61 IntervalDtype,
62 DatetimeTZDtype,
63 StringDtype,
64 BooleanDtype,
65 # missing
66 NA,
67 isna,
68 isnull,
69 notna,
70 notnull,
71 # indexes
72 Index,
73 CategoricalIndex,
74 RangeIndex,
75 MultiIndex,
76 IntervalIndex,
77 TimedeltaIndex,
78 DatetimeIndex,
79 PeriodIndex,
80 IndexSlice,
81 # tseries
82 NaT,
83 Period,
84 period_range,
85 Timedelta,
86 timedelta_range,
87 Timestamp,
88 date_range,
89 bdate_range,
90 Interval,
91 interval_range,
92 DateOffset,
93 # conversion
94 to_numeric,
95 to_datetime,
96 to_timedelta,
97 # misc
98 Flags,
99 Grouper,
100 factorize,
101 unique,
102 value_counts,
103 NamedAgg,
104 array,
105 Categorical,
106 set_eng_float_format,
107 Series,
108 DataFrame,
109 )
111 from pandas.core.dtypes.dtypes import SparseDtype
113 from pandas.tseries.api import infer_freq
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pandas/core/api.py:1
----> 1 from pandas._libs import (
2 NaT,
3 Period,
4 Timedelta,
5 Timestamp,
6 )
7 from pandas._libs.missing import NA
9 from pandas.core.dtypes.dtypes import (
10 ArrowDtype,
11 CategoricalDtype,
(...)
14 PeriodDtype,
15 )
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pandas/_libs/__init__.py:18
16 import pandas._libs.pandas_parser # noqa: E501 # isort: skip # type: ignore[reportUnusedImport]
17 import pandas._libs.pandas_datetime # noqa: F401,E501 # isort: skip # type: ignore[reportUnusedImport]
---> 18 from pandas._libs.interval import Interval
19 from pandas._libs.tslibs import (
20 NaT,
21 NaTType,
(...)
26 iNaT,
27 )
File interval.pyx:1, in init pandas._libs.interval()
File hashtable.pyx:1, in init pandas._libs.hashtable()
File missing.pyx:1, in init pandas._libs.missing()
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pandas/_libs/tslibs/__init__.py:39
1 __all__ = [
2 "dtypes",
3 "localize_pydatetime",
(...)
35 "get_supported_reso",
36 ]
38 from pandas._libs.tslibs import dtypes # pylint: disable=import-self
---> 39 from pandas._libs.tslibs.conversion import localize_pydatetime
40 from pandas._libs.tslibs.dtypes import (
41 Resolution,
42 get_supported_reso,
(...)
46 periods_per_second,
47 )
48 from pandas._libs.tslibs.nattype import (
49 NaT,
50 NaTType,
51 iNaT,
52 nat_strings,
53 )
File conversion.pyx:1, in init pandas._libs.tslibs.conversion()
File <frozen importlib._bootstrap>:405, in parent(self)
KeyboardInterrupt:
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
RANDOM_SEED = 42
rng = np.random.default_rng(RANDOM_SEED)
from aquarel import load_theme
theme = load_theme("minimal_light")
theme.apply()
import matplotlib.style as style
style.use("tableau-colorblind10")
Nella cella seguente definisco la funzione di densità gaussiana:
def gaussian(x, mu, sigma):
return (
1 / (sigma * np.sqrt(2 * 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.
mu = 0
sigma = 1
# Define bounds of integral
a = -10
b = 10
n = 10000
# Generate function values
x_range = np.linspace(a, b, n)
fx = gaussian(x_range, mu, sigma)
_ = plt.plot(x_range, fx, "b-")
![../_images/154fd6579b3a18dfbdc491c7abd1763e1a1cbdb0b2c2c8cb4995ed4f71c4c4db.png](../_images/154fd6579b3a18dfbdc491c7abd1763e1a1cbdb0b2c2c8cb4995ed4f71c4c4db.png)
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):
delta = (b - a) / n
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].
approx = integral_approximation(fx, a, b, n)
approx
0.9999000000000001
Usiamo ora l’approssimazione di SciPy.
# Scipy approximation
integrate.quad(
lambda x: 1 / (sigma * np.sqrt(2 * 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.
a = -1.96
b = 1.96
n = 10000
x_range = np.linspace(a, b, n)
fx = gaussian(x_range, mu, sigma)
# Our integral approximation function
def integral_approximation(f, a, b, n):
delta = (b - a) / n
return np.sum(delta * f)
approx = integral_approximation(fx, a, b, n)
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 * 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:
a = -1.0
b = 1.0
n = 10000
x_range = np.linspace(a, b, n)
fx = gaussian(x_range, mu, sigma)
# Our integral approximation function
def integral_approximation(f, a, b, n):
delta = (b - a) / n
return np.sum(delta * f)
approx = integral_approximation(fx, a, b, n)
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 * 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.
Introduzione ai 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:
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:
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:
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.
Watermark#
%load_ext watermark
%watermark -n -u -v -iv -w
Last updated: Sat May 06 2023
Python implementation: CPython
Python version : 3.11.3
IPython version : 8.13.2
matplotlib: 3.7.1
numpy : 1.24.3
arviz : 0.15.1
seaborn : 0.12.2
scipy : 1.10.1
Watermark: 2.3.1