Skip to content

Feature rescoring module and outputting merged results and model fits - #53

Draft
amcim wants to merge 23 commits into
amrml-rewritefrom
feature-rescoring
Draft

amcim wants to merge 23 commits into
amrml-rewritefrom
feature-rescoring

Conversation

@amcim

@amcim amcim commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Export, feature rescoring, and recreating old amRml's results

This PR adds feature rescoring and recreates three outputs old amRml wrote that we did not:

  • exportMergedResults() writes our results in old amRml's merged format, which feature rescoring reads. This is a temporary solution, it goes once our column names match old amRml's or the feature rescoring and amRviz columns update.
  • Feature rescoring is added as R/feature-rescoring.R, lifted from PR Add function for feature prioritization #17 with one bug fix (below). rescoreFeatures() runs it on a fit.
  • Run records: a species column in results/, a run.json with the run's settings, and saved model fits.

Line references are to the original amRml. The feature rescoring code is from PR #17.

Export (R/export.R)

Feature rescoring reads old amRml's merged tables (all_perf.parquet, all_top_features.parquet), which mergeMLresults() built from per-model files. Currently we write one table per output instead, so the export rewrites our results in the merged format and feature rescoring runs unchanged. Once the open renames are done (unit/target/encoding, phenotype, labels), feature rescoring can read results/ directly and the export goes.

runModelingWorkflow() calls exportMergedResults() (The temporary component) at the end of every run. It writes to <run>/merged/, one file per mode present, with old amRml's file names, columns and column order:

mode performance top features
vanilla all_perf.parquet all_top_features.parquet
stratified_year, stratified_country year_perf.parquet, country_perf.parquet year_top_features.parquet, country_top_features.parquet
cross_drug cross_drug_perf.parquet none, as in old amRml
cross_year, cross_country cross_year_perf.parquet, cross_country_perf.parquet none, as in old amRml
loto, logo LOO_year_perf.parquet, LOO_country_perf.parquet LOO_year_top_features.parquet, LOO_country_top_features.parquet
  • Column names: old amRml's, mapped from ours: drug_label = unit, drug_or_class = target, feature_subtype = encoding, strat_value/drug = train_group, strat_value_test/leaveout_strat_value/test_drug = test_group, num_obs_test_data = n_test.
  • Metadata comes from the tasks, not file names. Every file has a logical shuffled, and every top-features file an integer seed.
  • Left out: filename; train_prop, val_prop and the prop_vi bounds (they return with the three-way split and iterative feature elimination); our task_id and auprc.

Problems I noticed in mergeMLresults() that rewrite solves through the design:

old amRml here
File-name patterns accepted only lowercase feature types and unhyphenated drug codes, so AMRFinder, COG, Pfam, DefenseCas or AMX-CLA rows got NA metadata (R/merge_ml_results.R, lines 375, 407, 472) metadata comes from the tasks
shuffled_ files broke the stratified merge (lines 336-345) and were ignored by the cross and leave-one-out merges shuffled is a column now
Stratified top features had a character seed; leave-one-out top features had none an integer seed in every top-features file
An empty top-features file (a model with no non-zero coefficients) failed the whole merge (lines 135-200) built from one importance table

Feature rescoring (R/feature-rescoring.R, R/rescore.R)

R/feature-rescoring.R is PR #17's lines 1-387 and 702-1414, with one code change, a bug fix:

PR #17 change why
lines 989-1004 (models$summary) minimum_MCC_diff and maximum_MCC_diff named, with NA_real_ instead of NULL it crashed on S. flexneri, where AZM's shuffled MCCs are all undefined, and the unnamed blocks lost the minimum. A test fails on the original code
  • Not ported yet: lines 388-701, the network and plot (buildFeatureNetwork(), plotFeatureNetworkD3())
  • Exports: topFeaturesPerDrugOrClass() and runFeatureDyadDiscovery(), as in PR Add function for feature prioritization #17. Its other two exports are the network functions above.

rescoreFeatures(fit, ...) checks that the fit has fitted vanilla models with real and shuffled labels, and that the ORB has dyad_feature.parquet. It exports the merged files if they are missing, then runs runFeatureDyadDiscovery(); its thresholds pass through .... It is a separate file for now so the feature rescoring code changes as little as possible while this PR shows the two connect; it will move into R/feature-rescoring.R once feature rescoring reads our results directly.

Recreating old amRml's results

old amRml here
Species a token of every result file name (R/merge_ml_results.R, lines 30-115) a species column (the ORB's dataset_id, e.g. Sfl) in every results/ table
Run settings ml_parameters.json: mode, n_fold, split, seed, timestamp (R/generate_matrices_ml.R, lines 1529-1545) run.json: the ORB and manifest, the modes, thresholds, seeds and labels asked for, versions, start and finish times, task counts.
Model fits <name>_model_fit.rds, one per model, named by its matrix file, shuffled_ prefix and seed (R/run_ML.R, lines 845-864) Same models but stored differnetly. results/models/<task_id>.rds, one per fitted task, with the matrix's feature IDs. A task is one matrix x label kind x seed, so each fitted task is one model, the same unit old amRml saved
  • run.json, not ml_parameters.json: old amRml read its file back as configuration (R/prep_ml.R, lines 20-45). Here its only a record
  • **Fits carry their feature IDs: the model is fitted on predictors renamed x1, x2, ..., so each saved fit includes the list of feature IDs in that order, to say which feature each predictor is.

How to use it

# Feature rescoring needs vanilla models with real and shuffled labels, at several seeds.
fit <- runModelingWorkflow("path/to/ORB",
  modes = "vanilla", seeds = c(31, 79, 51), labels = c("real", "shuffled")
)
rescored <- rescoreFeatures(fit)
rescored$features$top
rescored$dyads$top

Verification

  • My merged files match old amRml's row for row for AMP, AZM and TMP with real labels. CIP differs because of the pooling change in the fitting PR, and shuffled rows because of the shuffle seed. Old amRml's own merge crashed on these files, on the empty top-features bug above.
  • Feature rescoring gives identical top features and dyads for AMP, AZM and TMP from old amRml's merged files and from ours.
  • The run records: a fresh fit of the same tasks matches the earlier fit exactly, apart from species, timings and dates. Every fit was saved, a saved fit reproduces its predictions, and the feature rescoring results are unchanged.

Files

file what changed
R/export.R new: exportMergedResults(), temporary stopgap
R/feature-rescoring.R new: feature rescoring, from PR #17
R/rescore.R new: rescoreFeatures(), will eventually go in R/feature-rescoring.R
R/fit-models.R species, save_fits, run.json
R/tasks.R expandTasks() keeps the seeds and labels asked for
R/workflow.R writes merged/; passes save_fits
R/arg-checks.R, R/generate-matrices.R merged/ and run.json are protected outputs
R/globals.R, DESCRIPTION, NAMESPACE, man/ feature rescoring's imports and column names; new exports; help pages

Future work

  • The feature rescoring network and plot.
  • The column renames that retire the export.
  • Parallel running, resuming a run, and safer overwriting.

@amcim amcim added the rewrite These PRs are relevant to the ongoing rewrite and merging will be to amrml-rewrite branch label Oct 8, 2026
@amcim amcim changed the title Feature rescoring Feature rescoring module and outputting merged results and model fits Oct 8, 2026
@amcim

amcim commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Just noting here that this PR should not be merged before PR #17 is finished

Bringing #53 up to par with previous rewrite branch commits before making feature changes.
First pass implementation of MAGIC scoring. Lots of old code needs to be updated and redundancies trimmed out. This is taking the basics of PR #17 and  putting a weird new spin on them, so this will need testing and critique. See the design document for more deets.
Merging this PR in sync with `runner` to update upstream workflow to match. Otherwise, testing rescoring and network construction is using older functionality.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rewrite These PRs are relevant to the ongoing rewrite and merging will be to amrml-rewrite branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants