Skip to content

Add function for feature prioritization - #17

Open
AbhirupaGhosh wants to merge 19 commits into
mainfrom
feature_rescoring_simple
Open

Add function for feature prioritization#17
AbhirupaGhosh wants to merge 19 commits into
mainfrom
feature_rescoring_simple

Conversation

@AbhirupaGhosh

Copy link
Copy Markdown
Contributor

This function computes feature improvement by reading and processing feature data from parquet files, applying various transformations and calculations to derive insights on feature importance and contributions.

Description

What kind of change(s) are included?

  • Feature (adds or updates new capabilities)
  • Bug fix (fixes an issue).
  • Enhancement (adds functionality).
  • Breaking change (these changes would cause existing functionality to not work as expected).

Checklist

Please ensure that all boxes are checked before indicating that this pull request is ready for review.

  • I have read and followed the CONTRIBUTING.md guidelines.
  • I have searched for existing content to ensure this is not a duplicate.
  • I have performed a self-review of these additions (including spelling, grammar, and related).
  • I have added comments to my code to help provide understanding.
  • I have added a test which covers the code changes found within this PR.
  • I have deleted all non-relevant text in this pull request template.
  • Reviewer assignment: Tag a relevant team member to review and approve the changes.

This function computes feature improvement by reading and processing feature data from parquet files, applying various transformations and calculations to derive insights on feature importance and contributions.
AbhirupaGhosh and others added 3 commits March 13, 2026 19:57
Added computeFeatureScore function to calculate feature scores based on various metrics and cluster information.
@AbhirupaGhosh
AbhirupaGhosh marked this pull request as draft April 10, 2026 21:25
@AbhirupaGhosh AbhirupaGhosh changed the title Add computeFeatureImprovement function for feature analysis Add function for feature prioritization Jul 24, 2026
@AbhirupaGhosh
AbhirupaGhosh marked this pull request as ready for review July 24, 2026 18:57
@AbhirupaGhosh

AbhirupaGhosh commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

This script introduces a workflow for prioritizing molecular features associated with resistance across drugs and drug classes.

This workflow identifies features that are consistently important across repeated model fits (seeds), making the results less noisy and more interpretable.

The workflow:

  • scores and summarizes feature importance across seeds,
  • prioritizes stable, high-ranking features for each drug or drug class,
  • aggregates prioritized features into protein clusters,
  • identifies shared and unique clusters across drugs/classes, and
  • builds feature–cluster networks for visualization and downstream interpretation.

The run would be

all_top_features_parquet <- "inst/extdata/all_top_features.parquet"
scored_features <- scoreFeaturesWithinSeed(all_top_features_parquet)
feature_summary <- summariseFeaturesAcrossSeeds(scored_features)

top_features <- topFeaturesPerDrugOrClass(feature_summary,
                                        rank_score_quantile = 0.95,
                                        threshold_sd_rank = 1)

cluster_feature_parquet <- "inst/extdata/cluster_feature.parquet"
top_clusters <- summariseClusters(top_features, cluster_feature_parquet)
protein_names_parquet <- "inst/extdata/protein_names.parquet"

feature_network <- buildFeatureNetwork(top_features, top_clusters, 
cluster_feature_parquet, protein_names_parquet)

plotFeatureNetworkD3(feature_network)

@AbhirupaGhosh
AbhirupaGhosh requested review from amcim and eboyer221 July 24, 2026 18:58
@jananiravi

jananiravi commented Jul 28, 2026

Copy link
Copy Markdown
Member

Minor suggestions for now or later:

  • in example runs, changed = to <-
  • species = full_name, spp = 3_letter_name?
  • there's a mix of small and caps (which I'm editing)
image
  • Sign gets fixed in the next one feature_summary but Variable is still in caps.
image

@AbhirupaGhosh, can you clarify the following:

  • Variable = feature_name?
  • what's 'signal network'?

Standardize feature column names across the codebase (Variable -> variable, Importance -> importance, Sign -> sign) and update all uses (mutate, group_by, joins, transmute, distinct). Rename summariseFeatureAcrossSeeds -> summariseFeaturesAcrossSeeds, buildSignalNetwork -> buildFeatureNetwork, and plotSignalNetworkD3 -> plotFeatureNetworkD3. Adjust join keys (variable == feature), grouping and summarise calls, examples, and roxygen docs to match the new names. Includes minor formatting and whitespace cleanups. These changes unify naming and clarify the feature/cluster network API.

@jananiravi jananiravi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few minor fixes inline + some comments for clarification.

All other scripts are named do_something. so 'prioritize_features' or 'rescore_features' or something like that?

Note from Slack
In summariseClusters() and buildFeatureNetwork(), when a top-ranked domain/COG feature gets joined to cluster_feature, it fans out into every cluster carrying that domain — including clusters with no real connection to the resistance signal, just shared domain architecture.

Concretely:

  • A single promiscuous COG (fan-out up to 189) inflates frequency/n_variables/cluster_mean_rank_score for up to 189 different clusters in summariseClusters(), none of which may be the true driver.
  • In buildFeatureNetwork(), feature_cluster_edges will make that one feature a hub node connected to up to ~189 cluster nodes, which will dominate/clutter plotFeatureNetworkD3()'s output and overstate specificity.

So, is this fan-out intentional (i.e., "show all plausible clusters a domain could implicate"), or should there be a cap/weighting (e.g., down-weight edges by 1/n_clusters, or exclude domains above some fan-out threshold) so ubiquitous COGs/Pfams don't swamp the network? This is a modeling decision, not a coding bug.

Comment thread R/feature_rescoring.R Outdated
protein_scale_realization <- ranked_features |>
dplyr::filter(!is.na(cluster), shuffled == FALSE) |>
top_clusters <- top_features |>
dplyr::left_join(cluster_feature, by = dplyr::join_by(Variable == feature)) |>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning message:

In dplyr::left_join(top_features, cluster_feature, by = dplyr::join_by(variable ==  :
  Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 54683 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this warning.

Comment thread R/feature_rescoring.R

cluster_table <- top_clusters |>
dplyr::mutate(model_id = make_model_id(drug_label, drug_or_class)) |>
dplyr::left_join(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning message:

In dplyr::left_join(feature_table, cluster_feature, by = dplyr::join_by(variable ==  :
  Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 151601 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this warning.

Comment thread R/feature_rescoring.R
dplyr::ungroup() |>
dplyr::group_by(drug_label, drug_or_class) |>
dplyr::filter(
# n_seeds == max(n_seeds),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentionally left out? what's the consistency across seeds you're looking for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I am using the threshold_sd_rank as a proxy for consistency.
max(n_seeds) is too stringent

Comment thread R/feature_rescoring.R
) |>
dplyr::mutate(
contribution = Importance / sum(Importance, na.rm = TRUE),
contribution = importance / sum(importance, na.rm = TRUE),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cumulative impact first then rank?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ranking based on the contribution or importance will give the same. I kept contribution if we want to care about contribution %tile.

Comment thread R/feature_rescoring.R
mean_rank_score = mean(rank_score, na.rm = TRUE),
best_rank = min(rank, na.rm = TRUE),
rank_consistent = dplyr::n_distinct(rank) == 1,
rank_sd = sd(rank, na.rm = TRUE),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rank_score, mean_rank_score are normalized but not this one? are these comparable?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rank_score_sd instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used rank_sd because it is easy to dry run with whole numbers.

Comment thread R/feature_rescoring.R
contribution = Importance / sum(Importance, na.rm = TRUE),
contribution = importance / sum(importance, na.rm = TRUE),
rank = dplyr::dense_rank(dplyr::desc(contribution)),
n_features = dplyr::n(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this to get totals by feature scale? If so, group_by then n() within summarize?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants