1A.data - DataFrame et Matrice

Links: notebook, html ., PDF, python, slides ., GitHub

Les DataFrame se sont imposés pour manipuler les données avec le module pandas. Le module va de la manipulation des données jusqu’au calcul d’une régresion linéaire.

Avec cette façon de représenter les données, associée à des un ensemble de méthodes couramment utilisées, ce qu’on faisait en une ou deux boucles se fait maintenant en une seule fonction. Cette séance contient beaucoup d’exemples et peu d’exercices. Il est conseillé de supprimer toutes les sorties et de les exécuter une à une.

from jyquickhelper import add_notebook_menu
add_notebook_menu()
run previous cell, wait for 2 seconds

L’introduction ne contient pas d’éléments nécessaires à la réalisation du TD.

Trouver chaussure à ses stats

La programmation est omni-présente lorsqu’on manipule des données. On leur applique des traitements parfois standards, souvent adaptés pour la circonstance. On souhaite toujours programmer le moins possible mais aussi ne pas avoir à réapprendre un langage à chaque fois qu’on doit manipuler les données.

Le logiciel MATLAB a proposé voici 30 ans un premier environnement de travail facilitant le calcul matriciel et ce standard s’est imposé depuis. Comme MATLAB est un logiciel payant, des équivalents open source et gratuits ont été développés. Ils proposent tous le calcul matriciel, la possibilité de visualiser, un environnement de développement. Ils différent pas des performances différentes et des éventails d’extensions différentes.

  • R : la référence pour les statisticiens, il est utilisé par tous les chercheurs dans ce domaine.
  • SciLab : développé par l”INRIA.
  • Octave : clone open source de MATLAB, il n’inclut pas autant de librairies mais il est gratuit.
  • Julia : c’est le plus jeune, il est plus rapide mais ses librairies sont moins nombreuses.

Ils sont tous performants en qui concerne le calcul numérique, ils le sont beaucoup moins lorsqu’il s’agit de faire des traitements qui ne sont pas numériques (traiter du texte par exemple) car ils n’ont pas été prévus pour cela à la base (à l’exception de Julia peut être qui est plus jeune Python v. Clojure v. Julia). Le langage Python est devenu depuis 2012 une alternative intéressante pour ces raisons (voir également Why Python?) :

  • Il propose les même fonctionnalités de base (calcul matriciel, graphiques, environnement).
  • Python est plus pratique pour tout ce qui n’est pas numérique (fichiers, web, server web, SQL, …).
  • La plupart des librairies connues et écrites en C++ ont été portée sous Python.
  • Il est plus facile de changer un composant important en Python (numpy par exemple) si le nouveau est plus efficace.

Un inconvénient peut-être est qu’il faut installer plusieurs extensions avant de pouvoir commencer à travailler (voir Installation de Python) :

Optionnels :

  • ggplot : extension très populaire sur R et portée sur Python
  • Spyder : environnement type R, MATLAB, …
  • scipy : autres traitements numériques (voir NumPy vs. SciPy vs. other packages)
  • dask : dataframe distribué et capables de gérer des gros volumes de données (> 5Go)

Les environnements Python évoluent très vite, les modules mentionnés ici sont tous maintenus mais il eut en surgir de nouveau très rapidement. Quelques environnements à suivre :

Si vous ne voulez pas programmer, il existe des alternatives. C’est assez performant sur de petits jeux de données mais cela devient plus complexe dès qu’on veut programmer car le code doit tenir compte des spécificités de l’outil.

  • Orange : écrit en Python
  • Weka : écrit en Java (le pionnier)
  • dataiku : startup française
  • RapidMiner : version gratuite et payante
  • AzureML : solution Microsoft de workflow de données

C’est parfois plus pratique pour commencer mais mal commode si on veut automatiser un traitrment pour répéter la même tâche de façon régulière. Pour les travaux pratiques à l’ENSAE, j’ai choisi les notebooks : c’est une page blanche où on peut mélanger texte, équations, graphiques, code et exécution de code.

Taille de DataFrame

Les DataFrame en Python sont assez rapides lorsqu’il y a moins de 10 millions d’observations et que le fichier texte qui décrit les données n’est pas plus gros que 10 Mo. Au delà, il faut soit être patient, soit être astucieux comme ici : DataFrame et SQL, Data Wrangling with Pandas.

Valeurs manquantes

Lorsqu’on récupère des données, il peut arriver qu’une valeur soit manquante.

DataFrame (pandas)

Quelques liens : An Introduction to Pandas

Un Data Frame est un objet qui est présent dans la plupart des logiciels de traitements de données, c’est une matrice, chaque colonne est de même type (nombre, dates, texte), elle peut contenir des valeurs manquantes. On peut considérer chaque colonne comme les variables d’une table (pandas.Dataframe - cette page contient toutes les méthodes de la classe).

import pandas
l = [ { "date":"2014-06-22", "prix":220.0, "devise":"euros" },
      { "date":"2014-06-23", "prix":221.0, "devise":"euros" },]
df = pandas.DataFrame(l)
df
date devise prix
0 2014-06-22 euros 220
1 2014-06-23 euros 221

2 rows × 3 columns

Avec une valeur manquante :

l = [ { "date":"2014-06-22", "prix":220.0, "devise":"euros" },
      { "date":"2014-06-23", "devise":"euros"  },]
df = pandas.DataFrame(l)
df
date devise prix
0 2014-06-22 euros 220
1 2014-06-23 euros NaN

2 rows × 3 columns

NaN est une convention pour une valeur manquante. On extrait la variable prix :

df.prix
0    220
1    NaN
Name: prix, dtype: float64

Ou :

df["prix"]
0    220
1    NaN
Name: prix, dtype: float64

Pour extraire plusieurs colonnes :

df [["date","prix"]]
date prix
0 2014-06-22 220
1 2014-06-23 NaN

2 rows × 2 columns

Pour prendre la transposée (voir aussi DataFrame.transpose) :

df.T
0 1
date 2014-06-22 2014-06-23
devise euros euros
prix 220 NaN

3 rows × 2 columns

Lecture et écriture de DataFrame

Aujourd’hui, on n’a plus besoin de réécrire soi-même une fonction de lecture ou d’écriture de données présentées sous forme de tables. Il existe des fonctions plus génériques qui gère un grand nombre de cas. Cette section présente brièvement les fonctions qui permettent de lire/écrire un DataFrame aux formats texte/Excel. On reprend l’exemple de section précédente. L’instruction encoding=utf-8 n’est pas obligatoire mais conseillée lorsque les données contiennent des accents (voir read_csv).

import pandas
l = [ { "date":"2014-06-22", "prix":220.0, "devise":"euros" },
      { "date":"2014-06-23", "prix":221.0, "devise":"euros" },]
df = pandas.DataFrame(l)

# écriture au format texte
df.to_csv("exemple.txt",sep="\t",encoding="utf-8", index=False)

# on regarde ce qui a été enregistré
with open("exemple.txt", "r", encoding="utf-8") as f : text = f.read()
print(text)

# on enregistre au format Excel
df.to_excel("exemple.xlsx", index=False)

# on ouvre Excel sur ce fichier (sous Windows)
from pyquickhelper.loghelper import run_cmd
from pyquickhelper.loghelper.run_cmd import skip_run_cmd
out,err = run_cmd("exemple.xlsx", wait = False)
date        devise  prix
2014-06-22  euros   220.0
2014-06-23  euros   221.0

On peut récupérer des données directement depuis Internet ou une chaîne de caractères et afficher le début (head) ou la fin (tail). Le code qui suit est ce qu’on écrirait d’habitude :

if False:
    import pandas, urllib.request
    furl = urllib.request.urlopen("http://www.xavierdupre.fr/enseignement/complements/marathon.txt")
    df = pandas.read_csv(furl, sep="\t", names=["ville", "annee", "temps","secondes"])
    df.head()

Et pout éviter les erreurs de connexion internet, les données font partie intégrante du module :

from ensae_teaching_cs.data import marathon
import pandas
df = pandas.read_csv(marathon(filename=True),
                     sep="\t", names=["ville", "annee", "temps","secondes"])
df.head()
ville annee temps secondes
0 PARIS 2011 02:06:29 7589
1 PARIS 2010 02:06:41 7601
2 PARIS 2009 02:05:47 7547
3 PARIS 2008 02:06:40 7600
4 PARIS 2007 02:07:17 7637

La fonction describe permet d’en savoir un peu plus sur les colonnes numériques de cette table.

df.describe()
annee secondes
count 359.000000 359.000000
mean 1989.754875 7933.660167
std 14.028545 385.289830
min 1947.000000 7382.000000
25% 1981.000000 7698.000000
50% 1991.000000 7820.000000
75% 2001.000000 8046.500000
max 2011.000000 10028.000000

8 rows × 2 columns

DataFrame et Index

On désigne généralement une colonne ou variable par son nom. Les lignes peuvent être désignées par un entier.

import pandas
l = [ { "date":"2014-06-22", "prix":220.0, "devise":"euros" },
      { "date":"2014-06-23", "prix":221.0, "devise":"euros" },]
df = pandas.DataFrame(l)
df
date devise prix
0 2014-06-22 euros 220
1 2014-06-23 euros 221

2 rows × 3 columns

On extrait une ligne (loc) :

df.iloc[1]
date      2014-06-23
devise         euros
prix             221
Name: 1, dtype: object

Mais il est possible d’utiliser une colonne ou plusieurs colonnes comme index (set_index) :

dfi = df.set_index("date")
dfi
devise prix
date
2014-06-22 euros 220
2014-06-23 euros 221

2 rows × 2 columns

On peut maintenant désigner une ligne par une date :

dfi.loc["2014-06-23"]
devise    euros
prix        221
Name: 2014-06-23, dtype: object

Il est possible d’utiliser plusieurs colonnes comme index :

df = pandas.DataFrame([ {"prénom":"xavier", "nom":"dupré", "arrondissement":18},
       {"prénom":"clémence", "nom":"dupré", "arrondissement":15 } ])
dfi = df.set_index(["nom","prénom"])
dfi.loc["dupré","xavier"]
arrondissement    18
Name: (dupré, xavier), dtype: int64

Si on veut changer l’index ou le supprimer (reset_index) :

dfi.reset_index(drop=False, inplace=True)
        # le mot-clé drop pour garder ou non les colonnes servant d'index
        # inplace signifie qu'on modifie l'instance et non qu'une copie est modifiée
        # donc on peut aussi écrire dfi2 = dfi.reset_index(drop=False)
dfi.set_index(["nom", "arrondissement"],inplace=True)
dfi
prénom
nom arrondissement
dupré 18 xavier
15 clémence

2 rows × 1 columns

Les index sont particulièrement utiles lorsqu’il s’agit de fusionner deux tables. Pour des petites tables, la plupart du temps, il est plus facile de s’en passer.

Notation avec le symbole :

Le symbole : désigne une plage de valeurs.

from ensae_teaching_cs.data import marathon
import pandas
df = pandas.read_csv(marathon(filename=True),
                     sep="\t", names=["ville", "annee", "temps","secondes"])
df.head()
ville annee temps secondes
0 PARIS 2011 02:06:29 7589
1 PARIS 2010 02:06:41 7601
2 PARIS 2009 02:05:47 7547
3 PARIS 2008 02:06:40 7600
4 PARIS 2007 02:07:17 7637

On peut sélectionner un sous-ensemble de lignes :

df[3:6]
ville annee temps secondes
3 PARIS 2008 02:06:40 7600
4 PARIS 2007 02:07:17 7637
5 PARIS 2006 02:08:03 7683

3 rows × 4 columns

On extrait la même plage mais avec deux colonnes seulement :

df.loc[3:6,["annee","temps"]]
annee temps
3 2008 02:06:40
4 2007 02:07:17
5 2006 02:08:03
6 2005 02:08:02

4 rows × 2 columns

Le même code pour lequel on renomme les colonnes extraites :

sub = df.loc[3:6,["annee","temps"]]
sub.columns = ["year","time"]
sub
year time
3 2008 02:06:40
4 2007 02:07:17
5 2006 02:08:03
6 2005 02:08:02

4 rows × 2 columns

Exercice 1 : créer un fichier Excel

On souhaite récupérer les données donnees_enquete_2003_television.txt (source : INSEE).

  • POIDSLOG : Pondération individuelle relative
  • POIDSF : Variable de pondération individuelle
  • cLT1FREQ : Nombre d’heures en moyenne passées à regarder la télévision
  • cLT2FREQ : Unité de temps utilisée pour compter le nombre d’heures passées à regarder la télévision, cette unité est représentée par les quatre valeurs suivantes
    • 0 : non concerné
    • 1 : jour
    • 2 : semaine
    • 3 : mois

Ensuite, on veut :

  1. Supprimer les colonnes vides
  2. Obtenir les valeurs distinctes pour la colonne cLT2FREQ
  3. Modifier la matrice pour enlever les lignes pour lesquelles l’unité de temps (cLT2FREQ) n’est pas renseignée ou égale à zéro.
  4. Sauver le résultat au format Excel.

Vous aurez peut-être besoin des fonctions suivantes :

import pandas, io
# ...

Manipuler un DataFrame : filtrer, union, sort, group by, join, pivot

Si la structure DataFrame s’est imposée, c’est parce qu’on effectue toujours les mêmes opérations. Chaque fonction cache une boucle ou deux dont le coût est précisé en fin de ligne :

  • filter : on sélectionne un sous-ensemble de lignes qui vérifie une condition \rightarrow O(n)
  • union : concaténation de deux jeux de données \rightarrow O(n_1 + n_2)
  • sort : tri \rightarrow O(n \ln n)
  • group by : grouper des lignes qui partagent une valeur commune \rightarrow O(n)
  • join : fusionner deux jeux de données en associant les lignes qui partagent une valeur commune \rightarrow \in [O(n_1 + n_2), O(n_1 n_2)]
  • pivot : utiliser des valeurs présentes dans colonne comme noms de colonnes \rightarrow O(n)

Les 5 premières opérations sont issues de la logique de manipulation des données avec le langage SQL (ou le logiciel SAS). La dernière correspond à un tableau croisé dynamique. Pour illustrer ces opérations, on prendre le DataFrame suivant :

from ensae_teaching_cs.data import marathon
import pandas
df = pandas.read_csv(marathon(), sep="\t", names=["ville", "annee", "temps","secondes"])
print(df.columns)
print("villes",set(df.ville))
print("annee",list(set(df.annee))[:10],"...")
Index(['ville', 'annee', 'temps', 'secondes'], dtype='object')
villes {'FUKUOKA', 'STOCKOLM', 'PARIS', 'CHICAGO', 'AMSTERDAM', 'BOSTON', 'BERLIN', 'LONDON', 'NEW YORK'}
annee [1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956] ...

filter

Filter consiste à sélectionner un sous-ensemble de lignes du dataframe. Pour filter sur plusieurs conditions, il faut utiliser les opérateurs logique & (et), | (ou), ~ (non) (voir Mapping Operators to Functions).

subset = df [ df.annee == 1971 ]
subset.head()
ville annee temps secondes
112 FUKUOKA 1971 02:12:51 7971
204 NEW YORK 1971 02:22:54 8574
285 BOSTON 1971 02:18:45 8325
subset = df [ (df.annee == 1971) & (df.ville == "BOSTON") ]
subset.head()
ville annee temps secondes
285 BOSTON 1971 02:18:45 8325

union

union = concaténation de deux DataFrame (qui n’ont pas nécessaire les mêmes colonnes). On peut concaténer les lignes ou les colonnes.

concat_ligne = pandas.concat((df,df))
df.shape,concat_ligne.shape
((360, 4), (720, 4))
concat_col = pandas.concat((df,df), axis=1)
df.shape,concat_col.shape
((360, 4), (360, 8))

sort

Sort = trier

tri = df.sort_values( ["annee", "ville"], ascending=[0,1])
tri.head()
ville annee temps secondes
35 BERLIN 2011 02:03:38 7418
326 BOSTON 2011 02:03:02 7382
203 LONDON 2011 02:04:40 7480
0 PARIS 2011 02:06:29 7589
277 STOCKOLM 2011 02:14:07 8047

5 rows × 4 columns

group by

Cette opération consiste à grouper les lignes qui partagent une caractéristique commune (une ou ou plusieurs valeurs par exemple). Sur chaque groupe, on peut calculer une somme, une moyenne…

gr = df.groupby("annee")
gr
<pandas.core.groupby.DataFrameGroupBy object at 0x04887DB0>
nb = gr.count()
nb.sort_index(ascending=False).head()
ville annee temps secondes
annee
2011 5 5 5 5
2010 9 9 9 9
2009 9 9 9 9
2008 9 9 9 9
2007 9 9 9 9

5 rows × 4 columns

nb = gr.sum()
nb.sort_index(ascending=False).head(n=2)
secondes
annee
2011 37916
2010 68673

2 rows × 1 columns

nb = gr.mean()
nb.sort_index(ascending=False).head(n=3)
secondes
annee
2011 7583.200000
2010 7630.333333
2009 7652.555556

3 rows × 1 columns

Si les nom des colonnes utilisées lors de l’opération ne sont pas mentionnés, implicitement, c’est l’index qui sera choisi. On peut aussi aggréger les informations avec une fonction personnalisée.

def max_entier(x):
    return int(max(x))
nb = df[["annee","secondes"]].groupby("annee").agg(max_entier).reset_index()
nb.tail(n=3)
annee secondes
62 2009 8134
63 2010 7968
64 2011 8047

3 rows × 2 columns

Ou encore considérer des aggrégations différentes pour chaque colonne :

nb = df[["annee","ville","secondes"]].groupby("annee").agg({ "ville":len, "secondes":max_entier})
nb.tail(n=3)
secondes ville
annee
2009 8134 9
2010 7968 9
2011 8047 5

3 rows × 2 columns

join (merge ou fusion)

Fusionner deux tables consiste à apparier les lignes de la première table avec celle de la seconde si certaines colonnes de ces lignes partagent les mêmes valeurs. On distingue quatre cas :

  • INNER JOIN - inner : on garde tous les appariements réussis
  • LEFT OUTER JOIN - left : on garde tous les appariements réussis et les lignes non appariées de la table de gauche
  • RIGHT OUTER JOIN - right : on garde tous les appariements réussis et les lignes non appariées de la table de droite
  • FULL OUTER JOIN - outer : on garde tous les appariements réussis et les lignes non appariées des deux tables

Exemples et documentation : * merging, joining * join * merge ou DataFrame.merge * jointures SQL - illustrations avec graphiques en patates

Si les noms des colonnes utilisées lors de la fusion ne sont pas mentionnés, implicitement, c’est l’index qui sera choisi. Pour les grandes tables (> 100.000 lignes), il est fortement recommandés d’ajouter un index s’il n’existe pas avant de fusionner.

A quoi correspondent les quatre cas suivants :

from IPython.display import Image
Image("patates.png")
../_images/td1a_cenonce_session_10_68_0.png

On souhaite ajouter une colonne pays aux marathons se déroulant dans les villes suivanes.

values = [  {"V":'BOSTON', "C":"USA"},
            {"V":'NEW YORK', "C":"USA"},
            {"V":'BERLIN', "C":"Germany"},
            {"V":'LONDON', "C":"UK"},
            {"V":'PARIS', "C":"France"}]
pays = pandas.DataFrame(values)
pays
C V
0 USA BOSTON
1 USA NEW YORK
2 Germany BERLIN
3 UK LONDON
4 France PARIS

5 rows × 2 columns

dfavecpays = df.merge(pays, left_on="ville", right_on="V")
pandas.concat([dfavecpays.head(n=2),dfavecpays.tail(n=2)])
ville annee temps secondes C V
0 PARIS 2011 02:06:29 7589 France PARIS
1 PARIS 2010 02:06:41 7601 France PARIS
193 BOSTON 2010 02:05:52 7552 USA BOSTON
194 BOSTON 2011 02:03:02 7382 USA BOSTON

4 rows × 6 columns

pivot (tableau croisé dynamique)

Cette opération consiste à créer une seconde table en utilisant utiliser les valeurs d’une colonne comme nom de colonnes.

A B C
A1 B1 C1
A1 B2 C2
A2 B1 C3
A2 B2 C4
A2 B3 C5

L’opération pivot(A,B,C) donnera :

A B1 B2 B3
A1 C1 C2  
A2 C3 C4 C5

On applique cela aux marathons où on veut avoir les villes comme noms de colonnes et une année par lignes.

piv = df.pivot("annee","ville","temps")
pandas.concat([piv[20:23],piv[40:43],piv.tail(n=3)])
ville AMSTERDAM BERLIN BOSTON CHICAGO FUKUOKA LONDON NEW YORK PARIS STOCKOLM
annee
1967 NaN NaN 02:15:45 NaN 02:09:37 NaN NaN NaN NaN
1968 NaN NaN 02:22:17 NaN 02:10:48 NaN NaN NaN NaN
1969 NaN NaN 02:13:49 NaN 02:11:13 NaN NaN NaN NaN
1987 02:12:40 02:11:11 02:11:50 NaN 02:08:18 02:09:50 02:11:01 02:11:09 02:13:52
1988 02:12:38 02:11:45 02:08:43 02:08:57 02:11:04 02:10:20 02:08:20 02:13:53 02:14:26
1989 02:13:52 02:10:11 02:09:06 02:11:25 02:12:54 02:09:03 02:08:01 02:13:03 02:13:34
2009 02:06:18 02:06:08 02:08:42 02:05:41 02:05:18 02:05:10 02:09:15 02:05:47 02:15:34
2010 02:05:44 02:05:08 02:05:52 02:06:23 02:08:24 02:05:19 02:08:14 02:06:41 02:12:48
2011 NaN 02:03:38 02:03:02 NaN NaN 02:04:40 NaN 02:06:29 02:14:07

9 rows × 9 columns

Il existe une méthode qui effectue l’opération inverse : Dataframe.stack.

Exercice 2 : moyennes par groupes

Toujours avec le même jeu de données (marathon.txt), on veut ajouter une ligne à la fin du tableau croisé dynamique contenant la moyenne en secondes des temps des marathons pour chaque ville.

Dates

Les dates sont souvent compliquées à gérer car on n’utilise pas le mêmes format dans tous les pays. Pour faire simple, je recommande deux options :

Par exemple, voici le code qui a permis de générer la colonne seconde de la table marathon :

from datetime import datetime, time
from ensae_teaching_cs.data import marathon
import pandas
df = pandas.read_csv(marathon(), sep="\t", names=["ville", "annee", "temps","secondes"])
df = df [["ville", "annee", "temps"]]  # on enlève la colonne secondes pour la recréer
df["secondes"] = df.apply( lambda r : (datetime.strptime(r.temps,"%H:%M:%S") - \
                                         datetime(1900,1,1)).total_seconds(), axis=1)
df.head()
ville annee temps secondes
0 PARIS 2011 02:06:29 7589.0
1 PARIS 2010 02:06:41 7601.0
2 PARIS 2009 02:05:47 7547.0
3 PARIS 2008 02:06:40 7600.0
4 PARIS 2007 02:07:17 7637.0

Matrix, Array (numpy)

Le module le plus populaire sous Python est numpy. Il propose deux containers Matrix et Array qui facilitent le calcul matriciel. Ce module est écrit en C++, Fortran. Il sera plus rapide que tout code écrit en Python. De nombreuses modules Python s’appuient sur numpy : SciPy, pandas, scikit-learn, matplotlib, … Il y a deux différences entre un DataFrame et un tableau numpy :

  • Il n’y a pas d’index sur les lignes autre que l’index entier de la ligne.
  • Tous les types doivent être identiques (tous entier, tous réels, tous str). Il n’y a pas de mélange possible. C’est à cette condition que les calculs sont aussi rapides.
import numpy
print("int","\n",numpy.matrix([[1, 2], [3, 4,]]))
print("float","\n",numpy.matrix([[1, 2], [3, 4.1]]))
print("str","\n",numpy.matrix([[1, 2], [3, '4']]))
int
 [[1 2]
 [3 4]]
float
 [[ 1.   2. ]
 [ 3.   4.1]]
str
 [['1' '2']
 ['3' '4']]

Il y a deux types d’objets, array et matrix. Le type matrix se comporte comme on peut l’attendre d’une matrice. Le type array est plus générique et autorise plus de deux dimensions. Les opérateurs qui s’y appliquent ne comportent pas comme ceux d’une matrice, en particulier la multiplication qui se fait terme à terme pour un tableau.

m1 = numpy.matrix( [[0.0,1.0],[1.0,0.0]])
print("multiplication de matrices\n",m1 * m1)
m2 = numpy.array([[0.0,1.0],[1.0,0.0]])
print("multiplication de tableaux (terme à terme)\n",m2 * m2)
multiplication de matrices
 [[ 1.  0.]
 [ 0.  1.]]
multiplication de tableaux (terme à terme)
 [[ 0.  1.]
 [ 1.  0.]]

Un tableau en plusieurs dimensions :

cube = numpy.array( [  [[0.0,1.0],[1.0,0.0]],
                       [[0.0,1.0],[1.0,0.0]] ] )
print(cube.shape)
cube
(2, 2, 2)
array([[[ 0.,  1.],
        [ 1.,  0.]],

       [[ 0.,  1.],
        [ 1.,  0.]]])

Quelques liens pour apprendre à manipuler ces objets :

matrices nulle, identité, aléatoire

On utilise beaucoup les fonctions suivantes pour créer une matrice ou un tableau particulier.

# la matrice nulle
numpy.zeros( (3,4) )
array([[ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.]])
# la matrice de 1
numpy.ones( (3,4) )
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])
# la matrice identité
numpy.identity( 3 )
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
# la matrice aléatoire
numpy.random.random( (3,4))
array([[ 0.56295296,  0.77545561,  0.56041393,  0.90371888],
       [ 0.09984123,  0.59781939,  0.09845057,  0.30856921],
       [ 0.37161512,  0.5630934 ,  0.6359542 ,  0.13298039]])

Pour d’autres fonctionnalités aléatoires : numpy.random.

Quelques fonctions fréquemment utilisées

  • column_stack : pour assembler des colonnes les unes à côté des autres
  • vstack : pour assembler des lignes les unes à la suite des autres

de DataFrame à numpy

Le plus simple est sans doute d’utiliser pandas pour lire un fichier texte et d’utiliser la fonction as_matrix pour convertir tout ou partie du DataFrame en numpy.matrix.

from pandas import read_csv
import numpy
from datetime import datetime, time
from ensae_teaching_cs.data import marathon
df = read_csv(marathon(filename=True),
                     sep="\t", names=["ville", "annee", "temps","secondes"])
arr = df[["annee","secondes"]].as_matrix() # retourne un array (et non un matrix)
mat = numpy.matrix(arr)
print(type(arr),type(mat))
arr[:2,:]
<class 'numpy.ndarray'> <class 'numpy.matrixlib.defmatrix.matrix'>
array([[2011, 7589],
       [2010, 7601]], dtype=int64)

La conversion réciproque est aussi simple mais il faut préciser les noms des colonnes qui ne sont pas mémorisées dans l’objet numpy.array :

import pandas
df2 = pandas.DataFrame(arr, columns=["annee", "secondes"])
df2.head(n=2)
annee secondes
0 2011 7589
1 2010 7601

2 rows × 2 columns

Exercice 3 : régression linéaire

On souhaite implémenter une régression qui se traduit par le problème suivant : Y=XA+\epsilon. La solution est donnée par la formule matricielle : A^*=(X'X)^{-1}X'Y. On prépare les données suivantes.

from pandas import read_csv
from datetime import datetime, time
from ensae_teaching_cs.data import marathon
df = read_csv(marathon(filename=True),
              sep="\t", names=["ville", "annee", "temps","secondes"])
df = df [ (df["ville"] == "BERLIN") | (df["ville"] == "PARIS") ]
for v in ["PARIS","BERLIN"]:
    df["est" + v] = df.apply( lambda r : 1 if r["ville"] == v else 0, axis=1)
df.head(n = 3)
ville annee temps secondes estPARIS estBERLIN
0 PARIS 2011 02:06:29 7589 1 0
1 PARIS 2010 02:06:41 7601 1 0
2 PARIS 2009 02:05:47 7547 1 0

On veut construire le modèle : secondes = a_0 \; annee + a_1 \; stPARIS + a_2 \; estBERLIN. En appliquant la formule ci-dessus, déterminer les coefficients a_0,a_1,a_2.

Annexes

Créer un fichier Excel avec plusieurs feuilles

La page Allow ExcelWriter() to add sheets to existing workbook donne plusieurs exemples d’écriture.

import pandas
writer = pandas.ExcelWriter('tou_example.xlsx')
df.to_excel(writer, 'Data 0')
df.to_excel(writer, 'Data 1')