From 4f76809d7756f6e15bf0261ed6e5434ee06ce52f Mon Sep 17 00:00:00 2001 From: kojix2 <2xijok@gmail.com> Date: Sat, 25 Jan 2025 15:34:05 +0900 Subject: [PATCH 1/3] Add value_name for resolution argument --- d4tools/src/plot/cli.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/d4tools/src/plot/cli.yml b/d4tools/src/plot/cli.yml index 76d9097..27727ed 100644 --- a/d4tools/src/plot/cli.yml +++ b/d4tools/src/plot/cli.yml @@ -14,6 +14,7 @@ args: short: R long: resolution help: Specify the resolution of the output image + value_name: widthxheight - region: required: true short: r From 5281dc9a5154ddc38d1e7265ccaa527c9e2bd5f5 Mon Sep 17 00:00:00 2001 From: kojix2 <2xijok@gmail.com> Date: Sat, 25 Jan 2025 16:36:49 +0900 Subject: [PATCH 2/3] Add options for binning data --- d4tools/src/plot/cli.yml | 12 +++++++++++- d4tools/src/plot/mod.rs | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/d4tools/src/plot/cli.yml b/d4tools/src/plot/cli.yml index 27727ed..0e23328 100644 --- a/d4tools/src/plot/cli.yml +++ b/d4tools/src/plot/cli.yml @@ -20,4 +20,14 @@ args: short: r long: region help: Regions to be plotted - value_name: chr[:start[-end]] \ No newline at end of file + value_name: chr[:start[-end]] + - nbins: + short: n + long: nbins + help: Number of bins for data downsampling + value_name: n + - bin-width: + short: w + long: bin-width + help: Width of each bin in base pairs + value_name: width diff --git a/d4tools/src/plot/mod.rs b/d4tools/src/plot/mod.rs index 7918bfa..52c0f69 100644 --- a/d4tools/src/plot/mod.rs +++ b/d4tools/src/plot/mod.rs @@ -86,7 +86,40 @@ pub fn entry_point(args: Vec) -> Result<(), Box> }) .unwrap(); - let data = downsample_data(input, &chr, (left, right), 256)?; + let range_length = right as u64 - left as u64 + 1; + + let nbins = matches.value_of("nbins").map(|v| v.parse::().unwrap()); + let bin_width = matches + .value_of("bin-width") + .map(|v| v.parse::().unwrap()); + + let nbins = match (nbins, bin_width) { + (Some(n), None) => n, + (None, Some(width)) => { + if width == 0 { + panic!("Invalid bin-width: cannot be zero"); + } + (range_length / width).max(1) + } + (Some(n), Some(width)) => { + if width == 0 { + panic!("Invalid bin-width: cannot be zero"); + } + let calculated_nbins = (range_length / width).max(1); + if n != calculated_nbins { + panic!( + "Conflicting options: --nbins ({}) and --bin-width ({}) do not match.", + n, width + ); + } + n + } + (None, None) => 256, + }; + + let nbins = nbins as usize; + + let data = downsample_data(input, &chr, (left, right), nbins)?; let pos_range = plotters::data::fitting_range(data.iter().map(|x| &x.0)); let mut data_range = plotters::data::fitting_range(data.iter().map(|x| &x.1)); From a2755df9bff98babcfa3ae4a9985a1cbe3d7a961 Mon Sep 17 00:00:00 2001 From: kojix2 <2xijok@gmail.com> Date: Thu, 11 Jun 2026 00:19:33 +0900 Subject: [PATCH 3/3] Fix plot bin calculation edge cases Assisted-by: Codex 5.5 --- d4tools/src/plot/mod.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/d4tools/src/plot/mod.rs b/d4tools/src/plot/mod.rs index 52c0f69..a8535b5 100644 --- a/d4tools/src/plot/mod.rs +++ b/d4tools/src/plot/mod.rs @@ -54,6 +54,25 @@ fn downsample_data( .collect()) } +fn actual_range_length( + path: &str, + chr: &str, + mut range: (u32, u32), +) -> Result> { + let input: d4::D4TrackReader = d4::D4TrackReader::open(path)?; + let target = input + .header() + .chrom_list() + .iter() + .find(|this| this.name == chr) + .unwrap(); + + range.0 = range.0.min(target.size as u32); + range.1 = range.1.max(range.0).min(target.size as u32); + + Ok(range.1 as u64 - range.0 as u64 + 1) +} + pub fn entry_point(args: Vec) -> Result<(), Box> { let yaml = load_yaml!("cli.yml"); let matches = App::from_yaml(yaml) @@ -86,7 +105,7 @@ pub fn entry_point(args: Vec) -> Result<(), Box> }) .unwrap(); - let range_length = right as u64 - left as u64 + 1; + let range_length = actual_range_length(input, &chr, (left, right))?; let nbins = matches.value_of("nbins").map(|v| v.parse::().unwrap()); let bin_width = matches @@ -94,18 +113,26 @@ pub fn entry_point(args: Vec) -> Result<(), Box> .map(|v| v.parse::().unwrap()); let nbins = match (nbins, bin_width) { - (Some(n), None) => n, + (Some(n), None) => { + if n == 0 { + panic!("Invalid nbins: cannot be zero"); + } + n + } (None, Some(width)) => { if width == 0 { panic!("Invalid bin-width: cannot be zero"); } - (range_length / width).max(1) + (range_length - 1) / width + 1 } (Some(n), Some(width)) => { + if n == 0 { + panic!("Invalid nbins: cannot be zero"); + } if width == 0 { panic!("Invalid bin-width: cannot be zero"); } - let calculated_nbins = (range_length / width).max(1); + let calculated_nbins = (range_length - 1) / width + 1; if n != calculated_nbins { panic!( "Conflicting options: --nbins ({}) and --bin-width ({}) do not match.",