1. load libraries
2. Load Data into Seurat
ss_Bor_2023 <- readRDS("../Borch_Combined/data/ss_Borcherding_Malignant_6_Normal_1_Integrated_object.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_Bor_2023_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_Bor_2023_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_Annotation_Borcherding/STCAT_annotations.csv", row.names = 1)
# Confirm the rownames are cell barcodes
head(rownames(annots)) # e.g., AAACCTGGT...
[1] "SS_P1_AAATGCCTCAACGGCC-1" "SS_P1_AATCGGTGTTCCAACA-1" "SS_P1_ACACCCTTCGCGTTTC-1" "SS_P1_ACAGCCGAGCTCCTCT-1"
[5] "SS_P1_ACTTACTAGCTAAGAT-1" "SS_P1_AGGCCACGTACCGTAT-1"
# Match to your Seurat object (assuming your Seurat object is named `ss_Bor_2023`)
ss_Bor_2023$STCAT_Prediction <- annots[Cells(ss_Bor_2023), "Prediction"]
ss_Bor_2023$STCAT_Cluster <- annots[Cells(ss_Bor_2023), "Cluster"]
ss_Bor_2023$STCAT_Uncertainty <- annots[Cells(ss_Bor_2023), "Uncertainty.score"]
view(ss_Bor_2023@meta.data)
6. Read and merge with Seurat (STCAT annotation)
DimPlot(ss_Bor_2023_annotated, group.by = "Disease_state", label = TRUE, repel = T)

DimPlot(ss_Bor_2023_annotated, group.by = "orig.ident", label = TRUE, repel = T)

DimPlot(ss_Bor_2023_annotated, group.by = "integrated_snn_res.0.5", label = TRUE, repel = T)

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

NA
NA
NA
NA
7. Read and merge with Seurat (STCAT annotation)
table(ss_Bor_2023$STCAT_Prediction) # should show all predicted labels
CD4 proliferation CD4 Tc CD4 Tcm CD4 Tem CD4 Temra CD4 Tex
376 2353 21126 1320 103 1832
CD4 Tfh CD4 Th1 CD4 Th17 CD4 Tisg CD4 Tn CD4 Tn adhesion
224 63 725 1236 4420 3109
CD4 Treg CD4 Treg activated CD4 Trm CD4 Trm cell-death CD4 Trm naive-like CD4 Tstr
1729 132 1874 3252 621 434
Heterogeneous None T Tgd
128 5678 360
table(ss_Bor_2023$orig.ident) # should show L1–L7
Control SS_P1 SS_P2 SS_P3 SS_P4 SS_P5 SS_P6
4437 3443 34179 3084 849 1582 3521
# Contingency table of STCAT Prediction × Cell Line
table_annotation_orig_ident <- table(ss_Bor_2023$STCAT_Prediction, ss_Bor_2023$orig.ident)
table_annotation_cluster <- table(ss_Bor_2023$STCAT_Prediction, ss_Bor_2023$integrated_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_orig_ident, margin = 1)
# Heatmap with raw counts as labels
pheatmap(prop_table,
display_numbers = table_annotation_orig_ident,
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")

LS0tCnRpdGxlOiAiSG93IHRvIGNyZWF0ZSAuaDVhZCBmb3IgU1RDQVQiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgI3JtZGZvcm1hdHM6OnJlYWR0aGVkb3duCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfY29sbGFwc2VkOiB0cnVlCi0tLQoKIyAxLiBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNldXJhdE9iamVjdCkKbGlicmFyeShTZXVyYXREYXRhKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShBemltdXRoKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeSh0aW55dGV4KQoKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZGl0dG9TZXEpCmxpYnJhcnkoZ2dyZXBlbCkKI2xpYnJhcnkoZ2d0cmVlKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHBsb3RseSkgICMgM0QgcGxvdApsaWJyYXJ5KFNldXJhdCkgICMgSWRlbnRzKCkKbGlicmFyeShTZXVyYXREaXNrKSAgIyBTYXZlSDVTZXVyYXQoKQpsaWJyYXJ5KHRpYmJsZSkgICMgcm93bm5hbWVzX3RvX2NvbHVtbgpsaWJyYXJ5KGhhcm1vbnkpICMgUnVuSGFybW9ueSgpCiNvcHRpb25zKG1jLmNvcmVzID0gZGV0ZWN0Q29yZXMoKSAtIDEpCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShkYnBseXIpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpbnl0ZXgpCiNBemltdXRoIEFubm90YXRpb24gbGlicmFyaWVzCmxpYnJhcnkoQXppbXV0aCkKI1Byb2plY1RpbHMgQW5ub3RhdGlvbiBsaWJyYXJpZXMKbGlicmFyeShTVEFDQVMpCmxpYnJhcnkoUHJvamVjVElMcykKI3NpbmdsZVIgQW5ub3RhdGlvbiBsaWJyYXJpZXMKCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCgpgYGAKCgojIDIuIExvYWQgRGF0YSBpbnRvIFNldXJhdApgYGB7ciBsb2FkX3NldXJhdH0KCnNzX0Jvcl8yMDIzIDwtIHJlYWRSRFMoIi4uL0JvcmNoX0NvbWJpbmVkL2RhdGEvc3NfQm9yY2hlcmRpbmdfTWFsaWduYW50XzZfTm9ybWFsXzFfSW50ZWdyYXRlZF9vYmplY3QucmRzIikKCmBgYAoKIyAzLiBFeHBvcnQgdG8gLmg1YWQgKEFubkRhdGEgZm9ybWF0KQpgYGB7ciBBbm5EYXRhLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBTdGVwIDE6IFJlam9pbiBMYXllcnMgdG8gR2V0IFVuaWZpZWQgUmF3IENvdW50cwpzc19Cb3JfMjAyM1tbIlJOQSJdXSA8LSBKb2luTGF5ZXJzKHNzX0Jvcl8yMDIzW1siUk5BIl1dKQpyYXdfY291bnRzIDwtIHNzX0Jvcl8yMDIzW1siUk5BIl1dJGNvdW50cwoKCiMgU3RlcCAyOiBUcmFuc3Bvc2UgdG8gQ2VsbHMgw5cgR2VuZXMKIyBUcmFuc3Bvc2U6IGdlbmVzIMOXIGNlbGxzIOKGkiBjZWxscyDDlyBnZW5lcwpjb3VudHNfdHJhbnNwb3NlZCA8LSB0KGFzLm1hdHJpeChyYXdfY291bnRzKSkKCiMgU3RlcCAzOiBQcmVwYXJlIE1ldGFkYXRhIChPcHRpb25hbCwgUmVjb21tZW5kZWQpCmNlbGxfbWV0YWRhdGEgPC0gc3NfQm9yXzIwMjNbW11dICAjIEFsbCBjZWxsIG1ldGFkYXRhCgojIFN0ZXAgNDogQ29udmVydCB0byBTaW5nbGVDZWxsRXhwZXJpbWVudApsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpzY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQoCiAgYXNzYXlzID0gbGlzdChjb3VudHMgPSByYXdfY291bnRzKSwgICAgICAgIyBLZWVwIHJhd19jb3VudHMgKGdlbmVzIMOXIGNlbGxzISkKICBjb2xEYXRhID0gY2VsbF9tZXRhZGF0YSAgICAgICAgICAgICAgICAgICAjIEF0dGFjaCBjZWxsIG1ldGFkYXRhCikKCiNTdGVwIDU6IEV4cG9ydCB0byAuaDVhZApsaWJyYXJ5KHplbGxrb252ZXJ0ZXIpCndyaXRlSDVBRChzY2UsIGZpbGUgPSAiU1RDQVQvc3NfQm9yXzIwMjNfcmF3X2NvdW50cy5oNWFkIikKCmBgYAoKCiMgNC4gUHl0aG9uIFNjcmlwdCB0byBydW4gKG1ha2Ugc3VyZSB5b3UgaGF2ZSByaWdodCBkaXJlY3RvcnkpCmBgYHtyIFB5dGhvblNUQ0FULCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBpbXBvcnQgd2FybmluZ3MKIyB3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygiaWdub3JlIikKIyBpbXBvcnQgU1RDQVQKIyBpbXBvcnQgc2NhbnB5IGFzIHNjCiMgCiMgCiMgIyBMb2FkIHlvdXIgLmg1YWQgZGF0YQojIGFkYXRhID0gc2MucmVhZCgiL2hvbWUvYmlvaW5mby8yMDI1X05ld0hhcm1vbnlfSW50ZWdyYXRlZF9GaWxlcy9ZZWFyM19BbmFseXNpcy9CaW9tYXJrZXJzX1ZhbGlkYXRpb25fd2l0aF9QdWJsaWNfRGF0YS9Cb3JjaF9Db21iaW5lZC9TVENBVC9TVENBVF9Bbm5vdGF0aW9uX0JvcmNoZXJkaW5nL3NzX0Jvcl8yMDIzX3Jhd19jb3VudHMuaDVhZCIpCiMgcHJpbnQoYWRhdGEudmFyX25hbWVzWzoxMF0pCiMgCiMgCiMgIyBSdW4gU1RDQVQKIyByZXN1bHRzID0gU1RDQVQuU1RDQVQoYWRhdGEpCiMgCiMgIyBTYXZlIHJlc3VsdHMKIyByZXN1bHRzLndyaXRlKCIvaG9tZS9iaW9pbmZvLzIwMjVfTmV3SGFybW9ueV9JbnRlZ3JhdGVkX0ZpbGVzL1llYXIzX0FuYWx5c2lzL0Jpb21hcmtlcnNfVmFsaWRhdGlvbl93aXRoX1B1YmxpY19EYXRhL0JvcmNoX0NvbWJpbmVkL1NUQ0FUL1NUQ0FUX0Fubm90YXRpb25fQm9yY2hlcmRpbmcvU1RDQVRfQW5ub3RhdGlvbl9zc19Cb3JfMjAyM19yYXdfY291bnRzLmg1YWQiKQojIAojIAojIHByaW50KGFkYXRhKQojIAoKYGBgCgojIDUuIFJlYWQgYW5kIG1lcmdlIHdpdGggU2V1cmF0IChTVENBVCBhbm5vdGF0aW9uKQpgYGB7ciBTVENBVCwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojIExvYWQgU2V1cmF0IGFuZCByZWFkIHlvdXIgYW5ub3RhdGVkIHRhYmxlCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQoKIyBSZWFkIHlvdXIgYW5ub3RhdGlvbiB0YWJsZQphbm5vdHMgPC0gcmVhZC5jc3YoIlNUQ0FUL1NUQ0FUX0Fubm90YXRpb25fQm9yY2hlcmRpbmcvU1RDQVRfYW5ub3RhdGlvbnMuY3N2Iiwgcm93Lm5hbWVzID0gMSkKCiMgQ29uZmlybSB0aGUgcm93bmFtZXMgYXJlIGNlbGwgYmFyY29kZXMKaGVhZChyb3duYW1lcyhhbm5vdHMpKSAgIyBlLmcuLCBBQUFDQ1RHR1QuLi4KCiMgTWF0Y2ggdG8geW91ciBTZXVyYXQgb2JqZWN0IChhc3N1bWluZyB5b3VyIFNldXJhdCBvYmplY3QgaXMgbmFtZWQgYHNzX0Jvcl8yMDIzYCkKc3NfQm9yXzIwMjMkU1RDQVRfUHJlZGljdGlvbiA8LSBhbm5vdHNbQ2VsbHMoc3NfQm9yXzIwMjMpLCAiUHJlZGljdGlvbiJdCnNzX0Jvcl8yMDIzJFNUQ0FUX0NsdXN0ZXIgPC0gYW5ub3RzW0NlbGxzKHNzX0Jvcl8yMDIzKSwgIkNsdXN0ZXIiXQpzc19Cb3JfMjAyMyRTVENBVF9VbmNlcnRhaW50eSA8LSBhbm5vdHNbQ2VsbHMoc3NfQm9yXzIwMjMpLCAiVW5jZXJ0YWludHkuc2NvcmUiXQoKdmlldyhzc19Cb3JfMjAyM0BtZXRhLmRhdGEpCgpgYGAKCgojIDYuIFJlYWQgYW5kIG1lcmdlIHdpdGggU2V1cmF0IChTVENBVCBhbm5vdGF0aW9uKQpgYGB7ciBTVENBVF92aXMsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KCkRpbVBsb3Qoc3NfQm9yXzIwMjNfYW5ub3RhdGVkLCBncm91cC5ieSA9ICJEaXNlYXNlX3N0YXRlIiwgbGFiZWwgPSBUUlVFLCByZXBlbCA9IFQpCkRpbVBsb3Qoc3NfQm9yXzIwMjNfYW5ub3RhdGVkLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgbGFiZWwgPSBUUlVFLCByZXBlbCA9IFQpCkRpbVBsb3Qoc3NfQm9yXzIwMjNfYW5ub3RhdGVkLCBncm91cC5ieSA9ICJpbnRlZ3JhdGVkX3Nubl9yZXMuMC41IiwgbGFiZWwgPSBUUlVFLCByZXBlbCA9IFQpCkRpbVBsb3Qoc3NfQm9yXzIwMjNfYW5ub3RhdGVkLCBncm91cC5ieSA9ICJTVENBVF9QcmVkaWN0aW9uIiwgbGFiZWwgPSBUUlVFLCByZXBlbCA9IFQpCgoKCgpgYGAKCiMgNy4gUmVhZCBhbmQgbWVyZ2Ugd2l0aCBTZXVyYXQgKFNUQ0FUIGFubm90YXRpb24pCmBgYHtyIFNUQ0FUX3ZpczIsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KCnRhYmxlKHNzX0Jvcl8yMDIzJFNUQ0FUX1ByZWRpY3Rpb24pICAjIHNob3VsZCBzaG93IGFsbCBwcmVkaWN0ZWQgbGFiZWxzCnRhYmxlKHNzX0Jvcl8yMDIzJG9yaWcuaWRlbnQpICAgICAgICAgIyBzaG91bGQgc2hvdyBMMeKAk0w3CgojIENvbnRpbmdlbmN5IHRhYmxlIG9mIFNUQ0FUIFByZWRpY3Rpb24gw5cgQ2VsbCBMaW5lCnRhYmxlX2Fubm90YXRpb25fb3JpZ19pZGVudCA8LSB0YWJsZShzc19Cb3JfMjAyMyRTVENBVF9QcmVkaWN0aW9uLCBzc19Cb3JfMjAyMyRvcmlnLmlkZW50KQp0YWJsZV9hbm5vdGF0aW9uX2NsdXN0ZXIgPC0gdGFibGUoc3NfQm9yXzIwMjMkU1RDQVRfUHJlZGljdGlvbiwgc3NfQm9yXzIwMjMkaW50ZWdyYXRlZF9zbm5fcmVzLjAuNSkKCmxpYnJhcnkocGhlYXRtYXApCgojIFByb3BvcnRpb24gdGFibGUgKGNvbG9yIHNjYWxlKQpwcm9wX3RhYmxlIDwtIHByb3AudGFibGUodGFibGVfYW5ub3RhdGlvbl9jbHVzdGVyLCBtYXJnaW4gPSAxKQoKIyBIZWF0bWFwIHdpdGggcmF3IGNvdW50cyBhcyBsYWJlbHMKcGhlYXRtYXAocHJvcF90YWJsZSwKICAgICAgICAgZGlzcGxheV9udW1iZXJzID0gdGFibGVfYW5ub3RhdGlvbl9jbHVzdGVyLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBUUlVFLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygibGlnaHR5ZWxsb3ciLCAieWVsbG93IiwgImxpZ2h0Z3JlZW4iLCAid2hpdGUiKSkoMTAwKSwKICAgICAgICAgZm9udHNpemVfcm93ID0gMTAsCiAgICAgICAgIGZvbnRzaXplX2NvbCA9IDEwLAogICAgICAgICBtYWluID0gIlNUQ0FUIFByZWRpY3Rpb24gdnMgQ2x1c3RlcnMiKQoKCmxpYnJhcnkocGhlYXRtYXApCgojIFByb3BvcnRpb24gdGFibGUgKGNvbG9yIHNjYWxlKQpwcm9wX3RhYmxlIDwtIHByb3AudGFibGUodGFibGVfYW5ub3RhdGlvbl9vcmlnX2lkZW50LCBtYXJnaW4gPSAxKQoKIyBIZWF0bWFwIHdpdGggcmF3IGNvdW50cyBhcyBsYWJlbHMKcGhlYXRtYXAocHJvcF90YWJsZSwKICAgICAgICAgZGlzcGxheV9udW1iZXJzID0gdGFibGVfYW5ub3RhdGlvbl9vcmlnX2lkZW50LAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBUUlVFLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygibGlnaHR5ZWxsb3ciLCAieWVsbG93IiwgImxpZ2h0Z3JlZW4iLCAid2hpdGUiKSkoMTAwKSwKICAgICAgICAgZm9udHNpemVfcm93ID0gMTAsCiAgICAgICAgIGZvbnRzaXplX2NvbCA9IDEwLAogICAgICAgICBtYWluID0gIlNUQ0FUIFByZWRpY3Rpb24gdnMgUGF0aWVudHMiKQoKYGBg