MOFSR does not convert a whole-slide image into features. Its input
boundary is a numeric matrix with features in rows and
samples in columns. Raw WSI files such as
.svs, .ndpi, .scn, and
.mrxs must therefore pass through an image-analysis or
representation-learning workflow first.
The recommended general-purpose route is QuPath. QuPath uses OpenSlide and Bio-Formats to read many pyramidal WSI formats, but support varies between scanner variants. In particular, some MRXS variants are only partially supported. Validate pixel size, channels, focal planes, and the full-resolution image before analysis; opening a thumbnail is not sufficient. See the official QuPath format notes and OpenSlide format list.
This route is suitable for interpretable cell, tissue, intensity, morphology, texture, and spatial summary features.
The following base-R helper converts a QuPath TSV export into a MOFSR
pathology matrix. Replace sample_from_image when slide
filenames are not the biological sample identifiers.
qupath_to_mofsr <- function(
path,
sample_column = "Image",
sample_from_image = function(x) tools::file_path_sans_ext(basename(x))) {
measurements <- utils::read.delim(
path,
check.names = FALSE,
stringsAsFactors = FALSE
)
if (!sample_column %in% names(measurements)) {
stop(sprintf("Missing sample column: %s", sample_column))
}
sample_ids <- sample_from_image(measurements[[sample_column]])
if (anyNA(sample_ids) || any(sample_ids == "")) {
stop("Every exported row must map to a non-empty sample ID.")
}
numeric_columns <- vapply(measurements, is.numeric, logical(1))
feature_table <- measurements[, numeric_columns, drop = FALSE]
if (ncol(feature_table) == 0L) {
stop("No numeric QuPath measurements were found.")
}
aggregated <- stats::aggregate(
feature_table,
by = list(Sample = sample_ids),
FUN = function(x) stats::median(x, na.rm = TRUE)
)
pathology <- t(as.matrix(aggregated[, -1, drop = FALSE]))
colnames(pathology) <- aggregated$Sample
complete_features <- apply(pathology, 1, function(x) all(is.finite(x)))
pathology <- pathology[complete_features, , drop = FALSE]
if (nrow(pathology) == 0L) {
stop("No complete pathology features remain after aggregation.")
}
pathology
}
pathology_matrix <- qupath_to_mofsr("qupath_measurements.tsv")Median aggregation is a robust starting point, not a universal biological choice. Depending on the study, pre-specify additional summaries such as cell density, class proportions, quantiles, or spatial statistics. Feature definitions, magnification, stain normalization, segmentation model, and aggregation rules should remain fixed across the cohort.
For deep-learning representations, use QuPath’s TileExporter to export calibrated, tissue-filtered tiles at a fixed physical resolution. Run the tiles through a pathology encoder, then aggregate tile embeddings to one vector per slide or patient. Save the result as the same features × samples matrix used above.
Record at least the encoder name and version, weights, license, magnification or microns-per-pixel, tile size, tissue/tumor filter, normalization, aggregation method, and random seed. Do not mix embeddings from incompatible encoders or preprocessing pipelines in one modality matrix.
Every modality must use the same unique biological sample IDs. MOFSR
aligns matrices by column name in RunMOFS() and rejects
missing or non-finite values.
common_samples <- Reduce(
intersect,
list(
colnames(rna_matrix),
colnames(protein_matrix),
colnames(pathology_matrix)
)
)
if (length(common_samples) < 3L) {
stop("Too few matched samples remain for clustering.")
}
data_list <- list(
RNA = rna_matrix[, common_samples, drop = FALSE],
Protein = protein_matrix[, common_samples, drop = FALSE],
Pathology = pathology_matrix[, common_samples, drop = FALSE]
)
stopifnot(
check_sample_alignment(data_list, strict = TRUE),
all(vapply(data_list, function(x) all(is.finite(x)), logical(1)))
)
result <- RunMOFS(
data = data_list,
methods = c("CPCA", "CIMLR"),
max.clusters = 6,
optimal.clusters = 3
)