En tant que bio-informaticiens, nous avons souvent à manipuler des données qui ne sont pas organisées comme nous le voudrions. Un cas souvent rencontré est l’obtention de données qui se trouvent dans un format « long » au lieu de les avoir dans le format plus habituel, « large ». Pour ceux qui sont familiers avec la librairie ggplot du langage R, vous connaissez très bien le format « long ». C’est le format requis par ggplot pour lui permettre de produire ses élégants graphiques.

En partant du format long, comment passer à une martice large (avec les gènes en rangées et les échantillons en colonnes)? Il y a plusieurs moyens de faire cette conversion, mais commençons d’abord par générer des données pour que vous puissiez essayer de les pivoter par vous-mêmes.

 

Long


  genes   samples expression
1   BAD     S01    7.525395
2   BAD     S02    9.020534
3   BAD     S03   14.099557
4  TP53     S01    5.175252
5  TP53     S02    4.437941
6  TP53     S03    3.827847
7   A2M     S01    1.055729
8   A2M     S02   17.104853
9   A2M     S03   14.456857
Large


  genes      S01       S02       S03
1   A2M 1.055730 17.104854 14.456857
2   BAD 7.525395  9.020534 14.099557
3  TP53 5.175253  4.437941  3.827848

Table1. Petit ensemble de données présenté dans le format long (à gauche) et large (à droite).

Génération de données

Nous pouvons générer un petit ensemble de données contenant 3 gènes et 3 échantillons en R en utilisant les fonctions rep et rnorm.


# répèter chaque item du vecteur n fois
genes = rep(c('BAD', 'TP53', 'A2M'),each=3)  
#[1] "BAD"  "BAD"  "BAD"  "TP53" "TP53" "TP53" "A2M"  "A2M"  "A2M"

# répèter chaque vecteur n fois
samples = rep(c('S01', 'S02', 'S03'),times=3)  
#[1] "S01" "S02" "S03" "S01" "S02" "S03" "S01" "S02" "S03"

# choisir des nombres provenant d'une distribution normale 
expression = abs(rnorm(9, 1, 10))
#[1] 17.623573 13.709192  4.529254  3.056479  2.407857 15.619984  2.309779
#[8]  8.497227 14.941006

df = as.data.frame(cbind(genes, samples, expression))
df$expression = as.numeric(as.character(df$expression)) 

Le code ci-dessus génère la matrice longue présentée dans la Table1. Nous pouvons utiliser la même idée pour construire un ensemble de données de 10 000 gènes par 1 000 échantillons*.


ngenes = 10000
nsamples = 1000
genes = rep(paste('G',seq(ngenes), sep=''),each=nsamples)
samples = rep(paste('S',seq(nsamples), sep=''),times=ngenes)
expression = abs(rnorm(ngenes*nsamples, 1, 10))
df = as.data.frame(cbind(genes, samples, expression))
df$expression = as.numeric(as.character(df$expression))
write.table(df, file='longbig.txt', sep='\t', quote=F)

Pivotement en R

En R, les librairies les plus utilisées pour pivoter des données sont : tidyr et reshape2. Les fonctions dcast de reshape2 et spread de tidyr sont faciles à utiliser. Nous n’avons qu’à spécifier les données à pivoter, quelle variable devrait être utilisée pour remplir la matrice et comment seront organisées les colonnes et les rangées.

La fonction dcast s’attend à recevoir une formule spécifiant quelles variables serviront à identifier les rangées et quelle variable mettre en colonnes. Le paramètre value.var spécifie la variable à utiliser pour remplir la matrice. Si les identificateurs de rangées sont composés de plusieurs variables, la formule ressemblera à : varRow1 + varRow2 ~ varCol . Par exemple, genes + organism ~ samples présentera les échantillons en colonnes et le gène et l’organisme comme identificateur de rangées.

Avec spread, seules les variables à distribuer en colonnes et celle à utiliser pour remplir la matrice sont spécifiées.


library(reshape2)
tb = dcast(df, genes ~ samples, value.var="expression")

library(tidyr)
tb = spread(df, samples, expression) 

Pour le plus gros dataset de 10 000 gènes et 1 000 échantillons, la matrice longue contient 10 000 000 rangées et 3 colonnes. Cela prend environ de 60-90 secondes pour lire le fichier de 331 MB en R et environ 10 secondes pour pivoter la table.

Pivotement en python

C’est aussi facile de passer du format long à large en python que ce l’est en R grâce au package pandas. Cette librairie offre beaucoup de fonctions intéressantes pour l’analyse de données. En fait, depuis l’ajout de fonctions de visualisation faciles à utiliser, je dois avouer que je me retrouve souvent à faire en python/pandas ce que j’avais l’habitude de faire en R.

Voici comment pivoter en python avec pandas:

import pandas
df = pandas.read_csv('longbig.txt', sep='\t')
tb = pandas.pivot_table(df, values="expression", index=["genes"], columns="samples")

Lire le fichier prend beaucoup moins de temps, mais l’étape de pivotement est plus longue. Au final, pivoter le gros jeu de données en python prend un temps équivalent à celui observé en R.

Agrégation

Les fonctions pivot_table en python et dcast en R permettent également de faire de l’agrégation de données, c’est-à-dire de réduire la dimensionnalité des données grâce à des fonctions comme la somme ou la moyenne. Par défaut, dcast utilise une fonction de décompte (length) comme fonction d’agrégation et pivot_table utilise la moyenne, mais vous pouvez utiliser la fonction de votre choix. Ainsi, si plus d’une rangée ont le même identifiant, la fonction d’agrégation sera appliquée automatiquement. Par exemple, dans le code suivant, si nous ignorons les échantillons dans l’étape de pivotement (on demande seulement une colonne présentant l’expression en fonction des gènes), nous aurons plus d’une valeur d’expression par gène. Une moyenne sera donc calculée pour chaque gène.

tab = pandas.pivot_table(df, values="expression", index=["genes"])

## Moyenne de l'expression des échantillons par gène
#genes
#G1        7.959172
#G10       8.017683
#G100      8.199654
#G1000     8.059022
#...

df.ix[df.genes=='G1',:].mean() # Vérifie que c'est bien la moyenne qui est obtenue en sortie
#expression    7.959172
#dtype: float64

Pour utiliser une fonction d’agrégation sur des valeurs non-numériques, n’oubliez pas de définir votre propre fonction retournant la valeur non-numérique qui vous intéresse!

* Si cette étape est trop longue en R, passez à python (vous aurez besoin de la librairie pandas) :

import random 
import pandas 
ngenes = 10000
nsamples =  1000
genes = ['G%i' %i for i in range(ngenes)]*nsamples
samples = ['S%i' %i for i in range(nsamples)]*ngenes
samples.sort()
expression =  [abs(random.gauss(1,10)) for i in range(nsamples*ngenes)]
df = pandas.DataFrame(zip(genes, samples), columns=['genes', 'samples'])
df['expression'] = expression
df.to_csv('longbig.txt', sep='\t')