data_beans/
column_subset.rs1pub fn stream_column_selection(
23 data: &dyn crate::sparse_io::SparseIo<IndexIter = Vec<usize>>,
24 selected_columns: &[usize],
25 row_filter: Option<&[usize]>,
26 out_row_names: &[Box<str>],
27 out_col_names: &[Box<str>],
28 file_out: &str,
29 backend_out: &crate::sparse_io::SparseIoBackend,
30) -> anyhow::Result<(usize, usize, usize)> {
31 use crate::sparse_io::*;
32
33 let nrow_full = data
34 .num_rows()
35 .ok_or_else(|| anyhow::anyhow!("backend has no `nrow`"))?;
36
37 let row_map: Option<Vec<Option<u64>>> = row_filter.map(|keep| {
39 debug_assert!(
40 keep.windows(2).all(|w| w[0] < w[1]),
41 "row filter must ascend"
42 );
43 let mut map = vec![None; nrow_full];
44 for (new, &old) in keep.iter().enumerate() {
45 map[old] = Some(new as u64);
46 }
47 map
48 });
49 let out_nrow = row_filter.map_or(nrow_full, <[usize]>::len);
50 let out_ncol = selected_columns.len();
51
52 let per_col_nnz: Vec<u64> = match &row_map {
55 None => selected_columns
56 .iter()
57 .map(|&c| {
58 data.column_nnz(c)
59 .ok_or_else(|| anyhow::anyhow!("no indptr entry for column {c}"))
60 })
61 .collect::<anyhow::Result<_>>()?,
62 Some(map) => {
63 let mut counts = vec![0u64; out_ncol];
65 let coarse = legume_numeric::matrix::utils::generate_minibatch_intervals(
66 out_ncol,
67 0,
68 Some(8192),
69 );
70 for (lb, ub) in coarse {
71 let (_, _, triplets) =
72 data.read_triplets_by_columns(selected_columns[lb..ub].to_vec())?;
73 for (r, c_local, _) in triplets {
74 if map[r as usize].is_some() {
75 counts[lb + c_local as usize] += 1;
76 }
77 }
78 }
79 counts
80 }
81 };
82 let nnz: u64 = per_col_nnz.iter().sum();
83
84 let mut out = create_sparse_streaming_empty(Some(file_out), Some(backend_out))?;
85 out.begin_streaming_csc((out_nrow, out_ncol, nnz as usize))?;
86
87 let blocks = legume_numeric::matrix::utils::byte_budget_intervals(
88 &per_col_nnz,
89 crate::sparse_io::SLAB_BUDGET_BYTES,
90 crate::sparse_io::TRIPLET_BYTES,
91 );
92
93 let t_stream = std::time::Instant::now();
94 let mut nnz_offset = 0u64;
95 for (lb, ub) in blocks {
96 let (_, _, triplets) = data.read_triplets_by_columns(selected_columns[lb..ub].to_vec())?;
103
104 let n_block = ub - lb;
105 let mut per_col: Vec<Vec<(u64, f32)>> = vec![Vec::new(); n_block];
106 for (r, c_local, x) in triplets {
107 let kept = match &row_map {
108 None => Some(r),
109 Some(map) => map[r as usize],
110 };
111 if let Some(new_r) = kept {
112 per_col[c_local as usize].push((new_r, x));
113 }
114 }
115 let mut local_colptr = Vec::with_capacity(n_block);
116 let mut row_indices = Vec::new();
117 let mut values = Vec::new();
118 for entries in &per_col {
119 local_colptr.push(row_indices.len() as u64);
120 for &(r, x) in entries {
121 row_indices.push(r);
122 values.push(x);
123 }
124 }
125 out.append_csc_slab(lb as u64, nnz_offset, &local_colptr, &row_indices, &values)?;
126 nnz_offset += values.len() as u64;
127 }
128
129 out.finalize_streaming_csc()?;
130 out.build_csr_from_csc_streaming()?;
131 log::info!(
132 "streamed {nnz} entries in {out_ncol} columns ({:.1}s)",
133 t_stream.elapsed().as_secs_f32()
134 );
135 out.register_row_names_vec(out_row_names);
136 out.register_column_names_vec(out_col_names);
137 Ok((out_nrow, out_ncol, nnz as usize))
138}