1. load libraries
2. Load Data into Seurat
ss_Herrera <- readRDS("Herrara_All_samples.rds")
4. Python Script to run (make sure you have right directory)
# import warnings
# warnings.filterwarnings("ignore")
# import STCAT
# import scanpy as sc
#
#
# # Load your .h5ad data
# adata = sc.read("/home/bioinfo/2025_NewHarmony_Integrated_Files/Year3_Analysis/Biomarkers_Validation_with_Public_Data/Borch_Combined/STCAT/STCAT_Annotation_Borcherding/ss_Herrera_raw_counts.h5ad")
# print(adata.var_names[:10])
#
#
# # Run STCAT
# results = STCAT.STCAT(adata)
#
# # Save results
# results.write("/home/bioinfo/2025_NewHarmony_Integrated_Files/Year3_Analysis/Biomarkers_Validation_with_Public_Data/Borch_Combined/STCAT/STCAT_Annotation_Borcherding/STCAT_Annotation_ss_Herrera_raw_counts.h5ad")
#
#
# print(adata)
#
5. Read and merge with Seurat (STCAT annotation)
# Load Seurat and read your annotated table
library(Seurat)
library(dplyr)
# Read your annotation table
annots <- read.csv("STCAT/STCAT_annotations.csv", row.names = 1)
# Confirm the rownames are cell barcodes
head(rownames(annots)) # e.g., AAACCTGGT...
[1] "SS1_Blood_H20_AAGGTTCCATATGAGA" "SS1_Blood_H20_CACTCCAGTGCACCAC" "SS1_Blood_H20_AACTCCCTCCGAACGC"
[4] "SS1_Blood_H20_AACTCTTAGACCGGAT" "SS1_Blood_H20_AAGGCAGAGTCAAGCG" "SS1_Blood_H20_AAGTCTGGTCTAAAGA"
# Match to your Seurat object (assuming your Seurat object is named `ss_Herrera`)
ss_Herrera$STCAT_Prediction <- annots[Cells(ss_Herrera), "Prediction"]
ss_Herrera$STCAT_Cluster <- annots[Cells(ss_Herrera), "Cluster"]
ss_Herrera$STCAT_Uncertainty <- annots[Cells(ss_Herrera), "Uncertainty.score"]
view(ss_Herrera@meta.data)
6. Read and merge with Seurat (STCAT annotation)
DimPlot(ss_Herrera, group.by = "tissue", label = TRUE, repel = T)

DimPlot(ss_Herrera, group.by = "sample_id", label = TRUE, repel = T)

DimPlot(ss_Herrera, group.by = "RNA_snn_res.0.5", label = TRUE, repel = T)

DimPlot(ss_Herrera, group.by = "STCAT_Prediction", label = TRUE, repel = T)

NA
NA
NA
NA
7. Read and merge with Seurat (STCAT annotation)
table(ss_Herrera$STCAT_Prediction) # should show all predicted labels
CD4 activated CD4 proliferation CD4 Tc CD4 Tcm CD4 Tem CD4 Temra
40 108 361 4935 272 62
CD4 Tex CD4 Tfh CD4 Th17 CD4 Tisg CD4 Tn CD4 Tn adhesion
906 31 137 702 3927 163
CD4 Treg CD4 Treg naive-like CD4 Trm CD4 Trm cell-death CD8 proliferation CD8 senescence
228 142 615 1134 134 4
CD8 Tc CD8 Tcm CD8 Tem CD8 Temra CD8 Tex CD8 Tn
743 19 669 1264 2 1381
CD8 Trm CD8 Trm naive-like CD8 Tstr Double Negative MAIT None T
142 476 26 32 385 9622
other CD8 T Tgd
9 1531
table(ss_Herrera$sample_id) # should show L1–L7
Healthy_Blood Healthy_Skin MF1_Blood MF1_Skin SS1_Blood SS1_Skin SS2_Blood SS2_Skin
4386 33 4880 293 3631 374 6078 244
SS3_Blood SS3_Skin SS4_Blood SS4_Skin SS5_Blood SS6_Blood
2109 272 2072 320 2832 2821
table(ss_Herrera$tissue)
Blood Skin
28809 1536
# Contingency table
table_annotation_tissue <- table(ss_Herrera$STCAT_Prediction, ss_Herrera$tissue)
table_annotation_sample_id <- table(ss_Herrera$STCAT_Prediction, ss_Herrera$sample_id)
table_annotation_cluster <- table(ss_Herrera$STCAT_Prediction, ss_Herrera$RNA_snn_res.0.5)
library(pheatmap)
# Proportion table (color scale)
prop_table <- prop.table(table_annotation_cluster, margin = 1)
# Heatmap with raw counts as labels
pheatmap(prop_table,
display_numbers = table_annotation_cluster,
cluster_rows = TRUE,
cluster_cols = TRUE,
color = colorRampPalette(c("lightyellow", "yellow", "lightgreen", "white"))(100),
fontsize_row = 10,
fontsize_col = 10,
main = "STCAT Prediction vs Clusters")

library(pheatmap)
# Proportion table (color scale)
prop_table <- prop.table(table_annotation_sample_id, margin = 1)
# Heatmap with raw counts as labels
pheatmap(prop_table,
display_numbers = table_annotation_sample_id,
cluster_rows = TRUE,
cluster_cols = TRUE,
color = colorRampPalette(c("lightyellow", "yellow", "lightgreen", "white"))(100),
fontsize_row = 10,
fontsize_col = 10,
main = "STCAT Prediction vs Patients")

# Proportion table (color scale)
prop_table <- prop.table(table_annotation_tissue, margin = 1)
# Heatmap with raw counts as labels
pheatmap(prop_table,
display_numbers = table_annotation_tissue,
cluster_rows = TRUE,
cluster_cols = TRUE,
color = colorRampPalette(c("lightyellow", "yellow", "lightgreen", "white"))(100),
fontsize_row = 10,
fontsize_col = 10,
main = "STCAT Prediction vs Patients")

Save the Seurat object as an RDS
saveRDS(ss_Herrera, file = "STCAT/All_Patients_Integrated_ss_Herrera_annotated.RDS")
LS0tCnRpdGxlOiAiSG93IHRvIGNyZWF0ZSAuaDVhZCBmb3IgU1RDQVQgb2YgSGVycmVyYSBkYXRhIgphdXRob3I6IE5hc2lyIE1haG1vb2QgQWJiYXNpCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCmxpYnJhcnkoU2V1cmF0RGF0YSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoQXppbXV0aCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkodGlueXRleCkKCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGRpdHRvU2VxKQpsaWJyYXJ5KGdncmVwZWwpCiNsaWJyYXJ5KGdndHJlZSkKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShwbG90bHkpICAjIDNEIHBsb3QKbGlicmFyeShTZXVyYXQpICAjIElkZW50cygpCmxpYnJhcnkoU2V1cmF0RGlzaykgICMgU2F2ZUg1U2V1cmF0KCkKbGlicmFyeSh0aWJibGUpICAjIHJvd25uYW1lc190b19jb2x1bW4KbGlicmFyeShoYXJtb255KSAjIFJ1bkhhcm1vbnkoKQojb3B0aW9ucyhtYy5jb3JlcyA9IGRldGVjdENvcmVzKCkgLSAxKQoKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZGJwbHlyKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeSh0aW55dGV4KQojQXppbXV0aCBBbm5vdGF0aW9uIGxpYnJhcmllcwpsaWJyYXJ5KEF6aW11dGgpCiNQcm9qZWNUaWxzIEFubm90YXRpb24gbGlicmFyaWVzCmxpYnJhcnkoU1RBQ0FTKQpsaWJyYXJ5KFByb2plY1RJTHMpCiNzaW5nbGVSIEFubm90YXRpb24gbGlicmFyaWVzCgpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQoKYGBgCgoKIyAyLiBMb2FkIERhdGEgaW50byBTZXVyYXQKYGBge3IgbG9hZF9zZXVyYXR9Cgpzc19IZXJyZXJhIDwtIHJlYWRSRFMoIkhlcnJhcmFfQWxsX3NhbXBsZXMucmRzIikKCmBgYAoKIyAzLiBFeHBvcnQgdG8gLmg1YWQgKEFubkRhdGEgZm9ybWF0KQpgYGB7ciBBbm5EYXRhLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBTdGVwIDE6IFJlam9pbiBMYXllcnMgdG8gR2V0IFVuaWZpZWQgUmF3IENvdW50cwpzc19IZXJyZXJhW1siUk5BIl1dIDwtIEpvaW5MYXllcnMoc3NfSGVycmVyYVtbIlJOQSJdXSkKcmF3X2NvdW50cyA8LSBzc19IZXJyZXJhW1siUk5BIl1dJGNvdW50cwoKCiMgU3RlcCAyOiBUcmFuc3Bvc2UgdG8gQ2VsbHMgw5cgR2VuZXMKIyBUcmFuc3Bvc2U6IGdlbmVzIMOXIGNlbGxzIOKGkiBjZWxscyDDlyBnZW5lcwpjb3VudHNfdHJhbnNwb3NlZCA8LSB0KGFzLm1hdHJpeChyYXdfY291bnRzKSkKCiMgU3RlcCAzOiBQcmVwYXJlIE1ldGFkYXRhIChPcHRpb25hbCwgUmVjb21tZW5kZWQpCmNlbGxfbWV0YWRhdGEgPC0gc3NfSGVycmVyYVtbXV0gICMgQWxsIGNlbGwgbWV0YWRhdGEKCiMgU3RlcCA0OiBDb252ZXJ0IHRvIFNpbmdsZUNlbGxFeHBlcmltZW50CmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudCgKICBhc3NheXMgPSBsaXN0KGNvdW50cyA9IHJhd19jb3VudHMpLCAgICAgICAjIEtlZXAgcmF3X2NvdW50cyAoZ2VuZXMgw5cgY2VsbHMhKQogIGNvbERhdGEgPSBjZWxsX21ldGFkYXRhICAgICAgICAgICAgICAgICAgICMgQXR0YWNoIGNlbGwgbWV0YWRhdGEKKQoKI1N0ZXAgNTogRXhwb3J0IHRvIC5oNWFkCmxpYnJhcnkoemVsbGtvbnZlcnRlcikKd3JpdGVINUFEKHNjZSwgZmlsZSA9ICJTVENBVC9zc19IZXJyZXJhX3Jhd19jb3VudHMuaDVhZCIpCgpgYGAKCgojIDQuIFB5dGhvbiBTY3JpcHQgdG8gcnVuIChtYWtlIHN1cmUgeW91IGhhdmUgcmlnaHQgZGlyZWN0b3J5KQpgYGB7ciBQeXRob25TVENBVCwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CiMgaW1wb3J0IHdhcm5pbmdzCiMgd2FybmluZ3MuZmlsdGVyd2FybmluZ3MoImlnbm9yZSIpCiMgaW1wb3J0IFNUQ0FUCiMgaW1wb3J0IHNjYW5weSBhcyBzYwojIAojIAojICMgTG9hZCB5b3VyIC5oNWFkIGRhdGEKIyBhZGF0YSA9IHNjLnJlYWQoIi9ob21lL2Jpb2luZm8vMjAyNV9OZXdIYXJtb255X0ludGVncmF0ZWRfRmlsZXMvWWVhcjNfQW5hbHlzaXMvQmlvbWFya2Vyc19WYWxpZGF0aW9uX3dpdGhfUHVibGljX0RhdGEvQm9yY2hfQ29tYmluZWQvU1RDQVQvU1RDQVRfQW5ub3RhdGlvbl9Cb3JjaGVyZGluZy9zc19IZXJyZXJhX3Jhd19jb3VudHMuaDVhZCIpCiMgcHJpbnQoYWRhdGEudmFyX25hbWVzWzoxMF0pCiMgCiMgCiMgIyBSdW4gU1RDQVQKIyByZXN1bHRzID0gU1RDQVQuU1RDQVQoYWRhdGEpCiMgCiMgIyBTYXZlIHJlc3VsdHMKIyByZXN1bHRzLndyaXRlKCIvaG9tZS9iaW9pbmZvLzIwMjVfTmV3SGFybW9ueV9JbnRlZ3JhdGVkX0ZpbGVzL1llYXIzX0FuYWx5c2lzL0Jpb21hcmtlcnNfVmFsaWRhdGlvbl93aXRoX1B1YmxpY19EYXRhL0JvcmNoX0NvbWJpbmVkL1NUQ0FUL1NUQ0FUX0Fubm90YXRpb25fQm9yY2hlcmRpbmcvU1RDQVRfQW5ub3RhdGlvbl9zc19IZXJyZXJhX3Jhd19jb3VudHMuaDVhZCIpCiMgCiMgCiMgcHJpbnQoYWRhdGEpCiMgCgpgYGAKCiMgNS4gUmVhZCBhbmQgbWVyZ2Ugd2l0aCBTZXVyYXQgKFNUQ0FUIGFubm90YXRpb24pCmBgYHtyIFNUQ0FULCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCiMgTG9hZCBTZXVyYXQgYW5kIHJlYWQgeW91ciBhbm5vdGF0ZWQgdGFibGUKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCgojIFJlYWQgeW91ciBhbm5vdGF0aW9uIHRhYmxlCmFubm90cyA8LSByZWFkLmNzdigiU1RDQVQvU1RDQVRfYW5ub3RhdGlvbnMuY3N2Iiwgcm93Lm5hbWVzID0gMSkKCiMgQ29uZmlybSB0aGUgcm93bmFtZXMgYXJlIGNlbGwgYmFyY29kZXMKaGVhZChyb3duYW1lcyhhbm5vdHMpKSAgIyBlLmcuLCBBQUFDQ1RHR1QuLi4KCiMgTWF0Y2ggdG8geW91ciBTZXVyYXQgb2JqZWN0IChhc3N1bWluZyB5b3VyIFNldXJhdCBvYmplY3QgaXMgbmFtZWQgYHNzX0hlcnJlcmFgKQpzc19IZXJyZXJhJFNUQ0FUX1ByZWRpY3Rpb24gPC0gYW5ub3RzW0NlbGxzKHNzX0hlcnJlcmEpLCAiUHJlZGljdGlvbiJdCnNzX0hlcnJlcmEkU1RDQVRfQ2x1c3RlciA8LSBhbm5vdHNbQ2VsbHMoc3NfSGVycmVyYSksICJDbHVzdGVyIl0Kc3NfSGVycmVyYSRTVENBVF9VbmNlcnRhaW50eSA8LSBhbm5vdHNbQ2VsbHMoc3NfSGVycmVyYSksICJVbmNlcnRhaW50eS5zY29yZSJdCgp2aWV3KHNzX0hlcnJlcmFAbWV0YS5kYXRhKQoKYGBgCgoKIyA2LiBSZWFkIGFuZCBtZXJnZSB3aXRoIFNldXJhdCAoU1RDQVQgYW5ub3RhdGlvbikKYGBge3IgU1RDQVRfdmlzLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTR9CgpEaW1QbG90KHNzX0hlcnJlcmEsIGdyb3VwLmJ5ID0gInRpc3N1ZSIsIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUKQpEaW1QbG90KHNzX0hlcnJlcmEsIGdyb3VwLmJ5ID0gInNhbXBsZV9pZCIsIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUKQpEaW1QbG90KHNzX0hlcnJlcmEsIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjAuNSIsIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUKQpEaW1QbG90KHNzX0hlcnJlcmEsIGdyb3VwLmJ5ID0gIlNUQ0FUX1ByZWRpY3Rpb24iLCBsYWJlbCA9IFRSVUUsIHJlcGVsID0gVCkKCgoKCmBgYAoKIyA3LiBSZWFkIGFuZCBtZXJnZSB3aXRoIFNldXJhdCAoU1RDQVQgYW5ub3RhdGlvbikKYGBge3IgU1RDQVRfdmlzMiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KCnRhYmxlKHNzX0hlcnJlcmEkU1RDQVRfUHJlZGljdGlvbikgICMgc2hvdWxkIHNob3cgYWxsIHByZWRpY3RlZCBsYWJlbHMKdGFibGUoc3NfSGVycmVyYSRzYW1wbGVfaWQpICAgICAgICAgIyBzaG91bGQgc2hvdyBMMeKAk0w3CnRhYmxlKHNzX0hlcnJlcmEkdGlzc3VlKSAKCiMgQ29udGluZ2VuY3kgdGFibGUgCnRhYmxlX2Fubm90YXRpb25fdGlzc3VlIDwtIHRhYmxlKHNzX0hlcnJlcmEkU1RDQVRfUHJlZGljdGlvbiwgc3NfSGVycmVyYSR0aXNzdWUpCnRhYmxlX2Fubm90YXRpb25fc2FtcGxlX2lkIDwtIHRhYmxlKHNzX0hlcnJlcmEkU1RDQVRfUHJlZGljdGlvbiwgc3NfSGVycmVyYSRzYW1wbGVfaWQpCnRhYmxlX2Fubm90YXRpb25fY2x1c3RlciA8LSB0YWJsZShzc19IZXJyZXJhJFNUQ0FUX1ByZWRpY3Rpb24sIHNzX0hlcnJlcmEkUk5BX3Nubl9yZXMuMC41KQoKbGlicmFyeShwaGVhdG1hcCkKCiMgUHJvcG9ydGlvbiB0YWJsZSAoY29sb3Igc2NhbGUpCnByb3BfdGFibGUgPC0gcHJvcC50YWJsZSh0YWJsZV9hbm5vdGF0aW9uX2NsdXN0ZXIsIG1hcmdpbiA9IDEpCgojIEhlYXRtYXAgd2l0aCByYXcgY291bnRzIGFzIGxhYmVscwpwaGVhdG1hcChwcm9wX3RhYmxlLAogICAgICAgICBkaXNwbGF5X251bWJlcnMgPSB0YWJsZV9hbm5vdGF0aW9uX2NsdXN0ZXIsCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgIGNsdXN0ZXJfY29scyA9IFRSVUUsCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJsaWdodHllbGxvdyIsICJ5ZWxsb3ciLCAibGlnaHRncmVlbiIsICJ3aGl0ZSIpKSgxMDApLAogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMCwKICAgICAgICAgZm9udHNpemVfY29sID0gMTAsCiAgICAgICAgIG1haW4gPSAiU1RDQVQgUHJlZGljdGlvbiB2cyBDbHVzdGVycyIpCgoKbGlicmFyeShwaGVhdG1hcCkKCiMgUHJvcG9ydGlvbiB0YWJsZSAoY29sb3Igc2NhbGUpCnByb3BfdGFibGUgPC0gcHJvcC50YWJsZSh0YWJsZV9hbm5vdGF0aW9uX3NhbXBsZV9pZCwgbWFyZ2luID0gMSkKCiMgSGVhdG1hcCB3aXRoIHJhdyBjb3VudHMgYXMgbGFiZWxzCnBoZWF0bWFwKHByb3BfdGFibGUsCiAgICAgICAgIGRpc3BsYXlfbnVtYmVycyA9IHRhYmxlX2Fubm90YXRpb25fc2FtcGxlX2lkLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBUUlVFLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygibGlnaHR5ZWxsb3ciLCAieWVsbG93IiwgImxpZ2h0Z3JlZW4iLCAid2hpdGUiKSkoMTAwKSwKICAgICAgICAgZm9udHNpemVfcm93ID0gMTAsCiAgICAgICAgIGZvbnRzaXplX2NvbCA9IDEwLAogICAgICAgICBtYWluID0gIlNUQ0FUIFByZWRpY3Rpb24gdnMgUGF0aWVudHMiKQoKCiMgUHJvcG9ydGlvbiB0YWJsZSAoY29sb3Igc2NhbGUpCnByb3BfdGFibGUgPC0gcHJvcC50YWJsZSh0YWJsZV9hbm5vdGF0aW9uX3Rpc3N1ZSwgbWFyZ2luID0gMSkKCiMgSGVhdG1hcCB3aXRoIHJhdyBjb3VudHMgYXMgbGFiZWxzCnBoZWF0bWFwKHByb3BfdGFibGUsCiAgICAgICAgIGRpc3BsYXlfbnVtYmVycyA9IHRhYmxlX2Fubm90YXRpb25fdGlzc3VlLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBUUlVFLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygibGlnaHR5ZWxsb3ciLCAieWVsbG93IiwgImxpZ2h0Z3JlZW4iLCAid2hpdGUiKSkoMTAwKSwKICAgICAgICAgZm9udHNpemVfcm93ID0gMTAsCiAgICAgICAgIGZvbnRzaXplX2NvbCA9IDEwLAogICAgICAgICBtYWluID0gIlNUQ0FUIFByZWRpY3Rpb24gdnMgUGF0aWVudHMiKQoKYGBgCgoKCgoKCiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdCBhcyBhbiBSRFMKYGBge3Igc2F2ZVJEU30KCgpzYXZlUkRTKHNzX0hlcnJlcmEsIGZpbGUgPSAiU1RDQVQvQWxsX1BhdGllbnRzX0ludGVncmF0ZWRfc3NfSGVycmVyYV9hbm5vdGF0ZWQuUkRTIikKCgpgYGAKCgoKCg==