Le conteneur SummarizedExperiment en R : assays, rowData & colData

Gardez votre matrice de comptage, votre table de gènes et votre table d’échantillons dans un seul objet qui s’indexe de concert — pour qu’un réordonnancement malencontreux ne puisse jamais les désaligner silencieusement

Le SummarizedExperiment est le conteneur sur lequel repose presque tout workflow omique Bioconductor. Apprenez ce qu’il contient — une matrice assays (gènes × échantillons), un rowData par gène et un colData par échantillon — comment ces trois pièces restent alignées quand vous extrayez un sous-ensemble ou réordonnez, et comment en construire un à partir d’une simple matrice. Sur un exemple jouet et le vrai jeu de données airway, avec une note sur son extension single-cell, SingleCellExperiment.

Date de publication

29 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Un SummarizedExperiment regroupe trois choses dans un seul objet : une matrice assays (lignes = gènes/éléments, colonnes = échantillons), rowData (une ligne par gène) et colData (une ligne par échantillon).
  • Tout son intérêt est l’alignement : extrayez un sous-ensemble ou réordonnez l’objet et la matrice, rowData et colData bougent ensemble — ils ne peuvent jamais se désynchroniser silencieusement.
  • Construisez-en un avec SummarizedExperiment(assays = list(counts = m), colData = ..., rowData = ...) ; le constructeur lève une erreur quand les noms ne concordent pas — un garde-fou utile, pas une gêne.
  • Accédez-y avec assay()/assays(), colData(), rowData(), dim(), et rownames()/colnames().
  • Le vrai objet airway est un RangedSummarizedExperiment ; DESeqDataSet et (pour le single-cell) SingleCellExperiment l’étendent tous deux — apprenez-le une fois, utilisez-le partout dans Bioconductor.

Introduction

La plupart des analyses omiques commencent avec trois tables : une matrice de comptage (gènes × échantillons), une table de métadonnées d’échantillons (traitement, lignée cellulaire, batch), et une table d’informations sur les gènes (symbole, biotype, coordonnées). Gardez-les comme trois objets séparés et vous n’êtes qu’à une ligne d’étourderie du désastre — triez les colonnes de la matrice, retirez un échantillon, ou réordonnez les métadonnées, et les trois tables ne concordent plus. Rien ne lève d’erreur. Vos échantillons « traités » sont désormais étiquetés « contrôle », et chaque résultat en aval est silencieusement faux.

Le SummarizedExperiment est la réponse de Bioconductor : un conteneur unique qui relie la matrice, la table par échantillon et la table par gène pour qu’ils forment un sous-ensemble et se réordonnent comme une seule unité. C’est l’objet que comprennent airway, DESeq2, edgeR, limma et les outils single-cell. Apprenez son anatomie une fois et le reste de la pile génomique cesse de ressembler à un tas de data frames mal accordés.

Le problème : trois objets qui doivent rester alignés

Imaginez les trois tables séparément. La matrice a les échantillons en colonnes ; la table d’échantillons a une ligne par échantillon ; la table de gènes a une ligne par gène. Le lien entre elles est la position — la colonne 3 de la matrice est censée être la ligne 3 de la table d’échantillons. Re-triez l’une et pas les autres et ce lien se rompt silencieusement :

m <- matrix(1:6, nrow = 2,
            dimnames = list(c("geneA", "geneB"), c("s1", "s2", "s3")))
samples <- data.frame(condition = c("ctrl", "ctrl", "treated"),
                      row.names = c("s1", "s2", "s3"))

# Reorder the matrix columns but forget the sample table:
m <- m[, c("s3", "s1", "s2")]

colnames(m)            # s3, s1, s2
[1] "s3" "s1" "s2"
rownames(samples)      # still s1, s2, s3  -> now out of sync
[1] "s1" "s2" "s3"

La colonne 1 de la matrice est maintenant s3, mais la ligne 1 de samples est toujours s1. Un SummarizedExperiment rend cette erreur impossible.

Un seul conteneur : SummarizedExperiment

Un SummarizedExperiment contient les mêmes trois tables, mais comme un seul objet dont les dimensions sont verrouillées ensemble. Les colonnes de la matrice assay sont les lignes de colData ; les lignes de la matrice assay sont les lignes de rowData. Extrayez un sous-ensemble ou réordonnez l’objet et les trois pièces suivent de concert — il n’y a aucun moyen de bouger l’une sans les autres.

En construire un de zéro

Rien de magique ici. Partez d’une simple matrice d’entiers (gènes × échantillons), d’une table colData avec une ligne par échantillon, et d’une table rowData avec une ligne par gène — puis confiez les trois au constructeur. Les noms de lignes relient le tout.

library(SummarizedExperiment)

set.seed(1)
counts <- matrix(rpois(12, lambda = 50), nrow = 4,
                 dimnames = list(paste0("gene", 1:4),
                                 paste0("sample", 1:3)))
counts
      sample1 sample2 sample3
gene1      45      39      47
gene2      59      53      44
gene3      58      55      45
gene4      52      54      47
# One row per SAMPLE (matches the matrix columns):
colData <- DataFrame(condition = c("ctrl", "ctrl", "treated"),
                     row.names = colnames(counts))

# One row per GENE (matches the matrix rows):
rowData <- DataFrame(symbol = c("TP53", "EGFR", "MYC", "GAPDH"),
                     row.names = rownames(counts))

se <- SummarizedExperiment(assays  = list(counts = counts),
                           colData = colData,
                           rowData = rowData)
se
class: SummarizedExperiment 
dim: 4 3 
metadata(0):
assays(1): counts
rownames(4): gene1 gene2 gene3 gene4
rowData names(1): symbol
colnames(3): sample1 sample2 sample3
colData names(1): condition

L’affichage est une carte de l’objet : dim: 4 3 (4 gènes × 3 échantillons), un assay nommé counts, les quatre noms de gènes dans rownames, une colonne rowData (symbol), les trois noms d’échantillons dans colnames, et une colonne colData (condition). Tout ce que vous avez mis est comptabilisé, et les dimensions sont désormais liées ensemble.

Anatomie d’un SummarizedExperiment

Trois slots, chacun atteint par un accesseur du même nom :

flowchart TD
  SE["SummarizedExperiment"]
  SE --> A["assays() — les mesures<br/>une matrice gènes × échantillons<br/>(ex. comptages bruts)"]
  SE --> R["rowData() — table des éléments<br/>une ligne par gène<br/>(symbole, biotype, …)"]
  SE --> C["colData() — table d'échantillons<br/>une ligne par échantillon<br/>(condition, batch, …)"]
  A -. "les lignes s'alignent sur" .-> R
  A -. "les colonnes s'alignent sur" .-> C

Accédez à chaque slot avec son accesseur. Chaque bloc ci-dessous reconstruit d’abord l’objet jouet, pour que vous puissiez copier et exécuter n’importe lequel d’entre eux indépendamment :

library(SummarizedExperiment)
set.seed(1)
counts <- matrix(rpois(12, lambda = 50), nrow = 4,
                 dimnames = list(paste0("gene", 1:4), paste0("sample", 1:3)))
se <- SummarizedExperiment(
  assays  = list(counts = counts),
  colData = DataFrame(condition = c("ctrl", "ctrl", "treated"), row.names = colnames(counts)),
  rowData = DataFrame(symbol = c("TP53", "EGFR", "MYC", "GAPDH"), row.names = rownames(counts)))

dim(se)            # genes (rows) x samples (columns)
[1] 4 3
assayNames(se)     # which matrices are stored
[1] "counts"
assay(se)          # the counts matrix itself
      sample1 sample2 sample3
gene1      45      39      47
gene2      59      53      44
gene3      58      55      45
gene4      52      54      47
colData(se)        # the per-sample table
DataFrame with 3 rows and 1 column
          condition
        <character>
sample1        ctrl
sample2        ctrl
sample3     treated
rowData(se)        # the per-gene table
DataFrame with 4 rows and 1 column
           symbol
      <character>
gene1        TP53
gene2        EGFR
gene3         MYC
gene4       GAPDH

Quelques règles à fixer en mémoire :

  • assay() vs assays() — un conteneur peut contenir plusieurs matrices de même forme (comptages bruts, comptages normalisés, comptages logarithmiques). assays() est la liste nommée entière ; assay(se) (ou assay(se, "counts")) en extrait une comme simple matrice.
  • rowData vs colDatarowData annote les lignes (gènes), colData les colonnes (échantillons). Atteignez une seule colonne colData avec se$condition, exactement comme un data frame.
  • dim(se) est gènes × échantillons — les lignes d’abord, les colonnes ensuite, comme toute matrice.

L’indexation alignée : la fonctionnalité phare

C’est la raison d’être du conteneur. Indexez-le comme une matrice — se[genes, samples] — et l’assay, rowData et colData sont tous mis en sous-ensemble d’un seul geste. Prenez les deux premiers gènes :

library(SummarizedExperiment)
set.seed(1)
counts <- matrix(rpois(12, lambda = 50), nrow = 4,
                 dimnames = list(paste0("gene", 1:4), paste0("sample", 1:3)))
se <- SummarizedExperiment(
  assays  = list(counts = counts),
  colData = DataFrame(condition = c("ctrl", "ctrl", "treated"), row.names = colnames(counts)),
  rowData = DataFrame(symbol = c("TP53", "EGFR", "MYC", "GAPDH"), row.names = rownames(counts)))

sub <- se[1:2, ]     # first two GENES, all samples
rowData(sub)         # rowData followed automatically
DataFrame with 2 rows and 1 column
           symbol
      <character>
gene1        TP53
gene2        EGFR

rowData s’est réduit aux deux gènes que vous avez gardés — vous n’y avez jamais touché directement. Maintenant, le geste contre lequel l’introduction mettait en garde : sélectionner des échantillons par une condition dans colData. Les colonnes de la matrice et la table d’échantillons restent parfaitement cohérentes, car elles sont le même objet :

library(SummarizedExperiment)
set.seed(1)
counts <- matrix(rpois(12, lambda = 50), nrow = 4,
                 dimnames = list(paste0("gene", 1:4), paste0("sample", 1:3)))
se <- SummarizedExperiment(
  assays  = list(counts = counts),
  colData = DataFrame(condition = c("ctrl", "ctrl", "treated"), row.names = colnames(counts)),
  rowData = DataFrame(symbol = c("TP53", "EGFR", "MYC", "GAPDH"), row.names = rownames(counts)))

treated <- se[, se$condition == "treated"]   # keep only treated samples
assay(treated)        # matrix column(s) for treated samples
      sample3
gene1      47
gene2      44
gene3      45
gene4      47
colData(treated)      # the matching sample row(s) — still aligned
DataFrame with 1 row and 1 column
          condition
        <character>
sample3     treated

Un échantillon, la bonne colonne de comptages, la bonne ligne de métadonnées — aucune comptabilité à tenir, aucun risque d’inversion. Cette garantie est toute la raison d’être du format.

Un vrai cas : le jeu de données airway

L’objet jouet est la vraie chose en miniature. Le jeu de données airway — du RNA-seq de cellules musculaires lisses des voies respiratoires, traitées ou non à la dexaméthasone — se présente comme un RangedSummarizedExperiment (un SummarizedExperiment dont les lignes portent aussi des plages génomiques, stockées dans rowRanges()). Les mêmes accesseurs fonctionnent :

library(airway)
data("airway")
airway
class: RangedSummarizedExperiment 
dim: 63677 8 
metadata(1): ''
assays(1): counts
rownames(63677): ENSG00000000003 ENSG00000000005 ... ENSG00000273492
  ENSG00000273493
rowData names(10): gene_id gene_name ... seq_coord_system symbol
colnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521
colData names(9): SampleName cell ... Sample BioSample

dim: 63677 8 — 63 677 gènes répartis sur 8 échantillons, un assay counts, les identifiants de gènes dans rownames, les identifiants d’échantillons dans colnames. Extrayez les pièces exactement comme avant :

library(airway)
data("airway")

assay(airway)[1:5, 1:4]                       # raw integer counts: genes × samples
                SRR1039508 SRR1039509 SRR1039512 SRR1039513
ENSG00000000003        679        448        873        408
ENSG00000000005          0          0          0          0
ENSG00000000419        467        515        621        365
ENSG00000000457        260        211        263        164
ENSG00000000460         60         55         40         35
library(airway)
data("airway")

colData(airway)[, c("cell", "dex")]           # the experimental design
DataFrame with 8 rows and 2 columns
               cell      dex
           <factor> <factor>
SRR1039508  N61311     untrt
SRR1039509  N61311     trt  
SRR1039512  N052611    untrt
SRR1039513  N052611    trt  
SRR1039516  N080611    untrt
SRR1039517  N080611    trt  
SRR1039520  N061011    untrt
SRR1039521  N061011    trt  

Le colData est le plan d’expérience : quatre lignées cellulaires (cell), chacune mesurée non traitée (dex = untrt) et traitée (dex = trt). Cette table est ce que vous confiez à une formule de modèle comme ~ cell + dex. L’annotation par gène vit dans rowData (ici, les symboles de gènes et les biotypes) :

library(airway)
data("airway")

head(rowData(airway)[, c("gene_id", "gene_name", "gene_biotype")])
DataFrame with 6 rows and 3 columns
                        gene_id   gene_name   gene_biotype
                    <character> <character>    <character>
ENSG00000000003 ENSG00000000003      TSPAN6 protein_coding
ENSG00000000005 ENSG00000000005        TNMD protein_coding
ENSG00000000419 ENSG00000000419        DPM1 protein_coding
ENSG00000000457 ENSG00000000457       SCYL3 protein_coding
ENSG00000000460 ENSG00000000460    C1orf112 protein_coding
ENSG00000000938 ENSG00000000938         FGR protein_coding

C’est exactement l’objet que consomme la série RNA-seq bulk : la leçon matrice de comptage & CQ charge airway comme un SummarizedExperiment, et l’expression différentielle avec DESeq2 l’enveloppe dans un DESeqDataSet — qui est lui-même un SummarizedExperiment avec des slots supplémentaires. Apprenez le conteneur ici et ces leçons se lisent comme de petites étapes, pas comme du vocabulaire nouveau.

SingleCellExperiment : l’extension single-cell

Les données single-cell sont la même idée avec deux slots supplémentaires. SingleCellExperiment étend SummarizedExperiment : c’est un SummarizedExperiment (donc assay(), colData(), rowData() et l’indexation alignée fonctionnent tous à l’identique) plus reducedDims pour les représentations de cellules (PCA, t-SNE, UMAP) et altExps pour les éléments alternatifs (spike-ins, marqueurs dérivés d’anticorps) :

library(SingleCellExperiment)

# A SingleCellExperiment IS a SummarizedExperiment, plus:
sce <- SingleCellExperiment(assays = list(counts = counts))

reducedDim(sce, "PCA")    # per-cell coordinates from a dimension reduction
altExp(sce, "ERCC")       # a second feature set (e.g. spike-in controls)

Parce qu’il hérite de SummarizedExperiment, tout dans cette leçon se transpose directement — les seules nouveautés sont les représentations (embeddings) et les expériences alternatives. La future série single-cell RNA-seq construit tout son workflow sur SingleCellExperiment.

Problèmes fréquents

Les noms de lignes de colData ne correspondent pas aux colonnes de l’assay. Le constructeur refuse de deviner : si les noms de lignes de colData (ou rowData) ne sont pas identiques aux noms de colonnes (ou de lignes) de la matrice, il lève une erreur : “the rownames and colnames of the supplied assay(s) must be NULL or identical to those of the SummarizedExperiment object … to construct”. Cette erreur, c’est le garde-fou qui fait son travail. Alignez d’abord les noms vous-même — counts <- counts[, rownames(colData)] — puis construisez.

Confondre assay() et assays(). assays(se) renvoie la liste de toutes les matrices stockées ; assay(se) renvoie une seule matrice (la première, ou celle que vous nommez avec assay(se, "logcounts")). Si une fonction se plaint d’avoir reçu une liste là où elle voulait une matrice, vous avez appelé assays() là où vous vouliez assay().

Indexer en inversant les dimensions. L’indexation est se[genes, samples]les lignes (gènes) d’abord, les colonnes (échantillons) ensuite, comme toute matrice R. se[, se$dex == "trt"] sélectionne des échantillons ; se[my_genes, ] sélectionne des gènes. Inversez-les et vous obtenez soit une erreur, soit, pire, la mauvaise tranche.

Questions fréquentes

C’est un conteneur Bioconductor qui stocke une matrice assay (lignes = gènes/éléments, colonnes = échantillons) avec une table par échantillon (colData) et une table par gène (rowData) dans un seul objet. Ses dimensions sont verrouillées, de sorte que l’extraction de sous-ensembles ou le réordonnancement déplace la matrice et les deux tables de métadonnées ensemble — elles ne peuvent jamais se désynchroniser.

Un SummarizedExperiment peut contenir plusieurs matrices de même forme (ex. counts, logcounts). assays(se) renvoie la liste nommée entière ; assay(se) renvoie une seule matrice — la première, ou celle que vous nommez avec assay(se, "counts").

rowData annote les lignes de l’assay — une ligne par gène/élément (symbole, biotype, coordonnées). colData annote les colonnes — une ligne par échantillon (condition, batch, lignée cellulaire). Lisez un seul champ d’échantillon avec se$condition, exactement comme un data frame.

Oui. DESeqDataSet (DESeq2) étend RangedSummarizedExperiment, donc tous les accesseurs — assay(), colData(), rowData(), dim() — et l’indexation alignée fonctionnent dessus. Il ajoute simplement les slots dont DESeq2 a besoin (facteurs de taille, dispersions, résultats).

SingleCellExperiment est un SummarizedExperiment avec deux slots supplémentaires pour les données single-cell : reducedDims (représentations PCA, t-SNE, UMAP) et altExps (ensembles d’éléments alternatifs comme les spike-ins). Tout ce que vous apprenez sur SummarizedExperiment s’y applique à l’identique.

Testez vos connaissances

Construisez un petit SummarizedExperiment à partir d’une matrice de comptage de 3 gènes × 4 échantillons. Donnez-lui un colData avec une colonne group ("A", "A", "B", "B") et un rowData avec une colonne symbol. Puis gardez uniquement les échantillons du groupe « B » et confirmez que assay() et colData() sont toujours alignés.

Définissez row.names sur colData et rowData pour correspondre aux colnames et rownames de la matrice. Indexez avec se[, se$group == "B"], puis affichez assay() et colData() du résultat.

library(SummarizedExperiment)

m <- matrix(1:12, nrow = 3,
            dimnames = list(paste0("g", 1:3), paste0("s", 1:4)))

se <- SummarizedExperiment(
  assays  = list(counts = m),
  colData = DataFrame(group  = c("A", "A", "B", "B"), row.names = colnames(m)),
  rowData = DataFrame(symbol = c("TP53", "EGFR", "MYC"), row.names = rownames(m)))

b <- se[, se$group == "B"]   # keep group-B samples
assay(b)                      # columns s3, s4 only
colData(b)                    # rows s3, s4 only — still aligned

Seules les colonnes du groupe B de la matrice survivent, et colData affiche exactement ces deux échantillons — vous n’avez jamais indexé les métadonnées vous-même, le conteneur les a maintenues au pas.

A. Seule la matrice assay est mise en sous-ensemble ; rowData et colData gardent leurs tailles d’origine. B. Les 10 premiers gènes et les échantillons traités, avec rowData et colData mis en sous-ensemble pour correspondre. C. Une erreur, car on ne peut pas indexer un SummarizedExperiment avec une condition.

B. L’indexation est se[genes, samples]. Les 10 premières lignes (gènes) et les colonnes où dex == "trt" sont conservées, et rowData et colData sont mis en sous-ensemble de concert — cette indexation alignée est toute la raison d’être du conteneur.

Conclusion

Un SummarizedExperiment est l’objet sur lequel repose le monde Bioconductor : une matrice assays de gènes × échantillons, une table rowData pour les gènes, et une table colData pour les échantillons, le tout réuni dans un conteneur dont les dimensions bougent ensemble. Vous en avez construit un à partir d’une simple matrice, y avez accédé avec assay()/colData()/rowData(), et avez vu la fonctionnalité qui justifie son existence — l’indexation alignée, où se[genes, samples] garde la matrice et les deux tables de métadonnées cohérentes pour qu’un réordonnancement ne puisse jamais mal étiqueter un échantillon. Le vrai objet airway est la même chose à grande échelle, et DESeqDataSet et SingleCellExperiment ne font que l’étendre. Ensuite, mettez-le à l’œuvre dans la matrice de comptage RNA-seq & le CQ et l’expression différentielle avec DESeq2.

Leçons connexes

Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN
Note

Cette leçon est reproductible : chaque objet et chaque nombre de cette page a été produit par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.

Références

  • Morgan, M., Obenchain, V., Hester, J., & Pagès, H. (2024). SummarizedExperiment: a container for representing assay data and metadata. Bioconductor. Package R.
  • Amezquita, R. A., et al. (2020). Orchestrating single-cell analysis with Bioconductor (la classe SingleCellExperiment). Nature Methods, 17, 137–145.
  • Himes, B. E., et al. (2014). RNA-Seq transcriptome profiling identifies CRISPLD2 as a glucocorticoid responsive gene in airway smooth muscle cells (le jeu de données airway). PLoS ONE, 9(6), e99625.

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Le conteneur SummarizedExperiment en R : assays, rowData \&
    colData},
  date = {2026-06-29},
  url = {https://www.datanovia.com/learn/bioinformatics/foundations/summarizedexperiment},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Le conteneur SummarizedExperiment en R : assays, rowData & colData.” 2026. June 29. https://www.datanovia.com/learn/bioinformatics/foundations/summarizedexperiment.