1. load libraries
2. Load Seurat Object
All_samples_Merged <- readRDS("../0-Seurat_RDS_OBJECT_FINAL/Seurat_object_Final_changes/All_samples_Merged_with_STCAT_Annotation_final-5-09-2025.rds")
DefaultAssay(All_samples_Merged) <- "RNA"
All_samples_Merged <- NormalizeData(
All_samples_Merged,
normalization.method = "LogNormalize",
scale.factor = 1e4,
verbose = TRUE
)
Performing log-normalization
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
3.find Top markers
Idents(All_samples_Merged) <- "seurat_clusters"
# ---------------------------------------------------------
# 2️⃣ Find marker genes per cluster
SS_markers <- FindAllMarkers(
All_samples_Merged,
only.pos = TRUE,
min.pct = 0.25,
logfc.threshold = 0.25,
min.pct.diff = 0.2
)
library(dplyr)
# Precise blacklist for uninformative genes
blacklist_patterns <- c(
"^TRAV", "^TRBV", "^TRGV", "^TRDV", "^TRBC", "^TRAC", "^TRDC", "^TRGC", # TCR
"^IGH", "^IGK", "^IGL", "^IGJ", # Ig genes
"^RPL", "^RPS", # ribosomal
"^MT-", # mitochondria
"^HBA", "^HBB", "^HB[ABZ]", # hemoglobins
"^NEAT1$", "^MALAT1$", # optional lncRNAs
"^XIST$" )
blacklist_regex <- paste(blacklist_patterns, collapse = "|")
# Preview which markers will be removed
to_remove <- SS_markers %>%
filter(grepl(blacklist_regex, gene, ignore.case = TRUE))
message("Rows to remove: ", nrow(to_remove))
head(to_remove$gene)
[1] "TRAV17" "TRAV9-2" "RPL22L1" "RPL7" "RPL35A" "NEAT1"
# Filter markers (keep important metabolic/proliferation genes)
SS_markers_filtered <- SS_markers %>%
filter(!grepl(blacklist_regex, gene, ignore.case = TRUE))
4. Top5 Markers
library(dplyr)
# ---------------------------------------------------------
# Save filtered markers
write.csv(SS_markers_filtered, file = "SS_markers_filtered.csv", row.names = FALSE)
# ---------------------------------------------------------
# Extract top 25 markers per cluster
top25_markers <- SS_markers_filtered %>%
filter(p_val_adj < 0.05) %>% # ensure statistical significance
group_by(cluster) %>%
slice_max(order_by = avg_log2FC, n = 25) %>%
ungroup()
write.csv(top25_markers, file = "SS_markers_top25.csv", row.names = FALSE)
# ---------------------------------------------------------
# Extract top 5 markers per cluster
top5_markers <- SS_markers_filtered %>%
filter(p_val_adj < 0.05) %>% # ensure statistical significance
group_by(cluster) %>%
slice_max(order_by = avg_log2FC, n = 5) %>%
ungroup()
write.csv(top5_markers, file = "SS_markers_top5.csv", row.names = FALSE)
message("Filtered markers, top25, and top5 markers saved successfully.")
nbiSweden Approach-Top25
DefaultAssay(All_samples_Merged) <- "SCT"
par(mfrow = c(2, 5), mar = c(4, 6, 3, 1))
for (i in unique(top25_markers$cluster)) {
barplot(sort(setNames(top25_markers$avg_log2FC, top25_markers$gene)[top25_markers$cluster == i], F),
horiz = T, las = 1, main = paste0(i, " vs. rest"), border = "white", yaxs = "i"
)
abline(v = c(0, 0.25), lty = c(1, 2))
}

NA

nbiSweden Approach-Top5
par(mfrow = c(2, 5), mar = c(4, 6, 3, 1))
for (i in unique(top5_markers$cluster)) {
barplot(sort(setNames(top5_markers$avg_log2FC, top5_markers$gene)[top5_markers$cluster == i], F),
horiz = T, las = 1, main = paste0(i, " vs. rest"), border = "white", yaxs = "i"
)
abline(v = c(0, 0.25), lty = c(1, 2))
}

NA

Chatomics approach

nbiSweden Approach
DotPlot(All_samples_Merged, features = rev(as.character(unique(top5_markers$gene))), group.by = "seurat_clusters", assay = "RNA") + coord_flip()

scCustomize Dotplot
library(scCustomize)
Clustered_DotPlot(seurat_object = All_samples_Merged, features = top5_markers$gene, k = 6)
[[1]]
[[2]]



scCustomize::Clustered_DotPlot(All_samples_Merged, features = top5_markers$gene,
plot_km_elbow = FALSE)

LS0tCnRpdGxlOiAiSWRlbnRpZnkgVG9wNSBNYXJrZXJzIGFuZCBkbyBDbHVzdGVyIEFubm90YXRpb24iCmF1dGhvcjogIk5hc2lyIE1haG1vb2QgQWJiYXNpIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICB3b3JkX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgTG9hZCBiZWxvdyBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNvd3Bsb3QpCgoKbGlicmFyeShTQ3B1YnIpCmxpYnJhcnkoZHBseXIpCmBgYAoKCiMgMi4gTG9hZCBTZXVyYXQgT2JqZWN0IApgYGB7cn0KCkFsbF9zYW1wbGVzX01lcmdlZCA8LSByZWFkUkRTKCIuLi8wLVNldXJhdF9SRFNfT0JKRUNUX0ZJTkFML1NldXJhdF9vYmplY3RfRmluYWxfY2hhbmdlcy9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9TVENBVF9Bbm5vdGF0aW9uX2ZpbmFsLTUtMDktMjAyNS5yZHMiKQoKRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIlJOQSIKCkFsbF9zYW1wbGVzX01lcmdlZCA8LSBOb3JtYWxpemVEYXRhKAogICAgQWxsX3NhbXBsZXNfTWVyZ2VkLAogICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwKICAgIHNjYWxlLmZhY3RvciA9IDFlNCwKICAgIHZlcmJvc2UgPSBUUlVFCiAgKQoKCmBgYAoKIyAzLmZpbmQgVG9wIG1hcmtlcnMKYGBge3J9CklkZW50cyhBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJzZXVyYXRfY2x1c3RlcnMiCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDLvuI/ig6MgRmluZCBtYXJrZXIgZ2VuZXMgcGVyIGNsdXN0ZXIKU1NfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycygKICBBbGxfc2FtcGxlc19NZXJnZWQsCiAgb25seS5wb3MgPSBUUlVFLAogIG1pbi5wY3QgPSAwLjI1LAogIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUsCiAgbWluLnBjdC5kaWZmID0gMC4yCiAgCikKCmxpYnJhcnkoZHBseXIpCgojIFByZWNpc2UgYmxhY2tsaXN0IGZvciB1bmluZm9ybWF0aXZlIGdlbmVzCmJsYWNrbGlzdF9wYXR0ZXJucyA8LSBjKAogICJeVFJBViIsICJeVFJCViIsICJeVFJHViIsICJeVFJEViIsICJeVFJCQyIsICJeVFJBQyIsICJeVFJEQyIsICJeVFJHQyIsICMgVENSCiAgIl5JR0giLCAiXklHSyIsICJeSUdMIiwgIl5JR0oiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJZyBnZW5lcwogICJeUlBMIiwgIl5SUFMiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmlib3NvbWFsCiAgIl5NVC0iLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBtaXRvY2hvbmRyaWEKICAiXkhCQSIsICJeSEJCIiwgIl5IQltBQlpdIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGhlbW9nbG9iaW5zCiAgIl5ORUFUMSQiLCAiXk1BTEFUMSQiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBvcHRpb25hbCBsbmNSTkFzCiAgIl5YSVNUJCIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpibGFja2xpc3RfcmVnZXggPC0gcGFzdGUoYmxhY2tsaXN0X3BhdHRlcm5zLCBjb2xsYXBzZSA9ICJ8IikKCiMgUHJldmlldyB3aGljaCBtYXJrZXJzIHdpbGwgYmUgcmVtb3ZlZAp0b19yZW1vdmUgPC0gU1NfbWFya2VycyAlPiUKICBmaWx0ZXIoZ3JlcGwoYmxhY2tsaXN0X3JlZ2V4LCBnZW5lLCBpZ25vcmUuY2FzZSA9IFRSVUUpKQptZXNzYWdlKCJSb3dzIHRvIHJlbW92ZTogIiwgbnJvdyh0b19yZW1vdmUpKQpoZWFkKHRvX3JlbW92ZSRnZW5lKQoKIyBGaWx0ZXIgbWFya2VycyAoa2VlcCBpbXBvcnRhbnQgbWV0YWJvbGljL3Byb2xpZmVyYXRpb24gZ2VuZXMpClNTX21hcmtlcnNfZmlsdGVyZWQgPC0gU1NfbWFya2VycyAlPiUKICBmaWx0ZXIoIWdyZXBsKGJsYWNrbGlzdF9yZWdleCwgZ2VuZSwgaWdub3JlLmNhc2UgPSBUUlVFKSkKCgpgYGAKCgojIDQuIFRvcDUgTWFya2VycwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CgpsaWJyYXJ5KGRwbHlyKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTYXZlIGZpbHRlcmVkIG1hcmtlcnMKd3JpdGUuY3N2KFNTX21hcmtlcnNfZmlsdGVyZWQsIGZpbGUgPSAiU1NfbWFya2Vyc19maWx0ZXJlZC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgRXh0cmFjdCB0b3AgMjUgbWFya2VycyBwZXIgY2x1c3Rlcgp0b3AyNV9tYXJrZXJzIDwtIFNTX21hcmtlcnNfZmlsdGVyZWQgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JSAgIyBlbnN1cmUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gYXZnX2xvZzJGQywgbiA9IDI1KSAlPiUKICB1bmdyb3VwKCkKCndyaXRlLmNzdih0b3AyNV9tYXJrZXJzLCBmaWxlID0gIlNTX21hcmtlcnNfdG9wMjUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEV4dHJhY3QgdG9wIDUgbWFya2VycyBwZXIgY2x1c3Rlcgp0b3A1X21hcmtlcnMgPC0gU1NfbWFya2Vyc19maWx0ZXJlZCAlPiUKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lICAjIGVuc3VyZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBzbGljZV9tYXgob3JkZXJfYnkgPSBhdmdfbG9nMkZDLCBuID0gNSkgJT4lCiAgdW5ncm91cCgpCgp3cml0ZS5jc3YodG9wNV9tYXJrZXJzLCBmaWxlID0gIlNTX21hcmtlcnNfdG9wNS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCm1lc3NhZ2UoIkZpbHRlcmVkIG1hcmtlcnMsIHRvcDI1LCBhbmQgdG9wNSBtYXJrZXJzIHNhdmVkIHN1Y2Nlc3NmdWxseS4iKQoKYGBgCgoKIyMgbmJpU3dlZGVuIEFwcHJvYWNoLVRvcDI1CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIlNDVCIKCnBhcihtZnJvdyA9IGMoMiwgNSksIG1hciA9IGMoNCwgNiwgMywgMSkpCmZvciAoaSBpbiB1bmlxdWUodG9wMjVfbWFya2VycyRjbHVzdGVyKSkgewogICAgYmFycGxvdChzb3J0KHNldE5hbWVzKHRvcDI1X21hcmtlcnMkYXZnX2xvZzJGQywgdG9wMjVfbWFya2VycyRnZW5lKVt0b3AyNV9tYXJrZXJzJGNsdXN0ZXIgPT0gaV0sIEYpLAogICAgICAgIGhvcml6ID0gVCwgbGFzID0gMSwgbWFpbiA9IHBhc3RlMChpLCAiIHZzLiByZXN0IiksIGJvcmRlciA9ICJ3aGl0ZSIsIHlheHMgPSAiaSIKICAgICkKICAgIGFibGluZSh2ID0gYygwLCAwLjI1KSwgbHR5ID0gYygxLCAyKSkKfQoKYGBgCgojIyBuYmlTd2VkZW4gQXBwcm9hY2gtVG9wNQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CgpwYXIobWZyb3cgPSBjKDIsIDUpLCBtYXIgPSBjKDQsIDYsIDMsIDEpKQpmb3IgKGkgaW4gdW5pcXVlKHRvcDVfbWFya2VycyRjbHVzdGVyKSkgewogICAgYmFycGxvdChzb3J0KHNldE5hbWVzKHRvcDVfbWFya2VycyRhdmdfbG9nMkZDLCB0b3A1X21hcmtlcnMkZ2VuZSlbdG9wNV9tYXJrZXJzJGNsdXN0ZXIgPT0gaV0sIEYpLAogICAgICAgIGhvcml6ID0gVCwgbGFzID0gMSwgbWFpbiA9IHBhc3RlMChpLCAiIHZzLiByZXN0IiksIGJvcmRlciA9ICJ3aGl0ZSIsIHlheHMgPSAiaSIKICAgICkKICAgIGFibGluZSh2ID0gYygwLCAwLjI1KSwgbHR5ID0gYygxLCAyKSkKfQoKYGBgCgojIyBDaGF0b21pY3MgYXBwcm9hY2ggCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KbGlicmFyeShkcGx5cikKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkobWFnaWNrKQoKCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJTQ1QiCmRhdGFfbWF0IDwtIEdldEFzc2F5RGF0YShBbGxfc2FtcGxlc19NZXJnZWQsIGFzc2F5ID0gIlNDVCIsIHNsb3QgPSAiZGF0YSIpCgojIENvbnZlcnQgc3BhcnNlIG1hdHJpeCB0byBkZW5zZSBtYXRyaXggKGJld2FyZSBtZW1vcnkgdXNlKQptYXQgPC0gYXMubWF0cml4KGRhdGFfbWF0KQoKIyBDbGVhbiB0b3A1X21hcmtlcnM6IHVubGlzdCBhbmQga2VlcCBvbmx5IHZhbGlkIGdlbmUgbmFtZXMgcHJlc2VudCBpbiBkYXRhX21hdAp0b3A1X21hcmtlcnNfY2xlYW4gPC0gdW5saXN0KHRvcDVfbWFya2VycykKdG9wNV9tYXJrZXJzX2NsZWFuIDwtIHRvcDVfbWFya2Vyc19jbGVhblt0b3A1X21hcmtlcnNfY2xlYW4gJWluJSByb3duYW1lcyhtYXQpXQoKIyBTdWJzZXQgdGhlIG1hdHJpeCB1c2luZyBvbmx5IHZhbGlkIG1hcmtlcnMKbWF0X3N1YnNldCA8LSBtYXRbdG9wNV9tYXJrZXJzX2NsZWFuLCAsIGRyb3AgPSBGQUxTRV0KCiMgU2NhbGUgcm93cwptYXRfc2NhbGVkIDwtIHQoc2NhbGUodChtYXRfc3Vic2V0KSkpCgojIE5vdyBwcm9jZWVkIHdpdGggaGVhdG1hcCBwbG90dGluZyBldGMuCgoKCmNsdXN0ZXJfYW5ubzwtIEFsbF9zYW1wbGVzX01lcmdlZEBtZXRhLmRhdGEkc2V1cmF0X2NsdXN0ZXJzCgoKIyB3aGF0J3MgdGhlIHZhbHVlIHJhbmdlIGluIHRoZSBtYXRyaXgKcXVhbnRpbGUobWF0LCBjKDAuMSwgMC45NSkpCgpTZXVyYXQ6OlB1cnBsZUFuZFllbGxvdygpCgojIyBtYWtlIHRoZSBibGFjayBjb2xvciBtYXAgdG8gMC4gdGhlIHllbGxvdyBtYXAgdG8gaGlnaGVzdCBhbmQgdGhlIHB1cmxlIG1hcCB0byB0aGUgbG93ZXN0CmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0xLCAwLCAzKSwgYygiI0ZGMDBGRiIsICJibGFjayIsICIjRkZGRjAwIikpCgpIZWF0bWFwKG1hdF9zY2FsZWQsIG5hbWUgPSAiRXhwcmVzc2lvbiIsICAKICAgICAgICBjb2x1bW5fc3BsaXQgPSBmYWN0b3IoY2x1c3Rlcl9hbm5vKSwKICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICBjbHVzdGVyX2NvbHVtbl9zbGljZXMgPSBUUlVFLAogICAgICAgIGNvbHVtbl90aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSA4KSwKICAgICAgICBjb2x1bW5fZ2FwID0gdW5pdCgwLjUsICJtbSIpLAogICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgc2hvd19yb3dfZGVuZCA9IEZBTFNFLAogICAgICAgIGNvbCA9IGNvbF9mdW4sCiAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDgpLAogICAgICAgIGNvbHVtbl90aXRsZV9yb3QgPSA5MCwKICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IEhlYXRtYXBBbm5vdGF0aW9uKGZvbyA9IGFubm9fYmxvY2soZ3AgPSBncGFyKGZpbGwgPSBzY2FsZXM6Omh1ZV9wYWwoKSg5KSkpKSwKICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgIHVzZV9yYXN0ZXIgPSBUUlVFLAogICAgICAgIHJhc3Rlcl9xdWFsaXR5ID0gOCkKYGBgCgojIyBuYmlTd2VkZW4gQXBwcm9hY2gKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMn0KCkRvdFBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCBmZWF0dXJlcyA9IHJldihhcy5jaGFyYWN0ZXIodW5pcXVlKHRvcDVfbWFya2VycyRnZW5lKSkpLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBhc3NheSA9ICJSTkEiKSArIGNvb3JkX2ZsaXAoKQoKYGBgCgojIyBzY0N1c3RvbWl6ZSBEb3RwbG90CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTJ9CmxpYnJhcnkoc2NDdXN0b21pemUpCkNsdXN0ZXJlZF9Eb3RQbG90KHNldXJhdF9vYmplY3QgPSBBbGxfc2FtcGxlc19NZXJnZWQsIGZlYXR1cmVzID0gdG9wNV9tYXJrZXJzJGdlbmUsIGsgPSA2KQoKCnNjQ3VzdG9taXplOjpDbHVzdGVyZWRfRG90UGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIGZlYXR1cmVzID0gdG9wNV9tYXJrZXJzJGdlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2ttX2VsYm93ID0gRkFMU0UpCmBgYAoKCgoKCgo=