use std::path::PathBuf;
use hdf5::File;
pub struct SparseCountMatrix {
pub cells: Vec<String>,
pub features: Vec<String>,
pub entries: Vec<(u32,u32,u32)>
}
impl SparseCountMatrix {
pub fn new() -> Self {
Self {
cells: Vec::new(),
features: Vec::new(),
entries: Vec::new()
}
}
pub fn add_feature(&mut self, feature: &String) -> usize {
let id = self.features.len();
self.features.push(feature.clone());
id as usize
}
pub fn add_cell(&mut self, cell: &String) -> usize {
let id = self.cells.len();
self.cells.push(cell.clone());
id as usize
}
pub fn add_value(
&mut self,
cell: usize,
feature: usize,
value: u32
) {
self.entries.push((cell as u32, feature as u32, value));
}
pub fn save_to_anndata(&self, p: &PathBuf) -> anyhow::Result<()> {
if p.exists() {
std::fs::remove_file(&p).expect("Failed to delete previous output file");
}
let file = File::create(p)?;
let csr_data: Vec<u32> = self.entries.iter().map(|(_row,_col,data)| *data).collect();
let csr_cols: Vec<u32> = self.entries.iter().map(|(_row,col,_data)| *col).collect();
let csr_rows: Vec<u32> = self.entries.iter().map(|(row,_col,_data)| *row).collect();
let mut ind_ptr:Vec<u32> = Vec::new();
ind_ptr.push(0);
for i in 1..(csr_rows.len()) {
if csr_rows[i] != csr_rows[i-1] {
ind_ptr.push(i as u32);
}
}
ind_ptr.push((csr_data.len()) as u32);
let group = file.create_group("X")?;
let builder = group.new_dataset_builder();
let _ = builder.with_data(&csr_data.as_slice()).create("data")?; let builder = group.new_dataset_builder();
let _ = builder.with_data(&csr_cols.as_slice()).create("indices")?; let builder = group.new_dataset_builder();
let _ = builder.with_data(&ind_ptr.as_slice()).create("indptr")?;
let n_rows = self.cells.len();
let n_cols = self.features.len();
let builder = group.new_dataset_builder();
let _ = builder.with_data(&[n_rows,n_cols].as_slice()).create("shape")?;
create_str_attr_unicode_to_group(&group, "encoding-type", "csr_matrix")?;
create_str_attr_unicode_to_group(&group, "encoding-version", "0.1.0")?;
let list_cell_names = vec_to_h5_string(self.cells.as_slice());
let group = file.create_group("obs")?;
let builder = group.new_dataset_builder();
let _ = builder.
with_data(list_cell_names.as_slice()).
create("_index")?;
let list_features = vec_to_h5_string(self.features.as_slice());
let group = file.create_group("var")?;
let builder = group.new_dataset_builder();
let _ = builder.
with_data(list_features.as_slice()).
create("_index")?;
Ok(())
}
}
fn vec_to_h5_string(list: &[String]) -> Vec<hdf5::types::VarLenUnicode> {
list.iter().map(|f| f.parse().unwrap()).collect()
}
fn create_str_attr_unicode_to_group (group: &hdf5::Group, name: &str, value: &str) -> hdf5::Result<()> {
let attr = group.new_attr::<hdf5::types::VarLenUnicode>().create(name)?;
let value: hdf5::types::VarLenUnicode = value.parse().unwrap();
attr.write_scalar(&value)
}