Pandas : pour gérer des tables excels - lignes - colonnes¶

1 : Création¶

Création des colonnes : les séries¶

In [1]:
import numpy as np
import pandas as pd
print(pd.__version__)
1.4.2
In [2]:
series1 = pd.Series([10, 20, 30, 40])
print(series1)
0    10
1    20
2    30
3    40
dtype: int64
In [3]:
l=series1.to_list()
print(l)
[10, 20, 30, 40]
In [4]:
a=np.arange(10,15)
series2 = pd.Series(a, index = [1, 3, 5, 7, 9]) 
print(series2)
1    10
3    11
5    12
7    13
9    14
dtype: int64
In [5]:
l=series2.to_list()
print(l)
[10, 11, 12, 13, 14]
In [6]:
# on a une "colonne" d'indidus identifiés par leur nom (<=> id)
prenoms = ['liz', 'bob', 'bill', 'eve'] 

# on crée des colonnes à ajouter à cette colonne d'individu
age    = pd.Series([25,   30,  35,  40], index = prenoms)
taille = pd.Series([160, 175, 170, 180], index = prenoms)
sexe   = pd.Series(list('bhff'),         index = prenoms)

#Les sequences doivent toutes avoir la même size sinon ça bogue
print(len(prenoms), len(age), len(taille), len(sexe))
4 4 4 4

Création de la table excel : le dataframe¶

In [7]:
df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})

# On récupère une table excel : 
#
#         age     taille  sexe
# liz     25      160     f
# bob     30      175     h
# bill    35      170     h
# eve     40      180     f

df
Out[7]:
age taille sexe
liz 25 160 b
bob 30 175 h
bill 35 170 f
eve 40 180 f
In [8]:
## Création plus subtile : NaN et colonne constante
In [9]:
age = pd.Series([30, 20, 50],       index=['alice', 'bob',  'julie'])
height = pd.Series([150, 170, 168], index=['alice', 'marc', 'julie'])
df2 = pd.DataFrame({'age': age, 'height': height, 'city': 'Nice'})
print(df2)
        age  height  city
alice  30.0   150.0  Nice
bob    20.0     NaN  Nice
julie  50.0   168.0  Nice
marc    NaN   170.0  Nice

2 : Analyse du dataframe :¶

index, columns, values¶

In [10]:
# on recharge pour éviter les ennuis
df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})

print(df.index)    # index des lignes
print(df.columns)  # index des colonnes
print(df.values)   # toutes les valeurs
print(df.shape)    # format du tableau numpy
print(type(df.values))
Index(['alice', 'bill', 'bob', 'eve', 'julie', 'liz'], dtype='object')
Index(['age', 'taille', 'sexe'], dtype='object')
[[30.0 nan nan]
 [nan 170.0 'f']
 [20.0 175.0 'h']
 [nan 180.0 'f']
 [50.0 nan nan]
 [nan 160.0 'b']]
(6, 3)
<class 'numpy.ndarray'>

Un Index pandas est une séquence immutable (non modifiable)¶

c'est aussi un tableau numpy¶

In [11]:
print(df.index[0]) #liz
# df.index[0]="coucou" : bogue ! c'est immutable
print(df.index*2) # opération vectorisé sur un tableau numpy
a=np.array([1,2]).reshape(2,1)
df.index*a
alice
Index(['alicealice', 'billbill', 'bobbob', 'eveeve', 'juliejulie', 'lizliz'], dtype='object')
Out[11]:
Index([('alice', 'bill', 'bob', 'eve', 'julie', 'liz'), ('alicealice', 'billbill', 'bobbob', 'eveeve', 'juliejulie', 'lizliz')], dtype='object')

3 : Appercu du dataframe (du tableau excel) :¶

2 lignes du haut (4 lignes si rien)¶

In [12]:
df.head(2)
Out[12]:
age taille sexe
alice 30.0 NaN NaN
bill NaN 170.0 f

2 lignes du bas (4 lignes si rien)¶

In [13]:
df.tail(2)
Out[13]:
age taille sexe
julie 50.0 NaN NaN
liz NaN 160.0 b

Statistiques générales¶

In [14]:
df
Out[14]:
age taille sexe
alice 30.0 NaN NaN
bill NaN 170.0 f
bob 20.0 175.0 h
eve NaN 180.0 f
julie 50.0 NaN NaN
liz NaN 160.0 b
In [15]:
df.describe()
Out[15]:
age taille
count 3.000000 4.000000
mean 33.333333 171.250000
std 15.275252 8.539126
min 20.000000 160.000000
25% 25.000000 167.500000
50% 30.000000 172.500000
75% 40.000000 176.250000
max 50.000000 180.000000
In [16]:
df.describe(include='all')
# unique : nombre de valeurs possibles
# freq : frequence la plus élevée
# top : valeur de la fréquence la plus élevée
Out[16]:
age taille sexe
count 3.000000 4.000000 4
unique NaN NaN 3
top NaN NaN f
freq NaN NaN 2
mean 33.333333 171.250000 NaN
std 15.275252 8.539126 NaN
min 20.000000 160.000000 NaN
25% 25.000000 167.500000 NaN
50% 30.000000 172.500000 NaN
75% 40.000000 176.250000 NaN
max 50.000000 180.000000 NaN

Echanger les lignes et les colonnes: transposé : T¶

In [17]:
df.T
Out[17]:
alice bill bob eve julie liz
age 30.0 NaN 20.0 NaN 50.0 NaN
taille NaN 170.0 175.0 180.0 NaN 160.0
sexe NaN f h f NaN b

4 : Accès aux données¶

In [18]:
# on recharge pour éviter les ennuis
df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})

df
Out[18]:
age taille sexe
alice 30.0 NaN NaN
bill NaN 170.0 f
bob 20.0 175.0 h
eve NaN 180.0 f
julie 50.0 NaN NaN
liz NaN 160.0 b

une ligne¶

In [19]:
# on recharge pour éviter les ennuis
df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})
df
Out[19]:
age taille sexe
alice 30.0 NaN NaN
bill NaN 170.0 f
bob 20.0 175.0 h
eve NaN 180.0 f
julie 50.0 NaN NaN
liz NaN 160.0 b
In [20]:
# avec un loc : toute la ligne
a=df.loc['liz'] # la ligne de liz : accès par l'index nommée (le label de ligne)
print(a)
print('## taille: ', a['taille'])
print(a[0])
age         NaN
taille    160.0
sexe          b
Name: liz, dtype: object
## taille:  160.0
nan
In [21]:
# Avec un loc : ligne et colonne : le bon usage
a=df.loc['liz', 'taille'] # la ligne de liz et son age : l
print(a)
print(type(a))
160.0
<class 'numpy.float64'>
In [22]:
print(df)
df.iloc[1] # accès par l'index numéroté automatique : on perd l'index nommé
        age  taille sexe
alice  30.0     NaN  NaN
bill    NaN   170.0    f
bob    20.0   175.0    h
eve     NaN   180.0    f
julie  50.0     NaN  NaN
liz     NaN   160.0    b
Out[22]:
age         NaN
taille    170.0
sexe          f
Name: bill, dtype: object

plusieurs lignes : slicing¶

In [23]:
df.loc['bill':'eve'] # les lignes de bill à eve compris
Out[23]:
age taille sexe
bill NaN 170.0 f
bob 20.0 175.0 h
eve NaN 180.0 f
In [24]:
df[['taille', 'age']]
Out[24]:
taille age
alice NaN 30.0
bill 170.0 NaN
bob 175.0 20.0
eve 180.0 NaN
julie NaN 50.0
liz 160.0 NaN

une colonne, avec son index¶

In [25]:
print(df.loc[:,'taille']) # slicing
print(type(df.loc[:,'taille']))
alice      NaN
bill     170.0
bob      175.0
eve      180.0
julie      NaN
liz      160.0
Name: taille, dtype: float64
<class 'pandas.core.series.Series'>
In [26]:
df.loc[:,'taille'].index
Out[26]:
Index(['alice', 'bill', 'bob', 'eve', 'julie', 'liz'], dtype='object')
In [27]:
df.loc[:,'taille'].values
Out[27]:
array([ nan, 170., 175., 180.,  nan, 160.])

sélection de lignes et de colonnes par slicing¶

In [28]:
df.loc['bill':'eve','taille':] # slicing
Out[28]:
taille sexe
bill 170.0 f
bob 175.0 h
eve 180.0 f
In [29]:
df.loc['bill'::2,'taille'] # slicing
Out[29]:
bill    170.0
eve     180.0
liz     160.0
Name: taille, dtype: float64

sélection par test : c'est de l'indexation avancée numpy classique¶

In [30]:
print(df.loc[:,'age']<32)     # slicing + comparaison vectorisée : on récupère un masque tableau de booléens
df.loc[df.loc[:,'age']<32 ]   # le masque permet de filtrer le tableau de départ
alice     True
bill     False
bob       True
eve      False
julie    False
liz      False
Name: age, dtype: bool
Out[30]:
age taille sexe
alice 30.0 NaN NaN
bob 20.0 175.0 h

5 : Statistiques¶

Par colonne¶

In [31]:
# on recharge pour éviter les ennuis
df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})
# Quel est la moyenne de tous les âges
# A noter que le mean laisser passer les NaN
c = df.loc[:, 'age']
m = c.mean()
print(type(c))
print(c)
print(f"L'âge moyen est de {m:.1f} ans.") # f string python
<class 'pandas.core.series.Series'>
alice    30.0
bill      NaN
bob      20.0
eve       NaN
julie    50.0
liz       NaN
Name: age, dtype: float64
L'âge moyen est de 33.3 ans.

6 : Modification du tableau excel¶

Création d'un id numéroté¶

In [32]:
print(df)
df=df.reset_index() # prend l'index et le met dans une colonne et ajoute un id numéroté
df
        age  taille sexe
alice  30.0     NaN  NaN
bill    NaN   170.0    f
bob    20.0   175.0    h
eve     NaN   180.0    f
julie  50.0     NaN  NaN
liz     NaN   160.0    b
Out[32]:
index age taille sexe
0 alice 30.0 NaN NaN
1 bill NaN 170.0 f
2 bob 20.0 175.0 h
3 eve NaN 180.0 f
4 julie 50.0 NaN NaN
5 liz NaN 160.0 b

Renommer un attribut¶

In [33]:
df = df.rename(columns={'index': 'prenom'})
df
Out[33]:
prenom age taille sexe
0 alice 30.0 NaN NaN
1 bill NaN 170.0 f
2 bob 20.0 175.0 h
3 eve NaN 180.0 f
4 julie 50.0 NaN NaN
5 liz NaN 160.0 b
In [34]:
df = df.rename(columns={'taille': 'mesure'})
df
Out[34]:
prenom age mesure sexe
0 alice 30.0 NaN NaN
1 bill NaN 170.0 f
2 bob 20.0 175.0 h
3 eve NaN 180.0 f
4 julie 50.0 NaN NaN
5 liz NaN 160.0 b

Changer d'id¶

In [35]:
df = df.set_index('age') # on met comme index la colonne des âges
df
Out[35]:
prenom mesure sexe
age
30.0 alice NaN NaN
NaN bill 170.0 f
20.0 bob 175.0 h
NaN eve 180.0 f
50.0 julie NaN NaN
NaN liz 160.0 b
In [36]:
df.head(2)
Out[36]:
prenom mesure sexe
age
30.0 alice NaN NaN
NaN bill 170.0 f

Ecriture tout en un plus expressive : à utiliser !¶

In [37]:
print(age)
print(taille)
print(sexe)
df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})

# on utilise une écriture avec enchainement d'étapes : c'est plus lisible
# il faut que les méthodes retournent un dataframe
df = (df.reset_index()                           # on reste l'index : le prenom passe en colonne
        .rename(columns={'index': 'prenom'})     # on renomme l'index en prenom
        .set_index('age'))                       # on met l'age comme index 

df
alice    30
bob      20
julie    50
dtype: int64
liz     160
bob     175
bill    170
eve     180
dtype: int64
liz     b
bob     h
bill    f
eve     f
dtype: object
Out[37]:
prenom taille sexe
age
30.0 alice NaN NaN
NaN bill 170.0 f
20.0 bob 175.0 h
NaN eve 180.0 f
50.0 julie NaN NaN
NaN liz 160.0 b
In [38]:
print(df.iloc[0])
print(type(df.iloc[0]))
prenom    alice
taille      NaN
sexe        NaN
Name: 30.0, dtype: object
<class 'pandas.core.series.Series'>
In [39]:
print(df.iloc[0:3])
print(type(df.iloc[0:3]))
     prenom  taille sexe
age                     
30.0  alice     NaN  NaN
NaN    bill   170.0    f
20.0    bob   175.0    h
<class 'pandas.core.frame.DataFrame'>
In [40]:
df.loc[:,:]
Out[40]:
prenom taille sexe
age
30.0 alice NaN NaN
NaN bill 170.0 f
20.0 bob 175.0 h
NaN eve 180.0 f
50.0 julie NaN NaN
NaN liz 160.0 b
In [41]:
df.loc[:,'taille'].mean()
Out[41]:
171.25
In [42]:
# Création de dataframes par index et colums
In [43]:
## Exemple 1
In [44]:
a = np.random.randint(1, 20, 9).reshape(3, 3)
p = pd.DataFrame(a, index=['a', 'b', 'c'], columns=['x', 'y', 'z'])
print(p)
    x   y   z
a   3   7  12
b   3  16   3
c  10   9  17
In [45]:
## Exemple 2
In [46]:
df1 = pd.DataFrame(
        np.ones((2, 2)), 
        index=list('ab'), 
        columns=list('xy')
)
df1
Out[46]:
x y
a 1.0 1.0
b 1.0 1.0
In [47]:
df2 = pd.DataFrame(
    np.arange(1,5).reshape(2,2), 
    index=list('ac'), 
    columns=list('xz'))
df2
Out[47]:
x z
a 1 2
c 3 4

Addition de dataframes¶

In [48]:
# On a 2 dataframes avec x et a en commun.
# L'addition crée un dataframe avec toutes les lignes et toutes les colonnnes
# L'addtion se fait si les valeurs existent dans les deux dataframes de départ
df1+df2
Out[48]:
x y z
a 2.0 NaN NaN
b NaN NaN NaN
c NaN NaN NaN

Remplacer un NaN par une valeur par défaut : fill_value = 0¶

In [49]:
# On passe la fonction add pour faire l'addition
# On met une valeur par défaut à 0 pour les NaN
# La valeur par défaut s'applique si on a au moins une valeur concrète
df1.add(df2, fill_value=0)
Out[49]:
x y z
a 2.0 1.0 2.0
b 1.0 1.0 NaN
c 3.0 NaN 4.0

On remplace les NaN par -1 : .fillna()¶

In [50]:
df3=df1.add(df2, fill_value=0).fillna(-1)
df3
Out[50]:
x y z
a 2.0 1.0 2.0
b 1.0 1.0 -1.0
c 3.0 -1.0 4.0

On supprime les lignes avec des NaN : .dropna¶

In [51]:
df1.add(df2, fill_value=0).dropna()
Out[51]:
x y z
a 2.0 1.0 2.0
In [52]:
age = pd.Series([30, 20, 50], index=['alice', 'bob', 'julie'])
In [53]:
age
Out[53]:
alice    30
bob      20
julie    50
dtype: int64

Import-Export¶

Exportation¶

In [54]:
# Ecrire un DataFrame dans un fichier CSV
print(df, '\n')
df.to_csv('my_data.csv') # ça écrit dans un fichier CSV
!cat my_data.csv         # on affiche le fichier CSV (! permet de passer une commande de l'OS)
     prenom  taille sexe
age                     
30.0  alice     NaN  NaN
NaN    bill   170.0    f
20.0    bob   175.0    h
NaN     eve   180.0    f
50.0  julie     NaN  NaN
NaN     liz   160.0    b 

age,prenom,taille,sexe
30.0,alice,,
,bill,170.0,f
20.0,bob,175.0,h
,eve,180.0,f
50.0,julie,,
,liz,160.0,b
In [55]:
# Ecrire un DataFrame dans un fichier JSON
print(df3, '\n')
df3.to_json('my_data.json')
!cat my_data.json
     x    y    z
a  2.0  1.0  2.0
b  1.0  1.0 -1.0
c  3.0 -1.0  4.0 

{"x":{"a":2.0,"b":1.0,"c":3.0},"y":{"a":1.0,"b":1.0,"c":-1.0},"z":{"a":2.0,"b":-1.0,"c":4.0}}

Importation¶

In [56]:
# Lire un fichier CSV pour remplir un DataFrame
new_df = pd.read_csv('my_data.csv')
print(new_df)
    age prenom  taille sexe
0  30.0  alice     NaN  NaN
1   NaN   bill   170.0    f
2  20.0    bob   175.0    h
3   NaN    eve   180.0    f
4  50.0  julie     NaN  NaN
5   NaN    liz   160.0    b
In [57]:
# Lire un fichier JSON pour remplir un DataFrame
new_df3 = pd.read_json('my_data.json')
print(new_df3)
   x  y  z
a  2  1  2
b  1  1 -1
c  3 -1  4
In [61]:
# Importer avec seaborn
import seaborn as sns
titanic = sns.load_dataset('titanic')
titanic.head()
Out[61]:
survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
0 0 3 male 22.0 1 0 7.2500 S Third man True NaN Southampton no False
1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
4 0 3 male 35.0 0 0 8.0500 S Third man True NaN Southampton no True