This script assesses IL-17 pathway biology in B-ALL leukaemia cells from the CNS and BM compartments. The CNS immune environment is enriched for Vγ6Vδ4 γδ T cells, which are a major source of IL-17 in barrier tissues, raising the question of whether leukaemia cells are IL-17 responsive and whether this responsiveness is tissue-specific or population-specific.
The full IL-17 receptor family (Il17ra–Il17re) and ligand family
(Il17a–Il17f, Il25) are examined alongside canonical downstream
transcriptional targets. Results are stratified by tissue (BM vs CNS)
and propagating population (LSK_IL7R vs LK_CLP). This script depends on
scored objects produced by
05_population_scoring_cns_analysis.Rmd.
library(Seurat)
## Loading required package: SeuratObject
## Loading required package: sp
##
## Attaching package: 'SeuratObject'
## The following objects are masked from 'package:base':
##
## intersect, t
library(ggplot2)
library(patchwork)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(knitr)
library(qs2)
## qs2 0.1.7
library(viridis)
## Loading required package: viridisLite
library(tidyr)
library(tibble)
library(scales)
##
## Attaching package: 'scales'
## The following object is masked from 'package:viridis':
##
## viridis_pal
tissue_cols <- c("BM" = "steelblue", "CNS" = "firebrick")
pop_cols <- c("LSK_IL7R" = "#1a3a6b",
"LK_CLP" = "#8b0000",
"Intermediate" = "grey75")
bm_res_col <- "originalexp_snn_res.0.3"
cns_res_col <- "originalexp_snn_res.0.3"
ambient_blacklist <- c("Slc1a2", "Sparcl1", "Apod", "Cpe",
"Mobp", "S100b", "Atp1a2","Ndrg2")
# Load scored leukaemia objects from script 10
# These already contain: population, LSK_IL7R_BM1, CNS_sig1, BM_sig1,
# bulk_CNS_sig1, bulk_BM_sig1, Phase, S.Score, G2M.Score
leuk_BM <- qs_read("/exports/eddie/scratch/aduguid3/harmony_clustering/05_leuk_BM_scored.qs")
leuk_CNS_clean <- qs_read("/exports/eddie/scratch/aduguid3/harmony_clustering/05_leuk_CNS_clean_scored.qs")
leuk_all <- qs_read("/exports/eddie/scratch/aduguid3/harmony_clustering/05_leuk_all_scored.qs")
stopifnot("leuk_BM not found" = exists("leuk_BM"))
stopifnot("leuk_CNS_clean not found" = exists("leuk_CNS_clean"))
stopifnot("leuk_all not found" = exists("leuk_all"))
# Load clean pseudobulk DE results from script 04
res_clean_df <- read.csv("/exports/eddie/scratch/aduguid3/Rmarkdown/04_DE_CNSvsBM_clean.csv")
stopifnot("res_clean_df not found" = exists("res_clean_df"))
# Barcode prefix vectors for leuk_all metadata transfers
bm_cells <- colnames(leuk_all)[grepl("^BM_", colnames(leuk_all))]
cns_cells <- colnames(leuk_all)[grepl("^CNS_", colnames(leuk_all))]
bm_orig <- sub("^BM_", "", bm_cells)
cns_orig <- sub("^CNS_", "", cns_cells)
cat("BM cells:", ncol(leuk_BM), "\n")
## BM cells: 44606
cat("CNS cells:", ncol(leuk_CNS_clean), "\n")
## CNS cells: 47017
cat("All cells:", ncol(leuk_all), "\n")
## All cells: 91623
cat("DE genes loaded:", nrow(res_clean_df), "\n")
## DE genes loaded: 16195
# Confirm required metadata columns are present in scored objects
# transplant_source should already be present from script 10
required_cols <- c("population", "LSK_IL7R_BM1", "Tissue", "transplant_source")
cat("BM object — required columns present:\n")
## BM object — required columns present:
for (col in required_cols) {
cat(" ", col, ":", col %in% colnames(leuk_BM@meta.data), "\n")
}
## population : TRUE
## LSK_IL7R_BM1 : TRUE
## Tissue : TRUE
## transplant_source : TRUE
cat("\nCNS object — required columns present:\n")
##
## CNS object — required columns present:
for (col in required_cols) {
cat(" ", col, ":", col %in% colnames(leuk_CNS_clean@meta.data), "\n")
}
## population : TRUE
## LSK_IL7R_BM1 : TRUE
## Tissue : TRUE
## transplant_source : TRUE
# If transplant_source is missing, derive from Mouse_ID
if (!"transplant_source" %in% colnames(leuk_BM@meta.data)) {
cat("\ntransplant_source missing — deriving from Mouse_ID\n")
source_map <- c("1838161" = "AD4_EM3", "1838162" = "AD4_EM3",
"1838163" = "AD2.2_EM4","1838171" = "AD2.2_EM4",
"1838279" = "AD5_EM4", "1838280" = "AD5_EM4")
leuk_BM$transplant_source <- unname(source_map[as.character(leuk_BM$Mouse_ID)])
leuk_CNS_clean$transplant_source <- unname(source_map[as.character(leuk_CNS_clean$Mouse_ID)])
cat("transplant_source assigned\n")
}
cat("\nBM population distribution:\n")
##
## BM population distribution:
print(table(leuk_BM$population))
##
## Intermediate LK_CLP LSK_IL7R
## 5133 29528 9945
cat("\nCNS population distribution:\n")
##
## CNS population distribution:
print(table(leuk_CNS_clean$population))
##
## Intermediate LK_CLP LSK_IL7R
## 5063 31012 10942
The IL-17 receptor family has five members with distinct ligand specificities. The canonical IL-17A/F receptor is the Il17ra/Il17rc heterodimer. Il17ra also pairs with Il17rb to form the receptor for IL-17B and IL-17E (Il25 in mouse). All five receptor variants are assessed to determine which are expressed in leukaemia cells and whether the canonical or non-canonical receptor complex is present.
All six IL-17 family ligands are assessed. Note that IL-17E is annotated as Il25 in mouse rather than Il17e — Il17e is absent from the mouse genome annotation and searching for it will return no results. Il25 signals through the Il17ra/Il17rb heterodimer and is typically produced by epithelial and stromal cells rather than immune cells.
Canonical IL-17 downstream targets include chemokines driving neutrophil recruitment (Cxcl1, Cxcl2, Cxcl5), pro-inflammatory cytokines (Il6, Tnf, Il1b), and matrix remodelling genes (Mmp3, Mmp13). The intracellular adaptor Act1 is encoded by Traf3ip2 in mouse — the human gene symbol Act1 is not used in the mouse annotation. Defb4 (defensin beta 4) is not expressed in haematopoietic cells and is excluded. Il17raf is poorly annotated in mouse and excluded.
# IL-17 receptor family — all five variants
il17_receptors <- c(
"Il17ra", # main subunit — pairs with Il17rc for IL-17A/F
# — pairs with Il17rb for IL-17B/Il25
"Il17rb", # receptor for IL-17B and Il25 (IL-17E)
"Il17rc", # co-receptor with Il17ra for IL-17A/F
"Il17rd", # Sef — modulates MAPK/FGFR signalling
"Il17re" # receptor for IL-17C
)
# IL-17 ligand family
# Note: Il17e = Il25 in mouse — Il17e is not a valid mouse gene symbol
il17_ligands <- c(
"Il17a", # canonical — produced by γδ T cells, Th17
"Il17b", # ligand for Il17ra/Il17rb heterodimer
"Il17c", # ligand for Il17re
"Il17d", # receptor not well defined
"Il17f", # pairs with Il17a — signals via Il17ra/Il17rc
"Il25" # Il17e in mouse — ligand for Il17ra/Il17rb
)
# Canonical IL-17 downstream transcriptional targets
# Act1 = Traf3ip2 in mouse (intracellular adaptor linking Il17ra to NF-κB/MAPK)
# Defb4 excluded — not expressed in haematopoietic cells
# Il17raf excluded — poorly annotated in mouse
il17_targets <- c(
# Chemokines — neutrophil recruitment
"Cxcl1", "Cxcl2", "Cxcl5", "Ccl2", "Ccl7", "Ccl20",
# Cytokines
"Il6", "Tnf", "Il1b", "Csf3", "Csf2",
# Antimicrobial / alarmins
"S100a8", "S100a9", "Lcn2",
# Matrix remodelling
"Mmp3", "Mmp13",
# Signalling mediators
"Traf6", "Traf3ip2", # Traf3ip2 = Act1 in mouse
"Nfkb1", "Nfkb2", "Ikbkb",
# Negative regulator
"Socs3"
)
# Helper — check gene availability and report missing
check_genes <- function(genes, obj, label) {
found <- genes[genes %in% rownames(obj)]
missing <- genes[!genes %in% rownames(obj)]
cat(label, "—", length(found), "of", length(genes), "genes found\n")
if (length(missing) > 0)
cat(" Missing:", paste(missing, collapse = ", "), "\n")
invisible(found)
}
cat("=== IL-17 Receptors ===\n")
## === IL-17 Receptors ===
il17r_bm <- check_genes(il17_receptors, leuk_BM, "BM")
## BM — 5 of 5 genes found
il17r_cns <- check_genes(il17_receptors, leuk_CNS_clean, "CNS")
## CNS — 5 of 5 genes found
cat("\n=== IL-17 Ligands ===\n")
##
## === IL-17 Ligands ===
il17l_bm <- check_genes(il17_ligands, leuk_BM, "BM")
## BM — 6 of 6 genes found
il17l_cns <- check_genes(il17_ligands, leuk_CNS_clean, "CNS")
## CNS — 6 of 6 genes found
cat("\n=== IL-17 Target Genes ===\n")
##
## === IL-17 Target Genes ===
il17t_bm <- check_genes(il17_targets, leuk_BM, "BM")
## BM — 22 of 22 genes found
il17t_cns <- check_genes(il17_targets, leuk_CNS_clean, "CNS")
## CNS — 22 of 22 genes found
# Compute detection rate and mean expression for receptors and ligands
expr_summary <- function(genes, label) {
lapply(genes, function(gene) {
bm_expr <- if (gene %in% rownames(leuk_BM)) {
GetAssayData(leuk_BM, layer = "counts")[gene, ]
} else rep(0, ncol(leuk_BM))
cns_expr <- if (gene %in% rownames(leuk_CNS_clean)) {
GetAssayData(leuk_CNS_clean, layer = "counts")[gene, ]
} else rep(0, ncol(leuk_CNS_clean))
data.frame(
gene = gene,
type = label,
BM_pct_expr = round(mean(bm_expr > 0) * 100, 2),
BM_mean_expr = round(mean(bm_expr), 4),
CNS_pct_expr = round(mean(cns_expr > 0) * 100, 2),
CNS_mean_expr = round(mean(cns_expr), 4)
)
}) %>% bind_rows()
}
receptor_summary <- expr_summary(il17_receptors, "Receptor")
ligand_summary <- expr_summary(il17_ligands, "Ligand")
kable(receptor_summary,
caption = "IL-17 receptor family — detection rate and mean expression in BM and CNS leukaemia")
| gene | type | BM_pct_expr | BM_mean_expr | CNS_pct_expr | CNS_mean_expr |
|---|---|---|---|---|---|
| Il17ra | Receptor | 47.76 | 0.7898 | 44.77 | 0.7205 |
| Il17rb | Receptor | 47.34 | 0.8070 | 47.31 | 0.8109 |
| Il17rc | Receptor | 0.10 | 0.0011 | 0.08 | 0.0009 |
| Il17rd | Receptor | 0.26 | 0.0028 | 0.31 | 0.0035 |
| Il17re | Receptor | 0.05 | 0.0005 | 0.04 | 0.0004 |
kable(ligand_summary,
caption = "IL-17 ligand family — detection rate and mean expression in BM and CNS leukaemia")
| gene | type | BM_pct_expr | BM_mean_expr | CNS_pct_expr | CNS_mean_expr |
|---|---|---|---|---|---|
| Il17a | Ligand | 0.00 | 0.0002 | 0.00 | 0e+00 |
| Il17b | Ligand | 0.00 | 0.0000 | 0.00 | 0e+00 |
| Il17c | Ligand | 0.07 | 0.0007 | 0.06 | 6e-04 |
| Il17d | Ligand | 0.10 | 0.0010 | 0.07 | 8e-04 |
| Il17f | Ligand | 0.12 | 0.0013 | 0.09 | 9e-04 |
| Il25 | Ligand | 0.00 | 0.0000 | 0.00 | 0e+00 |
# Summarise receptor findings
cat("=== Receptor expression summary ===\n\n")
## === Receptor expression summary ===
cat("Il17ra: BM", receptor_summary$BM_pct_expr[receptor_summary$gene == "Il17ra"],
"% / CNS", receptor_summary$CNS_pct_expr[receptor_summary$gene == "Il17ra"], "%\n")
## Il17ra: BM 47.76 % / CNS 44.77 %
cat("Il17rb: BM", receptor_summary$BM_pct_expr[receptor_summary$gene == "Il17rb"],
"% / CNS", receptor_summary$CNS_pct_expr[receptor_summary$gene == "Il17rb"], "%\n")
## Il17rb: BM 47.34 % / CNS 47.31 %
cat("Il17rc: BM", receptor_summary$BM_pct_expr[receptor_summary$gene == "Il17rc"],
"% / CNS", receptor_summary$CNS_pct_expr[receptor_summary$gene == "Il17rc"], "%\n\n")
## Il17rc: BM 0.1 % / CNS 0.08 %
cat("Interpretation:\n")
## Interpretation:
cat("Il17ra and Il17rb are co-expressed at ~47% in both tissues.\n")
## Il17ra and Il17rb are co-expressed at ~47% in both tissues.
cat("Il17rc is essentially absent (<0.1%) in both tissues.\n")
## Il17rc is essentially absent (<0.1%) in both tissues.
cat("Without Il17rc, the canonical Il17ra/Il17rc receptor for IL-17A/F\n")
## Without Il17rc, the canonical Il17ra/Il17rc receptor for IL-17A/F
cat("cannot be formed. Leukaemia cells are poised to respond to IL-17B\n")
## cannot be formed. Leukaemia cells are poised to respond to IL-17B
cat("or Il25 via the non-canonical Il17ra/Il17rb heterodimer.\n\n")
## or Il25 via the non-canonical Il17ra/Il17rb heterodimer.
cat("Ligand expression is negligible in leukaemia cells (<0.1% detection)\n")
## Ligand expression is negligible in leukaemia cells (<0.1% detection)
cat("confirming leukaemia cells are IL-17 responsive but not IL-17 producing.\n")
## confirming leukaemia cells are IL-17 responsive but not IL-17 producing.
il17r_detected <- il17_receptors[il17_receptors %in%
c(rownames(leuk_BM), rownames(leuk_CNS_clean))]
p_bm_dot <- DotPlot(leuk_BM,
features = il17r_detected,
group.by = bm_res_col,
cols = c("lightgrey", "#1a3a6b"),
dot.scale = 8) +
RotatedAxis() +
ggtitle("BM — IL-17 receptor expression by cluster") +
theme(axis.text.x = element_text(size = 9))
p_cns_dot <- DotPlot(leuk_CNS_clean,
features = il17r_detected,
group.by = cns_res_col,
cols = c("lightgrey", "#8b0000"),
dot.scale = 8) +
RotatedAxis() +
ggtitle("CNS — IL-17 receptor expression by cluster") +
theme(axis.text.x = element_text(size = 9))
p_bm_dot / p_cns_dot
IL-17 receptor expression across clusters — BM and CNS
p_bm_pop <- DotPlot(leuk_BM,
features = il17r_detected,
group.by = "population",
cols = c("lightgrey", "#1a3a6b"),
dot.scale = 8) +
RotatedAxis() +
ggtitle("BM — IL-17 receptors by population")
## Warning: Scaling data with a low number of groups may produce misleading
## results
p_cns_pop <- DotPlot(leuk_CNS_clean,
features = il17r_detected,
group.by = "population",
cols = c("lightgrey", "#8b0000"),
dot.scale = 8) +
RotatedAxis() +
ggtitle("CNS — IL-17 receptors by population")
## Warning: Scaling data with a low number of groups may produce misleading
## results
p_bm_pop + p_cns_pop
IL-17 receptor expression by population — BM and CNS
# Only plot the two expressed receptors
il17r_expressed <- c("Il17ra", "Il17rb")
plots <- lapply(il17r_expressed, function(gene) {
FeaturePlot(leuk_all,
features = gene,
cols = c("lightgrey", "firebrick"),
order = TRUE,
min.cutoff = "q10",
split.by = "Tissue") +
theme(legend.position = "right")
})
wrap_plots(plots, ncol = 1)
IL-17 receptor expression on Harmony-integrated UMAP
Downstream IL-17 pathway targets are scored onto leukaemia cells
using AddModuleScore. Even in the absence of Il17rc,
pathway activity may be detectable if leukaemia cells are responding to
IL-17B or Il25 via Il17ra/Il17rb. Score ranges are assessed by tissue
and propagating population.
leuk_BM <- AddModuleScore(leuk_BM,
features = list(il17t_bm),
name = "IL17_pathway",
seed = 42)
leuk_CNS_clean <- AddModuleScore(leuk_CNS_clean,
features = list(il17t_cns),
name = "IL17_pathway",
seed = 42)
cat("IL-17 pathway score ranges:\n")
## IL-17 pathway score ranges:
cat(" BM cells:", round(range(leuk_BM$IL17_pathway1), 3), "\n")
## BM cells: -0.085 0.251
cat(" CNS cells:", round(range(leuk_CNS_clean$IL17_pathway1), 3), "\n")
## CNS cells: -0.091 0.259
# Transfer to leuk_all for UMAP
il17_vec <- c(
setNames(leuk_BM$IL17_pathway1[match(bm_orig, colnames(leuk_BM))], bm_cells),
setNames(leuk_CNS_clean$IL17_pathway1[match(cns_orig, colnames(leuk_CNS_clean))], cns_cells)
)
leuk_all <- AddMetaData(leuk_all, metadata = il17_vec, col.name = "IL17_pathway1")
leuk_all_bm <- subset(leuk_all, subset = Tissue == "BM")
leuk_all_cns <- subset(leuk_all, subset = Tissue == "CNS")
wt_il17 <- wilcox.test(leuk_BM$IL17_pathway1, leuk_CNS_clean$IL17_pathway1)
bind_rows(
leuk_BM@meta.data %>% select(Tissue, IL17_pathway1),
leuk_CNS_clean@meta.data %>% select(Tissue, IL17_pathway1)
) %>%
ggplot(aes(x = Tissue, y = IL17_pathway1, fill = Tissue)) +
geom_violin(trim = FALSE, alpha = 0.8) +
geom_boxplot(width = 0.1, fill = "white", outlier.size = 0.2) +
scale_fill_manual(values = tissue_cols) +
geom_hline(yintercept = 0, linetype = "dashed", colour = "grey40") +
theme_classic(base_size = 12) +
labs(y = "IL-17 pathway score",
title = paste0("IL-17 pathway activity — BM vs CNS\n",
"Wilcoxon p = ", signif(wt_il17$p.value, 3))) +
theme(legend.position = "none")
IL-17 pathway activity score — BM vs CNS
v_bm <- VlnPlot(leuk_BM,
features = "IL17_pathway1",
group.by = bm_res_col,
pt.size = 0,
cols = viridis(length(unique(
leuk_BM@meta.data[[bm_res_col]])))) +
ggtitle("BM — IL-17 pathway score by cluster") +
geom_hline(yintercept = 0, linetype = "dashed", colour = "grey40") +
theme(legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1))
v_cns <- VlnPlot(leuk_CNS_clean,
features = "IL17_pathway1",
group.by = cns_res_col,
pt.size = 0,
cols = viridis(length(unique(
leuk_CNS_clean@meta.data[[cns_res_col]])))) +
ggtitle("CNS — IL-17 pathway score by cluster") +
geom_hline(yintercept = 0, linetype = "dashed", colour = "grey40") +
theme(legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1))
v_bm / v_cns
IL-17 pathway score by cluster — BM and CNS
bind_rows(
leuk_BM@meta.data %>% select(Tissue, population, IL17_pathway1),
leuk_CNS_clean@meta.data %>% select(Tissue, population, IL17_pathway1)
) %>%
filter(population != "Intermediate") %>%
ggplot(aes(x = population, y = IL17_pathway1, fill = population)) +
geom_violin(trim = FALSE, alpha = 0.8) +
geom_boxplot(width = 0.1, fill = "white", outlier.size = 0.2) +
scale_fill_manual(values = pop_cols) +
facet_wrap(~Tissue) +
geom_hline(yintercept = 0, linetype = "dashed", colour = "grey40") +
theme_classic(base_size = 12) +
labs(y = "IL-17 pathway score",
title = "IL-17 pathway activity by population and tissue") +
theme(legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1))
IL-17 pathway score by population and tissue
lapply(c("BM", "CNS"), function(tiss) {
dat <- if (tiss == "BM") leuk_BM@meta.data else leuk_CNS_clean@meta.data
lsk <- dat %>% filter(population == "LSK_IL7R") %>% pull(IL17_pathway1)
clp <- dat %>% filter(population == "LK_CLP") %>% pull(IL17_pathway1)
data.frame(
Tissue = tiss,
mean_LSK_IL7R = round(mean(lsk), 4),
mean_LK_CLP = round(mean(clp), 4),
wilcox_p = signif(wilcox.test(lsk, clp)$p.value, 3)
)
}) %>%
bind_rows() %>%
kable(caption = "IL-17 pathway score — LSK_IL7R vs LK_CLP by tissue")
| Tissue | mean_LSK_IL7R | mean_LK_CLP | wilcox_p |
|---|---|---|---|
| BM | 0.0004 | 0.0120 | 0 |
| CNS | -0.0023 | 0.0069 | 0 |
p_il17_bm <- FeaturePlot(leuk_all_bm,
features = "IL17_pathway1",
cols = c("lightgrey", "firebrick"),
order = TRUE,
min.cutoff = "q10") +
ggtitle("BM — IL-17 pathway score")
p_il17_cns <- FeaturePlot(leuk_all_cns,
features = "IL17_pathway1",
cols = c("lightgrey", "firebrick"),
order = TRUE,
min.cutoff = "q10") +
ggtitle("CNS — IL-17 pathway score")
p_il17_bm + p_il17_cns
IL-17 pathway activity on Harmony-integrated UMAP
Leukaemia cells were found to express Il17ra and Il17rb but not Il17rc, and produce no IL-17 ligands. IL-17 pathway activity scores were uniform across tissues and clusters with no tissue-specific enrichment. As a final check, all IL-17 receptor, ligand and target genes are cross-referenced against the pseudobulk CNS vs BM DE results to determine whether any are significantly differentially expressed at the tissue level.
il17_all_genes <- c(il17_receptors, il17_ligands, il17_targets)
il17_de <- res_clean_df %>%
filter(gene %in% il17_all_genes,
!is.na(padj)) %>%
mutate(
gene_type = case_when(
gene %in% il17_receptors ~ "Receptor",
gene %in% il17_ligands ~ "Ligand",
TRUE ~ "Target"
),
direction = case_when(
padj < 0.05 & log2FoldChange > 0 ~ "CNS-enriched",
padj < 0.05 & log2FoldChange < 0 ~ "BM-enriched",
TRUE ~ "NS"
)
) %>%
arrange(padj) %>%
select(gene, gene_type, log2FoldChange, baseMean, padj, direction)
cat("IL-17 pathway genes in CNS vs BM DE:", nrow(il17_de), "\n")
## IL-17 pathway genes in CNS vs BM DE: 15
cat("Significant (padj < 0.05):",
sum(il17_de$padj < 0.05, na.rm = TRUE), "\n")
## Significant (padj < 0.05): 0
cat("CNS-enriched:", sum(il17_de$direction == "CNS-enriched"), "\n")
## CNS-enriched: 0
cat("BM-enriched: ", sum(il17_de$direction == "BM-enriched"), "\n")
## BM-enriched: 0
kable(il17_de %>% mutate(across(where(is.numeric), ~round(., 4))),
caption = "IL-17 pathway genes in CNS vs BM pseudobulk DE — leukaemia cells")
| gene | gene_type | log2FoldChange | baseMean | padj | direction |
|---|---|---|---|---|---|
| Traf3ip2 | Target | 0.1663 | 4970.6501 | 0.1878 | NS |
| Tnf | Target | 0.1819 | 788.6269 | 0.4461 | NS |
| Il17ra | Receptor | -0.0870 | 5543.6867 | 0.6334 | NS |
| S100a9 | Target | -1.5716 | 8.0922 | 0.7075 | NS |
| S100a8 | Target | -1.7112 | 7.6889 | 0.7192 | NS |
| Il17rd | Receptor | 0.3729 | 23.3149 | 0.7855 | NS |
| Mmp13 | Target | 0.1731 | 205.5965 | 0.9064 | NS |
| Il17f | Ligand | -0.2999 | 7.8364 | 0.9270 | NS |
| Traf6 | Target | -0.0220 | 5123.2998 | 0.9436 | NS |
| Il17rb | Receptor | 0.0498 | 5935.3512 | 0.9474 | NS |
| Nfkb2 | Target | 0.0565 | 595.5345 | 0.9576 | NS |
| Il17rc | Receptor | -0.1938 | 6.9059 | 0.9669 | NS |
| Nfkb1 | Target | -0.0239 | 18426.9172 | 0.9761 | NS |
| Socs3 | Target | 0.0275 | 512.6285 | 0.9874 | NS |
| Ikbkb | Target | 0.0045 | 7301.4304 | 0.9913 | NS |
IL-17 pathway analysis in B-ALL leukaemia cells revealed the following:
Receptor profile: Leukaemia cells express the non-canonical IL-17 receptor components Il17ra (~47% detection, mean ~0.79) and Il17rb (~47% detection, mean ~0.81) in both BM and CNS compartments. Il17rc, required for the canonical IL-17A/F receptor heterodimer, is essentially absent (<0.1% detection). This profile indicates leukaemia cells are poised to respond to IL-17B or Il25 via the Il17ra/Il17rb heterodimer rather than to canonical IL-17A produced by γδ T cells.
Ligand profile: All six IL-17 family ligands are essentially absent from leukaemia cells (<0.1% detection), confirming leukaemia cells are IL-17 responsive but not IL-17 producing. Any IL-17 pathway activity must arise from extrinsic ligand produced by other cells in the niche.
Population specificity: Il17ra and Il17rb expression is enriched in LSK_IL7R cells relative to LK_CLP in both tissues, suggesting that if non-canonical IL-17 signalling occurs it would preferentially affect the LSK_IL7R propagating population.
Tissue specificity: Receptor expression, pathway activity scores and pseudobulk DE all show no significant tissue difference — IL-17 responsiveness via the Il17ra/Il17rb axis is a cell-intrinsic property shared across BM and CNS compartments rather than a CNS-specific adaptation.
Open question: The identity of the Il17b/Il25 ligand source in the niche remains to be determined. This will be addressed in the immune cell reintegration and CellChat analyses (scripts 11–12), where Il17b and Il25 expression across immune cell types will be assessed.
sessionInfo()
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-pc-linux-gnu
## Running under: Rocky Linux 9.5 (Blue Onyx)
##
## Matrix products: default
## BLAS/LAPACK: /opt/intel/oneapi/mkl/2024.0/lib/libmkl_gf_lp64.so.2; LAPACK version 3.10.1
##
## locale:
## [1] LC_CTYPE=en_GB.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_GB.UTF-8 LC_COLLATE=en_GB.UTF-8
## [5] LC_MONETARY=en_GB.UTF-8 LC_MESSAGES=en_GB.UTF-8
## [7] LC_PAPER=en_GB.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Europe/London
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] scales_1.4.0 tibble_3.3.1 tidyr_1.3.1 viridis_0.6.5
## [5] viridisLite_0.4.2 qs2_0.1.7 knitr_1.51 dplyr_1.1.4
## [9] patchwork_1.3.2 ggplot2_4.0.2 Seurat_5.4.0 SeuratObject_5.3.0
## [13] sp_2.1-4
##
## loaded via a namespace (and not attached):
## [1] deldir_2.0-4 pbapply_1.7-4 gridExtra_2.3
## [4] rlang_1.1.7 magrittr_2.0.3 RcppAnnoy_0.0.23
## [7] otel_0.2.0 spatstat.geom_3.7-0 matrixStats_1.5.0
## [10] ggridges_0.5.6 compiler_4.4.1 png_0.1-8
## [13] vctrs_0.6.5 reshape2_1.4.4 stringr_1.5.1
## [16] pkgconfig_2.0.3 fastmap_1.2.0 labeling_0.4.3
## [19] utf8_1.2.4 promises_1.5.0 rmarkdown_2.30
## [22] ggbeeswarm_0.7.3 purrr_1.0.2 xfun_0.56
## [25] cachem_1.1.0 jsonlite_2.0.0 goftest_1.2-3
## [28] later_1.4.6 spatstat.utils_3.2-1 irlba_2.3.7
## [31] parallel_4.4.1 cluster_2.1.6 R6_2.6.1
## [34] ica_1.0-3 spatstat.data_3.1-9 bslib_0.7.0
## [37] stringi_1.8.4 RColorBrewer_1.1-3 reticulate_1.45.0
## [40] spatstat.univar_3.1-6 parallelly_1.37.1 lmtest_0.9-40
## [43] jquerylib_0.1.4 scattermore_1.2 Rcpp_1.0.12
## [46] tensor_1.5.1 future.apply_1.11.2 zoo_1.8-15
## [49] sctransform_0.4.3 httpuv_1.6.16 Matrix_1.7-0
## [52] splines_4.4.1 igraph_2.2.2 tidyselect_1.2.1
## [55] abind_1.4-5 rstudioapi_0.16.0 dichromat_2.0-0.1
## [58] yaml_2.3.12 stringfish_0.18.0 spatstat.random_3.4-4
## [61] codetools_0.2-20 miniUI_0.1.2 spatstat.explore_3.7-0
## [64] listenv_0.9.1 lattice_0.22-6 plyr_1.8.9
## [67] withr_3.0.2 shiny_1.12.1 S7_0.2.1
## [70] ROCR_1.0-12 ggrastr_1.0.2 evaluate_1.0.5
## [73] Rtsne_0.17 future_1.33.2 fastDummies_1.7.5
## [76] survival_3.6-4 RcppParallel_5.1.8 polyclip_1.10-7
## [79] fitdistrplus_1.2-6 pillar_1.9.0 KernSmooth_2.23-24
## [82] plotly_4.12.0 generics_0.1.3 RcppHNSW_0.6.0
## [85] globals_0.16.3 xtable_1.8-4 glue_1.8.0
## [88] lazyeval_0.2.2 tools_4.4.1 data.table_1.15.4
## [91] RSpectra_0.16-2 RANN_2.6.2 dotCall64_1.2
## [94] cowplot_1.2.0 grid_4.4.1 nlme_3.1-164
## [97] beeswarm_0.4.0 vipor_0.4.7 cli_3.6.5
## [100] spatstat.sparse_3.1-0 spam_2.11-3 fansi_1.0.6
## [103] uwot_0.2.4 gtable_0.3.6 sass_0.4.9
## [106] digest_0.6.35 progressr_0.18.0 ggrepel_0.9.6
## [109] htmlwidgets_1.6.4 farver_2.1.2 htmltools_0.5.8.1
## [112] lifecycle_1.0.5 httr_1.4.7 mime_0.12
## [115] MASS_7.3-60.2