Notation de signatures géniques par cellule en R avec AUCell & UCell

Quantifiez avec quelle intensité chaque cellule exprime un programme génique — un module interféron, une signature de lignage, une voie biologique — avec AUCell et UCell, puis validez le score par rapport à un contraste biologique connu

Un guide pratique de la notation de signatures géniques par cellule pour le RNA-seq single-cell en R. Notez un module de réponse à l’interféron, une signature de lymphocytes T et une signature de monocytes par cellule avec les standards fondés sur les rangs AUCell et UCell, comparez-les à GSVA, ssGSEA et singscore, et validez chaque score par rapport à la vérité terrain interféron-bêta intégrée au benchmark immunitaire PBMC.

Date de publication

4 juillet 2026

Modifié

7 juillet 2026

AstucePoints clés
  • La notation de signatures transforme un ensemble de gènes en un seul nombre par cellule. Une fois que vous disposez des regroupements et des étiquettes de types cellulaires, la question suivante est souvent avec quelle intensité chaque cellule exprime-t-elle ce programme ? — une réponse à l’interféron, une identité de lignage, une voie biologique. Un score de signature y répond par cellule, sur un continuum, sans tracer de nouvelles frontières de regroupement.
  • AUCell et UCell sont les standards single-cell modernes, et tous deux sont fondés sur les rangs. Ils notent chaque cellule à partir du classement des gènes de la signature au sein de cette cellule, ce qui les rend robustes à la profondeur de séquençage et à la parcimonie des données single-cell — aucune normalisation inter-cellules du score n’est nécessaire (Aibar et al., 2017 ; Andreatta & Carmona, 2021).
  • UCell est un seul appel sur un objet Seurat ; AUCell construit les rangs puis note. AddModuleScore_UCell(obj, features = sigs) ajoute une colonne de score par signature ; AUCell se divise en AUCell_buildRankings() puis AUCell_calcAUC(). Les grands atlas de référence notent leurs programmes géniques ainsi — l’atlas de moelle osseuse humaine BoneMarrowMap note ses signatures de cellules souches par cellule avec AUCell (Zeng et al., 2025).
  • GSVA, ssGSEA et singscore sont les alternatives — conçues pour le bulk, utilisables sur des cellules individuelles. GSVA et ssGSEA donnent un enrichissement signé, bidirectionnel ; singscore est un score de rang à échantillon unique. Ils s’accordent avec AUCell/UCell sur les signaux forts, mais pour des données single-cell parcimonieuses, les méthodes par cellule fondées sur les rangs sont le choix par défaut.
  • Validez toujours une signature par rapport à un contraste connu avant de lui faire confiance. Ici, les données ont une vérité terrain intégrée — la moitié des cellules ont été stimulées à l’interféron-bêta — donc une signature de gènes interféron doit obtenir un score plus élevé dans les cellules stimulées. Chaque méthode le retrouve. Vérifier la cohérence par rapport à une biologie connue est la discipline qui distingue un signal réel d’un artéfact d’apparence plausible.

Introduction

Vous avez exécuté le workflow standard de RNA-seq single-cell (scRNA-seq) : contrôle qualité, normalisation, regroupement et annotation des types cellulaires. Vous disposez d’une carte de quelles cellules sont présentes. Mais une grande partie de la biologie n’est pas un type cellulaire du tout — c’est un programme que les cellules activent à des degrés divers : une réponse à l’interféron, un état du cycle cellulaire, une signature d’épuisement, une voie métabolique. Ces phénomènes vivent sur un continuum, et une étiquette de regroupement discrète évacue ce continuum.

Cette leçon répond à une question différente de celle de l’annotation. Non pas « de quel type est cette cellule ? » mais « avec quelle intensité cette cellule exprime-t-elle ce programme génique particulier ? » L’outil est la notation de signatures géniques : vous confiez à la méthode un ensemble de gènes soigneusement choisi — une signature — et elle renvoie un seul nombre par cellule mesurant à quel point cette cellule est enrichie pour cet ensemble. Les deux méthodes fondées sur les rangs qui dominent en single-cell sont AUCell et UCell ; les alternatives établies sont GSVA, ssGSEA et singscore.

Nous travaillons sur le classique benchmark immunitaire PBMC IFN-β : des cellules mononucléées du sang périphérique (PBMC — les cellules immunitaires du sang) séparées en un groupe contrôle et un groupe stimulé à l’interféron-bêta (IFN-β), une cytokine qui déclenche une réponse transcriptionnelle forte et bien caractérisée (Kang et al., 2018). Ce plan expérimental nous donne une vérité terrain intégrée : une signature de gènes stimulés par l’interféron (ISG — des gènes activés par la signalisation de l’interféron) doit obtenir un score plus élevé dans les cellules stimulées que dans les contrôles. Si une méthode de notation ne peut pas retrouver un contraste aussi fort, nous ne devrions pas lui faire confiance sur un contraste subtil.

Ce qu’est la notation de signatures par cellule — et quand l’utiliser

Une signature est simplement un ensemble de gènes qui marquent un programme : ISG15, IFIT1, MX1 et consorts pour une réponse à l’interféron ; CD3D, CD3E, IL7R pour les lymphocytes T. Un score de signature condense cet ensemble en un seul nombre par cellule — élevé lorsque la cellule exprime fortement l’ensemble, bas lorsqu’elle ne le fait pas. Vous passez d’une matrice de ~13 000 gènes à une poignée de scores par cellule interprétables, un par programme qui vous intéresse.

Utilisez la notation de signatures lorsque vous voulez :

  • Quantifier un programme connu par cellule — une réponse à l’interféron, une signature d’hypoxie, un programme de « stemness » ou d’épuisement — et voir comment il varie au sein de vos regroupements et entre eux, plutôt qu’uniquement entre eux.
  • Noter un état cellulaire qui traverse plusieurs types cellulaires — le cycle cellulaire, le stress, ou une réponse à un traitement ne sont pas un regroupement unique ; un score le capture partout où il apparaît.
  • Comparer un ensemble de gènes soigneusement choisi entre conditions ou échantillons sur une échelle commune, par cellule.

N’y ayez pas recours quand un seul gène marqueur répond déjà à la question, ou quand la « signature » se réduit à un ou deux gènes — un score sur un ensemble de deux gènes n’est qu’une moyenne bruitée, et autant tracer les gènes. La méthode gagne son intérêt lorsque le programme est réparti sur de nombreux gènes et que vous voulez le signal collectif.

Les données : des PBMC stimulés à l’interféron avec une vérité terrain intégrée

Le fichier commité ifnb_subset.rds est une tranche équilibrée du benchmark IFN-β de Kang : une matrice de comptage brute de 13 565 gènes répartis sur 3 000 cellules, plus une table de métadonnées. La colonne stim enregistre si chaque cellule était une cellule CTRL (contrôle) ou STIM (stimulée à l’IFN-β) — 1 500 de chaque — et seurat_annotations contient les 13 types cellulaires déjà appelés pour nous. Nous n’utilisons jamais ces étiquettes pour construire le score ; nous les gardons en réserve pour le valider.

library(Seurat)

d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
obj <- NormalizeData(obj, verbose = FALSE)

dim(obj)                       # genes x cells
[1] 13565  3000
table(obj$stim)                # the control / IFN-beta contrast: our ground truth

CTRL STIM 
1500 1500 
table(obj$seurat_annotations)  # 13 annotated cell types

   CD14 Mono  CD4 Naive T CD4 Memory T    CD16 Mono            B        CD8 T 
         925          534          370          255          194          182 
 T activated           NK           DC  B Activated           Mk          pDC 
         125          144          106           77           52           23 
       Eryth 
          13 

La log-normalisation (NormalizeData) est l’unique étape de prétraitement que chaque méthode présentée suppose : le score est calculé sur l’expression normalisée, pas sur les comptages bruts. Chaque bloc ci-dessous reconstruit à partir exactement de ce point, de sorte que vous pouvez copier n’importe lequel d’entre eux et l’exécuter de façon autonome.

Définir les signatures

Nous notons trois signatures. La première est la vedette de la validation : huit gènes stimulés par l’interféron canoniques, le cœur de la réponse IFN-β. Les deux autres sont des signatures de lignage — un ensemble de lymphocytes T et un ensemble de monocytes — qui devraient s’illuminer dans leurs propres types cellulaires, une seconde vérification orthogonale que les scores ont un sens.

sigs <- list(
  ISG      = c("ISG15", "IFI6", "IFIT1", "IFIT3", "MX1", "OAS1", "ISG20", "IRF7"),  # interferon-stimulated
  Tcell    = c("CD3D", "CD3E", "CD8A", "IL7R", "CCR7"),                             # T-cell identity
  Monocyte = c("CD14", "LYZ", "S100A8", "S100A9", "FCN1"))                          # monocyte identity

lengths(sigs)   # genes per signature
     ISG    Tcell Monocyte 
       8        5        5 

Une signature pratique compte généralement 20 à 200 gènes ; les nôtres sont délibérément petites et choisies à la main pour que la biologie soit transparente. Les méthodes fondées sur les rangs ci-dessous sont robustes à la taille de la signature, mais un ensemble de seulement quelques gènes donne au score moins de matière — préférez un ensemble soigneusement choisi de plusieurs dizaines de gènes dans le travail réel.

AUCell : classer chaque cellule, noter l’aire sous la courbe

AUCell (aire sous la courbe, par cellule) a été introduite avec SCENIC pour noter l’activité des régulons dans les cellules individuelles (Aibar et al., 2017). L’idée est élégante et robuste à la profondeur : au sein de chaque cellule, on classe tous les gènes par expression ; puis on demande à quel point les gènes de la signature siègent près du sommet de ce classement. Concrètement, elle calcule, par cellule, l’aire sous la courbe de récupération (l’AUC) des gènes de la signature le long du classement — élevée quand les gènes de la signature figurent parmi les gènes les plus exprimés de la cellule, basse quand ils sont dispersés au bas de la liste. Comme elle n’utilise que le classement au sein de chaque cellule, elle ne se soucie ni des comptages absolus ni de la profondeur de séquençage.

Elle s’exécute en deux étapes. AUCell_buildRankings() classe les gènes au sein de chaque cellule une fois ; AUCell_calcAUC() note ensuite n’importe quel nombre de signatures par rapport à ces classements.

library(Seurat)
library(AUCell)
library(ggplot2)

d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
obj <- NormalizeData(obj, verbose = FALSE)
sigs <- list(
  ISG      = c("ISG15", "IFI6", "IFIT1", "IFIT3", "MX1", "OAS1", "ISG20", "IRF7"),
  Tcell    = c("CD3D", "CD3E", "CD8A", "IL7R", "CCR7"),
  Monocyte = c("CD14", "LYZ", "S100A8", "S100A9", "FCN1"))

set.seed(42)
rankings <- AUCell_buildRankings(GetAssayData(obj, layer = "data"),
                                 plotStats = FALSE, verbose = FALSE)
auc  <- AUCell_calcAUC(sigs, rankings, verbose = FALSE)
aucm <- getAUC(auc)                       # signatures x cells (3 x 3000)

# The money result: the ISG score MUST be higher in the stimulated cells
tapply(aucm["ISG", ], obj$stim, mean)
      CTRL       STIM 
0.08742204 0.75932580 
obj$ISG_AUCell <- aucm["ISG", ]
VlnPlot(obj, features = "ISG_AUCell", group.by = "stim", pt.size = 0,
        cols = c(CTRL = "#3a86d4", STIM = "#d1495b")) +
  labs(title = "Interferon signature (AUCell)", x = NULL, y = "ISG AUCell score") +
  theme(legend.position = "none")

A violin plot of the per-cell AUCell interferon-stimulated-gene (ISG) score split by condition, control versus IFN-beta-stimulated. The control violin sits low and tight near an AUC of 0.09; the stimulated violin sits far higher near 0.76, with almost no overlap. The interferon score cleanly separates the two conditions, recovering the stimulation the cells received without ever being told about it.

Le score ISG AUCell moyen est de 0.087 dans les contrôles et de 0.759 dans les cellules stimulées — une séparation d’un facteur huit à neuf, et les violons se chevauchent à peine. AUCell a retrouvé la condition expérimentale à partir d’une signature de huit gènes seulement, sans qu’on lui ait jamais indiqué quelles cellules étaient stimulées. C’est exactement la validation que vous voulez voir avant de faire confiance à un score sur une question dont vous ne connaissez pas déjà la réponse. Les grands atlas de référence s’appuient là-dessus : l’atlas BoneMarrowMap note ses signatures de cellules souches hématopoïétiques par cellule avec AUCell (score_Genesets_AUCell) pour placer les cellules requêtes sur une carte de différenciation (Zeng et al., 2025).

UCell : la même idée, un seul appel sur l’objet Seurat

UCell reprend la même philosophie fondée sur les rangs et l’emballe pour le workflow Seurat (Andreatta & Carmona, 2021). Son score repose sur la statistique U de Mann-Whitney : pour chaque cellule, elle demande si les gènes de la signature se classent significativement plus haut que le reste, normalisé sur une échelle de 0 à 1. Elle est déterministe, économe en mémoire, robuste à la taille du jeu de données et — le gain pratique — elle travaille directement sur l’objet Seurat et ajoute les scores directement aux métadonnées.

library(Seurat)
library(UCell)

d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
obj <- NormalizeData(obj, verbose = FALSE)
sigs <- list(
  ISG      = c("ISG15", "IFI6", "IFIT1", "IFIT3", "MX1", "OAS1", "ISG20", "IRF7"),
  Tcell    = c("CD3D", "CD3E", "CD8A", "IL7R", "CCR7"),
  Monocyte = c("CD14", "LYZ", "S100A8", "S100A9", "FCN1"))

# One call: scores every signature, adds ISG_UCell / Tcell_UCell / Monocyte_UCell columns
obj <- AddModuleScore_UCell(obj, features = sigs, name = "_UCell")

tapply(obj$ISG_UCell, obj$stim, mean)   # same contrast, UCell scale
     CTRL      STIM 
0.1361304 0.8341464 

UCell raconte la même histoire sur sa propre échelle : le score ISG est en moyenne de 0.136 dans les contrôles et de 0.834 dans les cellules stimulées. Les nombres absolus diffèrent de ceux d’AUCell (chaque méthode a sa propre échelle), mais la conclusion est identique et tout aussi nette — le programme interféron est activé dans les cellules stimulées et éteint dans les contrôles. Deux méthodes indépendantes fondées sur les rangs qui s’accordent sont en elles-mêmes rassurantes.

Voir le score sur la carte

Un score de signature est une quantité par cellule, il se pose donc directement sur un UMAP (uniform manifold approximation and projection — la projection en 2-D des cellules). Colorer la carte selon le score ISG UCell montre la réponse à l’interféron comme un gradient à travers le tissu : des régions entières s’illuminent, suivant les cellules stimulées où qu’elles siègent.

set.seed(42)
library(Seurat)
library(UCell)
library(patchwork)

d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
sigs <- list(ISG = c("ISG15", "IFI6", "IFIT1", "IFIT3", "MX1", "OAS1", "ISG20", "IRF7"))

obj <- NormalizeData(obj, verbose = FALSE) |>
  FindVariableFeatures(nfeatures = 2000, verbose = FALSE) |> ScaleData(verbose = FALSE) |>
  RunPCA(npcs = 30, verbose = FALSE) |> RunUMAP(dims = 1:30, verbose = FALSE)
obj <- AddModuleScore_UCell(obj, features = sigs, name = "_UCell")

p1 <- DimPlot(obj, group.by = "stim") + ggtitle("Condition")
p2 <- FeaturePlot(obj, features = "ISG_UCell") +
  scale_color_viridis_c() + ggtitle("ISG score (UCell)")
p1 + p2

Two side-by-side UMAP embeddings of the 3,000 PBMCs. The left panel colours cells by condition, control versus IFN-beta-stimulated, which separate into two offset clouds. The right panel colours the same cells by their UCell interferon-stimulated-gene score on a viridis scale: the stimulated cloud is bright yellow (high score) and the control cloud is dark purple (low score), so the score map mirrors the condition map almost exactly.

Les deux panneaux sont presque la même image : la région que l’UMAP remplit de cellules stimulées est exactement la région que le score ISG peint en clair. Cette correspondance — un score que vous avez calculé à partir de huit gènes reproduisant la condition expérimentale que vous voyez dans la projection — est la forme visuelle de la validation.

Les signatures de lignage tombent sur les bonnes cellules

La signature interféron est pilotée par la condition ; les signatures de lymphocytes T et de monocytes sont pilotées par l’identité, elles donnent donc une seconde vérification orthogonale. Moyennez chaque score UCell au sein de chaque type cellulaire annoté et disposez-le en heatmap : une bonne signature devrait culminer dans exactement le type cellulaire qu’elle marque.

library(Seurat)
library(UCell)
library(ggplot2)

d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
obj <- NormalizeData(obj, verbose = FALSE)
sigs <- list(
  ISG      = c("ISG15", "IFI6", "IFIT1", "IFIT3", "MX1", "OAS1", "ISG20", "IRF7"),
  Tcell    = c("CD3D", "CD3E", "CD8A", "IL7R", "CCR7"),
  Monocyte = c("CD14", "LYZ", "S100A8", "S100A9", "FCN1"))
obj <- AddModuleScore_UCell(obj, features = sigs, name = "_UCell")

# Mean score per signature per cell type (base R aggregation)
cols <- c("ISG_UCell", "Tcell_UCell", "Monocyte_UCell")
m <- sapply(cols, function(col) tapply(obj[[col]][, 1], obj$seurat_annotations, mean))
colnames(m) <- c("ISG", "Tcell", "Monocyte")

# Where does each lineage signature peak?
sort(m[, "Tcell"],    decreasing = TRUE)[1:3]
 CD4 Naive T        CD8 T CD4 Memory T 
   0.4571680    0.3630815    0.3510867 
sort(m[, "Monocyte"], decreasing = TRUE)[1:3]
CD14 Mono        DC CD16 Mono 
0.6385011 0.3369248 0.2883582 
df <- data.frame(cell_type = rownames(m)[row(m)],
                 signature = colnames(m)[col(m)],
                 score     = as.vector(m))
ggplot(df, aes(cell_type, signature, fill = score)) +
  geom_tile() +
  scale_fill_viridis_c() +
  labs(x = NULL, y = NULL, fill = "Mean\nUCell") +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

A heatmap of three UCell signature scores (rows: interferon, T-cell, monocyte) averaged across the 13 annotated cell types (columns), on a viridis colour scale. The T-cell signature row is brightest over the T-cell columns (CD4 Naive T, CD8 T, CD4 Memory T); the monocyte signature row is brightest over CD14 Mono and the dendritic cells; the interferon row is more evenly warm across all cell types, because it tracks the stimulation condition rather than a single lineage.

Les signatures de lignage tombent là où elles le devraient. Le score des lymphocytes T culmine dans les cellules CD4 Naive T (0.457), suivies des autres sous-ensembles T — CD8 T et CD4 Memory T. Le score des monocytes culmine dans les cellules CD14 Mono (0.639), avec les cellules dendritiques ensuite. La ligne interféron est plus uniformément chaude, parce qu’elle reflète la condition de stimulation plutôt qu’un lignage — ce qui est exactement juste. Chaque signature rapporte la biologie pour laquelle elle a été construite.

Le choix de la méthode : GSVA, ssGSEA et singscore

AUCell et UCell sont les standards par cellule fondés sur les rangs, mais trois autres méthodes apparaissent constamment dans la littérature — le plus souvent parce qu’elles viennent du monde du RNA-seq bulk et que les chercheurs les reportent. Il vaut la peine de savoir ce qu’elles font et en quoi elles diffèrent.

  • GSVA (gene set variation analysis) estime un score d’enrichissement signé par échantillon en comparant la distribution d’expression de chaque gène à travers les cellules à une statistique fondée sur l’ensemble (Hänzelmann et al., 2013). Signé signifie que le score peut être négatif (le programme est déplété), ce qu’AUCell et UCell ne peuvent pas exprimer.
  • ssGSEA (single-sample gene set enrichment analysis) est la forme à échantillon unique de GSEA : elle classe les gènes au sein d’un échantillon et intègre une statistique d’enrichissement cumulée le long de la signature (Barbie et al., 2009).
  • singscore est un score à échantillon unique fondé sur les rangs, conceptuellement le plus proche des trois de UCell, et il peut noter une signature dirigée (haut/bas) ou non dirigée (Foroutan et al., 2018).

Une note d’API cruciale qui piège les gens : GSVA 2.x utilise des objets de paramètres. L’ancien appel gsva(expr, genesets) a été supprimé — vous construisez désormais un objet gsvaParam() (ou ssgseaParam()) et passez celui-ci à gsva(). Voici les trois notant la signature ISG sur les mêmes données :

library(Seurat)
library(GSVA)
library(singscore)

d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
obj <- NormalizeData(obj, verbose = FALSE)
sigs <- list(
  ISG      = c("ISG15", "IFI6", "IFIT1", "IFIT3", "MX1", "OAS1", "ISG20", "IRF7"),
  Tcell    = c("CD3D", "CD3E", "CD8A", "IL7R", "CCR7"),
  Monocyte = c("CD14", "LYZ", "S100A8", "S100A9", "FCN1"))
expr <- as.matrix(GetAssayData(obj, layer = "data"))   # dense matrix for the bulk-style methods

# GSVA 2.x: build a param object, then call gsva() on it (the old gsva(expr, sigs) API is gone)
g_gsva <- gsva(gsvaParam(expr, sigs, kcdf = "Gaussian"), verbose = FALSE)
tapply(g_gsva["ISG", ], obj$stim, mean)     # GSVA is signed: control is negative
      CTRL       STIM 
-0.8013506  0.7173729 
# ssGSEA via the same param-object interface
g_ssgsea <- gsva(ssgseaParam(expr, sigs), verbose = FALSE)
tapply(g_ssgsea["ISG", ], obj$stim, mean)
     CTRL      STIM 
0.1476112 0.8179692 
# singscore: rank the genes once, then score the ISG set
ranked <- rankGenes(expr)
ss <- simpleScore(ranked, upSet = sigs$ISG)$TotalScore
tapply(ss, obj$stim, mean)                   # singscore, ISG by condition
      CTRL       STIM 
-0.3276772  0.3873182 

Les trois s’accordent avec AUCell et UCell : le score interféron est bien plus élevé dans les cellules stimulées. GSVA passe de -0.80 (contrôle) à +0.72 (stimulé) — l’inversion de signe est GSVA qui vous dit que le programme est déplété dans les contrôles et enrichi dans les cellules stimulées. ssGSEA va de 0.15 → 0.82 et singscore de -0.33 → 0.39. Cinq méthodes différentes, cinq échelles différentes, une conclusion unanime. Cette unanimité sur un signal fort est le point clé : quand la biologie est réelle, le choix du scoreur change rarement la conclusion.

Quelle méthode, quand

Vous avez… Utilisez Pourquoi
Des données single-cell, et vous voulez un score par cellule robuste UCell ou AUCell Fondés sur les rangs, robustes à la profondeur et à la parcimonie ; UCell est un seul appel sur l’objet Seurat, AUCell est le choix par défaut natif de SCENIC
De nombreuses signatures à noter sur tout un atlas AUCell Construisez les rangs des cellules une fois, puis notez n’importe quel nombre d’ensembles de gènes par rapport à eux
Le besoin d’un score signé (enrichi vs déplété) GSVA Bidirectionnel par conception ; la seule méthode ici qui rapporte la déplétion
Une signature dirigée haut/bas, ou une matrice bulk/pseudobulk singscore ou ssGSEA Scores de rang à échantillon unique conçus pour le bulk ; gèrent les ensembles de gènes haut-et-bas
Du pseudobulk (agrégé par échantillon) plutôt que par cellule GSVA / ssGSEA Ceux-ci ont été conçus pour le cadre au niveau de l’échantillon, à plus faible bruit

La recommandation pour le travail en single-cell est simple : par défaut, UCell ou AUCell. Ils ont été construits pour la parcimonie et la variation de profondeur des cellules individuelles, ils n’ont besoin d’aucune normalisation inter-cellules du score, et ils passent à l’échelle. Recourez à GSVA quand vous avez spécifiquement besoin d’un enrichissement signé, et à singscore/ssGSEA quand vous travaillez avec une signature dirigée ou une matrice pseudobulk.

La validation est la discipline, pas une réflexion après coup

L’unique habitude qui compte plus que le choix de la méthode : ne faites jamais confiance à un score de signature que vous n’avez pas validé. Une fonction de notation renverra toujours des nombres — d’apparence plausible, bien distribués — que la signature ait ou non un sens dans vos données. Trois façons de vérifier avant de bâtir sur un score :

  • Notez par rapport à un contraste connu. Si votre plan expérimental a une vérité terrain intégrée — une stimulation, un génotype, un point temporel — une signature pertinente doit bouger dans la direction attendue, comme le score ISG l’a fait entre CTRL et STIM ici. C’est la vérification la plus forte disponible.
  • Notez une signature de contrôle positif. Une signature de lignage doit culminer dans son propre type cellulaire, comme l’ont fait les signatures de lymphocytes T et de monocytes. Si une signature pour un programme que vous comprenez tombe sur les mauvaises cellules, la notation — ou la signature — est fausse, et une signature plus subtile le sera aussi.
  • Vérifiez la couverture des gènes. Une signature ne vaut que par les gènes réellement présents dans votre objet. Si la moitié des gènes de la signature manquent (nomenclature différente, ensemble de features filtré), le score est construit sur le reste — discrètement plus faible que vous ne le pensez.

Faites cela d’abord, et le score devient une preuve. Sautez-le, et ce n’est que de la décoration.

Problèmes courants

gsva(expr, sigs) échoue avec « unable to find an inherited method » ou une erreur d’argument. Vous êtes sur GSVA 2.x, où l’ancienne API directe a été supprimée. Construisez d’abord un objet de paramètres et passez-le à gsva() : gsva(gsvaParam(expr, sigs, kcdf = "Gaussian")) pour GSVA, gsva(ssgseaParam(expr, sigs)) pour ssGSEA. Pour des données single-cell log-normalisées, utilisez kcdf = "Gaussian" ; pour des comptages bruts, utilisez kcdf = "Poisson".

Certains gènes de la signature manquent de l’objet, et le score paraît faible. Les méthodes notent silencieusement sur les gènes de la signature présents, quels qu’ils soient. Vérifiez le recouvrement avant de noter — lengths(lapply(sigs, intersect, rownames(obj))) versus lengths(sigs) — et corrigez les incohérences de nomenclature (symboles vs identifiants Ensembl, capitalisation d’espèce) pour que la signature complète soit réellement utilisée.

Les scores ne séparent pas les groupes que vous attendiez. Écartez d’abord les causes mécaniques : notez-vous sur des données normalisées (NormalizeData exécuté) plutôt que sur des comptages bruts ? Les gènes de la signature sont-ils présents (ci-dessus) ? La signature est-elle assez grande — une « signature » de deux ou trois gènes donne un score bruité ? Ce n’est qu’une fois que tout cela est propre qu’un score plat signifie quelque chose de biologique : les gènes du programme ne varient réellement pas à travers vos cellules.

Foire aux questions

Pour le RNA-seq single-cell, choisissez par défaut UCell ou AUCell : tous deux sont fondés sur les rangs, ils sont donc robustes à la profondeur de séquençage et à la parcimonie des données single-cell et n’ont besoin d’aucune normalisation inter-cellules du score. UCell est un seul appel qui ajoute les scores à votre objet Seurat ; AUCell construit les rangs des cellules une fois puis note n’importe quel nombre de signatures. GSVA et ssGSEA ont été conçus pour le RNA-seq bulk et donnent un score d’enrichissement signé — utile quand vous devez exprimer la déplétion, ou quand vous travaillez avec des données pseudobulk (agrégées par échantillon) plutôt qu’avec des cellules individuelles.

Oui. Exécutez d’abord la log-normalisation (NormalizeData dans Seurat) — chaque méthode ici note sur l’expression normalisée, pas sur les comptages bruts. Les méthodes fondées sur les rangs (AUCell, UCell, singscore) sont alors robustes aux différences de profondeur résiduelles parce qu’elles n’utilisent que le classement des gènes au sein de chaque cellule. Pour GSVA et ssGSEA sur des données log-normalisées, réglez kcdf = "Gaussian" ; utilisez kcdf = "Poisson" seulement si vous passez des comptages bruts.

Une signature pratique compte généralement 20 à 200 gènes. Les méthodes fondées sur les rangs gèrent les petits ensembles, mais une signature de seulement deux ou trois gènes donne un score bruité — autant tracer les gènes directement. Les très grands ensembles (plusieurs centaines) diluent le signal spécifique avec des gènes exprimés de façon générique. Réduisez aux gènes qui marquent réellement le programme, et vérifiez combien sont effectivement présents dans votre objet avant de faire confiance au score.

AddModuleScore (intégré à Seurat) moyenne l’expression des gènes de la signature par rapport à un ensemble de gènes de contrôle échantillonné aléatoirement — un score de différence de moyennes sensible à la profondeur de séquençage et à l’échantillonnage de contrôle. UCell utilise plutôt la statistique de rang de Mann-Whitney par cellule, qui est déterministe et robuste à la profondeur et à la taille du jeu de données. Pour un score par cellule stable et reproductible, UCell (ou AUCell) est le choix le plus défendable ; AddModuleScore convient pour un coup d’œil rapide.

Oui, mais pas avec AUCell ou UCell seuls, qui notent un seul ensemble (haut). Utilisez singscore avec à la fois upSet et downSet (simpleScore(ranked, upSet = up, downSet = down)) pour un score dirigé, ou GSVA, qui est signé et reflète la déplétion sous forme de score négatif. Pour un programme purement régulé à la hausse — comme la signature interféron ici — un score à ensemble unique de UCell ou AUCell est le choix le plus simple.

Testez votre compréhension

En utilisant ifnb_subset.rds, notez une signature de phase S — les gènes de phase S livrés avec Seurat, cc.genes$s.genes — par cellule avec UCell, et validez-la. Il n’y a pas de vérité terrain de stimulation pour le cycle cellulaire ici, alors utilisez plutôt une vérification par contrôle positif : les sous-ensembles activés et en prolifération (les lymphocytes B et T activés, et les cellules dendritiques plasmacytoïdes) devraient porter un score de phase S moyen plus élevé que les cellules au repos terminalement différenciées. Calculez le score UCell de phase S moyen par type cellulaire et inspectez le classement. Le score se comporte-t-il comme un score de cycle cellulaire le devrait ?

cc.genes$s.genes est un vecteur de caractères de symboles de gènes de phase S. Passez-le comme liste nommée à AddModuleScore_UCell(obj, features = list(Sphase = cc.genes$s.genes)), puis agrégez avec tapply(obj$Sphase_UCell, obj$seurat_annotations, mean) et triez. N’oubliez pas d’intersecter la signature avec rownames(obj) si certains gènes sont absents.

library(Seurat); library(UCell)
d <- readRDS("_data/ifnb_subset.rds")
obj <- CreateSeuratObject(counts = d$counts, meta.data = d$meta)
obj <- NormalizeData(obj, verbose = FALSE)

# S-phase signature shipped with Seurat, restricted to genes present in the object
s_sig <- intersect(cc.genes$s.genes, rownames(obj))
obj <- AddModuleScore_UCell(obj, features = list(Sphase = s_sig), name = "_UCell")

# Positive-control check: which cell types score highest for S phase?
sort(tapply(obj$Sphase_UCell, obj$seurat_annotations, mean), decreasing = TRUE)

En exécutant le bloc, les sous-ensembles en prolifération et récemment activés — lymphocytes B activés, lymphocytes T activés, et cellules dendritiques plasmacytoïdes (pDC) — obtiennent les scores les plus élevés, tandis que les érythrocytes terminalement différenciés siègent au bas. C’est à peu près le gradient qu’un score de cycle cellulaire devrait produire : les populations en division et fraîchement activées au-dessus des populations au repos et post-mitotiques. Sans contraste de stimulation sur lequel s’appuyer, ce classement par contrôle positif est votre validation — une signature pour un programme que vous comprenez tombant sur les cellules que vous attendez est la preuve que le score est digne de confiance.

A. Rien — des scores identiques prouvent qu’il n’y a pas de réponse à l’interféron. B. Si les gènes de la signature sont effectivement présents dans l’objet et si les données ont été log-normalisées avant la notation ; une incohérence dans les noms de gènes ou une notation sur des comptages bruts peut aplatir un signal réel. C. Ré-exécuter avec une graine aléatoire différente jusqu’à ce que les groupes se séparent.

B. Un score plat est un résultat, mais écartez d’abord les causes mécaniques. Si les symboles de gènes de la signature ne correspondent pas aux noms de features de l’objet (symboles vs identifiants Ensembl, capitalisation d’espèce), les méthodes notent silencieusement sur les quelques-uns qui se recouvrent — ou aucun — et le score s’effondre. De même, noter sur des comptages bruts au lieu d’une expression log-normalisée fausse chaque méthode. Vérifiez le recouvrement des gènes (intersect(sig, rownames(obj))) et que NormalizeData a été exécuté ; ce n’est que lorsque tout cela est propre qu’un score plat signifie que le programme ne varie réellement pas. (La réponse C est fausse — UCell est déterministe ; la graine ne change pas le score.)

Conclusion

Le regroupement vous dit quelles cellules sont présentes ; la notation de signatures vous dit avec quelle intensité chaque cellule exécute un programme. Les méthodes fondées sur les rangs UCell et AUCell sont les standards single-cell — robustes à la profondeur et à la parcimonie, ne nécessitant aucune normalisation inter-cellules et aussi simples qu’un seul appel sur un objet Seurat. Sur le benchmark IFN-β, une signature interféron de huit gènes a obtenu un score ~9× plus élevé dans les cellules stimulées avec les deux, et les signatures de lignage sont tombées exactement sur leurs propres types cellulaires — la vérité terrain intégrée qui transforme un score en preuve. Les alternatives issues du monde bulk GSVA (signée), ssGSEA et singscore se sont toutes accordées sur le signal fort, chacune sur sa propre échelle, et chacune a sa niche. Mais la méthode importe moins que l’habitude : notez par rapport à un contraste connu, vérifiez un contrôle positif, confirmez que vos gènes sont présents — validez d’abord, et seulement ensuite lisez la biologie sur le score. La notation par cellule est le point d’entrée de l’analyse fonctionnelle ; les leçons suivantes s’appuient dessus pour quantifier l’activité des voies et des facteurs de transcription à travers les cellules.

Leçons associées

🟢 Avec un agent IA

Demandez à Prova « comment noter une signature génique par cellule sur mon propre objet Seurat avec UCell et AUCell, et vérifier que le score est digne de confiance ? » — elle répond avec du code R que vous pouvez exécuter sur vos propres données : définir la signature, appeler AddModuleScore_UCell() (ou construire les rangs AUCell), valider par rapport à un contraste connu, et tracer le score sur votre UMAP. The runtime is the judge. Demander à Prova →

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é, rendu dans l’image Docker datanovia/quarto-bioinformatics — copiez les blocs et exécutez-les dans l’ordre pour les reproduire. The runtime is the judge.

Références

  • Aibar, S., et al. (2017). SCENIC: single-cell regulatory network inference and clustering — introduces AUCell for per-cell gene-set scoring. Nature Methods, 14, 1083–1086. https://doi.org/10.1038/nmeth.4463
  • Andreatta, M., & Carmona, S. J. (2021). UCell: robust and scalable single-cell gene signature scoring. Computational and Structural Biotechnology Journal, 19, 3796–3798. https://doi.org/10.1016/j.csbj.2021.06.043
  • Hänzelmann, S., Castelo, R., & Guinney, J. (2013). GSVA: gene set variation analysis for microarray and RNA-seq data. BMC Bioinformatics, 14, 7. https://doi.org/10.1186/1471-2105-14-7
  • Barbie, D. A., et al. (2009). Systematic RNA interference reveals that oncogenic KRAS-driven cancers require TBK1 — introduces ssGSEA. Nature, 462, 108–112. https://doi.org/10.1038/nature08460
  • Foroutan, M., et al. (2018). Single sample scoring of molecular phenotypes (singscore). BMC Bioinformatics, 19, 404. https://doi.org/10.1186/s12859-018-2435-4
  • Zeng, A. G. X., et al. (2025). Single-cell transcriptional atlas of human hematopoiesis (BoneMarrowMap) — scores stem-cell signatures per cell with AUCell. Blood Cancer Discovery, 6(4), 307–324. https://doi.org/10.1158/2643-3230.BCD-24-0342
  • Kang, H. M., et al. (2018). Multiplexed droplet single-cell RNA-sequencing using natural genetic variation — the source of the IFN-β–stimulated PBMC dataset. Nature Biotechnology, 36, 89–94. https://doi.org/10.1038/nbt.4042

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Notation de signatures géniques par cellule en R avec AUCell
    \& UCell},
  date = {2026-07-04},
  url = {https://www.datanovia.com/learn/bioinformatics/single-cell/scrnaseq-signature-scoring.html},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Notation de signatures géniques par cellule en R avec AUCell & UCell.” 2026. July 4. https://www.datanovia.com/learn/bioinformatics/single-cell/scrnaseq-signature-scoring.html.