Ce rapport présente une analyse complète de données de transcriptomique spatiale générées par la technologie Xenium In Situ (10x Genomics) sur un tissu de cancer du poumon humain (données publiques). Le pipeline comprend :
Note : Ce rapport suppose que les objets
xenium.obj, AllMarkers.sketch,
cellchat et ekegg sont déjà en mémoire dans la
session R courante. Si ce n’est pas le cas, décommentez
readRDS() ci-dessous.
library(Seurat)
library(SeuratObject)
library(tidyverse)
library(ggplot2)
library(patchwork)
library(dplyr)
library(SingleR)
library(celldex)
library(EnhancedVolcano)
library(CellChat)
library(clusterProfiler)
library(org.Hs.eg.db)
library(enrichplot)Idents(xenium.obj) <- "SingleR_label"
VlnPlot(
xenium.obj,
features = c("nFeature_Xenium", "nCount_Xenium"),
ncol = 2,
pt.size = 0
) &
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 7),
plot.title = element_text(face = "bold", size = 11)
)Figure 1. Distribution du nombre de gènes détectés (nFeature) et de transcrits (nCount) par type cellulaire annoté. La majorité des cellules présentent entre 5 et 50 gènes, cohérent avec le panel Xenium (~313 gènes).
Interprétation : Les cellules DC et épithéliales présentent les comptages les plus élevés. Les populations immunitaires rares (Pro-Myelocyte, BM) montrent des comptages plus faibles, reflétant une complexité transcriptomique moindre dans ce panel ciblé.
DimPlot(
xenium.obj,
label = TRUE,
label.size = 3,
reduction = "full.umap",
raster = FALSE,
group.by = "cluster_full",
alpha = 0.1
) +
NoLegend() +
ggtitle("Clusters — Jeu de données complet") +
theme(plot.title = element_text(face = "bold", hjust = 0.5))Figure 2. UMAP du jeu complet (161 894 cellules) après projection depuis le sous-ensemble sketch. 21 clusters sont identifiés (0–20).
FeaturePlot(
xenium.obj,
features = "AGER",
label = TRUE,
raster = FALSE,
reduction = "full.umap"
) +
ggtitle("Expression d'AGER — Pneumocyte Type I") +
theme(plot.title = element_text(face = "bold", hjust = 0.5))Figure 3. Expression du gène AGER sur le UMAP complet. L’expression est concentrée dans le cluster 4, correspondant aux pneumocytes de type I.
DefaultAssay(xenium.obj) <- "Xenium"
ImageDimPlot(
xenium.obj,
group.by = "cluster_full",
cols = "polychrome",
size = 1.25,
dark.background = TRUE
) +
ggtitle("Distribution Spatiale des Clusters") +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, color = "white"),
plot.background = element_rect(fill = "black", color = NA)
)Figure 4. Distribution spatiale des 21 clusters sur la coupe tissulaire Xenium. L’architecture pulmonaire avec la plèvre est visible en haut de la coupe.
DimPlot(
xenium.obj,
group.by = "SingleR_label",
label = TRUE,
repel = TRUE,
reduction = "umap",
label.size = 2.5,
raster = FALSE
) +
NoLegend() +
ggtitle("Annotation SingleR — Assay Sketch") +
theme(plot.title = element_text(face = "bold", hjust = 0.5))Figure 5. Annotation SingleR sur l’assay sketch. Les cellules épithéliales dominent le compartiment droit ; les populations immunitaires occupent le compartiment gauche.
DimPlot(
xenium.obj,
group.by = "SingleR_label",
label = TRUE,
repel = TRUE,
reduction = "full.umap",
label.size = 2.5,
raster = FALSE
) +
NoLegend() +
ggtitle("Annotation SingleR — Jeu Complet") +
theme(plot.title = element_text(face = "bold", hjust = 0.5))Figure 6. Annotation SingleR sur le UMAP du jeu complet. Prédominance des cellules épithéliales (vert), cohérente avec le contexte de cancer pulmonaire.
DefaultAssay(xenium.obj) <- "Xenium"
ImageDimPlot(
xenium.obj,
fov = "fov",
group.by = "SingleR_label",
cols = "polychrome",
size = 0.9,
dark.background = TRUE
) +
ggtitle("Types Cellulaires — Distribution Spatiale") +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, color = "white"),
plot.background = element_rect(fill = "black", color = NA),
legend.text = element_text(color = "white", size = 6),
legend.title = element_text(color = "white", size = 8, face = "bold")
)Figure 7. Distribution spatiale des types cellulaires annotés par SingleR sur la coupe Xenium.
# COMPOSITION CELLULAIRE - VERSION ROBUSTE
# ----------------------------------------
# Extraire les métadonnées
meta_df <- xenium.obj@meta.data %>%
as.data.frame() %>%
mutate(SingleR_label = as.character(SingleR_label))
# Compter avec base R
cell_table <- table(meta_df$SingleR_label)
# Créer le dataframe
cell_counts <- data.frame(
SingleR_label = names(cell_table),
n_cells = as.numeric(cell_table)
) %>%
arrange(desc(n_cells)) %>%
mutate(
pct = n_cells / sum(n_cells) * 100,
SingleR_label = forcats::fct_reorder(SingleR_label, n_cells)
)
# Graphique
ggplot(cell_counts, aes(x = SingleR_label, y = pct, fill = SingleR_label)) +
geom_col(show.legend = FALSE, width = 0.75) +
geom_text(aes(label = sprintf("%.1f%%", pct)),
hjust = -0.1, size = 2.8, color = "#2c2c2c") +
coord_flip() +
scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +
scale_fill_viridis_d(option = "turbo") +
labs(
title = "Composition en Types Cellulaires",
subtitle = sprintf("Total : %s cellules", format(sum(cell_counts$n_cells), big.mark = " ")),
x = NULL,
y = "Proportion (%)"
) +
theme_minimal(base_size = 11) +
theme(
plot.title = element_text(face = "bold", size = 13),
plot.subtitle = element_text(color = "grey50"),
panel.grid.major.y = element_blank()
)Figure 8. Proportion de chaque type cellulaire dans le tissu analysé.
DefaultAssay(xenium.obj) <- "sketch"
AllMarkers.sketch <- FindAllMarkers(
xenium.obj,
only.pos = TRUE, # seulement les gènes sur-exprimés
min.pct = 0.25, # exprimé dans au moins 25% des cellules du cluster
logfc.threshold = 0.25 # au moins 25% de changement d'expression
)
# Vérifier
head(AllMarkers.sketch)DefaultAssay(xenium.obj) <- "sketch"
top5 <- AllMarkers.sketch %>%
group_by(cluster) %>%
slice_max(order_by = avg_log2FC, n = 5)
DotPlot(
xenium.obj,
features = unique(top5$gene),
group.by = "seurat_clusters"
) +
coord_flip() +
scale_color_distiller(palette = "RdBu", direction = -1) +
labs(
title = "Top 5 Marqueurs par Cluster",
x = "Gène",
y = "Cluster"
) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 8),
axis.text.y = element_text(size = 7),
plot.title = element_text(face = "bold", hjust = 0.5)
)Figure 9. Dot plot des top 5 marqueurs par cluster. La taille des points représente le pourcentage d’expression ; la couleur indique le niveau d’expression moyen.
Idents(xenium.obj) <- "SingleR_label"
deg <- FindMarkers(
xenium.obj,
ident.1 = "Macrophage",
ident.2 = "T_cells",
only.pos = FALSE,
min.pct = 0.25,
logfc.threshold = 0.25
)
EnhancedVolcano(
deg,
lab = rownames(deg),
x = "avg_log2FC",
y = "p_val_adj",
title = "Macrophages vs Lymphocytes T",
subtitle = "EnhancedVolcano",
pCutoff = 0.05,
FCcutoff = 1.0,
pointSize = 2.5,
labSize = 3,
colAlpha = 0.7,
drawConnectors = FALSE,
raster = TRUE
)Figure 10. Volcano plot comparant macrophages et lymphocytes T. Les gènes macrophagiques (Log2FC > 0) incluent MRC1, CD163, VSIG4 et MS4A4A. CD3E et TRAC sont les marqueurs T caractéristiques.
Gènes différentiellement exprimés notables :
| Gène | Direction | Fonction biologique |
|---|---|---|
| MRC1 | Macro ↑ | Récepteur mannose — macrophage résidentiel |
| CD163 | Macro ↑ | Récepteur scavenger — phénotype M2 |
| VSIG4 | Macro ↑ | Inhibiteur du complément |
| MS4A4A | Macro ↑ | Marqueur de différenciation macrophagique |
| TCIM | Macro ↑ | Régulateur transcriptionnel |
| CD3E | T cells ↑ | Chaîne ε du complexe TCR |
| TRAC | T cells ↑ | Chaîne constante α du récepteur T |
cellchat <- readRDS("../cellchat_obj.rds")
groupSize <- as.numeric(table(cellchat@idents))
par(mar = c(0, 0, 2, 0))
netVisual_circle(
cellchat@net$count,
vertex.weight = groupSize,
weight.scale = TRUE,
label.edge = FALSE,
vertex.label.cex = 0.6,
title.name = "Nombre d'interactions"
)Figure 11. Réseau d’interactions intercellulaires (nombre d’interactions). La taille des nœuds est proportionnelle à l’abondance cellulaire.
# Vérification des objets existants
if (!exists("cellchat")) {
stop("L'objet 'cellchat' n'est pas trouvé. Veuillez exécuter l'analyse CellChat d'abord.")
}
# Tailles des groupes pour toutes les cellules
groupSize_all <- as.numeric(table(cellchat@idents))
# Identification des types majeurs et mineurs
cell_counts <- table(cellchat@idents)
major_cells <- names(sort(cell_counts, decreasing = TRUE)[1:8])
minor_cells <- names(which(cell_counts < 500))
cat("Types cellulaires majeurs (Top 8) :", paste(major_cells, collapse = ", "), "\n")
cat("Types cellulaires mineurs (<500 cellules) :", paste(minor_cells, collapse = ", "), "\n")par(mar = c(0, 0, 2, 0))
netVisual_circle(
cellchat@net$count,
vertex.weight = groupSize_all,
vertex.label.cex = 0.6,
title.name = "Nombre d'interactions - Toutes les cellules"
)Figure 12A. Nombre d’interactions ligand-récepteur entre tous les types cellulaires.
# Garder les types les plus abondants automatiquement
cell_counts <- table(cellchat@idents)
major_cells <- names(sort(cell_counts, decreasing = TRUE)[1:8])
par(mar = c(0, 0, 2, 0))
netVisual_circle(
cellchat@net$weight,
vertex.weight = groupSize_all,
vertex.label.cex = 0.6,
weight.scale = TRUE,
title.name = "Force des interactions - Toutes les cellules"
)Figure 12B. Force des interactions entre les 8 types cellulaires les plus abondants.
## [1] "Epithelial_cells" "Macrophage" "T_cells"
## [4] "Fibroblasts" "Endothelial_cells" "DC"
## [7] "Osteoblasts" "HSC_-G-CSF"
minor_cells <- names(which(table(cellchat@idents) < 500))
par(mar = c(0, 0, 2, 0))
netVisual_circle(
cellchat@net$count,
vertex.weight = groupSize_all,
vertex.label.cex = 0.6,
sources.use = setdiff(unique(cellchat@idents), minor_cells),
targets.use = setdiff(unique(cellchat@idents), minor_cells),
title.name = "Interactions - Types majeurs uniquement"
)Figure 12C. Interactions impliquant les types cellulaires rares.
# Calculer les scores de centralité (indispensable avant la heatmap)
cellchat <- netAnalysis_computeCentrality(cellchat, slot.name = "netP")
netAnalysis_signalingRole_heatmap(
cellchat,
pattern = "outgoing",
title = "Rôles de signalisation sortante",
color.heatmap = "OrRd"
)Figure 13. Rôles de signalisation sortante par type cellulaire. Les lignes représentent les voies de signalisation actives.
library(clusterProfiler)
library(org.Hs.eg.db)
#DefaultAssay(xenium.obj) <- "sketch"
#AllMarkers <- FindAllMarkers(xenium.obj,
# only.pos = TRUE, # only positive markers
# min.pct = 0.25, # gene expressed in 25% of cells
# logfc.threshold = 0.25) # minimum fold change
#entrez_ids <- bitr(genes_cluster0, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
#genes_cluster0 <- AllMarkers %>%
# filter(cluster == 0, p_val_adj < 0.05, avg_log2FC > 0.5) %>%
# pull(gene)
#entrez_ids <- bitr(genes_cluster0, fromType = "SYMBOL",
# toType = "ENTREZID", OrgDb = org.Hs.eg.db)
#ekegg <- enrichKEGG(gene = entrez_ids$ENTREZID, organism = "hsa", pvalueCutoff = 0.05)
#dotplot(ekegg, showCategory = 20) + ggtitle("KEGG Pathways")
fichier <- list.files(pattern = "Rplot12", full.names = TRUE)[1]
# Afficher
knitr::include_graphics(fichier)Figure 14. Enrichissement KEGG des marqueurs du cluster 0 (cellules épithéliales). La voie PI3K-Akt présente le GeneRatio le plus élevé (~0.32), suivie des voies MAPK et Cadherin signaling.
#ekegg <- readRDS("ekegg.rds")
#cnetplot(
# ekegg,
#showCategory = 8
#)
#ggtitle("Réseau Gène-Concept — KEGG") +
#theme(plot.title = element_text(face = "bold", hjust = 0.5))
#ggsave("Rplot19.png", last_plot(), width = 8, height = 6)
fichier2 <- list.files(pattern = "Rplot19", full.names = TRUE)[1]
# Afficher
knitr::include_graphics(fichier2)Figure 15. Réseau gène-concept montrant les relations entre voies KEGG enrichies et gènes impliqués. Les nœuds colorés représentent les voies ; les nœuds gris représentent les gènes.
Voies KEGG significativement enrichies :
| Voie KEGG | GeneRatio | p.adjust |
|---|---|---|
| PI3K-Akt signaling pathway | 0.32 | 0.002 |
| MAPK signaling pathway | 0.27 | 0.008 |
| MicroRNAs in cancer | 0.27 | 0.009 |
| Cadherin signaling | 0.27 | 0.010 |
| Central carbon metabolism in cancer | 0.21 | 0.004 |
| EGFR tyrosine kinase inhibitor resistance | 0.21 | 0.005 |
Interprétation biologique : L’enrichissement PI3K-Akt et des voies de résistance aux inhibiteurs EGFR est caractéristique du cancer pulmonaire non à petites cellules (NSCLC). Ces résultats suggèrent des mécanismes d’oncogenèse et de chimiorésistance actifs dans les cellules épithéliales tumorales du cluster 0.
1. Architecture cellulaire complexe — 21 clusters distincts reflétant la diversité du microenvironnement tumoral.
2. Dominance épithéliale (~40% des cellules), avec une distribution spatiale cohérente avec l’architecture alvéolaire pulmonaire.
3. Microenvironnement immunitaire actif — macrophages M2 (CD163/MRC1), lymphocytes T, NK et DC, caractéristiques d’un microenvironnement immunosuppresseur.
4. Voies oncogéniques actives — PI3K-Akt/MAPK dans les cellules épithéliales tumorales, avec des mécanismes de résistance thérapeutique.
5. Communications intercellulaires complexes — réseau CellChat dense entre cellules épithéliales, DC et populations myéloïdes.
## R version 4.6.0 (2026-04-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=French_France.utf8 LC_CTYPE=French_France.utf8
## [3] LC_MONETARY=French_France.utf8 LC_NUMERIC=C
## [5] LC_TIME=French_France.utf8
##
## time zone: Europe/Paris
## tzcode source: internal
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] enrichplot_1.32.0 org.Hs.eg.db_3.23.1
## [3] AnnotationDbi_1.74.0 clusterProfiler_4.20.0
## [5] CellChat_2.2.0.9001 igraph_2.3.1
## [7] EnhancedVolcano_1.30.0 ggrepel_0.9.8
## [9] celldex_1.22.0 SingleR_2.14.0
## [11] SummarizedExperiment_1.42.0 Biobase_2.72.0
## [13] GenomicRanges_1.64.0 Seqinfo_1.2.0
## [15] IRanges_2.46.0 S4Vectors_0.50.0
## [17] BiocGenerics_0.58.0 generics_0.1.4
## [19] MatrixGenerics_1.24.0 matrixStats_1.5.0
## [21] patchwork_1.3.2 lubridate_1.9.5
## [23] forcats_1.0.1 stringr_1.6.0
## [25] dplyr_1.2.1 purrr_1.2.2
## [27] readr_2.2.0 tidyr_1.3.2
## [29] tibble_3.3.1 ggplot2_4.0.3
## [31] tidyverse_2.0.0 Seurat_5.5.0
## [33] SeuratObject_5.4.0 sp_2.2-1
##
## loaded via a namespace (and not attached):
## [1] goftest_1.2-3 Biostrings_2.80.0
## [3] HDF5Array_1.40.0 vctrs_0.7.3
## [5] ggtangle_0.1.2 spatstat.random_3.4-5
## [7] digest_0.6.39 png_0.1-9
## [9] shape_1.4.6.1 registry_0.5-1
## [11] gypsum_1.8.0 deldir_2.0-4
## [13] parallelly_1.47.0 MASS_7.3-65
## [15] fontLiberation_0.1.0 reshape2_1.4.5
## [17] qvalue_2.44.0 httpuv_1.6.17
## [19] foreach_1.5.2 withr_3.0.2
## [21] xfun_0.57 ggfun_0.2.0
## [23] ggpubr_0.6.3 survival_3.8-6
## [25] memoise_2.0.1 gson_0.1.0
## [27] systemfonts_1.3.2 ragg_1.5.2
## [29] tidytree_0.4.7 zoo_1.8-15
## [31] GlobalOptions_0.1.4 pbapply_1.7-4
## [33] Formula_1.2-5 KEGGREST_1.52.0
## [35] promises_1.5.0 otel_0.2.0
## [37] httr_1.4.8 rstatix_0.7.3
## [39] globals_0.19.1 fitdistrplus_1.2-6
## [41] rhdf5filters_1.24.0 rhdf5_2.56.0
## [43] rstudioapi_0.18.0 miniUI_0.1.2
## [45] DOSE_4.6.0 ggalluvial_0.12.6
## [47] processx_3.9.0 curl_7.1.0
## [49] h5mread_1.4.0 polyclip_1.10-7
## [51] ExperimentHub_3.2.0 SparseArray_1.12.2
## [53] xtable_1.8-8 doParallel_1.0.17
## [55] evaluate_1.0.5 S4Arrays_1.12.0
## [57] BiocFileCache_3.2.0 hms_1.1.4
## [59] irlba_2.3.7 colorspace_2.1-2
## [61] filelock_1.0.3 ggnetwork_0.5.14
## [63] ROCR_1.0-12 reticulate_1.46.0
## [65] spatstat.data_3.1-9 magrittr_2.0.5
## [67] lmtest_0.9-40 later_1.4.8
## [69] ggtree_4.2.0 lattice_0.22-9
## [71] spatstat.geom_3.7-3 NMF_0.28
## [73] future.apply_1.20.2 scattermore_1.2
## [75] cowplot_1.2.0 RcppAnnoy_0.0.23
## [77] pillar_1.11.1 nlme_3.1-169
## [79] iterators_1.0.14 sna_2.8
## [81] gridBase_0.4-7 compiler_4.6.0
## [83] beachmat_2.28.0 RSpectra_0.16-2
## [85] stringi_1.8.7 tensor_1.5.1
## [87] plyr_1.8.9 crayon_1.5.3
## [89] abind_1.4-8 gridGraphics_0.5-1
## [91] bit_4.6.0 codetools_0.2-20
## [93] textshaping_1.0.5 bslib_0.10.0
## [95] alabaster.ranges_1.12.0 GetoptLong_1.1.1
## [97] plotly_4.12.0 mime_0.13
## [99] splines_4.6.0 circlize_0.4.18
## [101] Rcpp_1.1.1-1.1 fastDummies_1.7.6
## [103] tidydr_0.0.6 dbplyr_2.5.2
## [105] sparseMatrixStats_1.24.0 knitr_1.51
## [107] blob_1.3.0 clue_0.3-68
## [109] BiocVersion_3.23.1 fs_2.1.0
## [111] listenv_0.10.1 DelayedMatrixStats_1.34.0
## [113] ggsignif_0.6.4 ggplotify_0.1.3
## [115] Matrix_1.7-5 callr_3.7.6
## [117] tzdb_0.5.0 svglite_2.2.2
## [119] tweenr_2.0.3 pkgconfig_2.0.3
## [121] network_1.20.0 tools_4.6.0
## [123] cachem_1.1.0 RSQLite_2.4.6
## [125] viridisLite_0.4.3 DBI_1.3.0
## [127] fastmap_1.2.0 rmarkdown_2.31
## [129] scales_1.4.0 grid_4.6.0
## [131] ica_1.0-3 broom_1.0.12
## [133] AnnotationHub_4.2.0 sass_0.4.10
## [135] coda_0.19-4.1 FNN_1.1.4.1
## [137] BiocManager_1.30.27 dotCall64_1.2
## [139] carData_3.0-6 RANN_2.6.2
## [141] alabaster.schemas_1.12.0 farver_2.1.2
## [143] scatterpie_0.2.6 yaml_2.3.12
## [145] cli_3.6.6 lifecycle_1.0.5
## [147] uwot_0.2.4 presto_1.0.0
## [149] backports_1.5.1 timechange_0.4.0
## [151] gtable_0.3.6 rjson_0.2.23
## [153] ggridges_0.5.7 progressr_0.19.0
## [155] parallel_4.6.0 ape_5.8-1
## [157] enrichit_0.1.4 jsonlite_2.0.0
## [159] RcppHNSW_0.6.0 bit64_4.8.0
## [161] Rtsne_0.17 yulab.utils_0.2.4
## [163] alabaster.matrix_1.12.0 spatstat.utils_3.2-2
## [165] BiocNeighbors_2.6.0 aisdk_1.1.0
## [167] jquerylib_0.1.4 alabaster.se_1.12.0
## [169] GOSemSim_2.38.0 spatstat.univar_3.1-7
## [171] lazyeval_0.2.3 alabaster.base_1.12.0
## [173] shiny_1.13.0 htmltools_0.5.9
## [175] collapse_2.1.6 GO.db_3.23.1
## [177] sctransform_0.4.3 rappdirs_0.3.4
## [179] glue_1.8.1 spam_2.11-3
## [181] httr2_1.2.2 XVector_0.52.0
## [183] gdtools_0.5.0 treeio_1.36.1
## [185] gridExtra_2.3 R6_2.6.1
## [187] ggiraph_0.9.6 labeling_0.4.3
## [189] cluster_2.1.8.2 rngtools_1.5.2
## [191] Rhdf5lib_2.0.0 aplot_0.2.9
## [193] statnet.common_4.13.0 DelayedArray_0.38.1
## [195] tidyselect_1.2.1 ggforce_0.5.0
## [197] fontBitstreamVera_0.1.1 car_3.1-5
## [199] future_1.70.0 KernSmooth_2.23-26
## [201] S7_0.2.2 fontquiver_0.2.1
## [203] data.table_1.18.4 htmlwidgets_1.6.4
## [205] ComplexHeatmap_2.28.0 RColorBrewer_1.1-3
## [207] rlang_1.2.0 spatstat.sparse_3.1-0
## [209] spatstat.explore_3.8-0 ggnewscale_0.5.2