Nell’articolo precedente Pandas: analisi dati con Python [parte 1], abbiamo introdotto Pandas e le sue strutture dati. Ci siamo focalizzati poi su come leggere i dati e manipolarli da diverse sorgenti dato. Ovviamente, la libreria Python è pensata per analizzare i dati. In questo articolo analizzeremo alcuni aspetti dell’analisi dei dati che la libreria ci fornisce. In particolare, discuteremo di come pulire i dati, estrarre statistiche, creare tabelle pivot e graficare i risultati.
Gli esempi che vedremo useranno i dati che abbiamo già scaricato e caricato nell’articolo Pandas: analisi dati con Python [parte 1]. Potete andare a leggerlo se dovete reimpostare l’ambiente di lavoro e/o caricare i dati necessari da Kaggle. Una volta che avete caricato i dati potete iniziare!
Pulizia dei dati
In data science, siamo soliti estrarre informazioni da enormi volumi di dati grezzi. Questi dati grezzi possono contenere valori “sporchi” e incoerenti, il che porta a un’analisi non accurata e a intuizioni poco utili. Nella maggior parte dei casi, le fasi iniziali di acquisizione e pulizia dei dati possono costituire l’80% del lavoro; pertanto, se intendete entrare in questo campo, dovete imparare a gestire i dati “sporchi”.
La pulizia dei dati si concentra principalmente nel rimuove i dati errati, corrotti, non correttamente formattati, duplicati o incompleti all’interno di un dataset.
In questa sezione, ci occuperemo di quanto segue:
- eliminazione delle colonne non necessarie dal dataset
- rimozione dei duplicati
- trovare e sostituire i valori mancanti nel DataFrame
- modificare l’indice del DataFrame
- rinominare le colonne di un DataFrame.
Utilizziamo il dataset del mercato immobiliare di Melbourne importato da Kaggle. Per prima cosa, rimescoliamo il DataFrame per ottenere righe con indici diversi. Per riprodurre ogni volta gli stessi dati rimescolati, utilizziamo un random_state.
shuffled_melbourne_data = melbourne_data.sample(frac=1, random_state=1) # produce the same shuffled data each time
shuffled_melbourne_data
Supponiamo che nel DataFrame si vogliano ottenere informazioni su:
- Quali sono i sobborghi migliori in cui acquistare?
- I sobborghi con un buon rapporto qualità-prezzo?
- Qual è il lato costoso della città?
- Dove comprare un’unità con due camere da letto?
Potremmo aver bisogno di tutte le colonne tranne le seguenti:
- Method
- SellerG
- Date
- Bathroom
- Car
- Landsize
- BuildingArea
- YearBuilt
Pandas ha un metodo drop() per rimuovere queste colonne.
# Define a list names of all the columns we want to drop
columns_to_drop = ['Method',
'SellerG',
'Date',
'Bathroom',
'Car',
'Landsize',
'BuildingArea',
'YearBuilt']
shuffled_melbourne_data.drop(columns = columns_to_drop, inplace=True, axis=1)
shuffled_melbourne_data
Attenzione!
L'opzione (inplace = True) assicura che il metodo non restituisca un nuovo DataFrame, ma cerchi le colonne da rimuovere dal DataFrame dato. Vero significa che la modifica è permanente.
Colonne eliminate!
Trovare e rimuovere i duplicati
Il metodo duplicated() restituisce valori booleani in formato colonna. I valori falsi significano che nessun dato è stato duplicato.
shuffled_melbourne_data.duplicated()
'''
Suburb
Bentleigh East False
...
Balwyn False
Length: 34857, dtype: bool
'''
Questo controllo dei duplicati può essere eseguito per i DataFrames di piccole dimensioni. Per i DataFrames più grandi è impossibile controllare ogni singolo valore. Pertanto, concateneremo il metodo any() a duplicated().
shuffle_melbourne_data.duplicated().any()
# True
Se il metodo any() restituisce True, significa che ci sono dei duplicati nel DataFrame. Per rimuoverli, utilizzeremo il metodo drop_duplicates().
shuffled_melbourne_data.drop_duplicates(inplace=True) # remove dupicates
shuffled_melbourne_data.duplicated().any() # Checking returns False
# False
Trovare e sostituire i valori mancanti nel DataFrame
Per verificare la presenza di valori mancanti, ossia che assumono valore null, sono disponibili quattro metodi. Essi sono:
- isnull(): restituisce un set di dati con valori booleani.
shuffled_melbourne_data.isna()
- isna(): simile a isnull()
- isna().any(): restituisce un formato di colonna di valori booleani
shuffled_melbourne_data.isna().any()
'''
Address False
Rooms False
Type False
Price True
Distance True
Postcode True
Bedroom2 True
CouncilArea True
Lattitude True
Longtitude True
Regionname True
Propertycount True
dtype: bool
'''
- isna().sum(): restituisce un totale in colonna di tutti i nulli disponibili.
shuffled_melbourne_data.isna().sum()
'''
Address 0
Rooms 0
Type 0
Price 7580
Distance 1
Postcode 1
Bedroom2 8202
CouncilArea 3
Lattitude 7961
Longtitude 7961
Regionname 3
Propertycount 3
dtype: int64
'''
I valori mancanti possono essere gestiti in due modi:
- Rimuovendo le righe che contengono i valori mancanti.
In questo caso utilizziamo il metodo dropna() per eliminare le righe. Per gli esempi successivi non elimineremo ancora le righe nel nostro set di dati.
shuffled_melbourne_data.dropna(inplace=True) # removes all rows with null values
- Sostituendo le celle vuote con nuovi valori
Possiamo decidere di sostituire tutti i valori nulli con un valore utilizzando il metodo fillna(). La sintassi è DataFrame.fillna(value, method, axis, inplace, limit, downcast) dove il valore può essere un dizionario che prende i nomi delle colonne come chiave.
shuffled_melbourne_data.fillna({'Price':1435000, 'Distance':13, 'Postcode':3067, 'Bedroom2': 2, 'CouncilArea': 'Yarra City Council'}, inplace=True)
print(shuffled_melbourne_data.isna().sum())
'''
Address 0
Rooms 0
Type 0
Price 0
Distance 0
Postcode 0
Bedroom2 0
CouncilArea 0
Lattitude 7961
Longtitude 7961
Regionname 3
Propertycount 3
dtype: int64
'''
Abbiamo sostituito con successo i valori vuoti specificati delle colonne del dizionario all’interno del metodo fillna().
Modifica dell’indice del DataFrame
Quando si lavora con i dati, di solito si desidera un campo con valore univoco come indice dei dati. Per esempio, nel nostro set di dati cars.json, si può supporre che quando una persona cerca un record di un’auto, probabilmente lo cercherà tramite il suo modello (valori nella colonna Model).
La verifica se la colonna del modello ha valori univoci restituisce True, quindi sostituiamo l’indice esistente con questa colonna usando set_index:
cars = pd.read_json('/content/cars.json', orient=’index’)
print(cars['model'].is_unique)
# True
cars.set_index('model', inplace=True)
Ora è possibile accedere a ciascun record direttamente con loc[].
cars.loc['M3']
'''
price 60500
wiki http://en.wikipedia.org/wiki/Bmw_m3
img 250px-BMW_M3_E92.jpg
Name: M3, dtype: object
'''
Rinominare le colonne di un DataFrame
A volte si può desiderare di rinominare le colonne dei dati per una migliore interpretazione, magari perché alcuni nomi non sono facili da capire. Per farlo, si può usare il metodo rename() del DataFrame e passare un dizionario in cui la chiave è il nome della colonna corrente e il valore è il nuovo nome.
Utilizziamo il DataFrame Melbourne originale, in cui nessuna colonna è stata rimossa. Potremmo voler rinominare:
- Room con No_ofRooms.
- Type con HousingType.
- Method con SaleMethod
- SellerG con RealEstateAgent.
- Bedroom2 con No_ofBedrooms.
# Use initial Melbourne DataFrame
#Create a dictionary of columns to rename. Value is the new name
columns_to_rname = {
'Rooms': 'No_ofRooms',
'Type': 'HousingType',
'Method': 'SaleMethod',
'SellerG': 'RealEstateAgent',
'Bedroom2': 'No_ofBedrooms'
}
shuffled_melbourne_data.rename(columns=columns_to_rename, inplace=True)
shuffled_melbourne_data.head()
Trovare le correlazioni
Pandas dispone di un metodo corr() che ci permette di trovare le relazioni tra le colonne del nostro insieme di dati.
Il metodo restituisce una tabella che rappresenta la relazione tra due colonne. I valori variano da -1 a 1, dove -1 indica una correlazione negativa e 1 una correlazione perfetta. Il metodo ignora automaticamente i valori nulli e i valori non numerici presenti nel set di dati.
shuffled_melbourne_data.corr()
Statistiche descrittive
Esistono diversi metodi in Pandas DataFrames e Series per eseguire statistiche di sintesi. Questi metodi includono: mean, median, max, std, sum e min.
Ad esempio, possiamo trovare la media del prezzo nel nostro DataFrame.
shuffled_melbourne_data['Price'].mean()
# 1134144.8576355954
# Or for all columns
shuffled_melbourne_data.mean()
No_ofRooms 3.031071e+00
Price 1.134145e+06
...
Propertycount 7.570273e+03
dtype: float64
Se vogliamo ottenere una panoramica delle statistiche di base, che comprendono quelle citate in precedenza, per tutti gli attributi dei nostri dati possiamo usare il metodo describe().
shuffled_melbourne_data.describe()
Operazione groupby per dividere, aggregare e trasformare i dati
La funzione groupby() di Pandas consente di riorganizzare i dati. Si limita a dividere i dati in gruppi classificati in base ad alcuni criteri e restituisce una serie di oggetti DataFrame.
Dividere i dati in gruppi
Dividiamo i dati di Melbourne in gruppi in base al numero di camere da letto. Per visualizzare i gruppi, si utilizza l’attributo groups.
grouped_melb_data = shuffled_melbourne_data.groupby('No_ofBedrooms')
shuffled_grouped_melb_data.groups
'''
{1.0: ['Hawthorn', 'St Kilda'], 2.0: ['Bentleigh East', 'Yarraville', 'Richmond', 'Mill Park', 'Southbank', 'Surrey Hills', 'Caulfield North', 'Reservoir', 'Mulgrave', 'Altona', 'Ormond', 'Bonbeach', 'St Kilda', 'South Yarra', 'Malvern', 'Essendon'],
...
30.0: ['Burwood']}}
'''
Poiché il risultato è un dict e i dati sono enormi, il risultato è difficilmente leggibile. Per ovviare a questo problema possiamo usare il metodo keys() per ottenere solamente le chiavi che rappresentano i gruppi.
grouped_melb_data.groups.keys()
# dict_keys([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 16.0, 20.0, 30.0])
Possiamo anche raggruppare più colonne come segue.
grouped_melb_data = shuffled_melbourne_data.head(20).groupby(['No_ofBedrooms', 'Price'])
grouped_melb_data.groups.keys()
'''
dict_keys([(1.0, 365000.0), (1.0, 385000.0), (2.0, 380000.0), (2.0, 438000.0), ... (4.0, 1435000.0)])
Se vogliamo selezionare un gruppo di nostro interesse, utilizzeremo il metodo get_group().
grouped_melb_data = melbourne_data.groupby('No_ofBedrooms')
grouped_melb_data.get_group(2) # select 2 bedrooms group
Dati aggregati
Una funzione aggregata restituisce un singolo valore collettivo per ogni gruppo. Ad esempio, desideriamo calcolare la media dei prezzi di ciascun gruppo. Utilizzeremo la funzione agg.
print(grouped_melb_data['Price'].agg([np.mean]))
'''
mean
No_ofBedrooms
0.0 1.054029e+06
1.0 6.367457e+05
...
30.0 1.435000e+06
'''
Trasformazione
Pandas dispone di un’operazione di trasformazione che viene utilizzata con la funzione groupby(). Ciò permette di riassumere i dati in modo efficace. Quando trasformiamo un gruppo, otteniamo un oggetto indicizzato della stessa dimensione di quello che viene raggruppato. Ad esempio, nel nostro set di dati, possiamo ottenere i prezzi medi per ogni gruppo No_ofBedrooms e combinare i risultati nel nostro set di dati originale per altri calcoli.
Il primo approccio sarebbe quello di cercare di raggruppare i dati in un nuovo DataFrame e combinarli in un processo a più fasi, per poi unire i risultati nel DataFrame originale. Creeremo un nuovo DataFrame con i prezzi medi e lo fonderemo di nuovo con l’originale.
price_means = shuffled_melbourne_data.groupby('No_ofBedrooms')['Price'].mean().rename('MeanPrice').reset_index()
df_1 = shuffled_melbourne_data.merge(price_means)
df_1
Complicato? Allora possiamo usare l’operatore transform per effettuare la stessa operazione in un solo passo.
shuffled_melbourne_data['MeanPrice'] = shuffled_melbourne_data.groupby('No_ofBedrooms')['Price'].transform('mean')
shuffled_melbourne_data
Tabella pivot
Una tabella pivot di Pandas è simile all’operazione groupby. È un’operazione comune per i programmi che utilizzano dati tabellari come i fogli di calcolo. La differenza tra l’operazione groupby e la tabella pivot è che una tabella pivot prende in input dati semplici in colonna e raggruppa le voci in una tabella bidimensionale che fornisce una sintesi multidimensionale dei dati. La sua sintassi è:
pandas.pivot_table(data, values=None, index=None, columns=None, aggfunc=’mean’, fill_value=None, margins=False, dropna=True, margins_name=’All’, observed=False)
Creiamo ora una tabella pivot di esempio.
pivot_tbl = shuffled_melbourne_data.pivot_table(index='HousingType')
pivot_tbl
Cosa abbiamo ottenuto? Il risultato è un nuovo DataFrame, chiamato pivot_tbl, che è raggruppato per HousingType. Poiché non sono stati specificati altri parametri nella funzione, i dati sono stati aggregati in base alla media per ogni colonna con dati numerici, poiché per impostazione predefinita il parametro aggfunc restituisce la media.
Aggregazione dei dati per colonne specifiche
È anche possibile aggregare i dati per colonne specifiche. Nell’esempio che segue, aggreghiamo per le colonne Price e No_ofRooms.
pivot_tbl = shuffled_melbourne_data.pivot_table(index='HousingType', values=['Price', 'No_ofRooms'])
pivot_tbl
Utilizzo dei metodi di aggregazione nella tabella pivot
È possibile modificare il modo in cui i dati vengono aggregati nella tabella pivot. Con i metodi di aggregazione è così possibile eseguire analisi complesse. Per utilizzarli, è necessario specificare la funzione di aggregazione nel parametro aggfunc.
Ad esempio, se vogliamo trovare la somma di tutti i conteggi delle proprietà per ogni regione (attributo Regionname), useremo il seguente codice.
pivot_tbl_sum = shuffled_melbourne_data.pivot_table(index='Regionname', aggfunc='sum', values='Propertycount')
pivot_tbl_sum
Metodi di aggregazione multipli
Per applicare più metodi di aggregazione, è necessario specificare una lista di metodi di aggregazione nel parametro aggfunc. Ad esempio , per trovare la somma e il numero massimo di proprietà per ogni regione il codice sarà il seguente.
pivot_tbl_sum_max = shuffled_melbourne_data.pivot_table(index='Regionname', aggfunc=['sum', 'max'], values='Propertycount')
pivot_tbl_sum_max
Specificare il metodo di aggregazione per ogni colonna
Negli esempi precedenti abbiamo applicato gli stessi metodi di aggregazione ad una o più colonne. Per specificare la funzione di aggregazione di interesse per ciascuna colonna, si può utilizzare un dizionario contenente la colonna come chiave e la funzione di aggregazione come valore. Ecco un esempio.
pivot_tbl = shuffled_melbourne_data.pivot_table(index='Regionname', values=['Price', 'Propertycount'], aggfunc={'Price':'sum', 'Propertycount':'mean'})
pivot_tbl
Dividere i dati nella tabella pivot per colonna con colonne
È possibile aggiungere una colonna alla tabella pivot con il parametro columns. In questo modo i dati vengono divisi orizzontalmente, mentre il parametro index specifica la colonna per divide i dati verticalmente. Nell’esempio seguente, i dati vengono suddivisi in base al tipo di abitazione.
pivot_tbl = shuffled_melbourne_data.pivot_table(
index='Regionname',
values=['Price', 'Propertycount'],
aggfunc={'Price':'sum', 'Propertycount':'mean'},
columns='HousingType'
)
pivot_tbl
Aggiunta di totali a ciascun gruppo
A volte è necessario aggiungere dei totali dopo aver aggregato. Per farlo, si utilizza il parametro margins. Per impostazione predefinita, è impostato su False e la sua etichetta è impostata su ‘All’. Per rinominarlo, bisogna utilizzare il parametro margin_name. Vediamo come possiamo ottenere i totali di tutti i conteggi delle proprietà per ogni regione.
pivot_tbl = shuffled_melbourne_data.pivot_table(
index='Regionname',
values='Propertycount',
margins=True,
margins_name='Totals',
aggfunc='sum',
columns='HousingType')
pivot_tbl
Come avrete notato, il codice della tabella pivot è più semplice e leggibile rispetto all’operazione groupby.
Ordinare i DataFrame
Pandas fornisce un metodo sort_values() per ordinare un DataFrame. I tipi di ordinamento che si possono eseguire sono i seguenti:
- ordine crescente
- ordine decrescente
- ordinamento per più colonne
Ordinamento crescente
Ordiniamo i dati di Melbourne in base al prezzo. Per impostazione predefinita, i dati vengono ordinati in ordine crescente.
shuffled_melbourne_data.sort_values(by=['Price'], inplace=True)
shuffled_melbourne_data.head(3)
Ordinamento decrescente
Per ordinare in ordine decrescente, è sufficiente aggiungere la condizione ascending=False al metodo sort_values(). Ordiniamo i dati di Melbourne per prezzo.
shuffled_melbourne_data.sort_values(by=['Price'], inplace=True, ascending=False)
shuffled_melbourne_data.head(3)
Ordinamento per colonne multiple
Per ordinare su più colonne si deve passare un elenco nel parametro by. L’ordine degli elementi della lista è importante. Infatti, a parità di valore del primo elemento della lista, si ordina in base al secondo attributo e così via. Nell’esempio successivo ordiniamo in base No_ofRooms e Price.
shuffled_melbourne_data.sort_values(by=['No_ofRooms','Price'], inplace=True)
shuffled_melbourne_data.head(3)
Graficare i dati
Per creare dei grafici, Pandas utilizza il metodo plot(). Poiché questo metodo si integra con la libreria Matplotlib, possiamo usare il modulo Pyplot per queste visualizzazioni.
Per prima cosa, importiamo il modulo matplotlib.pyplot e diamogli plt come alias, quindi definiamo alcuni parametri delle figure che verranno generate.
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 25, 'figure.figsize': (50, 8)}) # set font and plot size to be larger
Esistono diverse tipologie di grafici per visualizzare le informazioni di interesse. Di seguito vediamo alcuni esempi che vengono maggiormente usati.
Scatter plot
Lo scopo principale di un diagramma di dispersione è verificare se due variabili sono correlate. In questo esempio proviamo a confrontare No_ofRooms e Price.
shuffled_melbourne_data.plot(kind='scatter', x='No_ofRooms', y='Price', title='Price vs Number of Rooms')
plt.show()
Un grafico a dispersione non è una prova definitiva di una correlazione. Per avere una panoramica delle correlazioni tra le diverse colonne, si può utilizzare il metodo corr() visto in precedenza.
Istogrammi
Gli istogrammi aiutano a visualizzare la distribuzione dei valori in un insieme di dati. Forniscono un’interpretazione visiva dei dati numerici mostrando il numero di oggetti che rientrano in un intervallo di valori specificato (chiamato “bin”).
shuffled_melbourne_data['Price'].plot(kind='hist', title='Pricing')
plt.show()
Grafici a torta
I diagrammi a torta mostrano la percetuali di dati che rientrano inun determinato gruppo rispetto all’intero dataset.
price_count = shuffled_melbourne_data.groupby('Address')['Price'].sum()
print(price_count) #price totals each group
'''
Address
1 Abercrombie St 1435000.0
1 Aberfeldie Wy 680000.0
...
9b Stewart St 1160000.0
Name: Price, Length: 34009, dtype: float64
'''
small_groups = price_count[price_count < 1500000]
large_groups = price_count[price_count > 1500000]
small_sums = pd.Series([small_groups.sum()], index=["Other"])
large_groups = large_groups.append(small_sums)
large_groups.iloc[0:10].plot(kind="pie", label="")
plt.show()
Conversione in CSV, JSON, Excel o SQL
Dopo aver lavorato sui dati, si può decidere di convertire i dati da un formato in un altro. Pandas dispone di metodi per effettuare queste conversioni con la stessa facilità con cui leggiamo i dati.
Convertire JSON in CSV
Per convertire in CSV, si usa il metodo df.to_csv(). Quindi, per convertire il nostro file cars.json, dovremmo procedere in questo modo.
cars = pd.read_json('cars.json', orient='index')
cars_csv = cars.to_csv(r'New path to where new CSV file will be stored\New File Name.csv', index=False) # disable index as we do not need it in csv format.
Convertire CSV in JSON
Convertiamo ora in JSON usando il metodo pd.to_json().
melbourne_data = pd.read_csv('Melbourne_housing_FULL.csv, orient-'index'
melbourne_data.to_json(r'New path to where new JSON file will be stored\New File Name.json')
Esportazione di una tabella SQL Server in CSV
Possiamo anche convertire la tabella SQL in un file CSV:
cars_df = pd.read_sql_query("SELECT * FROM vehicle_data", conn)
cars_df.to_csv(r'New path to where new csv file will be stored\New File Name.csv', index=False)
Convertire CSV in Excel
I file CSV possono essere anche convertiti in Excel:
melbourne_data = pd.read_csv('Melbourne_housing_FULL.csv, orient-'index')
melbourne_data.to_excel(r'New path to where new excel file will be stored\New File Name.xlsx', index=None, header=True)
Convertire Excel in CSV
In modo analogo i file Excel possono essere convertiti in CSV:
stud_data = pd.read_excel('/content/students.xlsx')
stud_data.to_csv(r'New path to where new csv file will be stored\New File Name.csv', index=None, header=True)
Convertire un DataFrame in tabella SQL
Pandas dispone del metodo df.to_sql() per convertire un DataFrame in una tabella SQL.
cars = pd.read_json('cars.json', orient='index')
from sqlalchemy import create_engine #pip install sqlalchemy
engine = create_engine('sqlite://', echo=False)
cars.to_sql(name='Cars', con=engine) # name='Cars' is the name of the SQL table