1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use crate::hdf5_io::resolve_backend_file;
use crate::qc::{squeeze_by_nnz, SqueezeCutoffs};
use crate::sparse_io::open_sparse_matrix;
use clap::Args;
use log::info;
mod convert;
mod group_table;
mod split;
mod subsample;
mod subset;
pub use convert::*;
pub use split::*;
pub use subsample::*;
pub use subset::*;
#[derive(Args, Debug)]
pub struct ReorderRowsArgs {
/// Data file -- either `.zarr` or `.h5`
pub data_file: Box<str>,
/// Row/feature name file (name per each line; `.tsv.gz` or `.tsv`)
#[arg(short, long, required = true)]
pub row_file: Box<str>,
/// output header
#[arg(short, long, required = true)]
pub output: Box<str>,
/// keep a `.zarr` directory instead of producing a `.zarr.zip` archive
#[arg(long = "no-zip", default_value_t = true, action = clap::ArgAction::SetFalse)]
pub zip: bool,
}
#[derive(Args, Debug)]
pub struct SubsetArgs {
/// data file -- `.zarr`, `.zarr.zip`, or `.h5`
pub data_file: Box<str>,
/// output file, in the input's format unless the name gives `.zarr` or
/// `.zarr.zip`
#[arg(short, long, required = true)]
pub output: Box<str>,
/// never open the explorer; subset only picks there, so it stops,
/// naming subset-rows and subset-columns, which take a selection
#[arg(long, default_value_t = false)]
pub batch_process: bool,
#[command(flatten)]
pub session: legume_tui::cli::SessionArgs,
}
#[derive(Args, Debug)]
pub struct SubsetColumnsArgs {
/// data file -- either `.zarr` or `.h5`
pub data_file: Box<str>,
/// column indices to take: singles and inclusive ranges, e.g. `0,1,2,3`,
/// `1-10`, or `1-20,50-55` (past the last column, cut)
#[arg(short = 'i', long, conflicts_with_all = ["name_file", "pattern"])]
pub column_indices: Option<Box<str>>,
/// column name file where each line is a column name
#[arg(short = 'f', long, conflicts_with = "pattern")]
pub name_file: Option<Box<str>>,
/// the columns whose names this regex finds (case-insensitive), e.g.
/// `/count/spliced$` or `^MT-`
#[arg(long)]
pub pattern: Option<Box<str>>,
/// never open the explorer: what to keep is given on the command line
#[arg(long, default_value_t = false)]
pub batch_process: bool,
#[command(flatten)]
pub session: legume_tui::cli::SessionArgs,
/// delimiter for base-key extraction (e.g., '@' to match "ACGT-1@batch" with "ACGT-1")
#[arg(short = 'd', long, default_value = "@")]
pub delimiter: char,
/// enable prefix matching (stored name is prefix of query or vice
/// versa); `--allow-prefix=false` turns it off, as `--exact-names` does
#[arg(
long,
action = clap::ArgAction::Set,
num_args = 0..=1,
require_equals = true,
default_missing_value = "true",
default_value_t = true
)]
pub allow_prefix: bool,
/// Match names exactly (plus the delimiter base key): no prefix matching.
/// `--allow-prefix` defaults to on and, being a bare flag, cannot be turned
/// off from the command line; this is the off switch. Numeric or short gene
/// names are prefixes of many others, so a name list can otherwise keep far
/// more rows than it names.
#[arg(long, default_value_t = false)]
pub exact_names: bool,
/// squeeze
#[arg(long, default_value_t = false)]
pub do_squeeze: bool,
/// minimum number of non-zero cutoff for rows
#[arg(long, default_value_t = 1)]
pub row_nnz_cutoff: usize,
/// minimum number of non-zero cutoff for columns
#[arg(long, default_value_t = 1)]
pub column_nnz_cutoff: usize,
/// output file
#[arg(short, long, required = true)]
pub output: Box<str>,
/// keep a `.zarr` directory instead of producing a `.zarr.zip` archive
#[arg(long = "no-zip", default_value_t = true, action = clap::ArgAction::SetFalse)]
pub zip: bool,
}
#[derive(Args, Debug)]
pub struct SubsetRowsArgs {
/// data file -- either `.zarr` or `.h5`
pub data_file: Box<str>,
/// row indices to take: singles and inclusive ranges, e.g. `0,1,2,3`,
/// `1-10`, or `1-20,50-55` (past the last row, cut)
#[arg(short = 'i', long, conflicts_with_all = ["name_file", "pattern"])]
pub row_indices: Option<Box<str>>,
/// row name file where each line is a row name
#[arg(short = 'f', long, conflicts_with = "pattern")]
pub name_file: Option<Box<str>>,
/// the rows whose names this regex finds (case-insensitive), e.g.
/// `/count/spliced$` or `^MT-`
#[arg(long)]
pub pattern: Option<Box<str>>,
/// never open the explorer: what to keep is given on the command line
#[arg(long, default_value_t = false)]
pub batch_process: bool,
#[command(flatten)]
pub session: legume_tui::cli::SessionArgs,
/// delimiter for base-key extraction (e.g., '@' to match "gene@batch" with "gene")
#[arg(short = 'd', long, default_value = "@")]
pub delimiter: char,
/// enable prefix matching (stored name is prefix of query or vice
/// versa); `--allow-prefix=false` turns it off, as `--exact-names` does
#[arg(
long,
action = clap::ArgAction::Set,
num_args = 0..=1,
require_equals = true,
default_missing_value = "true",
default_value_t = true
)]
pub allow_prefix: bool,
/// Match names exactly (plus the delimiter base key): no prefix matching.
/// `--allow-prefix` defaults to on and, being a bare flag, cannot be turned
/// off from the command line; this is the off switch. Numeric or short gene
/// names are prefixes of many others, so a name list can otherwise keep far
/// more rows than it names.
#[arg(long, default_value_t = false)]
pub exact_names: bool,
/// squeeze
#[arg(long, default_value_t = false)]
pub do_squeeze: bool,
/// minimum number of non-zero cutoff for rows
#[arg(long, default_value_t = 1)]
pub row_nnz_cutoff: usize,
/// minimum number of non-zero cutoff for columns
#[arg(long, default_value_t = 1)]
pub column_nnz_cutoff: usize,
/// output file
#[arg(short, long, required = true)]
pub output: Box<str>,
/// keep a `.zarr` directory instead of producing a `.zarr.zip` archive
#[arg(long = "no-zip", default_value_t = true, action = clap::ArgAction::SetFalse)]
pub zip: bool,
}
/// Cut, in place, the rows and columns of the backend at `backend_file`
/// with fewer than `row` and `column` non-zeros: the `--squeeze` of the
/// commands that build or derive a backend. The path is one the caller
/// resolved and finalizes itself, so nothing is zipped here.
pub fn squeeze_in_place(
backend_file: &str,
row: usize,
column: usize,
block_size: Option<usize>,
) -> anyhow::Result<()> {
info!("Squeeze the backend data {backend_file}");
let (backend, file) = resolve_backend_file(backend_file, None)?;
let data = open_sparse_matrix(&file, &backend)?;
squeeze_by_nnz(
data.as_ref(),
SqueezeCutoffs { row, column },
block_size,
true,
)?;
let data = open_sparse_matrix(&file, &backend)?;
info!(
"after squeeze -- data: {} rows x {} columns",
data.num_rows().unwrap_or_default(),
data.num_columns().unwrap_or_default()
);
Ok(())
}