Compute highly variable genes using Support Vector Regression (SVR). The SVR method uses machine learning to model the mean-variance relationship: 1. Computes log(mean) and log(variance) for each gene 2. Fits SVR model to predict variance from mean 3. Calculates residuals (observed - predicted variance) 4. Standardizes residuals to identify outliers 5. Selects genes with high standardized residua
(adata: &IMAnnData, x: &IMArrayElement, params: HVGParams)
| 621 | /// - `mean_variance_trend`: Predicted variance from SVR model |
| 622 | /// - `highly_variable`: Boolean selection mask |
| 623 | fn compute_svr_hvg(adata: &IMAnnData, x: &IMArrayElement, params: HVGParams) -> anyhow::Result<()> { |
| 624 | let n_obs = adata.n_obs(); |
| 625 | let means: Vec<f64> = x |
| 626 | .sum_whole(&Direction::COLUMN)? |
| 627 | .iter() |
| 628 | .map(|sum: &f64| sum / n_obs as f64) |
| 629 | .collect(); |
| 630 | |
| 631 | let variances: Vec<f64> = x.variance_whole::<u32, f64>(&Direction::COLUMN)?; |
| 632 | |
| 633 | let log_means: Vec<f64> = means.iter().map(|&x| x.ln()).collect(); |
| 634 | let log_variances: Vec<f64> = variances.iter().map(|&x| x.ln()).collect(); |
| 635 | |
| 636 | let (residuals, y_pred) = fit_svr(&log_means, &log_variances)?; |
| 637 | let standardized_results = standardize_log_form_vec(&residuals); |
| 638 | |
| 639 | let mut highly_variable = vec![false; means.len()]; |
| 640 | if let Some(n_top) = params.n_top_genes { |
| 641 | let mut indices: Vec<usize> = (0..standardized_results.len()).collect(); |
| 642 | indices.sort_by(|&a, &b| { |
| 643 | standardized_results[b] |
| 644 | .partial_cmp(&standardized_results[a]) |
| 645 | .unwrap() |
| 646 | }); |
| 647 | for &idx in indices.iter().take(n_top) { |
| 648 | if means[idx] >= params.min_mean && means[idx] <= params.max_mean { |
| 649 | highly_variable[idx] = true; |
| 650 | } |
| 651 | } |
| 652 | } else { |
| 653 | for i in 0..means.len() { |
| 654 | highly_variable[i] = means[i] >= params.min_mean |
| 655 | && means[i] <= params.max_mean |
| 656 | && standardized_results[i] > params.min_dispersion; |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | let mut var_df = adata.var().get_data(); |
| 661 | var_df.with_column(Column::new("means".into(), means))?; |
| 662 | var_df.with_column(Column::new("variances".into(), variances))?; |
| 663 | var_df.with_column(Column::new("residuals".into(), residuals))?; |
| 664 | var_df.with_column(Column::new("highly_variable".into(), highly_variable))?; |
| 665 | var_df.with_column(Column::new( |
| 666 | "residuals_standardized".into(), |
| 667 | standardized_results, |
| 668 | ))?; |
| 669 | var_df.with_column(Column::new("mean_variance_trend".into(), y_pred))?; |
| 670 | |
| 671 | adata.var().set_data(var_df) |
| 672 | } |
no test coverage detected