Created on 24 Mar 2020 ;    Modified on 24 Mar 2020

Python Pandas: sorpresa!

In questi giorni, per analizzare alcuni andamenti dei dati della epidemia di Coronavirus COVID-19, sto utilizzando Pandas. E mi è capitato di osservare un comportamento sconcertante ...

Premessa

Per chi, come me, non è un professionista nell'analisi dei dati statistici, Pandas può essere un illustre sconosciuto. Al più può ricordare per assonanza il famoso e simpatico orso, simbolo cinese, e icona del WWF.

Chi invece si occupa di analisi dei dati, sa bene che Pandas è una delle librerie di analisi dati più utilizzate. Scritta in Python, si appoggia alle librerie di calcolo algebrico NumPy e di grafica matplotlib.

Le strutture dati utilizzate sono due, relativamente semplici. La Series: una estensione dell'array monodimensionale NumPy; e il DataFrame: una estensione dell'array bidimensionale di NumPy.

Il suo utilizzo non è del tutto banale [1], ma non è neanche impossibile da utilizzare.

La domanda

Bene. Lungi dal voler scrivere un tutorial sull'uso di Pandas (non sarei in grado), in questi giorni, utilizzandola mi sono imbattuto in questo comportamento.

Supponiamo di avere il seguente DataFrame di nome dfr:

    col1      col2    col3
0    c11     pippo     c13
1    c21       c22     c23
2    c31     pippo     c33

ovvero tre righe (le righe 0, 1, e 2) e tre colonne (le colonne col1, col2, col3).

Ora, supponiamo di voler sostituire i valori pippo, presenti in due celle di col2, con il valore pluto.

Per individuare le celle, posso usare la condizione (abbastanza intuitiva): dfr['col2'] == 'pippo'. Ovvero dammi ciò che in col2 ha valore pippo. Se applico questa a dfr.loc[], ottengo le due righe che contengono pippo; ad esempo, referenziandole con dfl:

> dfl = dfr.loc[dfr['col2'] == 'pippo']
> dfl
    col1      col2   col3
0    c11     pippo    c13
2    c31     pippo    c33

e se a dfl assegno pluto, ottengo la sostituzione, ma solo in dfl, non in dfr:

> dfl['col2'] = 'pluto'
> dfl
    col1      col2   col3
0    c11     pluto    c13
2    c31     pluto    c33
> dfr
    col1      col2     col3
0    c11     pippo      c13
1    c21       c22      c23
2    c31     pippo      c33

Come si vede, dfl è cambiata, e dfr no. Bene, diremo noi, che problema c'è? Significa che dfr[dfr['col2'] == 'pippo'] crea una copia dei dati. E quando effettuiamo la sostituzione l'originale non viene modificato. Mi sta bene.

Ore consideriamo quest'altro scenario:

> dfr.loc[dfr['col2'] == 'pippo', 'col2'] = 'pluto'
> dfr
    col1      col2     col3
0    c11     pluto      c13
1    c21       c22      c23
2    c31     pluto      c33

ovvero, usando la stessa funzione loc, ma con due indici, il comportamento è radicalmente diverso. In questo caso la loc non mi crea una copia dell'area individuata, ma opera direttamente sull'originale.

Questa differenza di comportamento non mi è piaciuta.


[1]Personalmente, ritengo che R sia pensata e organizzata meglio. Ma, essendo un fanatico di Python, continuo a ripetermi che le mie difficoltà con Pandas sono dovute alla scarsa dimestichezza, perché la uso raramente e con notevoli gap temporali.