Traitement de données avec PANDAS

Contenu

2. Traitement de données avec PANDAS#

Marc BUFFAT, Université Claude Bernard Lyon 1

../../_images/python_pandas.jpg
%matplotlib inline
from IPython.display import HTML,display
# bibliotheques de base
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['font.family'] = 'serif'
rcParams['font.size'] = 14

2.1. BIG DATA#

../../_images/b_1_q_0_p_0.jpg

2.2. croissance du stocage digital#

../../_images/Hilbert_InfoGrowth.png

2.3. Les bons outils suivant la taille des données#

  • petits fichiers ( ~ qques ko) : tableur: excel locacl

  • 1 Mo < x < 100 Mo : très lourd avec excel => script: Python (Pandas), R

  • 100 Mo < xx < 100 Go : impossible avec excel ==> script: Python (Pandas), R, SQL

  • 100 Go < xxx < 1 To ===> SQL base de données

  • 1 Tb et + ====> Hadoop

2.4. Traitement des données#

2.4.1. the (old) excel way#

  • utilisation d’un tableur (excel , localc)

  • simple pour des données simples

  • mais tricky pour des traitements complexes (VBA)

2.4.2. the (new) Pandas way#

  • basé sur un vrai language de programmation (Python)

  • traitement complexe des données (cleaning,..)

  • automatisation

  • performance

  • adapté au big data

2.5. Pandas: « Python Library for Data Analysis and Statistics »#

../../_images/python_pandas.jpg

2.5.1. Pandas: http://pandas.pydata.org#

Pandas (from Panel Data analysis) is a Python package providing fast, flexible, and expressive data structures designed to work with relational or labeled data both. It is a fundamental high-level building block for doing practical, real world data analysis in Python.

pandas is well suited for:

  • Tabular data with heterogeneously-typed columns, as in an SQL table or Excel spreadsheet

  • Ordered and unordered (not necessarily fixed-frequency) time series data.

  • Arbitrary matrix data (homogeneously typed or heterogeneous) with row and column labels

  • Any other form of observational / statistical data sets. The data actually need not be labeled at all to be placed into a pandas data structure

Exemples

2.5.2. Key features:#

  • Easy handling of missing data

  • Size mutability: columns can be inserted and deleted from DataFrame and higher dimensional objects

  • Automatic and explicit data alignment: objects can be explicitly aligned to a set of labels, or the data can be aligned automatically

  • Powerful, flexible group by functionality to perform split-apply-combine operations on data sets

  • Intelligent label-based slicing, fancy indexing, and subsetting of large data sets

  • Intuitive merging and joining data sets

  • Flexible reshaping and pivoting of data sets

  • Hierarchical labeling of axes

  • Robust IO tools for loading data from flat files, Excel files, databases, and HDF5

  • Time series functionality: date range generation and frequency conversion, moving window statistics, moving window linear regressions, date shifting and lagging, etc.

2.6. Data structure in Pandas#

  • Type de données de base: dataframe (tableau de type excel)

Une DataFrame PANDAS est une structure de données tabulaire bidimensionnelle de taille variable, potentiellement hétérogène, avec des axes libellés (lignes et colonnes). est une structure de données bidimensionnelle, c’est-à-dire que les données sont alignées de manière tabulaire en rangées et en colonnes.

../../_images/dataframe.png
import pandas as pd

2.7. DataFrame#

  • structure 2D équivalente à un tableur

  • structure hétérogéne avec index (ligne) et column (colonnes)

2.7.1. création à partir de tableaux numpy#

n=5
d = np.ones((n,2))
pd.DataFrame(d)
0 1
0 1.0 1.0
1 1.0 1.0
2 1.0 1.0
3 1.0 1.0
4 1.0 1.0

2.7.2. création à partir de dictionnaire et de tableaux#

2.7.2.1. nom de colonnes#

n = 5
d = { 'One' : np.linspace(1,n,n), 'Two': np.linspace(1,n,n)**2}
pd.DataFrame(d)
One Two
0 1.0 1.0
1 2.0 4.0
2 3.0 9.0
3 4.0 16.0
4 5.0 25.0

2.7.2.2. nom de ligne (index)#

data=pd.DataFrame(d,index=['a','b','c','d','e'])
data
One Two
a 1.0 1.0
b 2.0 4.0
c 3.0 9.0
d 4.0 16.0
e 5.0 25.0

2.7.2.3. selection colonne#

data['Two'] 
a     1.0
b     4.0
c     9.0
d    16.0
e    25.0
Name: Two, dtype: float64
data['Two']['b']
4.0
data['Two'][1]
4.0

2.7.2.4. selection ligne#

data.loc['a']
One    1.0
Two    1.0
Name: a, dtype: float64
data.loc['a']['Two']
1.0
data.loc['a'][0]
1.0
# position ligne/colonne
data.iloc[2,1]
9.0
data.iloc[0,0]
1.0

2.8. DataFrame à partir de fichier csv#

  • fichier csv: Comma-separated values, connu sous le sigle CSV, est un format informatique ouvert représentant des données tabulaires sous forme de valeurs séparées par des virgules dans un fichier texte. C’est un format d’échange courant pour les tableurs.

variante française: chiffre avec virgule, séparateur ;

2.8.1. lecture de données issue de Hubble#

le nom des colonnes est dans le fichier csv et l’index des lignes est numérique (de 0 a n-1)

data = pd.read_csv("hubble_data.csv")
data.head()
distance recession_velocity
0 0.032 170
1 0.034 290
2 0.214 -130
3 0.263 -70
4 0.275 -185

2.8.1.1. change le nom des colonnes#

data.columns = ['dist','rec_vel']
data.tail()
dist rec_vel
19 1.7 960
20 2.0 500
21 2.0 850
22 2.0 800
23 2.0 1090

2.8.1.2. lecture fichier csv sans entete de noms de colonnes#

data = pd.read_csv("hubble_data_no_headers.csv",
                   names=['dist','rec_vel'])
data.tail()
dist rec_vel
19 1.7 960
20 2.0 500
21 2.0 850
22 2.0 800
23 2.0 1090

2.8.2. accés aux données#

data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 24 entries, 0 to 23
Data columns (total 2 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   dist     24 non-null     float64
 1   rec_vel  24 non-null     int64  
dtypes: float64(1), int64(1)
memory usage: 512.0 bytes

2.8.2.1. selection colonne#

data['dist'].tail()
19    1.7
20    2.0
21    2.0
22    2.0
23    2.0
Name: dist, dtype: float64

2.8.2.2. sélection de ligne dans la colonne#

data['dist'][0:5]
0    0.032
1    0.034
2    0.214
3    0.263
4    0.275
Name: dist, dtype: float64

2.8.2.3. sélection de plusieurs colonnes#

data[['dist','rec_vel']].head()
dist rec_vel
0 0.032 170
1 0.034 290
2 0.214 -130
3 0.263 -70
4 0.275 -185

2.8.3. calcul sur les données#

2.8.3.1. calcul de l’énergie: \(E = K*dist + 0.5 *v^2\)#

K = 1000.
data['E'] = K*data['dist'] + 0.5*data['rec_vel']**2
data['dist2'] = data['dist']**2
data.head()
dist rec_vel E dist2
0 0.032 170 14482.0 0.001024
1 0.034 290 42084.0 0.001156
2 0.214 -130 8664.0 0.045796
3 0.263 -70 2713.0 0.069169
4 0.275 -185 17387.5 0.075625

2.8.3.2. suppression d’une colonne#

del data['dist2']
data.head()
dist rec_vel E
0 0.032 170 14482.0
1 0.034 290 42084.0
2 0.214 -130 8664.0
3 0.263 -70 2713.0
4 0.275 -185 17387.5

2.8.3.3. suppression d’un ligne (attention paramêtre inplace)#

data.drop(1)
dist rec_vel E
0 0.032 170 14482.0
2 0.214 -130 8664.0
3 0.263 -70 2713.0
4 0.275 -185 17387.5
5 0.275 -220 24475.0
6 0.450 200 20450.0
7 0.500 290 42550.0
8 0.500 270 36950.0
9 0.630 200 20630.0
10 0.800 300 45800.0
11 0.900 -30 1350.0
12 0.900 650 212150.0
13 0.900 150 12150.0
14 0.900 500 125900.0
15 1.000 920 424200.0
16 1.100 450 102350.0
17 1.100 500 126100.0
18 1.400 500 126400.0
19 1.700 960 462500.0
20 2.000 500 127000.0
21 2.000 850 363250.0
22 2.000 800 322000.0
23 2.000 1090 596050.0
data.head()
dist rec_vel E
0 0.032 170 14482.0
1 0.034 290 42084.0
2 0.214 -130 8664.0
3 0.263 -70 2713.0
4 0.275 -185 17387.5
data.iloc[1,0]
0.034
data.drop(1,inplace=True)
data.head()
dist rec_vel E
0 0.032 170 14482.0
2 0.214 -130 8664.0
3 0.263 -70 2713.0
4 0.275 -185 17387.5
5 0.275 -220 24475.0
data.iloc[1,0]
0.214

2.8.4. indexation des données:#

Au lieu d’utiliser l’indexation numérique de 0 à n-1, on va indexer les données par rapport à une colonne, par ex. la colonne “dist” pour annalyser

  • vel = F(dist)

  • E = G(dist)

data.set_index("dist",inplace=True)
data.head()
rec_vel E
dist
0.032 170 14482.0
0.214 -130 8664.0
0.263 -70 2713.0
0.275 -185 17387.5
0.275 -220 24475.0

2.8.4.1. tracer des données rec_vel = F(dist)#

data['rec_vel'].plot(figsize=(12,6),lw=2,grid=True,style='-o')
plt.title("")
Text(0.5, 1.0, '')
../../_images/c0afec589c23a8bc1c3cca09a1c724a9c67c24c04aa0f743f5008e03aedde640.png
data['E'].plot(figsize=(12,6),lw=2,grid=True,style='-o')
<Axes: xlabel='dist'>
../../_images/561dfe559b379fa4051616290cea682ec3fc15a02382628fde95e9504ba9f99a.png

2.8.4.2. traitement statistique:#

moyenne , medianne …

  • analyse par colonne

energy = data['E']
type(energy)
pandas.core.series.Series
energy.mean(), energy.median()
(140673.97826086957, 45800.0)
  • analyse globale

data.describe()
rec_vel E
count 23.000000 23.000000
mean 376.739130 140673.978261
std 379.166576 172733.702836
min -220.000000 1350.000000
25% 160.000000 18918.750000
50% 300.000000 45800.000000
75% 575.000000 169575.000000
max 1090.000000 596050.000000

2.8.5. analyse statistique#

  • variance, covariance, moyenne …..

matrice de covariance entre 3 tableaux x,y,z

\[\begin{split} cov_{x,y,z} = \left[ \begin{array}{ccc} cov_{x,x} & cov_{x,y} & cov_{x,z} \\ cov_{y,x} & cov_{y,y} & cov_{y,z} \\ cov_{z,x} & cov_{z,y} & cov_{z,z} \end{array} \right] = \left[ \begin{array}{ccc} \sigma^2_{x} & \sigma_{xy} & \sigma_{xz} \\ \sigma_{yx} & \sigma^2_{y} & \sigma_{yz} \\ \sigma_{zx} & \sigma_{zy} & \sigma^2_{z} \end{array} \right] \end{split}\]

avec (\(E\) est l’opérateur moyenne \(E(X) = \bar{X}\)) $\( cov_{x,y} = E[(X – E[X])(Y – E[Y])] = \overline{(X-\bar{X})(Y-\bar{Y})} = \frac{\sum(X_i – \bar{X})(Y_i – \bar{Y})}{N} \)$

data1 = data.reset_index()
data1.head()
dist rec_vel E
0 0.032 170 14482.0
1 0.214 -130 8664.0
2 0.263 -70 2713.0
3 0.275 -185 17387.5
4 0.275 -220 24475.0
CV = data1.cov()
display(CV)
dist rec_vel E
dist 0.399092 1.943741e+02 8.239335e+04
rec_vel 194.374051 1.437673e+05 5.939248e+07
E 82393.346148 5.939248e+07 2.983693e+10
# CV est un datafrme : accès
CV['rec_vel']['dist'] , CV.iloc[0,1]
(194.37405138339918, 194.37405138339918)

vérification: lissage par droite moindre carré V=F(dist)

 $$ V = a*dist + b $$

avec \( a = \frac{\sigma_{V,V}}{\sigma_{V,dist}} \) et \( b = \bar{V} - a * \overline{dist} \)

# verification lissage par droite moindre carré V=F(dist)
a = CV.iloc[1,1]/CV.iloc[1,0]
b = data1['rec_vel'].mean() - a * data1['dist'].mean()
print(a,b)
739.6424135160937 -325.5674203816508
rcParams['font.family'] = 'serif'
rcParams['font.size'] = 14
X = np.linspace(0.,2.,21)
plt.figure(figsize=(10,8))
plt.plot(data1['dist'],data1['rec_vel'],'-o',label="mesure")
plt.plot(X,a*X+b,'--',lw=2,label="lissage")
plt.legend()
plt.xlabel('distance [m]')
plt.ylabel('vitesse [m/s]')
plt.title("vitesse en fonction de la distance")
Text(0.5, 1.0, 'vitesse en fonction de la distance')
../../_images/8b5498e0c0ba5816222179db645c6055f30bdc46643a125dd6c28255fac95299.png

2.8.6. modification des données (loc, iloc)#

attention à l’aliasing !!! de nombreuses fonctions renvoient une copy du tableau (paramêtre inplace)

data['E']
dist
0.032     14482.0
0.214      8664.0
0.263      2713.0
0.275     17387.5
0.275     24475.0
0.450     20450.0
0.500     42550.0
0.500     36950.0
0.630     20630.0
0.800     45800.0
0.900      1350.0
0.900    212150.0
0.900     12150.0
0.900    125900.0
1.000    424200.0
1.100    102350.0
1.100    126100.0
1.400    126400.0
1.700    462500.0
2.000    127000.0
2.000    363250.0
2.000    322000.0
2.000    596050.0
Name: E, dtype: float64
data['E'][2.]=1620
print(data.tail())
      rec_vel         E
dist                   
1.7       960  462500.0
2.0       500    1620.0
2.0       850    1620.0
2.0       800    1620.0
2.0      1090    1620.0
/tmp/ipykernel_3693742/1408120798.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  data['E'][2.]=1620

2.8.6.1. utilisation de .at (attention ligne, colonne)#

data.at[2.,'E']=1800
print(data.tail())
      rec_vel         E
dist                   
1.7       960  462500.0
2.0       500    1800.0
2.0       850    1800.0
2.0       800    1800.0
2.0      1090    1800.0
data.loc[2,'E']
dist
2.0    1800.0
2.0    1800.0
2.0    1800.0
2.0    1800.0
Name: E, dtype: float64

2.8.7. autres structures de données#

  • Serie 1D

  • xarray: multi-dimensionnal

voir http://pandas.pydata.org

2.9. DataFrame a partir de fichier csv (exemple 2)#

data = pd.read_csv("wages_hours.csv")
data.head()
HRS\tRATE\tERSP\tERNO\tNEIN\tASSET\tAGE\tDEP\tRACE\tSCHOOL
0 2157\t2.905\t1121\t291\t380\t7250\t38.5\t2.340...
1 2174\t2.970\t1128\t301\t398\t7744\t39.3\t2.335...
2 2062\t2.350\t1214\t326\t185\t3068\t40.1\t2.851...
3 2111\t2.511\t1203\t49\t117\t1632\t22.4\t1.159\...
4 2134\t2.791\t1013\t594\t730\t12710\t57.7\t1.22...

2.9.1. selection du bon séparateur#

data = pd.read_csv("wages_hours.csv", sep = "\t")
data.head()
HRS RATE ERSP ERNO NEIN ASSET AGE DEP RACE SCHOOL
0 2157 2.905 1121 291 380 7250 38.5 2.340 32.1 10.5
1 2174 2.970 1128 301 398 7744 39.3 2.335 31.2 10.5
2 2062 2.350 1214 326 185 3068 40.1 2.851 * 8.9
3 2111 2.511 1203 49 117 1632 22.4 1.159 27.5 11.5
4 2134 2.791 1013 594 730 12710 57.7 1.229 32.5 8.8
#pd.read_csv?

2.9.2. Extraction de colonnes a traiter#

data2 = data[["AGE", "RATE"]]
data2.head()
AGE RATE
0 38.5 2.905
1 39.3 2.970
2 40.1 2.350
3 22.4 2.511
4 57.7 2.791

2.9.3. tri des données#

data_sorted = data2.sort_values("AGE")
data_sorted.head()
AGE RATE
3 22.4 2.511
27 37.2 3.015
31 37.4 1.901
33 37.5 1.899
32 37.5 3.009

2.9.3.1. indexation en fonction de la colonne AGE#

data_sorted.set_index("AGE", inplace=True)
data_sorted.head()
RATE
AGE
22.4 2.511
37.2 3.015
37.4 1.901
37.5 1.899
37.5 3.009

2.9.3.2. tracer du salaire en fonction de l’age#

data_sorted.plot()
plt.show()
../../_images/4e96d734c48d959497274d5db25fae349141be8030779aebf2cacb413400c62f.png

2.10. données fonction du temps#

2.10.1. gestion date (conversion)#

date=pd.to_datetime('2016-09-29T23:00:00+02:00')
date
Timestamp('2016-09-29 23:00:00+0200', tz='pytz.FixedOffset(120)')

2.10.2. localisation: timezone#

  • heure universelle

  • timezone: Europe/Paris

date.tz_convert(tz='Europe/Paris')
Timestamp('2016-09-29 23:00:00+0200', tz='Europe/Paris')

2.10.3. serie temporelle (regroupement)#

  • création de la série temporelle puis du dataframe

date=pd.date_range('2015-01', '2015-12-31',
                   freq='D')
date
DatetimeIndex(['2015-01-01', '2015-01-02', '2015-01-03', '2015-01-04',
               '2015-01-05', '2015-01-06', '2015-01-07', '2015-01-08',
               '2015-01-09', '2015-01-10',
               ...
               '2015-12-22', '2015-12-23', '2015-12-24', '2015-12-25',
               '2015-12-26', '2015-12-27', '2015-12-28', '2015-12-29',
               '2015-12-30', '2015-12-31'],
              dtype='datetime64[ns]', length=365, freq='D')
ts = pd.Series(np.random.randn(len(date)), 
               index=date)
data= ts.to_frame(name='val')
data.head()
val
2015-01-01 0.684475
2015-01-02 -2.884907
2015-01-03 0.296181
2015-01-04 -0.733121
2015-01-05 0.801108

2.10.4. aggregation / mois#

data['Mois']=data.index.month
data.head()
val Mois
2015-01-01 0.684475 1
2015-01-02 -2.884907 1
2015-01-03 0.296181 1
2015-01-04 -0.733121 1
2015-01-05 0.801108 1
datam=data.groupby('Mois').aggregate(np.mean)
datam.head()
val
Mois
1 0.038785
2 -0.090236
3 0.105795
4 0.000886
5 0.024717
datam.plot(kind='bar')
<Axes: xlabel='Mois'>
../../_images/543bc5e4e232a9c608805d02438f3d572871e6498d14b20a2ccf3e34b4b3e37d.png

2.10.5. boucle sur les valeurs d’une colonne#

somme=0.0
for val in data['val']:
    somme += val
print("somme de la colonne :",somme)
somme de la colonne : 6.612460288122153

2.10.6. avec un index#

data['val2']=0.0
for index,row in data.iterrows():
    val = row['val']
    data.loc[index,'val2'] = val**2
data.head()
val Mois val2
2015-01-01 0.684475 1 0.468505
2015-01-02 -2.884907 1 8.322690
2015-01-03 0.296181 1 0.087723
2015-01-04 -0.733121 1 0.537467
2015-01-05 0.801108 1 0.641774

2.11. Bibliothèque spécialisée#

Visualisation de données (statistique)

  • Boîte à moustaches: représentation visuelle permettant mesurer la distribution des données.

    • Le minimum est affiché à l’extrême gauche du graphique, à la fin de la « moustache » de gauche

    • Le premier quartile, Q1, est l’extrême gauche de la boîte (moustache gauche)

    • La médiane est représentée par une ligne au centre de la boîte

    • Troisième quartile, Q3, affiché à l’extrême droite de la boîte (moustache droite)

    • Le maximum est à l’extrême droite de la case

../../_images/moustache.png
  • On appelle Écart interquartile ou InterQuartile Range: IQR = Q3-Q1 qui mesure la dispersion des données (estimateur statistique robuste)

  • Minimum = Q1 - 1.5 IQR

  • Maximum = Q3 + 1.5 IQR

  • les points en dehors d’une répartition standard (outliers), i.e en dehors du [minimum:maximum], sont marquées comme des points

violin plot (variante)

  • Un violin plot est utilisé pour visualiser la distribution des données et sa densité de probabilité.

seaborn: statistical data visualization

import seaborn as sns
# donnees brutes
plt.figure(figsize=(12,6))
data['val'].plot(label='val')
data['val2'].plot(label='val2')
plt.legend()
<matplotlib.legend.Legend at 0x7ff4b28d8e20>
../../_images/b62d8fb8c4f9eeb15cdbafac8013c5ac858e7ddf3f3a4b5c542b5ba513fff2f2.png
plt.figure(figsize=(10,6))
plt.subplot(1,2,1)
sns.boxplot(data=data[['val','val2']])
plt.title("moustache plot");
plt.subplot(1,2,2)
sns.violinplot(data=data[['val','val2']])
plt.title("violin plot");
../../_images/ff6c35d0555908f33557577d008804ffaa5c65e296725088c9181488fac6f5c2.png

2.12. Bibliographie#

apprendre à lire et à chercher dans la documentation (très (trop) riche !!)

2.13. FIN#