Apprivoiser l'ami ursidé de Python : Pandas

Durant mon stage de M2, j’ai eu l’occasion de cha­touiller ce drôle d’animal qu’est pan­das. En effet, j’ai tra­vaillé sur des don­nées de pro­téo­mique conte­nues dans des fichiers tabu­lés. Il s'agissait de com­pa­rer la pré­sence des pro­téines ou leur expres­sion dans dif­fé­rents échan­tillons. Les abon­dances rela­tives (la variable étu­diée) étaient indi­quées pour les dif­fé­rentes pro­téines iden­ti­fiées (plu­sieurs mil­liers et cor­res­pon­dant aux lignes du fichier) dans les dif­fé­rents échan­tillons ana­ly­sés (cor­res­pon­dant aux colonnes). J’ai donc dû mani­pu­ler ces don­nées tabu­lées pour en tirer des conclu­sions vis-à-vis du com­por­te­ment des pro­téines selon les dif­fé­rents échan­tillons. Je viens ain­si vous pré­sen­ter ce qu’est pan­das et quelques com­mandes pour pou­voir l’utiliser « de façon basique » et assez faci­le­ment afin de com­men­cer rapi­de­ment à ana­ly­ser ses don­nées. Comme vous l'aurez com­pris, il s’agira d’un article intro­duc­tif.

Mais qu’est-ce que pandas ?

Non, ce n’est pas vrai­ment un ursi­dé qui m’a aidée à tri­fouiller dans les don­nées mises à ma dis­po­si­tion. Ce n’est pas non plus des « Troubles neu­ro­psy­chia­triques pédia­triques auto-immunes asso­ciés à une infec­tion à strep­to­coque » (mer­ci Wiki­pé­dia pour la décou­verte !). Alors qu’est-ce réel­le­ment ?

Aqua­relle pan­das by Cho­po­pope

 

Pan­das est une biblio­thèque open source qui vise à l’analyse et la mani­pu­la­tion de don­nées en Python. L'objectif est de per­mettre la mani­pu­la­tion de grands volumes de don­nées struc­tu­rées (tabu­lées, mul­ti-dimen­sion­nelles, voire hété­ro­gènes) et de don­nées tem­po­relles de façon simple mais effi­cace et rapide dans le but de leur exploi­ta­tion. La biblio­thèque contient donc un grand nombre d'outils pour récu­pé­rer, agré­ger, com­bi­ner, trans­for­mer, trier, décou­per, dépla­cer et expor­ter les don­nées, ain­si que gérer les valeurs man­quantes, cal­cu­ler des sta­tis­tiques et pro­duire des gra­phiques.

Enfin, pan­das a été conçu à par­tir de Num­py et reprend une struc­ture de don­nées uti­li­sée dans R (les data.frame). Il est donc natu­rel de retrou­ver des simi­la­ri­tés avec ce der­nier dans l'utilisation de pan­das.

Je tiens à pré­ci­ser que, hélas, pan­das ne sait pas encore faire le café… (Déso­lée Nol­wenn :p) Peut-être durant une pro­chaine ver­sion ! D’ailleurs, vous pou­vez très bien pro­po­ser des amé­lio­ra­tions et par­ti­ci­per au déve­lop­pe­ment ici. Il y a même un Git­ter et une mai­ling list ain­si que le tra­di­tion­nel Stack Over­flow pour ceux qui auraient des ques­tions !

Documentation

La docu­men­ta­tion offi­cielle est assez impo­sante avec 2215 pages pour la ver­sion PDF mais peut être assez inter­ac­tive via le site. De plus, je conseille les pages sui­vantes qui per­mettent de se plon­ger dans pan­das rapi­de­ment pour une uti­li­sa­tion simple sans avoir à digé­rer la docu­men­ta­tion en entier (heu­reu­se­ment pour nous !) :

  • Une page qui pré­tend per­mettre d’appréhender pan­das en 10 minutes
  • Des exemples rapides d’utilisation de pan­das façon « livre de cui­sine »
  • De nom­breux tuto­riels pour les nou­veaux uti­li­sa­teurs ou sur des points pré­cis

 

Et en bonus, il existe des cheat sheets ! Des fiches qui réunissent les com­mandes prin­ci­pales et essen­tielles de pan­das. Elles sont très pra­tiques, à impri­mer et affi­cher à côté de son ordi pour y jeter un coup d’œil rapide et ne pas perdre du temps à cher­cher la com­mande sur inter­net ou dans la doc ! Je vous ren­voie vers celle édi­tée par l'équipe de déve­lop­pe­ment de pan­das ici. Plu­sieurs autres peuvent éga­le­ment être trou­vées sur inter­net (Duck­Duck­Go est ton ami !).

Installation

À la date de fin d’écriture de l’article, la der­nière ver­sion (release) de pan­das est la 0.22.0 (Février 2018). Elle est sup­por­tée par les ver­sions de Python 2.7, 3.5 et 3.6, vous avez donc l’embarras du choix. Avant tout, pour pou­voir « jouer » avec pan­das, il faut pas­ser par l’installation. Heu­reu­se­ment pour nous, cette der­nière est assez simple puisqu’il s’agit d’un package Python.

Avec Pip (la méthode recommandée avec l'utilisation de virtualenv)

#Version 2.X de Python
pip install pandas

#Version 3.X de Python
pip3 install pandas

Avec Conda

On peut également passer par Conda pour l’installation (pour rappel, deux super articles sur cet autre animal : ici et ) :

conda install pandas

Avec les gestionnaires de paquets

Ou bien en utilisant les gestionnaires de paquets des distributions GNU/Linux en téléchargeant les paquets python-pandas pour Python 2.X et python3-pandas pour Python 3.X. Par exemple, pour une distribution de type Debian ou Ubuntu :

# Pour Python 2.X
sudo apt-get install python-pandas

#Pour Python 3.X
sudo apt-get install python3-pandas

Autres façons

Il est évidemment possible de passer par le site officiel. Si vous êtes plus aventurier(ère), vous pouvez également télécharger les codes sources depuis GitHub ou même la source tarball.

En conclusion, pas d’excuse pour ne pas trouver un moyen d’installer pandas qui soit à son goût ! A noter qu'une installation sans virtualenv peut tout de même déstabiliser le sytème d'exploitation (merci Nolwenn !).

Utilisation

Import de la bibliothèque

Avant même de penser à manipuler la bête, il faut l'importer :

import pandas as pd

Structures de données

Il faut savoir que pandas utilise principalement deux structures de données : la Series (1 dimension) et le DataFrame (2 dimensions). Pour résumer simplement, on va utiliser la structure Series lorsque l'on est en présence de données tabulées présentant plusieurs lignes et une seule colonne de données (de la variable nous intéressant), alors que l'on va plutôt avoir recours au DataFrame à partir de deux colonnes.

Series

Exemple de fichier de données que l'on peut traiter avec un objet Series:

Accession Abondance
P08637 792592,97
P00533 562817,06
P35222 7768216,5
... ...

Au niveau du code :

series = pd.Series(data=[abondance1, abondance2, etc], index=["Prot1", "Prot2", etc])

#Par exemple
series = pd.Series(data=[792592.97, 562817.06, 7768216.5], index=["P08637", "P00533", "P35222"])
print(series)

Affichage de l'objet series

DataFrame

Exemple de fichier de données permettant d'utiliser un DataFrame :

Accession Entry name Condition 1 Condition 2 Condition 3
P08637 P13_HUMAN 792592,97 971165,22 1175682,25
P00533 EGFR_HUMAN 562817,06 698207,22
P35222 CTNB1_HUMAN 7768216,5 2072283,5 7202552,25
... ... ... ... ...

Au niveau du code :

df = pd.DataFrame(data, index)

#Par exemple
idx = ["P08637", "P00533", "P35222"]
donnees = {"Entry name":["P13_HUMAN", "EGFR_HUMAN", "CTNB1_HUMAN"], "Condition 1":[792592.97, 562817.06, 7768216.5], "Condition 2":[971165.22, 698207.22, 2072283.5], "Condition 3":[1175682.25, None, 7202552.25]}
df = pd.DataFrame(data=donnees, index=idx)
print(df)

Affichage de l'objet df

Concernant le paramètre data, on peut passer des dictionnaires (de Series, arrays, etc), des DataFrames ou des ndarray de numpy (voir la doc).

Différentes Manips avec Pandas

/!\ Ici, je ne traiterai que d'exemples avec des données (à analyser) numériques avec des index non numériques (chaînes de caractères). Je vous invite donc à adapter ces exemples pour des données de nature différente ou hétérogènes (et à compléter cet article avec un autre présentant de nouveaux exemples et nouvelles applications !).

Je travaillerai avec un fichier contenant des données simulées qui se présente comme ceci :

Fichier de données

Import des données

À partir d'un fichier EXCEL

Oui, le monde n'est pas (encore) parfait. Il arrive (trop souvent) qu'il faille manipuler des fichiers Excel. Mais rassurez-vous, avec pandas, nous aurons seulement à récupérer les données du fichier et les stocker dans un DataFrame (ou Series s'il n'y a qu'une seule colonne de données). Nous ne toucherons plus à ce type de fichier pour nos traitements.

xl = pd.ExcelFile("path/to/file.xlsx")
print(xl.sheet_names) # Affiche les noms des différentes feuilles du fichier Excel
df = xl.parse("nameOfSheetToAnalyse", index_col=NumberOfTheColumnWhichIsTheIndexColumn)
# Ou encore
df = xl.parse(sheet_name="nameOfSheetToAnalyse", index_col=NumberOfTheColumnWhichIsTheIndexColumn)

#Par exemple
xl = pd.ExcelFile("/media/DATA/Bioinfo-fr/data_excel.xlsx")
print(xl.sheet_names)
df = xl.parse("data", index_col=0)
print(df) # Affiche le dataframe df contenant les données de la feuille 'data' du fichier Excel data_excel.xlsx

Résultat de sortie

Une autre façon de faire est possible en utilisant une seule fonction : read_excel().

df = pd.read_excel("path/to/file.xlsx", sheet_name=NumberOfTheSheetToAnalyseTheDataFrom, index_col=NumberOfTheColumnWhichIsTheIndexColumn)

#Par exemple
df = pd.read_excel("/media/DATA/Bioinfo-fr/data_excel.xlsx", sheet_name=0, index_col=0)

Apparemment, d'après une réponse sur StackOverflow, la première solution serait plus rapide. On remarque également que l'on peut soit préciser le nom de la feuille à analyser ou directement le numéro de celle-ci, ce qui est utile quand on ne connaît pas les intitulés des feuilles. Cela fonctionne pour les deux solutions, ce n'est pas dépendant de la façon de faire choisie. Je vous laisse aller lire la documentation pour plus de détails.

À partir d'un fichier CSV

Quand on a plus de chance, ce sont des fichiers au format csv que nous avons à notre disposition. De manière analogue à la fonction read_excel(), on utilise la fonction read_csv(). Il y a un paramètre optionnel delimiter='\t' si votre fichier est au format tsv.

df = pd.read_csv("path/to/file.csv", index_col=NumberOfTheColumnWhichIsTheIndexColumn)

#Par exemple
file_csv = "/media/DATA/Bioinfo-fr/data_CSV.csv"
df = pd.read_csv(file_csv, index_col=0)

Premier aperçu des données

Une fois importées, il est (enfin) possible de jeter un coup d’œil à nos données pour pouvoir commencer à les exploiter. Avec les quelques fonctions ou variables suivantes, nous aurons un rapide aperçu sur ces dernières.

#Renseigne sur le type des données de chaque colonne du dataframe
df.dtypes
résultat df.dtypes
# Sélectionne les premières lignes du dataframe (par défaut 5)
df.head()
# Sélectionne les dernières lignes du dataframe (par défaut 5)
df.tail(3)
résultat de la fonction head()
résultat appel fonction tail()
# Sélectionne la colonne des indices du dataframe
indices = df.index
# Noms des colonnes (hormis celui des indices)
colonnes = df.columns
# Valeurs des données
vals = df.values
résultat de l'appel de la variable index
résultat de l’appel à la variable columns
# Renvoie quelques statistiques sur les données sous forme d'un dataframe
# percentiles : par défaut 25%, 50% et 75% ; include : colonnes à inclure dans les statistiques (par défaut toutes les colonne de données numériques) ; exclude : colonnes à exclure (par défaut aucune)
df.describe(percentiles=[.25, .5, .75, .9], include='all'; exclude=None)

# Renvoie la moyenne des données (numériques) par colonne
df.mean() # ou df.mean(0)
# moyenne par ligne
df.mean(1)

résultat de l'appel de la fonction describe(). count : nombre de valeurs dans la colonne; [unique, top, freq] : pour les valeurs textuelles, top : valeur la plus représentée (aléatoire si plusieurs); [mean (moyenne), std (écart-type), min, max, pourcentiles] : pour les valeurs numériques

Traitements basiques

Après avoir eu une vision globale de nos données grâce à quelques statistiques, nous pouvons les traiter à l'aide des informations obtenues précédemment.

Gérer les données manquantes et/ou nulles

# Renvoie un dataframe sans valeur manquante (si le paramètre inplace est à False)
# axis : axe selon lequel supprimer les données (0 : ligne , 1 : colonne)
# how : any = si au moins une valeur manquante présente, l'ensemble de la ligne/colonne est supprimée ; all = toutes les valeurs d'un ensemble doivent être manquantes pour être supprimées
# inplace : supprime les valeurs dans la dataframe au lieu d'en renvoyer un nouveau
df_dropna = df.dropna(axis=0, how='any', inplace=False)

# Par exemple
df_dropna = df.dropna(how='any')

dataframe sans valeur manquante
# Renvoie un dataframe dont les valeurs manquantes ont été remplacées par une valeur précisée
df_fillna = df.fillna(value=5)
# Renvoie un dataframe avec à la place de chaque valeur un booléen renseignant si la valeur est manquante ou non
df_isnull = pd.isnull(df)
dataframe avec les valeurs manquantes remplacées par la valeur numérique 5

Trier les données

# Renvoie un dataframe avec les valeurs triées selon l'axe choisi (indices ou noms colonnes)
df.sort_index(axis=0, ascending=True)
Sortie de l'appel de la fonction sort_index()
# Renvoie le dataframe dont les valeurs sont triées selon les valeurs d'une colonne
df.sort_values(by='C2')
Sortie de l'appel de la fonction sort_values()

Valorisation des résultats/données

Visualisation

Il est souvent intéressant de pouvoir visualiser ses données sous la forme d'un graphique. Il est possible de créer un graphique directement à partir du dataframe et en utilisant la bibliothèque matplotlib ('import matplotlib.pyplot as plt' en début de script).

df.plot()
# Affiche le graphique
plt.show()
# Ou pour sauvegarder
plt.savefig("path/to/graph", format="png")
graphique généré à partir du dataframe

On peut évidemment choisir un type de graphique. Pour cela, il faut utiliser à la place de df.plot() les méthodes associées : df.plot.type() avec 'type' étant 'line', 'bar', 'scatter', 'hist', 'box', etc.
On peut également complexifier un peu selon nos envies et notre maîtrise de matplotlib. Je vous glisse (en cadeau car vous le méritez après avoir lu ce looong article) ici un exemple de graphique dont j'ai eu besoin. J'ai dû modifier la présentation de l'axe des abscisses pour pouvoir visualiser les noms de protéines de façon lisible.

N = len(df['C2'])
x = np.arange(1, N + 1)
width = 1.0
plt.scatter(x, df['C2'])
plt.xlabel('Protéines')
plt.ylabel('Abondance relative')
plt.title("Valeurs de la condition 2")
plt.xticks(x, df.index, rotation=90)
plt.autoscale(tight=False)
plt.grid()
plt.show()
Graphique un peu plus complexe

Export des résultats/données

L'export des données est similaire à leur import. Au final, vous pouvez manipuler différents types de fichiers, que ce soit en entrée ou en sortie. Un tableau récapitulatif des possibilités vous permet de le constater (cf documentation).

tableau récapitulatif des formats supportés

Informations complémentaires

Il est à noter que pandas peut être utilisé au sein de IPython/notebook Jupiter.

Durant l'écriture de cet article, j'ai même découvert qu'il existait GeoPandas pour manipuler les données géographiques. Donc si vous bossez sur du SIG, pandas est aussi là pour vous ;). Je ne l'ai pas utilisé, mais avec un rapide coup d’œil sur la doc, j’ai pu voir qu'il reposait sur le même principe global avec les types de données Series et DataFrame (GeoSeries et GeoDataFrame).

De plus, pandas offre bien d'autres possibilités et je vous invite à découvrir ces dernières et à venir nous les partager par l'écriture d'un article plus avancé sur l'utilisation de pandas ! Je vous laisse (enfin) vous dépatouiller avec Pandas !

Enfin, je tiens à remercier Olivier Dameron, Nolwenn et lroy qui se sont aventuré(e)s à relire mon article et Chopopope pour sa magnifique aquarelle ! \o/

Références

https://pypi.python.org/pypi/pandas

https://linuxfr.org/news/pandas-une-bibliotheque-pour-manipuler-facilement-des-donnees

http://sametmax.com/le-pandas-cest-bon-mangez-en/



Pour continuer la lecture :


Commentaires

2 réponses à “Apprivoiser l'ami ursidé de Python : Pandas”

  1. Bon­jour, mer­ci pour ce témoi­gnage. Pour aller dans le même sens, dans notre groupe bio­in­for­ma­tique ABI-SOFT à l'UMR GQE-Le Mou­lon), nous uti­li­sons Pan­da pour mani­pu­ler des don­nées de géno­ty­page de plantes et nous en sommes satis­faits. Par ailleurs j'en pro­fite pour faire la pub du fra­me­work Python Djan­go, qui per­met de faire des appli­ca­tions Web et donc affi­cher les tableaux pan­da sur le web. Cdlt. Del­phine

    1. Mer­ci pour ce retour. Je n'ai pas encore pu explo­rer assez le fra­me­work Djan­go pour tes­ter l'utilisation de Pan­das mais je vais y jeter un coup d'oeil dès que pos­sible. Mer­ci pour l'information !

Laisser un commentaire