introduction sur les tables de données¶
lecture d’une table de données¶
format CSV¶
la librairie de data-science¶
on importe pandas
¶
# le code
import pandas as pd
pd.__version__
'2.3.0'
lecture d’un fichier csv
¶
# le code
df = pd.read_csv('data/titanic.csv')
type(df)
pandas.core.frame.DataFrame
# pour vous convaincre que les types sont bien les mêmes
pd.core.frame.DataFrame is pd.DataFrame
True
# pour afficher les premières lignes
# à votre avis, comment on voit les dernières lignes ?
df.head(2)
lecture d’un fichier excel
¶
les fichiers de données en format Excel sont constitués de feuilles contenant des tables en 2 dimensions comme les tables des fichiers csv (nous ne parlons pas ici de macros ou calculs)
la fonction pandas
read_excel
permet de charger un fichier Excel et d’obtenir:
- un
dict
de DataFrame (les clés sont les noms des feuilles) - directement un
csv
si il contient une unique feuille
pour utiliser read_excel
vous devez préalablement importer la librairie openpyxl
%pip install openpyxl
si il vous dit Note: you may need to restart the kernel to use updated packages faites le
df1 = pd.read_excel('data/titanic.xlsx')
attention: les fonctions read_csv
et read_excel
sont des fonctions différentes
(les mêmes dataframes peuvent différer au chargement sur des types de données)
nous n’irons pas plus loin sur read_excel
, vous savez maintenant qu’il existe
# le code
# %pip install openpyxl
# le code
df1 = pd.read_excel('data/titanic.xlsx')
description rapide de la table des données¶
la méthode describe()
vous donne un premier aperçu rapide de vos données
sur une DataFrame
, elle vous donne
pour chaque colonne de type numérique
- le nombre de valeurs non-manquantes (voir colonne
Age
) - la moyenne
- l’écart-type
- le minimum
- les 3 quartiles (les valeurs à 25%, 50% et 75% de données)
- le maximum
df.describe()
-> PassengerId Survived Pclass Age SibSp Parch Fare
count 891.00 891.00 891.00 714.00 891.00 891.00 891.00
mean 446.00 0.38 2.30 29.69 0.52 0.38 32.20
std 257.35 0.48 0.83 14.52 1.10 0.80 49.69
min 1.00 0.00 1.00 0.42 0.00 0.00 0.00
25% 223.50 0.00 2.00 20.12 0.00 0.00 7.91
50% 446.00 0.00 3.00 28.00 0.00 0.00 14.45
75% 668.50 1.00 3.00 38.00 1.00 0.00 31.00
max 891.00 1.00 3.00 80.00 8.00 6.00 512.32
on remarque que pandas.DataFrame.describe
a, par défaut, appliqué les calculs sur les colonnes numériques
même quand ça n’a pas forcément beaucoup d’intérêt - voirSurvived
ouPclass
qui sont plutôt des catégoriesn’a rien fait sur les colonnes non-numériques
on aurait pu n’appliquer la méthode qu’à une sous-dataframe
df[['Age', 'Fare']].describe()
ou forcer la méthode à s’appliquer à toutes les colonnes
pour les colonnes non-numériques, seront affichés à la place
- le nombre de valeurs
- le nombre de valeurs
unique
s - la valeur la plus fréquente
top
- sa fréquence
freq
df.describe(include='all')
la méthode describe()
marche aussi sur les séries (colonnes)
df['Sex'].describe()
->
count 891
unique 2
top male
freq 577
Name: Sex, dtype: object
# le code
# pas besoin de 6 chiffres après la virgule
pd.options.display.precision = 2
df.describe()
df[['Age', 'Fare']].describe()
# le code
df.describe(include='all')
# le code
df['Sex'].describe()
count 891
unique 2
top male
freq 577
Name: Sex, dtype: object
les index et indices des tables¶
la notion d’index¶
la clé pour comprendre pandas
:
- les lignes et les colonnes ont des index
- les opérations sur ces index sont le plus efficace possible
(on verra par la suite que les lignes et les colonnes ont aussi naturellement des indices, numérotés à partir de0
)
Pourquoi (et comment) les opérations sur les index sont très efficaces ?
deux constats
rechercher dans une liste est très inefficace
(en moyenne essais pour localiser un élément dans une liste de éléments)accéder au élément d’un tableau est très efficace
(on est en temps constant - on ne parcourt bien sûr pas les éléments de 0 à )
l’idée
trouver une caractéristique qui identifie une observation de manière unique
(genre numéro de sécurité sociale pour un individu, ici par exemple lePassengerId
)calculer un index à partir de cette caractéristique
qui soit le plus unique possible (pour avoir peu de collisions)utiliser cet index comme entrée dans une table
la recherche peut alors être considérée comme en temps constant
comme l’accès à un élément d’un tableauc’est la technique des tables de hachage (comme les
dict
ouset
Python)
pandas
indexe ses lignes et ses colonnes suivant vos indications
dit autrement, c’est à vous de choisir parmi les colonnes
celle(s) qui peut servir d’identificateur pour servir d’index
(un index en pandas
n’est pas obligatoirement unique)
index des colonnes¶
dans notre fichier du Titanic seules les colonnes ont un nom
(les lignes n’en ont pas), du coup:
- les colonnes ont été indexées par leur nom
- les lignes ont été indexées par leur indice
i.e. une simple numérotation à partir de 0
on y reviendra
l’attribut columns
permet d’accéder aux colonnes de la table
df.columns
-> Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
dtype='object')
c’est un object de type Index
; il permet d’accéder facilement aux noms des colonnes
df.columns[0]
-> 'PassengerId'
# le code
df.columns
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
dtype='object')
df.columns[0]
'PassengerId'
accès aux colonnes avec df[]
¶
les colonnes ont un traitement privilégié en pandas
une table pandas
est un “dictionnaire”
- où les clés sont les noms des colonnes
- où les valeurs sont les colonnes (de type
Series
)
accès à la colonne Age
de la data-frame du Titanic
- on remarque les
891
entrées - on remarque les indices des lignes de
0
à890
- on constate que le type des éléments de cette colonne est
float64
- on constate que l’age du passager d’indice
3
est manquantNaN
(Not a Number)
(NaN
est un terme générique, il signifie ici que la valeur n’est simplement pas disponible)
df['Age']
-> 0 27.00
1 31.00
2 25.00
3 NaN
4 24.00
...
886 47.00
887 30.00
888 36.00
889 22.00
890 0.83
Name: Age, Length: 891, dtype: float64
attention pandas
accepte que plusieurs colonnes portent le même nom
en cas d’accès, il vous les donne toutes
df['Age'].head(2)
0 27.0
1 31.0
Name: Age, dtype: float64
# type Series
type(df['Age'])
pandas.core.series.Series
# on peut aussi passer une liste de colonnes
# auquel cas on récupère une dataframe
df[['Age', 'Sex']].head()
accès aux colonnes avec df.
¶
Lorsque le nom de la colonne ne comporte pas de caractère bizarre
on peut aussi accéder à une colonne au travers d’un attribut
Cette notation est plus lisible mais aussi plus limitée
par exemple ne fonctionne pas si le nom de la colonne contient un espace
Il faut le voir uniquement comme une commodité
Pas forcément recommandé aux débutants
Mais c’est très utilisé - il faut savoir le lire
df.Age is df['Age']
-> True
# le code
# on peut aussi accéder à une colonne par un attribut
# qui est une notation plus lisible
df.Age
0 27.00
1 31.00
2 25.00
3 NaN
4 24.00
...
886 47.00
887 30.00
888 36.00
889 22.00
890 0.83
Name: Age, Length: 891, dtype: float64
df.Age is df['Age']
True
type des colonnes pandas.Series
¶
type(df['Age'])
-> pandas.core.series.Series
le second type en pandas
est le type des colonnes, qui sont des Series
on peut voir la DataFrame
comme un dictionnaire qui associe
{ nom-de-colonne
un-objet-series
}
# le code
type(df['Age'])
pandas.core.series.Series
indexer les lignes¶
c’est une bonne pratique de choisir une colonne
comme index (des lignes) de la table
quand on le peut...
par exemple, dans la table du titanic, on remarque que
la colonne PassengerId
contient un identifiant unique pour chaque passager
choisissons cette colonne comme index (des lignes) de la table
pour cela, trois options
directement à la lecture du fichier
# option 1 df = pd.read_csv('data/titanic.csv', index_col='PassengerId')
après coup
# option 2 df = pd.read_csv('data/titanic.csv') df = df.set_index('PassengerId')
après coup sans ré-affecter df
# option 3 df = pd.read_csv('data/titanic.csv') df.set_index('PassengerId', inplace=True)
observez le changement dans la présentation de la table
# le code
# option 1.
df = pd.read_csv('data/titanic.csv', index_col='PassengerId')
# le code
# option 2.
df = pd.read_csv('data/titanic.csv')
# la table avant
df.head(1)
# la table après
# remarquez que 'PassengerId'
# n'est plus présenté de la même manière
df = df.set_index('PassengerId')
df.head(1)
# le code
# option 2-bis
# la méthode set_index avec son paramètre inplace à True modifie directement la table sans renvoyer une nouvelle dataframe
df = pd.read_csv('data/titanic.csv')
df.set_index('PassengerId', inplace=True)
df.head(1)
une série aussi possède un index¶
accèdons à la colonne
Name
remarquons qu’ici aussi, l’objet Series possède un index
qui en l’occurrence estPassengerId
(provient de ladf
)df['Name'] -> PassengerId 552 Sharp, Mr. Percival James R 638 Collyer, Mr. Harvey 499 Allison, Mrs. Hudson J C (Bessie Waldo Daniels) 261 Smith, Mr. Thomas 395 Sandstrom, Mrs. Hjalmar (Agnes Charlotta Bengt... ... 463 Gee, Mr. Arthur H 287 de Mulder, Mr. Theodore 326 Young, Miss. Marie Grice 396 Johansson, Mr. Erik 832 Richards, Master. George Sibley
toutes les colonnes de la dataframe partagent le même index
c’est de cette façon que les colonnes sont “alignées” entre ellesaccèdons à la colonne
Name
de la première ligne
puis à son index552
(PassengerId
)df['Name'][552] # passager d'id 552 -> 'Sharp, Mr. Percival James R'
# le code
df = pd.read_csv('data/titanic.csv', index_col='PassengerId')
df['Name']
PassengerId
552 Sharp, Mr. Percival James R
638 Collyer, Mr. Harvey
499 Allison, Mrs. Hudson J C (Bessie Waldo Daniels)
261 Smith, Mr. Thomas
395 Sandstrom, Mrs. Hjalmar (Agnes Charlotta Bengt...
...
463 Gee, Mr. Arthur H
287 de Mulder, Mr. Theodore
326 Young, Miss. Marie Grice
396 Johansson, Mr. Erik
832 Richards, Master. George Sibley
Name: Name, Length: 891, dtype: object
# ici on obtient bien la première ligne !
# quand on indexe une Series,
# c'est par l'index (552)
# et non pas par l'indice qui ici serait 0
df['Name'][552]
'Sharp, Mr. Percival James R'
différence entre index et indice¶
les indices c’est quand on compte nos éléments à partir de 0
(c’est valable pour les colonnes comme pour les lignes)
les index c’est quand on utilise des valeurs fournies par l’utilisateur, comme
- les noms de colonnes
- ou les identifiants de lignes
par ex. plus haut552
est l’index de la première ligne, parce que dans la colonne-indexPassengerId
,
la première ligne contient552
lorsqu’une table n’a pas d’index particulier - i.e. avant qu’on fasse un set_index()
:
- dans ce cas l’index commence à 0,
- et du coup, par accident, les indices et les index coincident
- pour information, pandas crée automatiquement un index de type
RangeIndex
l’index des lignes¶
il est accessible par l’attribut pandas.DataFrame.index
lisons la data-frame du titanic sans fixer l’index de lignes
df = pd.read_csv('data/titanic.csv')
df.index
-> RangeIndex(start=0, stop=891, step=1)
- l’index est alors un
RangeIndex
de0
à890
inclus
lisons la data-frame du titanic et choisissons la colonne PassengerId
comme index de ligne
df = pd.read_csv('data/titanic.csv').set_index('PassengerId')
df.index
-> Int64Index([552, 638, 499, 261, 395, 811, 758, 703, 406, 641,
...
556, 236, 224, 598, 258, 463, 287, 326, 396, 832],
dtype='int64', name='PassengerId', length=891)
- les index des lignes sont les valeurs de la colonne
PassengerId
pandas.Int64Index
et pandas.RangeIndex
sont tout deux des pandas.Index
# le code
df = pd.read_csv('data/titanic.csv')
df.index
RangeIndex(start=0, stop=891, step=1)
# le code
df = pd.read_csv('data/titanic.csv').set_index('PassengerId')
df.index
Index([552, 638, 499, 261, 395, 811, 758, 703, 406, 641,
...
556, 236, 224, 598, 258, 463, 287, 326, 396, 832],
dtype='int64', name='PassengerId', length=891)
résumé à propos des types DataFrame et Series¶
- les tables pandas sont représentées par le type
DataFrame
- une dataframe a
- un index pour accéder aux colonnes (
df.columns
) - et un index pour accéder aux lignes (
df.index
) - ces deux objets sont de type
Index
- un index pour accéder aux colonnes (
- une colonne, ou une ligne, sont de type
Series
qui correspond si on veut à des données en 1 seule dimension
forme et dimension de la table¶
pandas
est fondé sur numpy
(cela pourrait changer dans le futur)
la forme de la table est donnée par l’attribut pandas.DataFrame.shape
df.shape
-> (891, 11)
numpy
s’occupe de stocker et manipuler des tableaux de dimension 2pandas
apporte
- l’indexation du tableau
- des fonctions pratiques et de haut-niveau pour manipuler cette table de données
une pandas.dataFrame
est donc toujours une table à deux dimensions
on peut le vérifier par l’attribut pandas.DataFrame.ndim
df.ndim
-> 2
on retrouve là les attributs classiques de numpy
ndim
, shape
# le code
df = pd.read_csv('data/titanic.csv', index_col='PassengerId')
print(df.shape)
print(df.ndim)
(891, 11)
2
exercice basique¶
Le fichier data/petit-titanic.csv
contient les 10 premières lignes de passagers
Attention ce n’est pas exactement le même format que data/titanic.csv
# pour voir le contenu du fichier
%cat data/petit-titanic.csv
# remarquez qu'on peut aussi voir ce contenu en Python pur
#with open("data/petit-titanic.csv") as f:
# for line in f:
# print(line, end="")
552;0;2;Sharp, Mr. Percival James R;male;27.0;0;0;244358;26.0;;S
638;0;2;Collyer, Mr. Harvey;male;31.0;1;1;C.A. 31921;26.25;;S
499;0;1;Allison, Mrs. Hudson J C (Bessie Waldo Daniels);female;25.0;1;2;113781;151.55;C22 C26;S
261;0;3;Smith, Mr. Thomas;male;;0;0;384461;7.75;;Q
395;1;3;Sandstrom, Mrs. Hjalmar (Agnes Charlotta Bengtsson);female;24.0;0;2;PP 9549;16.7;G6;S
811;0;3;Alexander, Mr. William;male;26.0;0;0;3474;7.8875;;S
758;0;2;Bailey, Mr. Percy Andrew;male;18.0;0;0;29108;11.5;;S
703;0;3;Barbara, Miss. Saiide;female;18.0;0;1;2691;14.4542;;C
406;0;2;Gale, Mr. Shadrach;male;34.0;1;0;28664;21.0;;S
641;0;3;Jensen, Mr. Hans Peder;male;20.0;0;0;350050;7.8542;;S
- lisez le contenu de ce fichier avec les paramètres par défaut de
pandas.read_csv
affichez les 2 premières lignes avecpandas.DataFrame.head
voyez-vous les trois problèmes ?
essayez de les résoudre en lisant le help de la fonctionhelp(pd.read_csv)
oupd.read_csv?
ou passez à la question 2 pour être aidé
# votre code
- passez le bon séparateur à la méthode
pandas.read_csv
indiquez lui que l’entête ne contient pas la liste des noms des colonnes
passez-lui la liste des noms des colonnes puisqu’elles ne sont pas mentionnées dans le fichier (à récupérer plus haut)
spoiler
voyez les paramètres sep
, header
et names
# votre code
- appliquer la méthode
describe
aux colonnesAge
etFare
(ou plus exactement à une dataframe qui contient ces colonnes)
# votre code
- affichez le nombre de colonnes et de lignes
# votre code
- affichez les index des colonnes et des lignes
# votre code
- modifiez l’index des colonnes par la liste
columns = ['Identifiant', 'Survécu', 'Pclass',
'Nom', 'Genre', 'Age', 'SibSp', 'Parch',
'Ticket', 'Tarif', 'Cabine', 'Embarquement']
# votre code
- modifiez l’index des lignes par la colonne
'Identifiant'
# votre code