Traitement de données avec pandas

Pandas est une bibliothèque de données.

  • Données uni-dimensionnelles et temporelles (Series),
  • Données bi-dimensionnelles (DataFrame),
  • Traitement : indexation, extraction, allignement, regroupement, jointures,
  • Très efficace (code critique écrit en C/Cython),
  • Dessin (via matplotlib)
  • Parfaitement intégré avec IPython.
In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Données uni-dimensionnelles

Des tableaux homogènes, basés sur les arrays numpy.

  • Plus efficaces que les listes
  • Opérations élément-par-élément (comme les arrays numpy)
In [2]:
s = pd.Series(range(10))
s
Out[2]:
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64
In [3]:
10*s
Out[3]:
0     0
1    10
2    20
3    30
4    40
5    50
6    60
7    70
8    80
9    90
dtype: int64
In [4]:
s * s
Out[4]:
0     0
1     1
2     4
3     9
4    16
5    25
6    36
7    49
8    64
9    81
dtype: int64

Méthode .plot(), appelle matplotlib

In [5]:
# toujours cette ligne dans IPython pour afficher les graphiques
%matplotlib inline
In [6]:
x = s * s
x.plot()
Out[6]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb91552f050>

L'abscisse est appelée index dans le jargon de pandas.

Elle peut contenir tout type de données, mais ses valeurs doivent être uniques.

In [7]:
t = pd.Series([1,2,3], ['a', 'b', 'c'])
t
Out[7]:
a    1
b    2
c    3
dtype: int64
In [8]:
t.plot()
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb9154a57d0>

Les Series se comportent comme des tableaux associatifs

In [9]:
t['b']
Out[9]:
2

Support pour les dates

In [10]:
pd.Series(range(10,0,-1), index=pd.date_range('2015-3-23', periods=10)).plot()
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb9155563d0>

Données bi-dimensionnelles

In [11]:
d = pd.DataFrame({
    'Pression'    : range(10, 22),
    'Temperature' : np.random.randn(12)
})
d
Out[11]:
Pression Temperature
0 10 -0.842207
1 11 -0.703170
2 12 -0.787609
3 13 0.877794
4 14 -1.113236
5 15 -0.130890
6 16 0.038141
7 17 0.072686
8 18 0.689185
9 19 0.139529
10 20 1.711627
11 21 0.554641
In [12]:
d.plot()
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb911ac8250>

Les colonnes sont indexables

In [13]:
d['Pression']
Out[13]:
0     10
1     11
2     12
3     13
4     14
5     15
6     16
7     17
8     18
9     19
10    20
11    21
Name: Pression, dtype: int64
In [14]:
d['Temperature'].plot()
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb911b99950>

Les lignes sont indexables

In [15]:
d[2:7]
Out[15]:
Pression Temperature
2 12 -0.787609
3 13 0.877794
4 14 -1.113236
5 15 -0.130890
6 16 0.038141

Ordonner

In [16]:
d.sort(columns="Temperature")
Out[16]:
Pression Temperature
4 14 -1.113236
0 10 -0.842207
2 12 -0.787609
1 11 -0.703170
5 15 -0.130890
6 16 0.038141
7 17 0.072686
9 19 0.139529
11 21 0.554641
8 18 0.689185
3 13 0.877794
10 20 1.711627

Statistiques

In [17]:
d.max()
Out[17]:
Pression       21.000000
Temperature     1.711627
dtype: float64
In [18]:
d.describe()
Out[18]:
Pression Temperature
count 12.000000 12.000000
mean 15.500000 0.042208
std 3.605551 0.829465
min 10.000000 -1.113236
25% 12.750000 -0.724280
50% 15.500000 0.055413
75% 18.250000 0.588277
max 21.000000 1.711627

Les tableaux sont modifiables

In [19]:
d['Pression'] = np.random.randn(12)
d
Out[19]:
Pression Temperature
0 0.184172 -0.842207
1 -0.662485 -0.703170
2 -0.577594 -0.787609
3 1.090871 0.877794
4 -0.433989 -1.113236
5 -0.903735 -0.130890
6 -0.250935 0.038141
7 0.744094 0.072686
8 1.663164 0.689185
9 0.874518 0.139529
10 0.648450 1.711627
11 -1.518451 0.554641
In [20]:
d['Volume'] = range(12,0,-1)
d
Out[20]:
Pression Temperature Volume
0 0.184172 -0.842207 12
1 -0.662485 -0.703170 11
2 -0.577594 -0.787609 10
3 1.090871 0.877794 9
4 -0.433989 -1.113236 8
5 -0.903735 -0.130890 7
6 -0.250935 0.038141 6
7 0.744094 0.072686 5
8 1.663164 0.689185 4
9 0.874518 0.139529 3
10 0.648450 1.711627 2
11 -1.518451 0.554641 1

Transposée

In [21]:
d.T
Out[21]:
0 1 2 3 4 5 6 7 8 9 10 11
Pression 0.184172 -0.662485 -0.577594 1.090871 -0.433989 -0.903735 -0.250935 0.744094 1.663164 0.874518 0.648450 -1.518451
Temperature -0.842207 -0.703170 -0.787609 0.877794 -1.113236 -0.130890 0.038141 0.072686 0.689185 0.139529 1.711627 0.554641
Volume 12.000000 11.000000 10.000000 9.000000 8.000000 7.000000 6.000000 5.000000 4.000000 3.000000 2.000000 1.000000

Sélectionner des colonnes

In [22]:
d[['Pression', 'Volume']]
Out[22]:
Pression Volume
0 0.184172 12
1 -0.662485 11
2 -0.577594 10
3 1.090871 9
4 -0.433989 8
5 -0.903735 7
6 -0.250935 6
7 0.744094 5
8 1.663164 4
9 0.874518 3
10 0.648450 2
11 -1.518451 1

Les tableaux aussi s'additionnent composante par composante

In [23]:
d+d
Out[23]:
Pression Temperature Volume
0 0.368343 -1.684414 24
1 -1.324969 -1.406340 22
2 -1.155188 -1.575218 20
3 2.181742 1.755588 18
4 -0.867978 -2.226472 16
5 -1.807470 -0.261780 14
6 -0.501871 0.076282 12
7 1.488188 0.145372 10
8 3.326329 1.378370 8
9 1.749037 0.279058 6
10 1.296901 3.423255 4
11 -3.036902 1.109281 2

Sélectionner

In [24]:
a = pd.DataFrame({
    'qui'    : ['Jean', 'Garfield', 'Milou', 'Bob'],
    'espece' : ['homme', 'chat', 'chien', 'chien']
})
a
Out[24]:
espece qui
0 homme Jean
1 chat Garfield
2 chien Milou
3 chien Bob
In [25]:
a[a.espece == 'chien']
Out[25]:
espece qui
2 chien Milou
3 chien Bob
In [26]:
a[a.espece.isin(['homme', 'chat'])]
Out[26]:
espece qui
0 homme Jean
1 chat Garfield

Jointures

Une jointure est la fusion de deux tableaux le long d'une colonne

In [27]:
b = pd.DataFrame({
    'espece' : ['chien', 'chat', 'homme', 'homme', 'homme'],
    'sons'   : ['abboie', 'miaule', 'parle', 'crie', 'hurle']
})
b
Out[27]:
espece sons
0 chien abboie
1 chat miaule
2 homme parle
3 homme crie
4 homme hurle
In [28]:
a.merge(b)
Out[28]:
espece qui sons
0 homme Jean parle
1 homme Jean crie
2 homme Jean hurle
3 chat Garfield miaule
4 chien Milou abboie
5 chien Bob abboie

Regrouper

In [29]:
import urllib2, json
prenoms = urllib2.urlopen('http://opendata.paris.fr/api/records/1.0/download?dataset=liste_des_prenoms_2004_a_2012&format=json')
donnees_brutes = json.load(prenoms)
donnees_brutes[0]
Out[29]:
{u'datasetid': u'liste_des_prenoms_2004_a_2012',
 u'fields': {u'annee': 2011,
  u'nombre': 12,
  u'prenoms': u'Zachary',
  u'sexe': u'M'},
 u'record_timestamp': u'2015-03-16T17:33:33.316694',
 u'recordid': u'aa8805d011cdcc1b9cf1abcd8febd8946028050b'}
In [30]:
p = pd.DataFrame([d['fields'] for d in donnees_brutes])
p
Out[30]:
annee nombre prenoms sexe
0 2011 12 Zachary M
1 2011 5 Zakary M
2 2011 6 Zephyr M
3 2011 141 Zoe F
4 2011 6 Aïsha F
5 2011 16 Aïssatou F
6 2011 10 Chaïma F
7 2011 9 Ilyès M
8 2011 13 Naëlle F
9 2011 8 Leïna F
10 2011 6 Michaël M
11 2011 6 Selène F
12 2011 5 Shaïma F
13 2011 27 Thaïs F
14 2011 23 Angèle F
15 2011 9 Athenaïs F
16 2011 203 Inès F
17 2011 25 Maïssa F
18 2011 20 Helène F
19 2011 10 Naïla F
20 2011 6 Naïl M
21 2012 6 Aïdan M
22 2012 5 Annaëlle F
23 2012 11 Gaïa F
24 2012 36 Kaïs M
25 2012 5 Maï F
26 2012 11 Aaliyah F
27 2012 14 Abdoulaye M
28 2012 7 Abigail F
29 2012 5 Abigaïl F
... ... ... ... ...
13516 2014 5 Brayan M
13517 2014 5 Ezio M
13518 2014 5 Yohann M
13519 2014 5 Demba M
13520 2014 5 Éliott M
13521 2014 5 Daouda M
13522 2014 5 Barnabé M
13523 2014 5 Yaron M
13524 2014 5 Fallou M
13525 2014 5 Emir M
13526 2014 5 Aharon M
13527 2014 5 Salem M
13528 2014 5 Taha M
13529 2014 5 Iliane M
13530 2014 5 Mattia M
13531 2014 5 Giulio M
13532 2014 5 Giovanni M
13533 2014 5 Justin M
13534 2014 5 Gautier M
13535 2014 5 Éthan M
13536 2014 5 Kader M
13537 2014 5 Khaled M
13538 2014 5 Malone M
13539 2014 5 Francesco M
13540 2014 5 Frédéric M
13541 2014 5 Zayd M
13542 2014 5 Lirone M
13543 2014 5 Islem M
13544 2014 5 Kenzi M
13545 2014 5 Tim M

13546 rows × 4 columns

In [31]:
p.groupby(p.annee).max()
Out[31]:
annee nombre prenoms sexe
annee
2004 2004 319 Zoe X
2005 2005 329 Zoe X
2006 2006 314 Zoe X
2007 2007 313 Zoe X
2008 2008 316 Zoe X
2009 2009 350 Zuzanna X
2010 2010 398 Zoë X
2011 2011 374 Zoe M
2012 2012 370 Zohra M
2013 2013 381 Éva M
2014 2014 370 Éva M
In [32]:
x = p.groupby(p.annee).sum()
x
Out[32]:
annee nombre
annee
2004 2186364 32645
2005 2215525 32231
2006 2324954 33128
2007 2259882 32098
2008 2277072 32855
2009 2422854 33949
2010 2442150 34130
2011 2783224 33952
2012 2794668 33120
2013 2870538 34702
2014 2640354 31671
In [33]:
x['nombre'].plot()
Out[33]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb9119c4790>
In [34]:
p.groupby(['annee', 'sexe']).sum()
Out[34]:
nombre
annee sexe
2004 F 15683
M 16948
X 14
2005 F 15754
M 16457
X 20
2006 F 16103
M 16969
X 56
2007 F 15466
M 16611
X 21
2008 F 16172
M 16655
X 28
2009 F 16678
M 17231
X 40
2010 F 16627
M 17461
X 42
2011 F 16603
M 17349
2012 F 16070
M 17050
2013 F 17362
M 17340
2014 F 15068
M 16603

Pivoter

In [35]:
p.pivot_table(values='nombre', columns='annee', index='prenoms').sort(2014, ascending=False)
Out[35]:
annee 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014
prenoms
Gabriel 252 239 285 313.0 316 350 398.0 374.0 370 381 370
Adam 169 187 218 243.0 271 279 295.0 287.0 340 330 344
Raphaël 280 269 314 303.0 280 299 298.0 300.0 251 NaN 316
Louise 206 195 218 265.0 288 322 335.0 306.0 306 347 310
Louis 237 207 247 256.0 254 257 272.0 242.0 277 277 301
Paul 294 248 243 257.0 242 274 252.0 245.0 242 222 269
Arthur 203 261 225 241.0 267 258 302.0 306.0 299 271 255
Chloé NaN NaN NaN NaN NaN NaN NaN NaN NaN 236 216
Inès 268 282 255 199.0 227 216 207.0 203.0 201 201 211
Alice 193 193 153 190.0 136 202 213.0 203.0 197 229 210
Victor 226 173 184 181.0 207 207 219.0 191.0 204 218 208
Jeanne 156 179 159 192.0 175 172 192.0 197.0 163 183 201
Mohamed 150 155 155 130.0 144 169 156.0 187.0 163 211 199
Alexandre 319 329 309 298.0 260 286 267.0 243.0 217 220 198
Sarah 204 221 232 214.0 241 225 246.0 198.0 189 201 176
Lucas 189 197 168 214.0 192 221 186.0 191.0 175 166 173
Jules 187 209 161 216.0 190 199 168.0 163.0 162 165 172
Hugo 210 161 157 158.0 135 161 148.0 142.0 165 196 170
Joseph 78 75 93 84.0 89 126 120.0 125.0 120 150 163
Lina 91 94 114 125.0 136 144 169.0 154.0 142 153 162
Emma 265 274 282 246.0 255 239 250.0 216.0 177 168 161
Adèle 67 88 97 80.0 78 85 100.0 98.0 133 103 154
Rose 51 74 68 85.0 91 125 103.0 109.0 108 111 145
Juliette 170 137 149 154.0 166 175 165.0 180.0 163 139 145
Eva 139 69 83 87.5 82 87 102.5 84.5 65 94 145
Anna 117 141 116 119.0 127 130 161.0 132.0 146 128 144
Gaspard 100 119 112 135.0 145 122 127.0 129.0 170 173 139
Martin 118 106 107 105.0 150 111 123.0 113.0 99 94 136
Léa NaN NaN NaN NaN NaN NaN NaN NaN NaN 131 133
Jade 139 130 119 129.0 136 148 142.0 135.0 126 100 133
... ... ... ... ... ... ... ... ... ... ... ...
Zelie 13 11 10 14.0 25 33 24.0 30.0 32 NaN NaN
Zephyr NaN NaN NaN NaN NaN NaN NaN 6.0 NaN NaN NaN
Zeynab NaN NaN NaN NaN NaN NaN NaN NaN NaN 7 NaN
Ziad NaN NaN NaN NaN NaN NaN NaN NaN 5 NaN NaN
Zina NaN NaN NaN 6.0 NaN NaN NaN NaN NaN NaN NaN
Zinedine NaN NaN NaN NaN NaN NaN NaN NaN NaN 6 NaN
Zoe 109 100 117 137.0 141 154 113.0 141.0 112 NaN NaN
Zoé NaN NaN NaN NaN NaN NaN NaN NaN NaN 104 NaN
Zoë NaN NaN NaN NaN NaN 6 6.0 NaN NaN NaN NaN
Zoë NaN NaN NaN NaN NaN 6 6.0 NaN NaN NaN NaN
Zuzanna NaN NaN NaN NaN NaN 6 NaN NaN NaN NaN NaN
Zyad NaN NaN NaN NaN NaN NaN NaN NaN NaN 5 NaN
Zélie NaN NaN NaN NaN NaN NaN NaN NaN NaN 24 NaN
Ève NaN NaN NaN NaN NaN NaN NaN NaN NaN 7 NaN
Édouard NaN NaN NaN NaN NaN NaN NaN NaN NaN 6 NaN
Élias NaN NaN NaN NaN NaN NaN NaN NaN NaN 6 NaN
Élie NaN NaN NaN NaN NaN NaN NaN NaN NaN 9 NaN
Éline NaN NaN NaN NaN NaN NaN NaN NaN NaN 7 NaN
Élisa NaN NaN NaN NaN NaN NaN NaN NaN NaN 13 NaN
Élise NaN NaN NaN NaN NaN NaN NaN NaN NaN 15 NaN
Éloïse NaN NaN NaN NaN NaN NaN NaN NaN NaN 8 NaN
Éléna NaN NaN NaN NaN NaN NaN NaN NaN NaN 7 NaN
Éléonore NaN NaN NaN NaN NaN NaN NaN NaN NaN 31 NaN
Émile NaN NaN NaN NaN NaN NaN NaN NaN NaN 10 NaN
Émilie NaN NaN NaN NaN NaN NaN NaN NaN NaN 17 NaN
Énora NaN NaN NaN NaN NaN NaN NaN NaN NaN 8 NaN
Étienne NaN NaN NaN NaN NaN NaN NaN NaN NaN 5 NaN
Éva NaN NaN NaN NaN NaN NaN NaN NaN NaN 9 NaN
Éléna NaN NaN NaN NaN NaN NaN NaN NaN NaN 7 NaN
Étienne NaN NaN NaN NaN NaN NaN NaN NaN NaN 5 NaN

2415 rows × 11 columns