{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduzione a Probabilità e Statistica\n", "In questo notebook, esploreremo alcuni dei concetti di cui abbiamo parlato in precedenza. Molti concetti di probabilità e statistica sono ben rappresentati nelle principali librerie per l'elaborazione dei dati in Python, come `numpy` e `pandas`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import random\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variabili Casuali e Distribuzioni\n", "Iniziamo estraendo un campione di 30 valori da una distribuzione uniforme da 0 a 9. Calcoleremo anche la media e la varianza.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sample = [ random.randint(0,10) for _ in range(30) ]\n", "print(f\"Sample: {sample}\")\n", "print(f\"Mean = {np.mean(sample)}\")\n", "print(f\"Variance = {np.var(sample)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Per stimare visivamente quante diverse valori ci sono nel campione, possiamo tracciare l'**istogramma**:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.hist(sample)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analisi dei dati reali\n", "\n", "Media e varianza sono molto importanti quando si analizzano dati del mondo reale. Carichiamo i dati sui giocatori di baseball da [SOCR MLB Height/Weight Data](http://wiki.stat.ucla.edu/socr/index.php/SOCR_Data_MLB_HeightsWeights)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv(\"../../data/SOCR_MLB.tsv\",sep='\\t', header=None, names=['Name','Team','Role','Weight','Height','Age'])\n", "df\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Stiamo usando un pacchetto chiamato [**Pandas**](https://pandas.pydata.org/) per l'analisi dei dati. Parleremo più nel dettaglio di Pandas e di come lavorare con i dati in Python più avanti in questo corso.\n", "\n", "Calcoliamo i valori medi per età, altezza e peso:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df[['Age','Height','Weight']].mean()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ora concentriamoci sull'altezza e calcoliamo la deviazione standard e la varianza:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(list(df['Height'])[:20])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mean = df['Height'].mean()\n", "var = df['Height'].var()\n", "std = df['Height'].std()\n", "print(f\"Mean = {mean}\\nVariance = {var}\\nStandard Deviation = {std}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oltre alla media, è sensato esaminare il valore mediano e i quartili. Possono essere visualizzati usando un **box plot**:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,2))\n", "plt.boxplot(df['Height'].ffill(), vert=False, showmeans=True)\n", "plt.grid(color='gray', linestyle='dotted')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Possiamo anche creare diagrammi a scatola di sottoinsiemi del nostro dataset, ad esempio, raggruppati per ruolo del giocatore.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.boxplot(column='Height', by='Role', figsize=(10,8))\n", "plt.xticks(rotation='vertical')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Nota**: Questo diagramma suggerisce che, in media, l'altezza dei primi base è maggiore dell'altezza dei secondi base. Più avanti impareremo come possiamo testare questa ipotesi in modo più formale e come dimostrare che i nostri dati sono statisticamente significativi per mostrarlo. \n", "\n", "Età, altezza e peso sono tutte variabili casuali continue. Cosa pensi sia la loro distribuzione? Un buon modo per scoprirlo è tracciare l'istogramma dei valori: \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df['Weight'].hist(bins=15, figsize=(10,6))\n", "plt.suptitle('Weight distribution of MLB Players')\n", "plt.xlabel('Weight')\n", "plt.ylabel('Count')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Distribuzione Normale\n", "\n", "Creiamo un campione artificiale di pesi che segue una distribuzione normale con la stessa media e varianza dei nostri dati reali:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "generated = np.random.normal(mean, std, 1000)\n", "generated[:20]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.hist(generated, bins=15)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.hist(np.random.normal(0,1,50000), bins=300)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Poiché la maggior parte dei valori nella vita reale segue una distribuzione normale, non dovremmo utilizzare un generatore di numeri casuali uniforme per generare dati campione. Ecco cosa succede se proviamo a generare pesi con una distribuzione uniforme (generata da `np.random.rand`):\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wrong_sample = np.random.rand(1000)*2*std+mean-std\n", "plt.figure(figsize=(10,6))\n", "plt.hist(wrong_sample)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Intervalli di confidenza\n", "\n", "Calcoliamo ora gli intervalli di confidenza per i pesi e le altezze dei giocatori di baseball. Useremo il codice [da questa discussione su stackoverflow](https://stackoverflow.com/questions/15033511/compute-a-confidence-interval-from-sample-data):\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import scipy.stats\n", "\n", "def mean_confidence_interval(data, confidence=0.95):\n", " a = 1.0 * np.array(data)\n", " n = len(a)\n", " m, se = np.mean(a), scipy.stats.sem(a)\n", " h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1)\n", " return m, h\n", "\n", "for p in [0.85, 0.9, 0.95]:\n", " m, h = mean_confidence_interval(df['Weight'].fillna(method='pad'),p)\n", " print(f\"p={p:.2f}, mean = {m:.2f} ± {h:.2f}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test di Ipotesi\n", "\n", "Esploriamo diversi ruoli nel nostro dataset di giocatori di baseball:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.groupby('Role').agg({ 'Weight' : 'mean', 'Height' : 'mean', 'Age' : 'count'}).rename(columns={ 'Age' : 'Count'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mettiamo alla prova l'ipotesi che i prima base siano più alti dei seconda base. Il modo più semplice per farlo è testare gli intervalli di confidenza:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for p in [0.85,0.9,0.95]:\n", " m1, h1 = mean_confidence_interval(df.loc[df['Role']=='First_Baseman',['Height']],p)\n", " m2, h2 = mean_confidence_interval(df.loc[df['Role']=='Second_Baseman',['Height']],p)\n", " print(f'Conf={p:.2f}, 1st basemen height: {m1-h1[0]:.2f}..{m1+h1[0]:.2f}, 2nd basemen height: {m2-h2[0]:.2f}..{m2+h2[0]:.2f}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Possiamo vedere che gli intervalli non si sovrappongono.\n", "\n", "Un modo statisticamente più corretto per dimostrare l'ipotesi è utilizzare un **test t di Student**:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from scipy.stats import ttest_ind\n", "\n", "tval, pval = ttest_ind(df.loc[df['Role']=='First_Baseman',['Height']], df.loc[df['Role']=='Second_Baseman',['Height']],equal_var=False)\n", "print(f\"T-value = {tval[0]:.2f}\\nP-value: {pval[0]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I due valori restituiti dalla funzione `ttest_ind` sono:\n", "* il p-value può essere considerato come la probabilità che due distribuzioni abbiano la stessa media. Nel nostro caso, è molto basso, il che significa che ci sono forti prove a supporto che i primi basi siano più alti.\n", "* il t-value è il valore intermedio della differenza media normalizzata che viene utilizzato nel test t, ed è confrontato con un valore soglia per un dato livello di confidenza.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulazione di una Distribuzione Normale con il Teorema del Limite Centrale\n", "\n", "Il generatore pseudo-casuale in Python è progettato per fornirci una distribuzione uniforme. Se vogliamo creare un generatore per una distribuzione normale, possiamo usare il teorema del limite centrale. Per ottenere un valore distribuito normalmente, calcoleremo semplicemente la media di un campione generato uniformemente.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def normal_random(sample_size=100):\n", " sample = [random.uniform(0,1) for _ in range(sample_size) ]\n", " return sum(sample)/sample_size\n", "\n", "sample = [normal_random() for _ in range(100)]\n", "plt.figure(figsize=(10,6))\n", "plt.hist(sample)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Correlazione e Evil Baseball Corp\n", "\n", "La correlazione ci permette di trovare relazioni tra sequenze di dati. Nel nostro esempio giocattolo, immaginiamo che ci sia una cattiva corporazione di baseball che paga i suoi giocatori in base alla loro altezza - più il giocatore è alto, più soldi riceve. Supponiamo che ci sia uno stipendio base di $1000 e un bonus aggiuntivo da $0 a $100, a seconda dell'altezza. Prenderemo i giocatori reali della MLB e calcoleremo i loro stipendi immaginari:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "heights = df['Height'].fillna(method='pad')\n", "salaries = 1000+(heights-heights.min())/(heights.max()-heights.mean())*100\n", "print(list(zip(heights, salaries))[:10])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calcoliamo ora la covarianza e la correlazione di quelle sequenze. `np.cov` ci fornirà una cosiddetta **matrice di covarianza**, che è un'estensione della covarianza a variabili multiple. L'elemento $M_{ij}$ della matrice di covarianza $M$ è una correlazione tra le variabili di input $X_i$ e $X_j$, e i valori diagonali $M_{ii}$ sono la varianza di $X_{i}$. Analogamente, `np.corrcoef` ci fornirà la **matrice di correlazione**.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Covariance matrix:\\n{np.cov(heights, salaries)}\")\n", "print(f\"Covariance = {np.cov(heights, salaries)[0,1]}\")\n", "print(f\"Correlation = {np.corrcoef(heights, salaries)[0,1]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Una correlazione pari a 1 significa che esiste una forte **relazione lineare** tra due variabili. Possiamo vedere visivamente la relazione lineare tracciando un valore in funzione dell'altro:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.scatter(heights,salaries)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vediamo cosa succede se la relazione non è lineare. Supponiamo che la nostra azienda abbia deciso di nascondere l'ovvia dipendenza lineare tra altezze e salari, e abbia introdotto una certa non linearità nella formula, come `sin`:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "salaries = 1000+np.sin((heights-heights.min())/(heights.max()-heights.mean()))*100\n", "print(f\"Correlation = {np.corrcoef(heights, salaries)[0,1]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In questo caso, la correlazione è leggermente inferiore, ma è ancora piuttosto alta. Ora, per rendere la relazione ancora meno evidente, potremmo voler aggiungere un po' di casualità extra aggiungendo una variabile casuale allo stipendio. Vediamo cosa succede:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "salaries = 1000+np.sin((heights-heights.min())/(heights.max()-heights.mean()))*100+np.random.random(size=len(heights))*20-10\n", "print(f\"Correlation = {np.corrcoef(heights, salaries)[0,1]}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.scatter(heights, salaries)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Riesci a indovinare perché i punti si allineano in linee verticali in questo modo?\n", "\n", "Abbiamo osservato la correlazione tra un concetto artificialmente creato come lo stipendio e la variabile osservata *altezza*. Vediamo anche se le due variabili osservate, come altezza e peso, sono correlate:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.corrcoef(df['Height'].ffill(),df['Weight'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sfortunatamente, non abbiamo ottenuto alcun risultato - solo alcuni strani valori `nan`. Ciò è dovuto al fatto che alcuni dei valori nella nostra serie non sono definiti, rappresentati come `nan`, il che causa che il risultato dell'operazione sia indefinito altrettanto. Osservando la matrice possiamo vedere che `Weight` è la colonna problematica, perché è stata calcolata l'auto-correlazione tra i valori di `Height`.\n", "\n", "> Questo esempio mostra l'importanza della **preparazione** e **pulizia** dei dati. Senza dati adeguati non possiamo calcolare nulla.\n", "\n", "Usiamo il metodo `fillna` per riempire i valori mancanti e calcoliamo la correlazione: \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.corrcoef(df['Height'].fillna(method='pad'), df['Weight'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "C'è infatti una correlazione, ma non così forte come nel nostro esempio artificiale. Infatti, se guardiamo al grafico a dispersione di un valore in funzione dell'altro, la relazione sarebbe molto meno evidente:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.scatter(df['Weight'],df['Height'])\n", "plt.xlabel('Weight')\n", "plt.ylabel('Height')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusione\n", "\n", "In questo notebook abbiamo imparato come eseguire operazioni di base sui dati per calcolare funzioni statistiche. Ora sappiamo come utilizzare un solido apparato di matematica e statistica per dimostrare alcune ipotesi, e come calcolare intervalli di confidenza per variabili arbitrarie dato un campione di dati.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n\n\n**Disclaimer**: \nQuesto documento è stato tradotto utilizzando il servizio di traduzione automatica [Co-op Translator](https://github.com/Azure/co-op-translator). Sebbene ci impegniamo a garantire l’accuratezza, si prega di notare che le traduzioni automatiche possono contenere errori o imprecisioni. Il documento originale nella sua lingua nativa deve essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale effettuata da un traduttore umano. Non siamo responsabili per eventuali fraintendimenti o interpretazioni errate derivanti dall’uso di questa traduzione.\n\n" ] } ], "metadata": { "interpreter": { "hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "coopTranslator": { "original_hash": "0f899e3c5019f948e7c787b22f3b2304", "translation_date": "2026-01-16T13:49:37+00:00", "source_file": "1-Introduction/04-stats-and-probability/notebook.ipynb", "language_code": "it" } }, "nbformat": 4, "nbformat_minor": 4 }