Structure du DataFrame
¶
Pandas est très utilisé pour sa capacité à gérer des tableaux de données, comme un super tableur. Non seulement il offre de nombreux outils pour manipuler les données mais il est aussi très performant à savoir rapide et pouvant gérer de grosses données.
Pour commencer voici un simple tableau :
import pandas as pd
from IPython.display import display, HTML
CSS = """
.output {
flex-direction: row;
}
"""
HTML('<style>{}</style>'.format(CSS))
df = pd.DataFrame({'prix': [1.1, 0.8, 0.6], 'stock': [0, 22, 51]}, # columns are stored in a dictionnary
index = ['stylo','crayon','gomme'])
# another way to do the same dataframe. The list shape is index x columns:
df_bis = pd.DataFrame([[1.1, 0], [0.8, 22], [0.6, 51]],
index = ['stylo','crayon','gomme'],
columns = ['prix', 'stocks'])
display(df, df_bis)
prix | stock | |
---|---|---|
stylo | 1.1 | 0 |
crayon | 0.8 | 22 |
gomme | 0.6 | 51 |
prix | stocks | |
---|---|---|
stylo | 1.1 | 0 |
crayon | 0.8 | 22 |
gomme | 0.6 | 51 |
Pour bien comprendre ce qu'est un tableau, un DataFrame
dans le langage de Pandas, il faut le voir comme un ensemble Series
, une série étant une liste de valeurs avec un index.
Dans le cas précédent il y a 2 Series
à savoir le prix
et les stock
, chaque série étant indéxée par la liste des objets de mon inventaire.
Voici comment on faire ressortir les séries :
print(df.prix, '\n')
print(df['prix'], '\n') # use brackets when the name is a keyword or has spaces or accents
print(type(df.prix))
stylo 1.1 crayon 0.8 gomme 0.6 Name: prix, dtype: float64 stylo 1.1 crayon 0.8 gomme 0.6 Name: prix, dtype: float64 <class 'pandas.core.series.Series'>
On peut créer un DataFrame à partir de Series et il se débrouille comme il peut si les séries ont des index différents (en particulier il met des Not A Number, NaN, dans les cases vides) :
df2 = pd.DataFrame({'prix': df.prix, # we copy column 'prix' of df
'stock': df.stock,
'promo': pd.Series([0.2,0,0.05], index=['stylo','papier','crayon']) # Series with different index
})
df2
prix | stock | promo | |
---|---|---|---|
crayon | 0.8 | 22.0 | 0.05 |
gomme | 0.6 | 51.0 | NaN |
papier | NaN | NaN | 0.00 |
stylo | 1.1 | 0.0 | 0.20 |
On voit donc qu'une colonne n'est pas constituée que de valeurs mais a un index
et des values
:
print(df2.prix.index)
print(df2.prix.values)
Index(['crayon', 'gomme', 'papier', 'stylo'], dtype='object') [0.8 0.6 nan 1.1]
comme le tableau lui-même :
print(df2.index)
print(df2.values)
Index(['crayon', 'gomme', 'papier', 'stylo'], dtype='object') [[8.0e-01 2.2e+01 5.0e-02] [6.0e-01 5.1e+01 nan] [ nan nan 0.0e+00] [1.1e+00 0.0e+00 2.0e-01]]
Un tableau a aussi des noms pour les colonnes :
df2.columns
Index(['prix', 'stock', 'promo'], dtype='object')
df.gomme
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /tmp/ipykernel_701/1153377621.py in ?() ----> 1 df.gomme /opt/conda/lib/python3.11/site-packages/pandas/core/generic.py in ?(self, name) 6292 and name not in self._accessors 6293 and self._info_axis._can_hold_identifiers_and_holds_name(name) 6294 ): 6295 return self[name] -> 6296 return object.__getattribute__(self, name) AttributeError: 'DataFrame' object has no attribute 'gomme'
Il faut utiliser la méthode loc
. Cette méthode permet d'extraire
- une valeur,
- une ligne ou colonne éventuellement partiellement
- un sous-tableau.
La méthode iloc
fait la même chose mais avec des indices numérotés (integer).
df2.loc['crayon']
prix 0.80 stock 22.00 promo 0.05 Name: crayon, dtype: float64
Pour spécifier une partie on utilise les [mêmes conventions que pour Numpy](../lesson4 Numpy/np02 Filtres.ipynb).
df2.loc[['gomme','crayon'],:'stock']
prix | stock | |
---|---|---|
gomme | 0.6 | 51.0 |
crayon | 0.8 | 22.0 |
print(df2.iloc[0,0])
df2.iloc[0:2, :]
0.8
prix | stock | promo | |
---|---|---|---|
crayon | 0.8 | 22.0 | 0.05 |
gomme | 0.6 | 51.0 | NaN |
Bien sûr on peut extraire les séries puis demander certains indices de ces séries :
df2[['prix','stock']][0:2] # may seems more natural but is less efficient
prix | stock | |
---|---|---|
crayon | 0.8 | 22.0 |
gomme | 0.6 | 51.0 |
Optimisation (at & iat)¶
Si on désire récupérer une seule valeur du tableau, at
et iat
sont plus rapides que loc
et iloc
.
print(df2.at['gomme', 'stock']) # same than loc['gomme', 'stock'] buy faster
print(df2.iat[1, 0])
51.0 0.6
Les filtres logiques¶
On peut aussi utiliser les filtres logiques comme pour Numpy :
df2.loc[df2.promo > 0, 'promo'] += 0.10 # we add 10% sales for products already in sales
df2
prix | stock | promo | |
---|---|---|---|
crayon | 0.8 | 22.0 | 0.15 |
gomme | 0.6 | 51.0 | NaN |
papier | NaN | NaN | 0.00 |
stylo | 1.1 | 0.0 | 0.30 |
df2.query('0.1 < promo < 0.3')
prix | stock | promo | |
---|---|---|---|
crayon | 0.8 | 22.0 | 0.15 |
On note que l'écriture est simplifiée par rapport à la facon usuelle lorsqu'on désire faire un filtre compliqué. Voici quelques cas de filtres de query
avec un tableau qui a des colonnes A
, B
et C
qui soulignent son utilité :
query |
Python |
---|---|
'A in B' |
df[df.A.isin(df.B)] |
'A not in B' |
df[~df.A.isin(df.B)] |
'C in [1,2,4,8] |
df[df.C.isin([1,2,4,8])] |
'A == 3 and A > B > C' |
df[(df['A'] == 3) & (df['A'] > df['B']) & (df['B'] > df['C'])] |
Par contre il n'est pas simple d'utiliser query
pour modifier un tableau. Ainsi modifier les valeurs de promotion comme fait ci-dessus s'´ecrit :
df2.loc[df2.query('promo > 0').index, 'promo'] += 0.1 # it includes a partial copy of df2
Agir globalement¶
On préfère ne pas travailler sur les éléments mais sur les colonnes globalement avec les méthodes adaptées pour éviter de faire des boucles sur les éléments qui ralentissent l'exécution et compliquent le programme.
Il faut penser en terme de vecteurs et non plus en terme de valeurs. Le plus souvent on peut arriver au résultat souhaité sans faire de boucle (et donc nettement plus rapidement).
print("Stock value = %f\n" % (df.prix * df.stock).sum())
Stock value = 48.200000
On peut appliquer sur une colonne les opérations mathétiques que l'on a vues avec Numpy.
import numpy as np
np.sin(df2.promo) # just to show that it can be done
crayon 0.247404 gomme NaN papier 0.000000 stylo 0.389418 Name: promo, dtype: float64
Plus¶
Pour plus d'information vous pouvez regarder
- les lecons suivantes !
- la documentation de Pandas
- l'antisèche (que je vous conseille d'avoir sous la main au fur et à mesure des lecons)