use extendr_api::*;
use crate::core::math::sparse::parse_compressed_sparse_format;
use crate::single_cell::sc_analysis::hdwgcna_meta_cells::MetaCellParams;
use crate::single_cell::sc_analysis::hotspot::HotSpotParams;
use crate::single_cell::sc_analysis::milo_r::MiloRParams;
use crate::single_cell::sc_analysis::scenic::{
ExtraTreesConfig, GradientBoostingConfig, RandomForestConfig, RegressionLearner, ScenicParams,
};
use crate::single_cell::sc_analysis::seacells::SEACellsParams;
use crate::single_cell::sc_analysis::super_cells::SuperCellParams;
use crate::single_cell::sc_analysis::vision::SignatureGenes;
use crate::single_cell::sc_batch_correction::fast_mnn::FastMnnParams;
use crate::single_cell::sc_batch_correction::harmony::HarmonyParams;
use crate::single_cell::sc_data::data_io::MinCellQuality;
use crate::single_cell::sc_data::h5ad_multifile_io::H5adFileTask;
use crate::single_cell::sc_data::sc_synthetic_data::CellTypeConfig;
use crate::single_cell::sc_processing::doublet_detection::BoostParams;
use crate::single_cell::sc_processing::knn::KnnParams;
use crate::single_cell::sc_processing::scrublet::ScrubletParams;
pub fn assignments_to_r_list(assignments: &[Option<usize>], n_cells: usize) -> List {
let r_assignments: Vec<i32> = assignments
.iter()
.map(|&x| match x {
Some(id) => (id + 1) as i32,
None => -1,
})
.collect();
let n_unassigned = assignments.iter().filter(|x| x.is_none()).count();
let actual_k = assignments
.iter()
.filter_map(|&x| x)
.max()
.map(|x| x + 1)
.unwrap_or(0);
let mut metacells = vec![Vec::new(); actual_k];
for (cell_id, &metacell_id) in assignments.iter().enumerate() {
if let Some(id) = metacell_id {
metacells[id].push((cell_id + 1) as i32);
}
}
let unassigned: Vec<i32> = assignments
.iter()
.enumerate()
.filter_map(|(cell_id, &x)| {
if x.is_none() {
Some((cell_id + 1) as i32)
} else {
None
}
})
.collect();
let metacells_list: List = metacells.into_iter().map(Robj::from).collect();
list!(
assignments = r_assignments,
metacells = metacells_list,
unassigned = unassigned,
n_metacells = actual_k,
n_cells = n_cells,
n_unassigned = n_unassigned
)
}
impl CellTypeConfig {
pub fn from_r_list(r_list: List) -> Self {
let map = r_list.into_hashmap();
let marker_genes = map
.get("marker_genes")
.and_then(|v| v.as_integer_vector())
.map(|v| v.iter().map(|x| *x as usize).collect())
.unwrap_or_default();
CellTypeConfig { marker_genes }
}
}
impl MinCellQuality {
pub fn from_r_list(r_list: List) -> Self {
let min_qc = r_list.into_hashmap();
let min_unique_genes = min_qc
.get("min_unique_genes")
.and_then(|v| v.as_integer())
.unwrap_or(100) as usize;
let min_lib_size = min_qc
.get("min_lib_size")
.and_then(|v| v.as_integer())
.unwrap_or(250) as usize;
let min_cells = min_qc
.get("min_cells")
.and_then(|v| v.as_integer())
.unwrap_or(10) as usize;
let target_size = min_qc
.get("target_size")
.and_then(|v| v.as_real())
.unwrap_or(1e5) as f32;
MinCellQuality {
min_unique_genes,
min_lib_size,
min_cells,
target_size,
}
}
}
impl KnnParams {
pub fn from_r_list(r_list: List) -> Self {
let params_list = r_list.into_hashmap();
let knn_method = std::string::String::from(
params_list
.get("knn_method")
.and_then(|v| v.as_str())
.unwrap_or("annoy"),
);
let ann_dist = std::string::String::from(
params_list
.get("ann_dist")
.and_then(|v| v.as_str())
.unwrap_or("cosine"),
);
let k = params_list
.get("k")
.and_then(|v| v.as_integer())
.unwrap_or(15) as usize;
let n_tree = params_list
.get("n_trees")
.and_then(|v| v.as_integer())
.unwrap_or(50) as usize;
let search_budget = params_list
.get("search_budget")
.and_then(|v| v.as_integer())
.map(|v| v as usize);
let diversify_prob = params_list
.get("diversify_prob")
.and_then(|v| v.as_real())
.unwrap_or(0.0) as f32;
let delta = params_list
.get("delta")
.and_then(|v| v.as_real())
.unwrap_or(0.001) as f32;
let ef_budget = params_list
.get("ef_budget")
.and_then(|v| v.as_integer())
.map(|v| v as usize);
let m = params_list
.get("m")
.and_then(|v| v.as_integer())
.unwrap_or(16) as usize;
let ef_construction = params_list
.get("ef_construction")
.and_then(|v| v.as_integer())
.unwrap_or(200) as usize;
let ef_search = params_list
.get("ef_search")
.and_then(|v| v.as_integer())
.unwrap_or(100) as usize;
let n_list = params_list
.get("n_list")
.and_then(|v| v.as_integer())
.map(|v| v as usize);
let n_probe = params_list
.get("n_probe")
.and_then(|v| v.as_integer())
.map(|v| v as usize);
Self {
knn_method,
ann_dist,
k,
n_tree,
search_budget,
ef_budget,
diversify_prob,
delta,
m,
ef_construction,
ef_search,
n_list,
n_probe,
}
}
}
impl ScrubletParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let scrublet_list = r_list.into_hashmap();
let log_transform = scrublet_list
.get("log_transform")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let mean_center = scrublet_list
.get("mean_center")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let normalise_variance = scrublet_list
.get("normalise_variance")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let target_size = scrublet_list
.get("target_size")
.and_then(|v| v.as_real())
.map(|x| x as f32);
let min_gene_var_pctl = scrublet_list
.get("min_gene_var_pctl")
.and_then(|v| v.as_real())
.unwrap_or(0.85) as f32;
let hvg_method = std::string::String::from(
scrublet_list
.get("hvg_method")
.and_then(|v| v.as_str())
.unwrap_or("vst"),
);
let loess_span = scrublet_list
.get("loess_span")
.and_then(|v| v.as_real())
.unwrap_or(0.3);
let clip_max = scrublet_list
.get("clip_max")
.and_then(|v| v.as_real())
.map(|x| x as f32);
let sim_doublet_ratio = scrublet_list
.get("sim_doublet_ratio")
.and_then(|v| v.as_real())
.unwrap_or(2.0) as f32;
let expected_doublet_rate = scrublet_list
.get("expected_doublet_rate")
.and_then(|v| v.as_real())
.unwrap_or(0.1) as f32;
let stdev_doublet_rate = scrublet_list
.get("stdev_doublet_rate")
.and_then(|v| v.as_real())
.unwrap_or(0.02) as f32;
let n_bins = scrublet_list
.get("n_bins")
.and_then(|v| v.as_integer())
.unwrap_or(50) as usize;
let manual_threshold = scrublet_list
.get("manual_threshold")
.and_then(|v| v.as_real())
.map(|x| x as f32);
let no_pcs = scrublet_list
.get("no_pcs")
.and_then(|v| v.as_integer())
.unwrap_or(30) as usize;
let random_svd = scrublet_list
.get("random_svd")
.and_then(|v| v.as_bool())
.unwrap_or(true);
Self {
log_transform,
normalise_variance,
mean_center,
target_size,
min_gene_var_pctl,
hvg_method,
loess_span,
clip_max,
sim_doublet_ratio,
expected_doublet_rate,
stdev_doublet_rate,
n_bins,
manual_threshold,
no_pcs,
random_svd,
knn_params,
}
}
}
impl BoostParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let params_list = r_list.into_hashmap();
let log_transform = params_list
.get("log_transform")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let mean_center = params_list
.get("mean_center")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let normalise_variance = params_list
.get("normalise_variance")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let target_size = params_list
.get("target_size")
.and_then(|v| v.as_real())
.map(|x| x as f32);
let min_gene_var_pctl = params_list
.get("min_gene_var_pctl")
.and_then(|v| v.as_real())
.unwrap_or(0.85) as f32;
let hvg_method = std::string::String::from(
params_list
.get("hvg_method")
.and_then(|v| v.as_str())
.unwrap_or("vst"),
);
let loess_span = params_list
.get("loess_span")
.and_then(|v| v.as_real())
.unwrap_or(0.3);
let clip_max = params_list
.get("clip_max")
.and_then(|v| v.as_real())
.map(|x| x as f32);
let boost_rate = params_list
.get("boost_rate")
.and_then(|v| v.as_real())
.unwrap_or(0.25) as f32;
let replace = params_list
.get("replace")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let resolution = params_list
.get("resolution")
.and_then(|v| v.as_real())
.unwrap_or(1.0) as f32;
let louvain_iters = params_list
.get("louvain_iters")
.and_then(|v| v.as_integer())
.unwrap_or(10) as usize;
let n_iters = params_list
.get("n_iters")
.and_then(|v| v.as_integer())
.unwrap_or(10) as usize;
let no_pcs = params_list
.get("no_pcs")
.and_then(|v| v.as_integer())
.unwrap_or(30) as usize;
let random_svd = params_list
.get("random_svd")
.and_then(|v| v.as_bool())
.unwrap_or(true);
let p_thresh = params_list
.get("p_thresh")
.and_then(|v| v.as_real())
.unwrap_or(1e-7) as f32;
let voter_thresh = params_list
.get("voter_thresh")
.and_then(|v| v.as_real())
.unwrap_or(0.9) as f32;
Self {
log_transform,
mean_center,
normalise_variance,
target_size,
min_gene_var_pctl,
hvg_method,
loess_span,
clip_max,
boost_rate,
replace,
no_pcs,
random_svd,
resolution,
louvain_iters,
n_iters,
p_thresh,
voter_thresh,
knn_params,
}
}
}
impl FastMnnParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let fastmnn_list = r_list.into_hashmap();
let ndist = fastmnn_list
.get("ndist")
.and_then(|v| v.as_real())
.unwrap_or(3.0) as f32;
let cos_norm = fastmnn_list
.get("cos_norm")
.and_then(|v| v.as_logical())
.map(|rb| rb.is_true())
.unwrap_or(true);
let no_pcs = fastmnn_list
.get("no_pcs")
.and_then(|v| v.as_integer())
.unwrap_or(30) as usize;
let random_svd = fastmnn_list
.get("random_svd")
.and_then(|v| v.as_logical())
.map(|rb| rb.is_true())
.unwrap_or(true);
Self {
ndist,
no_pcs,
random_svd,
cos_norm,
knn_params,
}
}
}
impl MiloRParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let params_list = r_list.into_hashmap();
let prop = params_list
.get("prop")
.and_then(|v| v.as_real())
.unwrap_or(0.2);
let k_refine = params_list
.get("k_refine")
.and_then(|v| v.as_integer())
.unwrap_or(20) as usize;
let index_type = std::string::String::from(
params_list
.get("index_type")
.and_then(|v| v.as_str())
.unwrap_or("annoy"),
);
let refinement_strategy = std::string::String::from(
params_list
.get("refinement_strategy")
.and_then(|v| v.as_str())
.unwrap_or("approximate"),
);
Self {
prop,
k_refine,
refinement_strategy,
index_type,
knn_params,
}
}
}
impl SEACellsParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let seacells_list = r_list.into_hashmap();
let n_sea_cells = seacells_list
.get("n_sea_cells")
.and_then(|v| v.as_integer())
.unwrap_or(0) as usize;
let max_fw_iters = seacells_list
.get("max_fw_iters")
.and_then(|v| v.as_integer())
.unwrap_or(50) as usize;
let convergence_epsilon = seacells_list
.get("convergence_epsilon")
.and_then(|v| v.as_real())
.unwrap_or(1e-3) as f32;
let max_iter = seacells_list
.get("max_iter")
.and_then(|v| v.as_integer())
.unwrap_or(100) as usize;
let min_iter = seacells_list
.get("min_iter")
.and_then(|v| v.as_integer())
.unwrap_or(10) as usize;
let greedy_threshold = seacells_list
.get("greedy_threshold")
.and_then(|v| v.as_integer())
.unwrap_or(20000) as usize;
let graph_building = seacells_list
.get("graph_building")
.and_then(|v| v.as_str())
.unwrap_or("union")
.to_string();
let pruning = seacells_list
.get("pruning")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let pruning_threshold: f32 = seacells_list
.get("pruning_threshold")
.and_then(|v| v.as_real())
.unwrap_or(1e-7) as f32;
Self {
n_sea_cells,
max_fw_iters,
convergence_epsilon,
max_iter,
min_iter,
greedy_threshold,
graph_building,
pruning,
pruning_threshold,
knn_params,
}
}
}
impl MetaCellParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let meta_cell_params = r_list.into_hashmap();
let max_shared = meta_cell_params
.get("max_shared")
.and_then(|v| v.as_integer())
.unwrap_or(15) as usize;
let target_no_metacells = meta_cell_params
.get("target_no_metacells")
.and_then(|v| v.as_integer())
.unwrap_or(1000) as usize;
let max_iter = meta_cell_params
.get("max_iter")
.and_then(|v| v.as_integer())
.unwrap_or(5000) as usize;
Self {
max_shared,
target_no_metacells,
max_iter,
knn_params,
}
}
}
impl SuperCellParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let params = r_list.into_hashmap();
let walk_length = params
.get("walk_length")
.and_then(|v| v.as_integer())
.unwrap_or(3) as usize;
let graining_factor = params
.get("graining_factor")
.and_then(|v| v.as_real())
.unwrap_or(50.0);
let linkage_dist = params
.get("linkage_dist")
.and_then(|v| v.as_str())
.unwrap_or("average")
.to_string();
Self {
walk_length,
graining_factor,
linkage_dist,
knn_params,
}
}
}
impl SignatureGenes {
pub fn from_r_list(r_list: List) -> Self {
let r_list = r_list.into_hashmap();
let positive: Vec<usize> = r_list
.get("pos")
.and_then(|v| v.as_integer_vector())
.unwrap_or_default()
.iter()
.map(|x| *x as usize)
.collect();
let negative: Vec<usize> = r_list
.get("neg")
.and_then(|v| v.as_integer_vector())
.unwrap_or_default()
.iter()
.map(|x| *x as usize)
.collect();
Self { positive, negative }
}
}
impl HotSpotParams {
pub fn from_r_list(r_list: List) -> Self {
let knn_params = KnnParams::from_r_list(r_list.clone());
let params_list = r_list.into_hashmap();
let model = std::string::String::from(
params_list
.get("model")
.and_then(|v| v.as_str())
.unwrap_or("normal"),
);
let normalise = params_list
.get("normalise")
.and_then(|v| v.as_bool())
.unwrap_or(true);
Self {
model,
normalise,
knn_params,
}
}
}
impl HarmonyParams {
pub fn from_r_list(r_list: List) -> Self {
let defaults = Self::default();
let params_list = r_list.into_hashmap();
let k = params_list
.get("k")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.k);
let sigma = params_list
.get("sigma")
.and_then(|v| v.as_real_vector())
.map(|v| v.iter().map(|&x| x as f32).collect())
.unwrap_or(defaults.sigma);
let theta = params_list
.get("theta")
.and_then(|v| v.as_real_vector())
.map(|v| v.iter().map(|&x| x as f32).collect())
.unwrap_or(defaults.theta);
let lambda = params_list
.get("lambda")
.and_then(|v| v.as_real_vector())
.map(|v| v.iter().map(|&x| x as f32).collect())
.unwrap_or(defaults.lambda);
let block_size = params_list
.get("block_size")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.block_size);
let max_iter_kmeans = params_list
.get("max_iter_kmeans")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.max_iter_kmeans);
let max_iter_harmony = params_list
.get("max_iter_harmony")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.max_iter_harmony);
let epsilon_kmeans = params_list
.get("epsilon_kmeans")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.epsilon_kmeans);
let epsilon_harmony = params_list
.get("epsilon_harmony")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.epsilon_harmony);
let window_size = params_list
.get("window_size")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.window_size);
Self {
k,
sigma,
theta,
lambda,
block_size,
max_iter_kmeans,
max_iter_harmony,
epsilon_kmeans,
epsilon_harmony,
window_size,
}
}
}
impl RandomForestConfig {
pub fn from_r_list(r_list: List) -> Self {
let defaults = Self::default();
let params_list = r_list.into_hashmap();
let n_trees = params_list
.get("n_trees")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_trees);
let min_samples_leaf = params_list
.get("min_samples_leaf")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.min_samples_leaf);
let n_features_split = params_list
.get("n_features_split")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_features_split);
let subsample_rate = params_list
.get("subsample_rate")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.subsample_rate);
let bootstrap = params_list
.get("bootstrap")
.and_then(|v| v.as_bool())
.unwrap_or(defaults.bootstrap);
let max_depth = params_list
.get("max_depth")
.and_then(|v| v.as_integer())
.map(|v| Some(v as usize))
.unwrap_or(defaults.max_depth);
let subsample_frac = params_list
.get("subsample_frac")
.and_then(|v| v.as_real())
.map(|v| Some(v as f32))
.unwrap_or(defaults.subsample_frac);
Self {
n_trees,
min_samples_leaf,
n_features_split,
subsample_rate,
bootstrap,
max_depth,
subsample_frac,
}
}
}
impl ExtraTreesConfig {
pub fn from_r_list(r_list: List) -> Self {
let defaults = Self::default();
let params_list = r_list.into_hashmap();
let n_trees = params_list
.get("n_trees")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_trees);
let min_samples_leaf = params_list
.get("min_samples_leaf")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.min_samples_leaf);
let n_features_split = params_list
.get("n_features_split")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_features_split);
let n_thresholds = params_list
.get("n_thresholds")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_thresholds);
let max_depth = params_list
.get("max_depth")
.and_then(|v| v.as_integer())
.map(|v| Some(v as usize))
.unwrap_or(defaults.max_depth);
let subsample_frac = params_list
.get("subsample_frac")
.and_then(|v| v.as_real())
.map(|v| Some(v as f32))
.unwrap_or(defaults.subsample_frac);
Self {
n_trees,
min_samples_leaf,
n_features_split,
n_thresholds,
max_depth,
subsample_frac,
}
}
}
impl GradientBoostingConfig {
pub fn from_r_list(r_list: List) -> Self {
let defaults = Self::default();
let params_list = r_list.into_hashmap();
let n_trees_max = params_list
.get("n_trees_max")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_trees_max);
let learning_rate = params_list
.get("learning_rate")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.learning_rate);
let max_depth = params_list
.get("max_depth")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.max_depth);
let min_samples_leaf = params_list
.get("min_samples_leaf")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.min_samples_leaf);
let early_stop_window = params_list
.get("early_stop_window")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.early_stop_window);
let subsample_rate = params_list
.get("subsample_rate")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.subsample_rate);
let n_features_split = params_list
.get("n_features_split")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_features_split);
Self {
n_trees_max,
learning_rate,
max_depth,
min_samples_leaf,
early_stop_window,
subsample_rate,
n_features_split,
}
}
}
impl ScenicParams {
pub fn from_r_list(r_list: List) -> Self {
let defaults = Self::default();
let params = r_list.clone().into_hashmap();
let min_counts = params
.get("min_counts")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.min_counts);
let min_cells = params
.get("min_cells")
.and_then(|v| v.as_real())
.map(|v| v as f32)
.unwrap_or(defaults.min_cells);
let gene_batch_strategy = params
.get("gene_batch_strategy")
.and_then(|v| v.as_str())
.map(String::from)
.unwrap_or(defaults.gene_batch_strategy);
let gene_batch_size = params
.get("gene_batch_size")
.and_then(|v| v.as_integer())
.map(|v| Some(v as usize))
.unwrap_or(defaults.gene_batch_size);
let n_pcs = params
.get("n_pcs")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_pcs);
let n_subsample = params
.get("n_subsample")
.and_then(|v| v.as_integer())
.map(|v| v as usize)
.unwrap_or(defaults.n_subsample);
let learner_type = params
.get("learner_type")
.and_then(|v| v.as_str())
.unwrap_or("randomforest");
let regression_learner = match learner_type.to_lowercase().as_str() {
"extratrees" => RegressionLearner::ExtraTrees(ExtraTreesConfig::from_r_list(r_list)),
"grnboost2" => {
RegressionLearner::GradientBoosting(GradientBoostingConfig::from_r_list(r_list))
}
_ => RegressionLearner::RandomForest(RandomForestConfig::from_r_list(r_list)),
};
Self {
min_counts,
min_cells,
regression_learner,
gene_batch_strategy,
gene_batch_size,
n_pcs,
n_subsample,
}
}
}
impl H5adFileTask {
pub fn from_r_list(r_list: List) -> Self {
let map = r_list.into_hashmap();
let exp_id = map
.get("exp_id")
.and_then(|v| v.as_str())
.expect("exp_id missing or not a string")
.to_string();
let h5_path = map
.get("h5_path")
.and_then(|v| v.as_str())
.expect("h5_path missing or not a string")
.to_string();
let cs_type_str = map
.get("cs_type")
.and_then(|v| v.as_str())
.expect("cs_type missing or not a string");
let cs_type =
parse_compressed_sparse_format(cs_type_str).expect("cs_type must be 'csr' or 'csc'");
let no_cells = map
.get("no_cells")
.and_then(|v| v.as_integer())
.expect("no_cells missing") as usize;
let no_genes = map
.get("no_genes")
.and_then(|v| v.as_integer())
.expect("no_genes missing") as usize;
let mapping_robj = map
.get("gene_local_to_universe")
.expect("gene_local_to_universe missing");
let mapping_raw: Vec<i32> = mapping_robj
.as_integer_slice()
.expect("gene_local_to_universe must be integer vector")
.to_vec();
let gene_local_to_universe: Vec<Option<usize>> = mapping_raw
.into_iter()
.map(|v| {
if v == i32::MIN {
None
} else {
Some(v as usize)
}
})
.collect();
Self {
exp_id,
h5_path,
cs_type,
no_cells,
no_genes,
gene_local_to_universe,
}
}
}