Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clean up print_clonotypes #408

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion enclone_args/src/load_gex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub fn get_gex_info(ctl: &mut EncloneControl) -> Result<GexInfo, String> {
&mut json_metrics,
&mut metrics,
)?;
if ctl.gen_opt.gene_scan_test.is_some() && !ctl.gen_opt.accept_inconsistent {
if ctl.gen_opt.gene_scan.is_some() && !ctl.gen_opt.accept_inconsistent {
let mut allf = gex_features.clone();
unique_sort(&mut allf);
if allf.len() != 1 {
Expand Down
16 changes: 4 additions & 12 deletions enclone_args/src/proc_args_post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ pub fn proc_args_post(
"\nIf you use ALIGN_JALIGN_CONSISTENCY, you should also use PLAIN.\n".to_string(),
);
}
if ctl.gen_opt.gene_scan_exact && ctl.gen_opt.gene_scan_test.is_none() {
if ctl.gen_opt.gene_scan_exact && ctl.gen_opt.gene_scan.is_none() {
return Err(
"\nIt doesn't make sense to specify SCAN_EXIT unless SCAN is also specified.\n"
.to_string(),
Expand Down Expand Up @@ -680,17 +680,9 @@ pub fn proc_args_post(
for i in 0..ctl.clono_filt_opt.bounds.len() {
ctl.clono_filt_opt.bounds[i].require_valid_variables(ctl)?;
}
if ctl.gen_opt.gene_scan_test.is_some() {
ctl.gen_opt
.gene_scan_test
.as_ref()
.unwrap()
.require_valid_variables(ctl)?;
ctl.gen_opt
.gene_scan_control
.as_ref()
.unwrap()
.require_valid_variables(ctl)?;
if let Some(gene_scan_opts) = &ctl.gen_opt.gene_scan {
gene_scan_opts.test.require_valid_variables(ctl)?;
gene_scan_opts.control.require_valid_variables(ctl)?;
}
Ok(())
}
10 changes: 6 additions & 4 deletions enclone_args/src/process_special_arg2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Process a special argument, i.e. one that does not fit into a neat bucket.

use crate::proc_args2::{is_f64_arg, is_usize_arg};
use enclone_core::defs::EncloneControl;
use enclone_core::defs::{EncloneControl, GeneScanOpts};
use enclone_core::linear_condition::LinearCondition;
use enclone_core::{require_readable_file, tilde_expand_me};
use evalexpr::build_operator_tree;
Expand Down Expand Up @@ -363,15 +363,17 @@ pub fn process_special_arg2(
if x.len() != 3 {
return Err("\nArgument to SCAN must have three components.\n".to_string());
}
ctl.gen_opt.gene_scan_test = Some(LinearCondition::new(x[0])?);
ctl.gen_opt.gene_scan_control = Some(LinearCondition::new(x[1])?);
let threshold = LinearCondition::new(x[2])?;
for i in 0..threshold.var.len() {
if threshold.var[i] != *"t" && threshold.var[i] != *"c" {
return Err("\nIllegal variable in threshold for scan.\n".to_string());
}
}
ctl.gen_opt.gene_scan_threshold = Some(threshold);
ctl.gen_opt.gene_scan = Some(GeneScanOpts {
test: LinearCondition::new(x[0])?,
control: LinearCondition::new(x[1])?,
threshold,
});
} else if arg.starts_with("PLOT=") {
*using_plot = true;
let x = arg.after("PLOT=").split(',').collect::<Vec<&str>>();
Expand Down
11 changes: 8 additions & 3 deletions enclone_core/src/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,7 @@ pub struct GeneralOpt {
pub summary_csv: bool,
pub cr_version: String,
pub nwarn: bool,
pub gene_scan_test: Option<LinearCondition>,
pub gene_scan_control: Option<LinearCondition>,
pub gene_scan_threshold: Option<LinearCondition>,
pub gene_scan: Option<GeneScanOpts>,
pub gene_scan_exact: bool,
pub clonotype_group_names: Option<String>,
pub origin_color_map: HashMap<String, String>,
Expand Down Expand Up @@ -251,6 +249,13 @@ pub struct GeneralOpt {
pub session_narrative: String,
}

#[derive(Clone, PartialEq)]
pub struct GeneScanOpts {
pub test: LinearCondition,
pub control: LinearCondition,
pub threshold: LinearCondition,
}

// Some plot options. Note that plot options are not allowed to affect intermediate computation.

#[derive(Clone, Default)]
Expand Down
15 changes: 9 additions & 6 deletions enclone_print/src/finish_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub struct Sr {
pub subrows: Vec<Vec<String>>,
}

/// Return the string "picture" of this clonotype.
/// Also mutates out_data in some unclear way.
pub fn finish_table(
n: usize,
ctl: &EncloneControl,
Expand All @@ -31,7 +33,6 @@ pub fn finish_table(
dref: &[DonorReferenceItem],
peer_groups: &[Vec<(usize, u8, u32)>],
mlog: &mut Vec<u8>,
logz: &mut String,
stats: &[(String, Vec<String>)],
sr: Vec<Sr>,
extra_args: &[String],
Expand All @@ -40,7 +41,7 @@ pub fn finish_table(
rord: &[usize],
pass: usize,
cdr3_con: &[Vec<u8>],
) {
) -> String {
// Fill in exact_subclonotype_id, reorder.

let nexacts = exacts.len();
Expand Down Expand Up @@ -242,7 +243,8 @@ pub fn finish_table(
justify.push(justification(&rsi.cvars[cx][m]));
}
}
make_table(ctl, &mut rows, &justify, mlog, logz);
let mut logz = String::new();
make_table(ctl, &mut rows, &justify, mlog, &mut logz);

// Add phylogeny.

Expand Down Expand Up @@ -304,9 +306,9 @@ pub fn finish_table(
}
if (d1 == 0) ^ (d2 == 0) {
if d1 == 0 {
write!(*logz, "{} ==> {}", u1 + 1, u2 + 1).unwrap();
write!(logz, "{} ==> {}", u1 + 1, u2 + 1).unwrap();
} else {
write!(*logz, "{} ==> {}", u2 + 1, u1 + 1).unwrap();
write!(logz, "{} ==> {}", u2 + 1, u1 + 1).unwrap();
}
let s = format!(
"; u1 = {}, u2 = {}, d1 = {}, d2 = {}, d = {}\n",
Expand All @@ -316,9 +318,10 @@ pub fn finish_table(
d2,
d
);
*logz += &s;
logz += &s;
}
}
}
}
logz
}
183 changes: 84 additions & 99 deletions enclone_print/src/gene_scan.rs
Original file line number Diff line number Diff line change
@@ -1,121 +1,106 @@
// Copyright (c) 2021 10X Genomics, Inc. All rights reserved.

use enclone_core::defs::EncloneControl;
use enclone_core::{defs::GeneScanOpts, linear_condition::LinearCondition};

pub struct InSet {
pub test: bool,
pub control: bool,
}

pub fn gene_scan_test(
ctl: &EncloneControl,
opts: &GeneScanOpts,
exact: bool,
stats: &[(String, Vec<String>)],
stats_orig: &[(String, Vec<String>)],
nexacts: usize,
n: usize,
in_test: &mut Vec<bool>,
in_control: &mut Vec<bool>,
) {
) -> Vec<InSet> {
// See if we're in the test and control sets for gene scan (non-exact case).

if let Some(ref scan_test) = ctl.gen_opt.gene_scan_test {
if !ctl.gen_opt.gene_scan_exact {
let x = scan_test;
let means = x
.var
.iter()
.take(x.n())
.map(|xn| {
stats
.iter()
.find_map(|stat| {
if stat.0 == *xn {
Some(
stat.1
.iter()
.filter_map(|k| k.parse::<f64>().ok())
.sum::<f64>(),
)
} else {
None
}
})
.unwrap_or_default()
/ n as f64
})
.collect::<Vec<_>>();

in_test.push(x.satisfied(&means));
let x = ctl.gen_opt.gene_scan_control.as_ref().unwrap();
let means = x
.var
.iter()
.take(x.n())
.map(|xn| {
stats
.iter()
.find_map(|stat| {
if stat.0 == *xn {
Some(
stat.1
.iter()
.filter_map(|k| k.parse::<f64>().ok())
.sum::<f64>(),
)
} else {
None
}
})
.unwrap_or_default()
/ n as f64
})
.collect::<Vec<_>>();
in_control.push(x.satisfied(&means));
}
}

// See if we're in the test and control sets for gene scan (exact case).

if ctl.gen_opt.gene_scan_test.is_some() && ctl.gen_opt.gene_scan_exact {
let x = ctl.gen_opt.gene_scan_test.clone().unwrap();
for k in 0..nexacts {
let mut means = Vec::<f64>::new();
for xn in x.var.iter().take(x.n()) {
let mut vals = Vec::<f64>::new();
let mut count = 0;
for stat in stats_orig {
if stat.0 == *xn {
if count == k {
for k in &stat.1 {
if let Ok(v) = k.parse::<f64>() {
vals.push(v);
if !exact {
let in_set = |cond: &LinearCondition| {
cond.satisfied(
&cond
.var
.iter()
.take(cond.n())
.map(|xn| {
stats
.iter()
.find_map(|stat| {
if stat.0 == *xn {
Some(
stat.1
.iter()
.filter_map(|k| k.parse::<f64>().ok())
.sum::<f64>(),
)
} else {
None
}
})
.unwrap_or_default()
/ n as f64
})
.collect::<Vec<_>>(),
)
};
vec![InSet {
test: in_set(&opts.test),
control: in_set(&opts.control),
}]
} else {
// See if we're in the test and control sets for gene scan (exact case).
(0..nexacts)
.map(|k| {
let x = &opts.test;
let mut means = Vec::<f64>::new();
for xn in x.var.iter().take(x.n()) {
let mut vals = Vec::<f64>::new();
let mut count = 0;
for stat in stats_orig {
if stat.0 == *xn {
if count == k {
for k in &stat.1 {
if let Ok(v) = k.parse::<f64>() {
vals.push(v);
}
}
break;
}
break;
count += 1;
}
count += 1;
}
let n = vals.len() as f64;
means.push(vals.into_iter().sum::<f64>() / n);
}
let n = vals.len() as f64;
means.push(vals.into_iter().sum::<f64>() / n);
}
in_test.push(x.satisfied(&means));
let x = ctl.gen_opt.gene_scan_control.clone().unwrap();
let mut means = Vec::<f64>::new();
for xn in x.var.iter().take(x.n()) {
let mut vals = Vec::<f64>::new();
let mut count = 0;
for stat in stats_orig {
if stat.0 == *xn {
if count == k {
for k in &stat.1 {
if let Ok(v) = k.parse::<f64>() {
vals.push(v);
let in_test = x.satisfied(&means);
let x = &opts.control;
let mut means = Vec::<f64>::new();
for xn in x.var.iter().take(x.n()) {
let mut vals = Vec::<f64>::new();
let mut count = 0;
for stat in stats_orig {
if stat.0 == *xn {
if count == k {
for k in &stat.1 {
if let Ok(v) = k.parse::<f64>() {
vals.push(v);
}
}
break;
}
break;
count += 1;
}
count += 1;
}
means.push(vals.into_iter().sum::<f64>() / n as f64);
}
let in_control = x.satisfied(&means);
InSet {
test: in_test,
control: in_control,
}
means.push(vals.into_iter().sum::<f64>() / n as f64);
}
in_control.push(x.satisfied(&means));
}
})
.collect()
}
}
Loading
Loading