3. Visualizations
# Manually enter your data as a named vector
prediction_counts <- c(
"None T" = 9622,
"CD4 Tcm" = 4935,
"CD4 Tn" = 3927,
"Tgd" = 1531,
"CD8 Tn" = 1381,
"CD8 Temra" = 1264,
"CD4 Trm cell-death" = 1134,
"CD4 Tex" = 906,
"CD8 Tc" = 743,
"CD4 Tisg" = 702,
"CD8 Tem" = 669,
"CD4 Trm" = 615,
"CD8 Trm naive-like" = 476,
"MAIT" = 385,
"CD4 Tc" = 361,
"CD4 Tem" = 272,
"CD4 Treg" = 228,
"CD4 Tn adhesion" = 163,
"CD8 Trm" = 142,
"CD4 Treg naive-like" = 142,
"CD4 Th17" = 137,
"CD8 proliferation" = 134,
"CD4 proliferation" = 108,
"CD4 Temra" = 62,
"CD4 activated" = 40,
"Double Negative" = 32,
"CD4 Tfh" = 31,
"CD8 Tstr" = 26,
"CD8 Tcm" = 19,
"other CD8 T" = 9,
"CD8 senescence" = 4,
"CD8 Tex" = 2
)
# Convert to data frame
df <- data.frame(
Prediction = factor(names(prediction_counts), levels = names(sort(prediction_counts))),
Count = prediction_counts
)
# Load ggplot2
library(ggplot2)
# Horizontal barplot (flipped coordinates)
ggplot(df, aes(x = Count, y = Prediction)) +
geom_bar(stat = "identity", fill = "steelblue") +
theme_minimal() +
labs(
title = "Cell Type Predictions from STCAT",
x = "Number of Cells",
y = "Predicted Cell Type"
) +
theme(axis.text.y = element_text(size = 8))

NA
NA
Dimplot
DimPlot(ss_herrera, group.by = "tissue", label = TRUE, repel = T,label.box = T)

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

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

DimPlot(ss_herrera, group.by = "STCAT_Prediction", label = F, repel = F, label.box = T)

Distributions
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")

NA
NA
library(ggplot2)
library(ggridges)
ggplot(ss_herrera@meta.data, aes(x = STCAT_Uncertainty, y = STCAT_Prediction, fill = STCAT_Prediction)) +
geom_density_ridges(alpha = 0.7, scale = 2, rel_min_height = 0.01) +
theme_minimal() +
labs(
x = "Cluster Entropy Score",
y = "Predicted T Cell Type (STCAT)",
title = "Density Ridgeline of Cluster Entropy by T Cell Type"
) +
theme(
legend.position = "none",
axis.text.y = element_text(size = 9)
)

NA
NA
LS0tCnRpdGxlOiAiU1RDQVRfSGVycmVyYV9WaXN1YWxpemF0aW9uIgphdXRob3I6IE5hc2lyIE1haG1vb2QgQWJiYXNpCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCmxpYnJhcnkoU2V1cmF0RGF0YSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoQXppbXV0aCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkodGlueXRleCkKCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGRpdHRvU2VxKQpsaWJyYXJ5KGdncmVwZWwpCiNsaWJyYXJ5KGdndHJlZSkKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShwbG90bHkpICAjIDNEIHBsb3QKbGlicmFyeShTZXVyYXQpICAjIElkZW50cygpCmxpYnJhcnkoU2V1cmF0RGlzaykgICMgU2F2ZUg1U2V1cmF0KCkKbGlicmFyeSh0aWJibGUpICAjIHJvd25uYW1lc190b19jb2x1bW4KbGlicmFyeShoYXJtb255KSAjIFJ1bkhhcm1vbnkoKQojb3B0aW9ucyhtYy5jb3JlcyA9IGRldGVjdENvcmVzKCkgLSAxKQoKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZGJwbHlyKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeSh0aW55dGV4KQojQXppbXV0aCBBbm5vdGF0aW9uIGxpYnJhcmllcwpsaWJyYXJ5KEF6aW11dGgpCiNQcm9qZWNUaWxzIEFubm90YXRpb24gbGlicmFyaWVzCmxpYnJhcnkoU1RBQ0FTKQpsaWJyYXJ5KFByb2plY1RJTHMpCiNzaW5nbGVSIEFubm90YXRpb24gbGlicmFyaWVzCgpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQoKYGBgCgoKIyAyLiBMb2FkIERhdGEgaW50byBTZXVyYXQKYGBge3IgbG9hZF9zZXVyYXR9Cgpzc19oZXJyZXJhIDwtIHJlYWRSRFMoIlNUQ0FUL0FsbF9QYXRpZW50c19JbnRlZ3JhdGVkX3NzX0hlcnJlcmFfYW5ub3RhdGVkLlJEUyIpCgpgYGAKCiMgMy4gVmlzdWFsaXphdGlvbnMgCmBgYHtyIFNUQ0FUX3ZpcywgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KCiMgTWFudWFsbHkgZW50ZXIgeW91ciBkYXRhIGFzIGEgbmFtZWQgdmVjdG9yCnByZWRpY3Rpb25fY291bnRzIDwtIGMoCiAgIk5vbmUgVCIgPSA5NjIyLAogICJDRDQgVGNtIiA9IDQ5MzUsCiAgIkNENCBUbiIgPSAzOTI3LAogICJUZ2QiID0gMTUzMSwKICAiQ0Q4IFRuIiA9IDEzODEsCiAgIkNEOCBUZW1yYSIgPSAxMjY0LAogICJDRDQgVHJtIGNlbGwtZGVhdGgiID0gMTEzNCwKICAiQ0Q0IFRleCIgPSA5MDYsCiAgIkNEOCBUYyIgPSA3NDMsCiAgIkNENCBUaXNnIiA9IDcwMiwKICAiQ0Q4IFRlbSIgPSA2NjksCiAgIkNENCBUcm0iID0gNjE1LAogICJDRDggVHJtIG5haXZlLWxpa2UiID0gNDc2LAogICJNQUlUIiA9IDM4NSwKICAiQ0Q0IFRjIiA9IDM2MSwKICAiQ0Q0IFRlbSIgPSAyNzIsCiAgIkNENCBUcmVnIiA9IDIyOCwKICAiQ0Q0IFRuIGFkaGVzaW9uIiA9IDE2MywKICAiQ0Q4IFRybSIgPSAxNDIsCiAgIkNENCBUcmVnIG5haXZlLWxpa2UiID0gMTQyLAogICJDRDQgVGgxNyIgPSAxMzcsCiAgIkNEOCBwcm9saWZlcmF0aW9uIiA9IDEzNCwKICAiQ0Q0IHByb2xpZmVyYXRpb24iID0gMTA4LAogICJDRDQgVGVtcmEiID0gNjIsCiAgIkNENCBhY3RpdmF0ZWQiID0gNDAsCiAgIkRvdWJsZSBOZWdhdGl2ZSIgPSAzMiwKICAiQ0Q0IFRmaCIgPSAzMSwKICAiQ0Q4IFRzdHIiID0gMjYsCiAgIkNEOCBUY20iID0gMTksCiAgIm90aGVyIENEOCBUIiA9IDksCiAgIkNEOCBzZW5lc2NlbmNlIiA9IDQsCiAgIkNEOCBUZXgiID0gMgopCgojIENvbnZlcnQgdG8gZGF0YSBmcmFtZQpkZiA8LSBkYXRhLmZyYW1lKAogIFByZWRpY3Rpb24gPSBmYWN0b3IobmFtZXMocHJlZGljdGlvbl9jb3VudHMpLCBsZXZlbHMgPSBuYW1lcyhzb3J0KHByZWRpY3Rpb25fY291bnRzKSkpLAogIENvdW50ID0gcHJlZGljdGlvbl9jb3VudHMKKQoKIyBMb2FkIGdncGxvdDIKbGlicmFyeShnZ3Bsb3QyKQoKIyBIb3Jpem9udGFsIGJhcnBsb3QgKGZsaXBwZWQgY29vcmRpbmF0ZXMpCmdncGxvdChkZiwgYWVzKHggPSBDb3VudCwgeSA9IFByZWRpY3Rpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkNlbGwgVHlwZSBQcmVkaWN0aW9ucyBmcm9tIFNUQ0FUIiwKICAgIHggPSAiTnVtYmVyIG9mIENlbGxzIiwKICAgIHkgPSAiUHJlZGljdGVkIENlbGwgVHlwZSIKICApICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCgoKYGBgCgoKIyMgRGltcGxvdApgYGB7ciBTVENBVF92aXMyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQoKRGltUGxvdChzc19oZXJyZXJhLCBncm91cC5ieSA9ICJ0aXNzdWUiLCBsYWJlbCA9IFRSVUUsIHJlcGVsID0gVCxsYWJlbC5ib3ggPSBUKQpEaW1QbG90KHNzX2hlcnJlcmEsIGdyb3VwLmJ5ID0gInNhbXBsZV9pZCIsIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBULGxhYmVsLmJveCA9IFQpCkRpbVBsb3Qoc3NfaGVycmVyYSwgZ3JvdXAuYnkgPSAiUk5BX3Nubl9yZXMuMC41IiwgbGFiZWwgPSBUUlVFLCByZXBlbCA9IFQsIGxhYmVsLmJveCA9IFQpCkRpbVBsb3Qoc3NfaGVycmVyYSwgZ3JvdXAuYnkgPSAiU1RDQVRfUHJlZGljdGlvbiIsIGxhYmVsID0gRiwgcmVwZWwgPSBGLCBsYWJlbC5ib3ggPSBUKQpgYGAKCiMjIERpc3RyaWJ1dGlvbnMKYGBge3IgU1RDQVRfdmlzMywgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE0fQoKdGFibGUoc3NfaGVycmVyYSRTVENBVF9QcmVkaWN0aW9uKSAgIyBzaG91bGQgc2hvdyBhbGwgcHJlZGljdGVkIGxhYmVscwp0YWJsZShzc19oZXJyZXJhJHNhbXBsZV9pZCkgICAgICAgICAjIHNob3VsZCBzaG93IEwx4oCTTDcKdGFibGUoc3NfaGVycmVyYSR0aXNzdWUpIAoKIyBDb250aW5nZW5jeSB0YWJsZSAKdGFibGVfYW5ub3RhdGlvbl90aXNzdWUgPC0gdGFibGUoc3NfaGVycmVyYSRTVENBVF9QcmVkaWN0aW9uLCBzc19oZXJyZXJhJHRpc3N1ZSkKdGFibGVfYW5ub3RhdGlvbl9zYW1wbGVfaWQgPC0gdGFibGUoc3NfaGVycmVyYSRTVENBVF9QcmVkaWN0aW9uLCBzc19oZXJyZXJhJHNhbXBsZV9pZCkKdGFibGVfYW5ub3RhdGlvbl9jbHVzdGVyIDwtIHRhYmxlKHNzX2hlcnJlcmEkU1RDQVRfUHJlZGljdGlvbiwgc3NfaGVycmVyYSRSTkFfc25uX3Jlcy4wLjUpCgpsaWJyYXJ5KHBoZWF0bWFwKQoKIyBQcm9wb3J0aW9uIHRhYmxlIChjb2xvciBzY2FsZSkKcHJvcF90YWJsZSA8LSBwcm9wLnRhYmxlKHRhYmxlX2Fubm90YXRpb25fY2x1c3RlciwgbWFyZ2luID0gMSkKCiMgSGVhdG1hcCB3aXRoIHJhdyBjb3VudHMgYXMgbGFiZWxzCnBoZWF0bWFwKHByb3BfdGFibGUsCiAgICAgICAgIGRpc3BsYXlfbnVtYmVycyA9IHRhYmxlX2Fubm90YXRpb25fY2x1c3RlciwKICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gVFJVRSwKICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoImxpZ2h0eWVsbG93IiwgInllbGxvdyIsICJsaWdodGdyZWVuIiwgIndoaXRlIikpKDEwMCksCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDEwLAogICAgICAgICBmb250c2l6ZV9jb2wgPSAxMCwKICAgICAgICAgbWFpbiA9ICJTVENBVCBQcmVkaWN0aW9uIHZzIENsdXN0ZXJzIikKCgpsaWJyYXJ5KHBoZWF0bWFwKQoKIyBQcm9wb3J0aW9uIHRhYmxlIChjb2xvciBzY2FsZSkKcHJvcF90YWJsZSA8LSBwcm9wLnRhYmxlKHRhYmxlX2Fubm90YXRpb25fc2FtcGxlX2lkLCBtYXJnaW4gPSAxKQoKIyBIZWF0bWFwIHdpdGggcmF3IGNvdW50cyBhcyBsYWJlbHMKcGhlYXRtYXAocHJvcF90YWJsZSwKICAgICAgICAgZGlzcGxheV9udW1iZXJzID0gdGFibGVfYW5ub3RhdGlvbl9zYW1wbGVfaWQsCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgIGNsdXN0ZXJfY29scyA9IFRSVUUsCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJsaWdodHllbGxvdyIsICJ5ZWxsb3ciLCAibGlnaHRncmVlbiIsICJ3aGl0ZSIpKSgxMDApLAogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMCwKICAgICAgICAgZm9udHNpemVfY29sID0gMTAsCiAgICAgICAgIG1haW4gPSAiU1RDQVQgUHJlZGljdGlvbiB2cyBQYXRpZW50cyIpCgoKIyBQcm9wb3J0aW9uIHRhYmxlIChjb2xvciBzY2FsZSkKcHJvcF90YWJsZSA8LSBwcm9wLnRhYmxlKHRhYmxlX2Fubm90YXRpb25fdGlzc3VlLCBtYXJnaW4gPSAxKQoKIyBIZWF0bWFwIHdpdGggcmF3IGNvdW50cyBhcyBsYWJlbHMKcGhlYXRtYXAocHJvcF90YWJsZSwKICAgICAgICAgZGlzcGxheV9udW1iZXJzID0gdGFibGVfYW5ub3RhdGlvbl90aXNzdWUsCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgIGNsdXN0ZXJfY29scyA9IFRSVUUsCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJsaWdodHllbGxvdyIsICJ5ZWxsb3ciLCAibGlnaHRncmVlbiIsICJ3aGl0ZSIpKSgxMDApLAogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMCwKICAgICAgICAgZm9udHNpemVfY29sID0gMTAsCiAgICAgICAgIG1haW4gPSAiU1RDQVQgUHJlZGljdGlvbiB2cyB0aXNzdWUiKQoKCmBgYAoKCgojIyAKYGBge3IgLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTR9CgpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyaWRnZXMpCgpnZ3Bsb3Qoc3NfaGVycmVyYUBtZXRhLmRhdGEsIGFlcyh4ID0gU1RDQVRfVW5jZXJ0YWludHksIHkgPSBTVENBVF9QcmVkaWN0aW9uLCBmaWxsID0gU1RDQVRfUHJlZGljdGlvbikpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC43LCBzY2FsZSA9IDIsIHJlbF9taW5faGVpZ2h0ID0gMC4wMSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHggPSAiQ2x1c3RlciBFbnRyb3B5IFNjb3JlIiwKICAgIHkgPSAiUHJlZGljdGVkIFQgQ2VsbCBUeXBlIChTVENBVCkiLAogICAgdGl0bGUgPSAiRGVuc2l0eSBSaWRnZWxpbmUgb2YgQ2x1c3RlciBFbnRyb3B5IGJ5IFQgQ2VsbCBUeXBlIgogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpCiAgKQoKCmBgYAoKCgoKCg==