Skip to contents

The clusters and overlap analyses in the other articles are summaries: they reduce a transmission story to labels and fractions. To actually read a cluster — to see who was where, when each patient first turned positive, and how the genomes relate — it helps to look at all of that at once. The trace-phylogeny figure, drawn by hospitraceRVisualize::plot_trace_phylo_tree(), places the phylogenetic tree of a cluster’s isolates beside each patient’s movement through the facility, with surveillance cultures overlaid. It is the headline figure of the toolkit, and this article shows how to build it.

We reuse the threshold-free clustering from the introductory article, and the per-isolate lookup and patient categorization from the overlap analysis article, since the figure is driven by both.

library(hospitraceR)
library(hospitraceRVisualize)
library(ape)

# An example dataset of isolates and their patient epidemiological metadata.
extdata_dir <- file.path("..", "..", "inst", "extdata")
load(file.path(extdata_dir, "example.RData"))
dna_aln <- read.dna(file.path(extdata_dir, "example.fasta"), format = "fasta")

# Keep isolates whose patients appear in the location traces, then the variable sites
dna_aln <- dna_aln[dna_pt_labels[labels(dna_aln)] %in% rownames(facility_trace), ]
dna_var <- dna_aln[, apply(dna_aln, 2, function(x) sum(x == x[1]) < nrow(dna_aln))]

snp_dist <- get_snp_dist_matrix(dna_var)
tree <- get_phylo_tree(dna_var, snp_dist, "pars")
#> Final p-score 2673 after  20 nni operations

clusters <- get_tn_clusters_sv_index(
    dna_var, snp_dist, adm_seqs, adm_pos_pt_seqs, dna_pt_labels, dates, tree
)

Preparing the inputs

plot_trace_phylo_tree() needs three things from hospitraceR: the phylogenetic tree, an isolate lookup from get_isolate_lookup(), and the patient categorization from cluster_patient_categorization() (which colors each tip label by the patient’s inferred role). The remaining inputs — a patient-by-date trace matrix and the surveillance data frame — come straight from the dataset. We use floor_trace here so that different floors show up as different colors.

isolate_lookup <- get_isolate_lookup(
    clusters, dna_var, dna_pt_labels, adm_seqs, dates, surv_df
)
patient_cats <- cluster_patient_categorization(isolate_lookup, surv_df)

A facility-wide figure with every isolate would be unreadable, so the figure is drawn one cluster at a time via the cluster_filter argument. To pick an illustrative cluster, we categorize each cluster’s overlap (see the overlap analysis article) and take the largest patient-to-patient cluster — one where the location traces explain every conversion:

iso_overlap <- isolate_isolate_overlap(isolate_lookup, facility_trace)
cluster_overlap <- cluster_isolate_overlap(isolate_lookup, iso_overlap)
overlap_cats <- categorize_cluster_overlap(isolate_lookup, cluster_overlap, surv_df)

p2p_clusters <- as.numeric(names(overlap_cats)[overlap_cats == "patient-to-patient"])
cluster_sizes <- vapply(
    p2p_clusters,
    function(cl) length(unique(isolate_lookup$patient_id[isolate_lookup$cluster == cl])),
    integer(1)
)
focus_cluster <- p2p_clusters[which.max(cluster_sizes)]
focus_cluster
#> [1] 5

Drawing the figure

With the inputs assembled, the figure is a single call. The tree is shown on the left, with one representative isolate per patient; the colored bars are each patient’s location over time; and the dots mark surveillance cultures (negative, positive within the cluster, positive elsewhere). Tip labels are colored by the patient’s role from patient_cats.

plot_trace_phylo_tree(
    tree = tree,
    isolate_lookup = isolate_lookup,
    trace_data = floor_trace,
    surv_df = surv_df,
    cluster_filter = focus_cluster,
    clust_patient_categories = patient_cats
)

References