Add function for feature prioritization - #17
Conversation
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.
Added computeFeatureScore function to calculate feature scores based on various metrics and cluster information.
…ion and introducing wide table functions for global score calculations
…update parameters for clarity
… feature and cluster analysis
|
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:
The run would be |
|
Minor suggestions for now or later:
@AbhirupaGhosh, can you clarify the following:
|
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
left a comment
There was a problem hiding this comment.
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.
| 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)) |> |
There was a problem hiding this comment.
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.
|
|
||
| cluster_table <- top_clusters |> | ||
| dplyr::mutate(model_id = make_model_id(drug_label, drug_or_class)) |> | ||
| dplyr::left_join( |
There was a problem hiding this comment.
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.
| dplyr::ungroup() |> | ||
| dplyr::group_by(drug_label, drug_or_class) |> | ||
| dplyr::filter( | ||
| # n_seeds == max(n_seeds), |
There was a problem hiding this comment.
intentionally left out? what's the consistency across seeds you're looking for?
There was a problem hiding this comment.
yes, I am using the threshold_sd_rank as a proxy for consistency.
max(n_seeds) is too stringent
| ) |> | ||
| dplyr::mutate( | ||
| contribution = Importance / sum(Importance, na.rm = TRUE), | ||
| contribution = importance / sum(importance, na.rm = TRUE), |
There was a problem hiding this comment.
cumulative impact first then rank?
There was a problem hiding this comment.
ranking based on the contribution or importance will give the same. I kept contribution if we want to care about contribution %tile.
| 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), |
There was a problem hiding this comment.
rank_score, mean_rank_score are normalized but not this one? are these comparable?
There was a problem hiding this comment.
I used rank_sd because it is easy to dry run with whole numbers.
| 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(), |
There was a problem hiding this comment.
Is this to get totals by feature scale? If so, group_by then n() within summarize?


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?
Checklist
Please ensure that all boxes are checked before indicating that this pull request is ready for review.