26. Laboratorio su Misure di Frequenze e Rappresentazione Grafica dei Dati#

In questo Laboratorio, utilizzeremo un semplice dataset di esempio contenente osservazioni relativi a pesi e altezze di diversi soggetti. Il dataset è disponibile al seguente URL: http://antoninofurnari.it/downloads/height_weight.csv.

Carichiamo il dataset mediante la libreria Pandas e visualizziamo alcune informazioni sul DataFrame:

import pandas as pd
data=pd.read_csv('http://antoninofurnari.it/downloads/height_weight.csv')
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4231 entries, 0 to 4230
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   sex     4231 non-null   object 
 1   BMI     4231 non-null   float64
 2   height  4231 non-null   float64
 3   weight  4231 non-null   float64
dtypes: float64(3), object(1)
memory usage: 132.3+ KB

Il dataset contiene \(4321\) osservazioni e \(4\) colonne. In particolare, per ogni soggetto sono riportate le seguenti variabili:

  • Il sesso (sex);

  • Il body mass index (BMI);

  • L’altezza in cm (height);

  • Il peso in Kg (weight). Visualizziamo le prime righe del dataframe:

data.head()
sex BMI height weight
0 M 33.36 187.96 117.933920
1 M 26.54 177.80 83.914520
2 F 32.13 154.94 77.110640
3 M 26.62 172.72 79.378600
4 F 27.13 167.64 76.203456

26.1. Frequenze Assolute e Relative#

Possiamo ottenere i valori univoci in una variabile con la funzione unique():

data['sex'].unique()
array(['M', 'F'], dtype=object)

Le frequenze assolute vengono calcolate mediante il metodo value_counts di Pandas. Visualizziamo ad esempio, le frequenze assolute dei valori di sex:

sex_counts = data['sex'].value_counts()
sex_counts
F    2285
M    1946
Name: sex, dtype: int64

Un buon modo per capire se ha senso calcolare le frequenze assolute per una data variabile, è quello di controllare quanti valori univoci appaiono. Questo si può fare mediante la funzione unique():

print(data['sex'].nunique())
print(data['height'].nunique())
print(data['weight'].nunique())
2
18
172

Notiamo che, mentre sex e height contengono pochi valori, weight ne contiene molti. Controlliamo anche le frequenze assolute di weight:

height_counts = data['height'].value_counts()
height_counts
162.56    435
167.64    391
170.18    377
172.72    355
165.10    351
175.26    302
160.02    291
177.80    272
182.88    260
157.48    259
180.34    235
154.94    152
185.42    146
152.40    130
187.96    104
190.50     72
149.86     52
193.04     47
Name: height, dtype: int64

Il metodo restituisce una Series di Pandas che ha come indici i valori unici delle altezze e come valori le frequenze dei valori unici. Gli elementi della serie sono ordinati per valore (e dunque per frequenza). Sappiamo dunque che il primo elemento della serie è il più frequente, mentre l’ultimo è il meno frequente:

print("Altezza più frequente:",height_counts.iloc[0])
print("Altezza meno frequente:",height_counts.iloc[-1])
Altezza più frequente: 435
Altezza meno frequente: 47

🙋‍♂️ Domanda 1

Scrivendo:

print("Altezza più frequente:",height_counts[0])
print("Altezza meno frequente:",height_counts[-1])

otteniamo lo stesso risultato? Perché?

In genere, torna utile riordinare la serie ottenuta per indice, in modo da ottenere una serie di valori crescente con le relative frequenze.

data['height'].value_counts().sort_index()
149.86     52
152.40    130
154.94    152
157.48    259
160.02    291
162.56    435
165.10    351
167.64    391
170.18    377
172.72    355
175.26    302
177.80    272
180.34    235
182.88    260
185.42    146
187.96    104
190.50     72
193.04     47
Name: height, dtype: int64

26.1.1. Diagramma a Barre delle Frequenze Assolute#

Le frequenze dei dati possono essere rappresentate graficamente mediante un grafico a barre utilizzando le funzionalità di plot di Pandas. Tali funzionalità si appoggiano a matplotlib, per cui, per avere maggiore controllo su di esse dobbiamo importare pyplot:

from matplotlib import pyplot as plt
data['height'].value_counts().sort_index().plot.bar(figsize=(18,6))
plt.grid()
plt.show()
../_images/db319056669d16263e9f0daa51151895cbe4b84ed48571a84a130f0bc8e654db.png

dove il parametro figsize=(12,6) serve a specificare le dimensioni del plot, mentre plt.grid() serve a mostrare una griglia sovrapposta al plot.

Il grafico ottenuto è detto digramma a barre delle frequenze assolute e mostra il numero di volte in cui ciascun valore appare nel campione.

🙋‍♂️ Domanda 2

Che cosa ci dice la rappresentazione dei dati sopra? Quali sono i valori più frequenti? Quante persone hanno un’altezza pari a \(167.64\ cm\)?

Si noti che, alternativamente, è possibile plottare un grafico a barre direttamente tramite matplotlib come segue:

plt.figure(figsize=(18,6))
height_counts = data['height'].value_counts().sort_index()
plt.bar(height_counts.index, height_counts.values)
plt.xticks(height_counts.index, rotation='vertical')
plt.grid()
plt.show()
../_images/063a99b14af493b5508e3ad00c87d10cb3d5074b4bcabc0e32d2d8e500e56d92.png

In molti casi sarà possibile utilizzare le funzioni compatte messe a disposizione da Pandas, mentre in altri sarà necessario utilizzare matplotlib esplicitamente.

26.1.2. Frequenze Relative#

Possiamo calcolare le frequenze relative dividendo quelle assolute per il numero di elementi contenuti nel dataset:

data['height'].value_counts().sort_index()/len(data['height'].dropna())
149.86    0.012290
152.40    0.030726
154.94    0.035925
157.48    0.061215
160.02    0.068778
162.56    0.102813
165.10    0.082959
167.64    0.092413
170.18    0.089104
172.72    0.083905
175.26    0.071378
177.80    0.064287
180.34    0.055542
182.88    0.061451
185.42    0.034507
187.96    0.024580
190.50    0.017017
193.04    0.011108
Name: height, dtype: float64

O, alternativamente, specificando normalize=True (dividere per la somma degli elementi è un forma di normalizzazione) come parametro per value_counts:

data['height'].value_counts(normalize=True).sort_index()
149.86    0.012290
152.40    0.030726
154.94    0.035925
157.48    0.061215
160.02    0.068778
162.56    0.102813
165.10    0.082959
167.64    0.092413
170.18    0.089104
172.72    0.083905
175.26    0.071378
177.80    0.064287
180.34    0.055542
182.88    0.061451
185.42    0.034507
187.96    0.024580
190.50    0.017017
193.04    0.011108
Name: height, dtype: float64

Come si può vedere, tutti i numeri sono compresi tra zero e uno. Inoltre, la somma dei valori è pari a 1:

print("{:0.2f}".format(data['height'].value_counts(normalize=True).sort_index().sum()))
1.00

26.1.2.1. Diagramma a Barre delle Frequenze Relative#

Possiamo ottenere un diagramma a barre delle frequenze relative come segue:

(data['height'].value_counts(normalize=True).sort_index()).plot.bar(figsize=(18,6))
plt.grid()
plt.show()
../_images/fb7fe494f64a6d081b3a2ba06a6f12c7e7a1774559ef37a62230cbbe27c2a9aa.png

🙋‍♂️ Domanda 3

Cosa hanno in comune (graficamente) il diagramma a barre delle frequenze assolute e il diagramma a barre delle frequenze relative? Che cosa ci dice in più una rappresentazione rispetto all’altra?

I diagrammi a barre delle frequenze relative possono essere utili anche per confrontare diversi campioni di dati. Consideriamo ad esempio le altezze delle donne e degli uomini come due campioni separati nel dataset e calcoliamone le frequenze relative:

pmf_height_m = data[data['sex']=='M']['height'].value_counts(normalize=True).sort_index()
pmf_height_f = data[data['sex']=='F']['height'].value_counts(normalize=True).sort_index()

Mostriamo i due istogrammi mettendoli a confronto. Per poter mostrare i due istogrammi agevolemente nello stesso grafico, utilizzeremo matplotlib:

plt.figure(figsize=(18,6))
#sommiamo e sottraiamo 0.2 agli indici per "spostare" le barre e renderle
#visibili quando sovrappose. Inoltre impostiamo alpha=0.9 per rendere le barre
#parzialmente trasparenti
plt.bar(pmf_height_m.index+0.2, pmf_height_m.values, width=1, alpha=0.9)
plt.bar(pmf_height_f.index-0.2, pmf_height_f.values, width=1, alpha=0.9)
plt.xticks(data['height'].unique(), rotation='vertical')
plt.legend(['M','F']) #mostriamo una legenda
plt.grid()
plt.show()
../_images/f94af1b62863a84c9726a2c97bd169cfeb04b93d092762639f93b1fb1c518492.png

🙋‍♂️ Domanda 4

Cosa ci dicono i due plot? Quale delle due classi (‘F’ o ‘M’) contiene i soggetti più bassi? Esistono soggetti di sesso femminile alti? Esistono soggetti di sesso maschile bassi? Quanto sono “probabili”?

26.2. Empirical Cumulative Distribution Function (ECDF)#

Possiamo calcolare una ECDF mediante la funzione cumsum, che, data una serie di valori calcola ad ogni posizione \(i\) la somma dei primi \(i\) valori. Ad esempio:

a = pd.Series([1,3,6,8,9])
a.cumsum()
0     1
1     4
2    10
3    18
4    27
dtype: int64

Dal momento che le frequenze relative sono ordinate, la funzione cumsum calcola per ogni indice la somma delle frequenze relative dei valori precedenti. Calcoliamo le ECDF dei due campioni e confrontiamoli:

ecdf_weight_m = data[data['sex']=='M']['weight'].value_counts(normalize=True).sort_index().cumsum()
ecdf_weight_f = data[data['sex']=='F']['weight'].value_counts(normalize=True).sort_index().cumsum()

plt.figure(figsize=(12,8))
plt.plot(ecdf_weight_m.index, ecdf_weight_m.values)
plt.plot(ecdf_weight_f.index, ecdf_weight_f.values)
plt.legend(['M','F'])
plt.grid()
plt.show()
../_images/add401108f55afc165346db5193c0ccdd250338af33e01c123ee443913f9979d.png

🙋‍♂️ Domanda 5

Il confronto è adesso più chiaro? Quale dei due campioni contiene i pesi maggiori?

Le ECDF tornano utili per verificare graficamente se due fenomeni hanno distribuzioni simili. Prendiamo ad esempio il dataset “Titanic”, che riporta diversi dati sui passeggeri del Titanic:

titanic = \
pd.read_csv('https://raw.githubusercontent.com/agconti/kaggle-titanic/master/data/train.csv',
                     index_col='PassengerId')

Mostriamo le informazioni sul dataset:

titanic.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 1 to 891
Data columns (total 11 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Survived  891 non-null    int64  
 1   Pclass    891 non-null    int64  
 2   Name      891 non-null    object 
 3   Sex       891 non-null    object 
 4   Age       714 non-null    float64
 5   SibSp     891 non-null    int64  
 6   Parch     891 non-null    int64  
 7   Ticket    891 non-null    object 
 8   Fare      891 non-null    float64
 9   Cabin     204 non-null    object 
 10  Embarked  889 non-null    object 
dtypes: float64(2), int64(4), object(5)
memory usage: 83.5+ KB

Confrontiamo adesso le distribuzioni delle età dei passeggeri di sesso maschile e femminile:

a=titanic[titanic['Sex']=='male']['Age'].value_counts(normalize=True).sort_index().cumsum()
b=titanic[titanic['Sex']=='female']['Age'].value_counts(normalize=True).sort_index().cumsum()

plt.figure(figsize=(12,8))
plt.plot(a.index,a.values)
plt.plot(b.index,b.values)
plt.grid()
plt.legend(['M','F'])
plt.show()
../_images/d76afe7e837cc4da67844df83f70052c8495a537fb1d8e9334fc98a34b5659a5.png

Com’è possibile vedere, in questo caso le due curve hanno delle forme molto simili. Ciò suggerisce che i due fenomeni obbediscono a simili densità di probabilità. Tuttavia è possibile notare che le donne tendono ad essere più giovani degli uomini.

26.3. Istogrammi#

Possiamo plottare un istogramma con matplotlib come segue:

plt.figure(figsize=(12,6))
_,edges,_=plt.hist(data['weight'], width=10.8)
plt.xticks(edges)
plt.grid()
plt.show()
../_images/96852a9fb1a05a895ac7f189e8f5d2eff3aef69f753664e4172f0d2366a3561e.png

Oltre a graficare l’istogramma, la funzione plt.hist restituisce tre valori:

  • un vettore contenente i valori dell’istogramma;

  • i “bordi” di ogni bin;

  • una lista di handle agli elementi grafici visualizzati.

Nell’esempio sopra abbiamo utilizzato la notazione _,edges,_=... per scartare il primo e l’ultimo valore e ottenere i “bordi” dell’istogramma.

26.3.1. Struges e Rice#

Vediamo come rappresentare gli istogrammi scegliendo i numeri di bin con Struges e Rice:

import numpy as np
bins_struges=int(3.3*np.log(len(data['weight'])))
bins_rice=int(2*len(data['weight'])**(1/3))

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.title('Struges ({} bins)'.format(bins_struges))
plt.hist(data['weight'], bins=bins_struges)
plt.grid()
plt.subplot(1,2,2)
plt.title('Rice ({} bins)'.format(bins_rice))
plt.grid()
plt.hist(data['weight'], bins=bins_rice)
plt.show()
../_images/befa4570ca68be6b9e18c54ec9159b2088117211e5a44137ca8b209e3a571956.png

E’ possibile anche specificare i “bordi” dei bin esplicitamente:

plt.figure(figsize=(12,6))
_,edges,_=plt.hist(data['weight'], bins=[40,50,60,70,80,90,100,110,120,130], 
                   width=9.5) #costruiamo un istogramma con i bin definiti
plt.xticks(edges)
plt.grid()
plt.show()
../_images/9fe7020c5685339704d5ed2e621ba3f51ad3b69311e5c7706cd50b8b3163261f.png

L’istogramma mostrato sopra riporta le frequenze assolute per ogni bin e ci permette di rispondere a domande del genere “quanti soggetti hanno un peso compreso tra \(70 Kg\) e \(80 Kg\)”?

🙋‍♂️ Domanda 6

Si costruiscano degli istogrammi per rispondere alle domande

  • quanti soggetti di sesso maschile hanno un peso compreso tra \(70Kg\) e \(80Kg\)?

  • quanti soggetti di sesso femminile hanno un peso compreso tra \(60Kg\) e \(65Kg\)?

26.3.2. Istogrammi di densità#

Per ottenere un istogramma di densità, dobbiamo passare il parametro density=True alla funzione hist:

plt.figure(figsize=(12,6))
valori,bordi,_=plt.hist(data['weight'], bins=[40,50,60,70,80,90,100,110,120,130], 
                   width=9.5, density=True)
plt.xticks(bordi)
plt.grid()
plt.show()
../_images/171be5af468ad6176571ac48d5cff228fe48e929aba5779e7e7655898dab1267.png

Possiamo verificare che la condizione

\[ \sum_{i=0}^{n} b_i \cdot w_i = 1 \]

sia effettivamente soddisfatta:

larghezze = bordi[1:]-bordi[:-1]
print(larghezze) #tutti i bin hanno la stessa larghezza
print((larghezze*valori).sum()) #colcoliamo la somma dei prodotti
[10 10 10 10 10 10 10 10 10]
1.0

🙋‍♂️ Domanda 7

Utilizzare l’istogramma sopra per ottenere la probabilità che un soggetto abbia un peso compreso tra \(40Kg\) e \(50Kg\).

Gli istogrammi possono essere plottati anche direttamente da DataFrame:

data['weight'].plot.hist(figsize=(12,6), width=9.5, 
                         density='True',
                         bins=[40,50,60,70,80,90,100,110,120])
plt.grid()
plt.show()
../_images/61187643bf1eb10f989b0633d28eb44191fdb2a2d164706a1284ba6aedae0da8.png

La funzione prende gli stessi parametri in input della funzione hist di matplotlib. Tuttavia, a differenza di essa, non restituisce i valori dei bin e i rispettivi bordi.

26.3.3. Stima della densità#

Possiamo stimare e plottare le densità mediante Pandas come segue:

data['weight'].plot.density(figsize=(12,6))
plt.grid()
plt.show()
../_images/8e0cfabda7b0b55e7f8726ca8b37c4f114b746762cfaf09ccb02d798faaeef10.png

🙋‍♂️ Domanda 8

Si confronti il plot di densità visto sopra con istogrammi ottenuti con diversi numeri di bin. Qual è il vantaggio principale della stima della densità?

26.3.4. Confrontare campioni mediante istogrammi#

Gli istogrammi possono essere utili per comparare campioni. Confrontiamo ad esempio gli istogrammi del campione dei pesi degli uomini e del campione dei pesi delle donne:

plt.figure(figsize=(12,6))
plt.hist(data[data['sex']=='F']['weight'], width=7, alpha=0.9)
plt.hist(data[data['sex']=='M']['weight'], width=7, alpha=0.9)
plt.legend(['F','M'])
plt.grid()
plt.show()
../_images/3bc530579fb3ded776292992f96d9b06f596d48a5eb8f3a84dcd2d95fe42ba3c.png

🙋‍♂️ Domanda 9

Si metta in relazione il plot dei due istogrammi con il plot delle dure relative ECDF. I due plot sono concordi? Quali dei due plot è più immediato?

È possibile effettuare plot combinati simili direttamente da Pandas utilizzando il groupby:

data.groupby('sex')['weight'].plot.hist(width=7, alpha=0.9, density=True, figsize=(12,6))
data.groupby('sex')['weight'].plot.density()
plt.legend()
plt.grid()
plt.show()
../_images/62b5e7b391d6f0a887c6dafee2358fd5e5f7c5a9996e04832f52e4b2cd5686d3.png

26.4. Altre Visualizzazioni dei Dati#

Abbiamo visto che i grafici a barre possono essere utilizzati per graficare le frequenze assolute e relative dei dati. Vediamo adesso alcune altre visualizzazioni che fanno uso di grafici a barre e grafici a torta per confrontare diverse quantità. Per iniziare a discutere di visualizzazioni, utilizzeremo il dataset Titanic caricato precedentemente.

Mostriamo le prime righe del dataset:

titanic.head()
Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

26.4.1. Barplot#

Oltre a mostrare le frequenze dei dati, i barplot possono essere utili per confrontare diverse quantità numeriche. Supponiamo di voler confrontare il numero di donne e uomini a bordo del Titanic. Possiamo calcolare questi numeri mediante un groupby:

titanic.groupby('Sex')['Sex'].count()
Sex
female    314
male      577
Name: Sex, dtype: int64

Possiamo confrontare queste due quantità visivamente mediante un barplot come segue:

titanic.groupby('Sex')['Sex'].count().plot.bar()
plt.ylabel('Number of Passengers')
plt.show()
../_images/3fe495ba5e2542dd652633e8e23191cd9b64716b2b129c1fcf199418b36dd2aa.png

Confrontiamo adesso le età medie dei passeggeri imbarcati nelle varie classi. Costruiamo prima una tabella riassuntiva mediante groupby:

titanic.groupby('Pclass')['Age'].mean()
Pclass
1    38.233441
2    29.877630
3    25.140620
Name: Age, dtype: float64

Possiamo confrontare le quantità ottenute graficamente mediante barplot:

titanic.groupby('Pclass')['Age'].mean().plot.bar()
plt.ylabel('Age')
plt.show()
../_images/2da36afaff2bc22c3f059fb736a11c02e1d553b0f3312d69885d34bba77aeebc.png

Calcoliamo il numero di sopravvissuti per classe di imbarco:

pd.crosstab(titanic['Pclass'], titanic['Survived'])
Survived 0 1
Pclass
1 80 136
2 97 87
3 372 119

Per ottenere delle percentuali, possiamo normalizzare per righe come segue:

pd.crosstab(titanic['Pclass'], titanic['Survived'], normalize='index')
Survived 0 1
Pclass
1 0.370370 0.629630
2 0.527174 0.472826
3 0.757637 0.242363

Ogni riga della tabella riporta la percentuale di sopravvissuti (1) e la percentuale di non sopravvissuti (0). Visualizziamo un grafico a barre della tabella:

pd.crosstab(titanic['Pclass'], titanic['Survived'], normalize='index').plot.bar()
plt.ylabel('Survived %')
plt.show()
../_images/313e14c030c1f8eb88c9f6c3401d6232cd511dba025b100390344cc3b7d6ef05.png

Un grafico spesso utile a confrontare grandezze complementari (come nel caso delle percentuali viste sopra) è lo “stacked bar plot”. Per ottenere questo grafico bisogna passare stacked=True al metodo bar:

pd.crosstab(titanic['Pclass'], titanic['Survived'], normalize='index').plot.bar(stacked=True)
plt.ylabel('%')
plt.show()
../_images/2fad7d720d6577b5a653d0b2e4c8f4082b908e294573c98b6c48976181534258.png

Possiamo mettere in relazione anche più variabili. Ad esempio:

pd.crosstab([titanic['Pclass'], titanic['Sex']], titanic['Survived'], normalize='index')
Survived 0 1
Pclass Sex
1 female 0.031915 0.968085
male 0.631148 0.368852
2 female 0.078947 0.921053
male 0.842593 0.157407
3 female 0.500000 0.500000
male 0.864553 0.135447

Il relativo plot sarà:

pd.crosstab([titanic['Pclass'], titanic['Sex']], titanic['Survived'], normalize='index').plot.bar(stacked=True)
plt.ylabel('%')
plt.show()
../_images/d8cec47bded2cd9366cd8e014d42489b2ddb633fafa118b79190f565ed8b5649.png

🙋‍♂️ Domanda 10

Si confrontino mediante un barplot le età medie dei passeggeri per classe di imbarco e sesso.

Così come per i barplot, è possibile confrontare gli istogrammi di diversi campioni di dati. Confrontiamo le distribuzioni delle età distinte per sesso:

titanic.groupby('Sex')['Age'].plot.hist(alpha=0.7)
#alpha=0.7 serve a mostrare gli istogrammi come semi-trasparenti
plt.legend()
plt.show()
../_images/28b8eb8f08e98ccaf50eeeba70b74f5d2e2cc79058fbd09db3b24ff0333a35e8.png

In maniera simile, si possono confrontare densità:

titanic.groupby('Sex')['Age'].plot.density()
plt.legend()
plt.show()
../_images/918c2d12718990b2b88123e2c4727b4882cd23e69e60cef3dc9fca4703d8ed34.png

26.4.2. Grafici a Torta#

I grafici a torta vengono utilizzati per confrontare diverse quantità senza imporre un ordinamento a priori. Supponiamo ad esempio di voler confrontare il numero di sopravvissuti in rispetto ai due sessi. Iniziamo ottenendo una tabella che riassume i numeri da confrontare:

Possiamo plottare un grafico a torta come segue:

titanic.groupby('Sex')['Survived'].sum()
Sex
female    233
male      109
Name: Survived, dtype: int64
titanic.groupby('Sex')['Survived'].sum().plot.pie()
plt.xlabel('Sex')
plt.axis('equal')
plt.show()
../_images/0aeecd2ffe7cb044d86a7937f7014eec52a2a4ce4af65c67475af11597f1889a.png

🙋‍♂️ Domanda 11

Costruire un grafico a torta che confronti il numero di sopravvissuti nelle varie classi di imbarco.

26.5. Esercizi#

🧑‍💻 Esercizio 1

Considerando il dataset Titanic, si mostrino i diagrammi a barre delle frequenze assolute e relative dei valori della colonna Pclass. Scegliendo a caso tra i passeggeri, qual è la probabilità che egli si sia imbarcato in seconda classe?

🧑‍💻 Esercizio 2

Considerando il dataset Titanic, si mostri l’istogramma delle età dei passeggeri imbarcati in prima classe. Si utilizzi un criterio opportuno per scegliere il numero di bin.

🧑‍💻 Esercizio 3

Si modifichi il plot dell’esercizio precedente per costruire un istogramma che permetta di rispondere alla domanda “se scelgo casualmente un passeggero imbarcato in prima classe, qual è la probabilità che la sua età sia compresa tra \(20\) e \(30\) anni?

🧑‍💻 Esercizio 4

Considerando il dataset Titanic, si confrontino le distribuzioni cumulative delle età dei passeggeri imbarcati nelle varie classi. Le distribuzioni sono simili? In quale classe sono imbarcati i soggetti più giovani?

🧑‍💻 Esercizio 5

Considerando il dataset Titanic, si mostrino con uno stacked bar plot le percentuali di passeggeri appartenenti alle tre classi separatamente per i due sessi. Si notano differenze nella ripartizione?