La matrice de comptage RNA-seq en R : import et contrôle qualité

Là où commence toute analyse RNA-seq — comprenez la matrice de comptage, chargez-la dans un DESeqDataSet, et contrôlez-la avant de tester

Un guide pratique de la matrice de comptage RNA-seq bulk en R. Comprenez ce qu’est une matrice de comptage (gènes × échantillons de comptages de reads bruts), importez vos comptages dans R de deux façons courantes — depuis une table featureCounts/HTSeq avec DESeqDataSetFromMatrix, ou depuis Salmon/kallisto via tximport — appariez-les à une table d’échantillons, et réalisez les contrôles qualité dont toute analyse a besoin : tailles de librairie, distributions de comptages, et filtrage des gènes à faible comptage. Sur le jeu de données airway.

Date de publication

29 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • La matrice de comptage est le point de départ de toute analyse RNA-seq bulk : les lignes sont des gènes, les colonnes sont des échantillons, et chaque valeur est le nombre de reads attribués à ce gène dans cet échantillon.
  • Utilisez des comptages entiers bruts — jamais des valeurs TPM, FPKM ou normalisées. DESeq2 modélise les comptages et réalise sa propre normalisation en interne.
  • Importez vos comptages dans R de deux façons courantes : DESeqDataSetFromMatrix() depuis une table featureCounts/HTSeq, ou tximport()DESeqDataSetFromTximport() depuis des quantifications de transcrits Salmon/kallisto.
  • Les colonnes de comptage et la table d’échantillons (colData) doivent s’aligner dans le même ordre — le bug de configuration le plus courant.
  • CQ avant de tester : vérifiez les tailles de librairie (signalez les échantillons à très faible profondeur), inspectez les distributions de comptages, et filtrez les gènes à faible comptage (ils ne portent aucun signal et gonflent les tests multiples).

Introduction

Toute analyse RNA-seq bulk — expression différentielle, PCA, enrichissement — part du même objet : une matrice de comptage. Les outils en amont (l’aligneur et un quantificateur comme featureCounts ou Salmon) ont déjà transformé les reads de séquençage bruts en une table indiquant combien de reads sont tombés sur chaque gène dans chaque échantillon. Votre travail en R commence ici : comprendre cette table, la charger correctement, et la vérifier avant de faire confiance à quoi que ce soit en aval.

Ce guide utilise le jeu de données airway, fourni comme un objet de comptage prêt à l’emploi, de sorte que chaque étape est reproductible — et montre les motifs d’import que vous utiliseriez pour vos propres comptages.

À quoi ressemble une matrice de comptage

airway est un RangedSummarizedExperiment — un conteneur qui regroupe la matrice de comptage (assay) et la table d’échantillons (colData) afin qu’elles ne puissent jamais se désynchroniser. Examinons les deux.

library(airway)
data("airway")

dim(airway)                              # genes x samples
[1] 63677     8
assay(airway)[1:5, 1:4]                  # the count matrix: raw integer counts
                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

Et la table d’échantillons qui s’apparie avec ces colonnes — une ligne par échantillon, dans le même ordre :

# The sample table — one row per column of the matrix, in the SAME order.
as.data.frame(colData(airway)[, c("cell", "dex")])
              cell   dex
SRR1039508  N61311 untrt
SRR1039509  N61311   trt
SRR1039512 N052611 untrt
SRR1039513 N052611   trt
SRR1039516 N080611 untrt
SRR1039517 N080611   trt
SRR1039520 N061011 untrt
SRR1039521 N061011   trt

Trois choses définissent une matrice de comptage valide : les valeurs sont des comptages entiers bruts, les lignes sont des gènes (ici des identifiants Ensembl), et les colonnes sont des échantillons dont l’ordre correspond exactement à la table d’échantillons.

Importer vos propres comptages dans R

airway est pré-empaqueté ; vos données arriveront sous forme de fichiers. Les deux voies courantes :

Depuis une table de comptages (featureCounts, HTSeq, ou tout CSV gènes × échantillons) — lisez-la, séparez les colonnes de comptage des éventuelles colonnes d’annotation, et construisez l’objet avec une table d’échantillons correspondante :

library(DESeq2)

counts  <- as.matrix(read.csv("counts.csv", row.names = 1))   # genes x samples
coldata <- read.csv("samples.csv", row.names = 1)             # one row per sample

# The matrix columns and coldata rows MUST be in the same order:
counts <- counts[, rownames(coldata)]

dds <- DESeqDataSetFromMatrix(countData = counts,
                              colData   = coldata,
                              design    = ~ condition)

Depuis des quantificateurs de transcrits (Salmon, kallisto) — agrégez les transcrits au niveau des gènes avec tximport, puis transmettez le résultat à DESeq2 :

library(tximport)

txi <- tximport(files, type = "salmon", tx2gene = tx2gene)    # files = per-sample quant.sf
dds <- DESeqDataSetFromTximport(txi, colData = coldata, design = ~ condition)

Comme airway est déjà un SummarizedExperiment, nous construisons l’objet directement :

library(DESeq2)
dds <- DESeqDataSet(airway, design = ~ dex)
dds
class: DESeqDataSet 
dim: 63677 8 
metadata(2): '' version
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
AvertissementAlignez l’ordre des échantillons

DESeqDataSetFromMatrix vérifie les noms : si votre colData a des noms de lignes qui ne correspondent pas aux colonnes de comptage, il lève une erreur plutôt que de deviner — un garde-fou utile. Mais il ne peut pas détecter une inversion lorsque la table d’échantillons n’a aucun nom de ligne, car il se fie alors aveuglément à la position des colonnes. Donnez donc toujours des noms de lignes à colData, alignez-les d’abord (counts <- counts[, rownames(coldata)]), et confirmez avec all(colnames(counts) == rownames(coldata)) avant de construire l’objet.

Contrôle qualité avant l’analyse

Quelques vérifications rapides détectent les catastrophes — une librairie ratée, une inversion, un échantillon à faible profondeur — avant qu’elles ne vous coûtent cher.

Tailles de librairie

La taille de librairie est le total de reads alignés par échantillon. Une profondeur très inégale, ou un échantillon bien en dessous des autres, est un signal d’alerte (DESeq2 normalise pour la profondeur, mais une librairie à très faible profondeur porte tout de même peu d’information).

library(ggplot2)

libsize <- data.frame(sample   = colnames(assay(dds)),
                      millions = colSums(assay(dds)) / 1e6,
                      dex      = dds$dex)

ggplot(libsize, aes(reorder(sample, millions), millions, fill = dex)) +
  geom_col() +
  coord_flip() +
  scale_fill_viridis_d(end = 0.85) +
  labs(x = NULL, y = "Library size (million reads)", fill = "Treatment") +
  theme_minimal()

Horizontal bar chart of per-sample library sizes for the eight airway samples, total reads in millions on the x-axis, ranging from about 15 to 31 million and coloured by treatment — reasonably even sequencing depth with no sample far below the others.

Les huit échantillons se situent entre environ 15 et 31 millions de reads — suffisamment réguliers, aucun n’étant relégué bien en dessous des autres. Aucun échantillon ne mérite d’être écarté sur la seule profondeur.

Distributions de comptages

Une boîte à moustaches des comptages transformés en log par échantillon montre si les échantillons sont globalement comparables. Ils devraient se ressembler ; une boîte nettement décalée par rapport aux autres signale un problème.

logc  <- log2(assay(dds) + 1)                 # +1 avoids log(0)
boxdf <- stack(as.data.frame(logc))           # base-R wide → long (no tidyr)

ggplot(boxdf, aes(ind, values)) +
  geom_boxplot(fill = "#3a86d4", alpha = 0.7, outlier.size = 0.3) +
  labs(x = "Sample", y = "log2(count + 1)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Boxplots of log2 counts per sample for the eight airway samples, showing near-identical distributions across samples — a sign the libraries are comparable and there is no obvious technical outlier.

Filtrer les gènes à faible comptage

La plupart des gènes sont à peine détectés — ils ne portent aucune information et alourdissent seulement la charge des tests multiples. Écartez ceux qui n’ont presque aucun read sur l’ensemble des échantillons.

cat("genes before filtering:", nrow(dds), "\n")
genes before filtering: 63677 
dds <- dds[rowSums(counts(dds)) >= 10, ]      # keep genes with >= 10 reads total
cat("genes after filtering: ", nrow(dds), "\n")
genes after filtering:  22369 

Le filtre fait passer airway de 63 677 gènes à environ 22 000 — la majeure partie du génome n’est détectée dans aucune expérience donnée, et la retirer rend tout l’aval plus rapide et la correction FDR moins sévère. L’objet est maintenant prêt pour la PCA et le regroupement d’échantillons et l’expression différentielle.

Problèmes fréquents

Colonnes et lignes d’échantillons mal alignées. Si votre table d’échantillons a des noms de lignes, DESeq2 lève une erreur en cas d’incohérence ; le danger silencieux est un colData sans noms de lignes, où la position des colonnes est suivie aveuglément et où chaque échantillon peut finir mal étiqueté. Nommez toujours les lignes, réordonnez (counts[, rownames(coldata)]), et vérifiez all(colnames(counts) == rownames(coldata)) avant de construire l’objet.

Des valeurs normalisées au lieu de comptages bruts. Les valeurs TPM/FPKM/RPKM et transformées en log cassent le modèle de comptage. DESeq2 a besoin de comptages entiers bruts et normalise en interne. Si vos nombres comportent des décimales, ce ne sont pas des comptages bruts.

Identifiants de gènes stockés dans une colonne. Si votre CSV contient une colonne d’identifiants de gènes, définissez-la comme noms de lignes (read.csv(..., row.names = 1)) — sinon elle est traitée comme un échantillon et as.matrix convertit tout en texte.

Questions fréquentes

Des comptages entiers bruts. DESeq2 modélise les comptages avec une loi binomiale négative et estime ses propres facteurs de taille, il doit donc recevoir des entiers non normalisés. Ne lui fournissez jamais de valeurs TPM, FPKM, RPKM ou transformées en log — elles violent les hypothèses du modèle.

Les deux sont standard. featureCounts/HTSeq fournissent une table de comptages gènes × échantillons → DESeqDataSetFromMatrix(). Salmon/kallisto fournissent des estimations par transcrit → agrégez-les au niveau des gènes avec tximport()DESeqDataSetFromTximport() (cela transmet aussi à DESeq2 des informations d’offset utiles). Utilisez celle que votre pipeline a produite.

Un conteneur Bioconductor qui réunit la matrice de comptage (assay), la table par échantillon (colData) et les informations sur les gènes (rowData) dans un seul objet — de sorte que les échantillons et leurs métadonnées ne puissent jamais se désynchroniser. airway en est un ; DESeqDataSet l’étend.

Légèrement. Une règle courante consiste à conserver les gènes avec ≥ 10 reads au total sur l’ensemble des échantillons (ou ≥ 10 dans au moins quelques échantillons). Le but est seulement d’écarter les gènes quasi indétectables ; le filtrage indépendant de DESeq2 gère la coupe plus fine ensuite. Ne filtrez pas sur le fold change ou la p-value avant de tester.

Testez vos connaissances

À partir de assay(airway), calculez la taille de librairie de chaque échantillon en millions, puis indiquez combien de gènes ont zéro comptage dans tous les échantillons. Quelle fraction du génome cela représente-t-il environ ?

colSums() donne les tailles de librairie ; rowSums(cm) == 0 repère les gènes entièrement nuls ; mean() de ce vecteur logique donne la fraction.

cm <- assay(airway)
colSums(cm) / 1e6                      # library sizes (millions)
sum(rowSums(cm) == 0)                  # genes with zero counts everywhere
mean(rowSums(cm) == 0)                 # ...as a fraction (~0.47, about half)

Près de la moitié des gènes annotés ne sont jamais détectés dans cette expérience — c’est normal, et c’est exactement pourquoi le filtrage est la première étape.

A. TPM ou FPKM, car ils sont comparables entre échantillons. B. Des comptages entiers bruts. C. Des comptages transformés en log2.

B. Des comptages entiers bruts. DESeq2 modélise directement les comptages et normalise en interne ; les valeurs pré-normalisées ou transformées en log violent ses hypothèses.

Conclusion

Vous connaissez maintenant l’objet sur lequel repose toute analyse RNA-seq : une matrice de comptage de comptages entiers bruts, lignes = gènes, colonnes = échantillons, appariée à une table d’échantillons dans l’ordre correspondant. Vous avez vu les deux voies d’import (DESeqDataSetFromMatrix pour les tables de comptages, tximport pour Salmon/kallisto), construit un DESeqDataSet, et réalisé le CQ essentiel — tailles de librairie, distributions de comptages, et filtrage des gènes à faible comptage (ici 63 677 → ~22 000). Les règles pratiques : comptages bruts uniquement, alignez l’ordre des échantillons, et CQ avant de tester. Ensuite, explorez les échantillons avec la PCA et le regroupement, puis lancez l’expression différentielle.

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 figure et chaque nombre ont été produits par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.

Références

  • Love, M. I., Huber, W., & Anders, S. (2014). Moderated estimation of fold change and dispersion for RNA-seq data with DESeq2. Genome Biology, 15(12), 550.
  • Soneson, C., Love, M. I., & Robinson, M. D. (2015). Differential analyses for RNA-seq: transcript-level estimates improve gene-level inferences (tximport). F1000Research, 4, 1521.
  • 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 = {La matrice de comptage RNA-seq en R : import et contrôle
    qualité},
  date = {2026-06-29},
  url = {https://www.datanovia.com/learn/bioinformatics/bulk-rna-seq/rna-seq-count-matrix},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“La matrice de comptage RNA-seq en R : import et contrôle qualité.” 2026. June 29. https://www.datanovia.com/learn/bioinformatics/bulk-rna-seq/rna-seq-count-matrix.