2. Traitement de données avec PANDAS#
Marc BUFFAT, Université Claude Bernard Lyon 1
%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#
2.2. croissance du stocage digital#
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 »#
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.
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
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)
!head hubble_data.csv
distance,recession_velocity
.032,170
.034,290
.214,-130
.263,-70
.275,-185
.275,-220
.45,200
.5,290
.5,270
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 |
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 24 entries, 0 to 23
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 distance 24 non-null float64
1 recession_velocity 24 non-null int64
dtypes: float64(1), int64(1)
memory usage: 512.0 bytes
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()
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[19], line 1
----> 1 data = pd.read_csv("hubble_data_no_headers.csv",
2 names=['dist','rec_vel'])
3 data.tail()
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/util/_decorators.py:211, in deprecate_kwarg.<locals>._deprecate_kwarg.<locals>.wrapper(*args, **kwargs)
209 else:
210 kwargs[new_arg_name] = new_arg_value
--> 211 return func(*args, **kwargs)
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/util/_decorators.py:331, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
325 if len(args) > num_allow_args:
326 warnings.warn(
327 msg.format(arguments=_format_argument_list(allow_args)),
328 FutureWarning,
329 stacklevel=find_stack_level(),
330 )
--> 331 return func(*args, **kwargs)
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/io/parsers/readers.py:950, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, error_bad_lines, warn_bad_lines, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)
935 kwds_defaults = _refine_defaults_read(
936 dialect,
937 delimiter,
(...)
946 defaults={"delimiter": ","},
947 )
948 kwds.update(kwds_defaults)
--> 950 return _read(filepath_or_buffer, kwds)
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/io/parsers/readers.py:605, in _read(filepath_or_buffer, kwds)
602 _validate_names(kwds.get("names", None))
604 # Create the parser.
--> 605 parser = TextFileReader(filepath_or_buffer, **kwds)
607 if chunksize or iterator:
608 return parser
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1442, in TextFileReader.__init__(self, f, engine, **kwds)
1439 self.options["has_index_names"] = kwds["has_index_names"]
1441 self.handles: IOHandles | None = None
-> 1442 self._engine = self._make_engine(f, self.engine)
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1735, in TextFileReader._make_engine(self, f, engine)
1733 if "b" not in mode:
1734 mode += "b"
-> 1735 self.handles = get_handle(
1736 f,
1737 mode,
1738 encoding=self.options.get("encoding", None),
1739 compression=self.options.get("compression", None),
1740 memory_map=self.options.get("memory_map", False),
1741 is_text=is_text,
1742 errors=self.options.get("encoding_errors", "strict"),
1743 storage_options=self.options.get("storage_options", None),
1744 )
1745 assert self.handles is not None
1746 f = self.handles.handle
File ~/venvs/jupyter/lib/python3.10/site-packages/pandas/io/common.py:856, in get_handle(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)
851 elif isinstance(handle, str):
852 # Check whether the filename is to be opened in binary mode.
853 # Binary mode does not support 'encoding' and 'newline'.
854 if ioargs.encoding and "b" not in ioargs.mode:
855 # Encoding
--> 856 handle = open(
857 handle,
858 ioargs.mode,
859 encoding=ioargs.encoding,
860 errors=errors,
861 newline="",
862 )
863 else:
864 # Binary mode
865 handle = open(handle, ioargs.mode)
FileNotFoundError: [Errno 2] No such file or directory: 'hubble_data_no_headers.csv'
2.8.2. accés aux données#
data.info()
2.8.2.1. selection colonne#
data['dist'].tail()
2.8.2.2. sélection de ligne dans la colonne#
data['dist'][0:5]
2.8.2.3. sélection de plusieurs colonnes#
data[['rec_vel','dist','rec_vel']].head()
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()
2.8.3.2. suppression d’une colonne#
del data['dist2']
data.head()
2.8.3.3. suppression d’un ligne (attention paramêtre inplace)#
data.drop(1)
data.head()
data.iloc[1,0]
data.drop(1,inplace=True)
data.head()
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()
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("")
data['E'].plot(figsize=(12,6),lw=2,grid=True,style='-o')
2.8.4.2. traitement statistique:#
moyenne , medianne …
analyse par colonne
energy = data['E']
type(energy)
energy.mean(), energy.median()
analyse globale
data.describe()
2.8.5. analyse statistique#
variance, covariance, moyenne …..
matrice de covariance entre 3 tableaux x,y,z
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()
CV = data1.cov()
display(CV)
# CV est un datafrme : accès
CV['rec_vel']['dist'] , CV.iloc[0,1]
vérification: lissage par droite moindre carré V=F(dist)
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)
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")
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']
data['E'][2.]=1620
print(data.tail())
2.8.6.1. utilisation de .at (attention ligne, colonne)#
data.at[2.,'E']=1800
print(data.tail())
data.loc[2,'E']
2.8.7. autres structures de données#
Serie 1D
xarray: multi-dimensionnal
2.9. DataFrame a partir de fichier csv (exemple 2)#
data = pd.read_csv("wages_hours.csv")
data.head()
2.9.1. selection du bon séparateur#
data = pd.read_csv("wages_hours.csv", sep = "\t")
data.head()
#pd.read_csv?
2.9.2. Extraction de colonnes a traiter#
data2 = data[["AGE", "RATE"]]
data2.head()
2.9.3. tri des données#
data_sorted = data2.sort_values("AGE")
data_sorted.head()
2.9.3.1. indexation en fonction de la colonne AGE#
data_sorted.set_index("AGE", inplace=True)
data_sorted.head()
2.9.3.2. tracer du salaire en fonction de l’age#
data_sorted.plot()
plt.show()
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
2.10.2. localisation: timezone#
heure universelle
timezone:
Europe/Paris
date.tz_convert(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
ts = pd.Series(np.random.randn(len(date)),
index=date)
data= ts.to_frame(name='val')
data.head()
2.10.4. aggregation / mois#
data['Mois']=data.index.month
data.head()
datam=data.groupby('Mois').aggregate(np.mean)
datam.head()
datam.plot(kind='bar')
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)
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()
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
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()
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");
2.12. Bibliographie#
apprendre à lire et à chercher dans la documentation (très (trop) riche !!)