Skip to main content

ndhistogram/
lib.rs

1//! ndhistogram implements multi-dimensional histograms for Rust.
2//!
3//! This library aims to provide a similar feature set to the C++ library
4//! [boost-histogram](https://www.boost.org/doc/libs/1_75_0/libs/histogram)
5//! but with an idomatic pure-Rust implementation.
6//!
7//! Features include:
8//!
9//! - Histograms with any number of dimensions from 1 up to 21 dimensions.
10//! - Continuous (eg represented by a floating point number) and discrete axis (eg a category represented by a string value or enum) types that are composable (eg you may mix discrete and continuous axes).
11//! - Flexible bin values including any primitive number type, or a user-defined type.
12//! - Unweighted and weighted filling of histograms.
13//! - Flexible, user-definable axis types.
14//! - Sparse histograms to reduce the memory footprint of high bin count, mostly empty, histograms.
15//!
16//! ## Table of Contents
17//!
18//! 1. [Usage](#usage)
19//! 2. [Quick-start](#quick-start)
20//! 3. [Overview](#overview)
21//!    1. [Histogram Implementations](#histogram-implementations)
22//!    2. [Axis Implementations](#axis-implementations)
23//!    3. [Histogram Bin Values](#histogram-bin-values)
24//! 4. [How to Guide](#how-to-guide)
25//!    1. [Customize the Bin Value Type](#customize-the-bin-value-type)
26//!    2. [Create and Use a 2D Histogram](#create-and-use-a-2d-histogram)
27//!    3. [Create a Histogram with a Discrete Axis](#create-a-histogram-with-a-discrete-axis)
28//!    4. [Create a Histogram with Variable Sized Bins](#create-a-histogram-with-variable-sized-bins)
29//!    5. [Create a Histogram with a Periodic or Cyclic Axis](#create-a-histogram-with-a-periodic-or-cyclic-axis)
30//!    6. [Create a Sparse Histogram](#create-a-sparse-histogram)
31//!    7. [Merge Histograms](#merge-histograms)
32//!    8. [Iterate over Histogram Bins in Parallel](#iterate-over-histogram-bins-in-parallel)
33//! 5. [Crate Feature Flags](#crate-feature-flags)
34//! 6. [How to contribute](#how-to-contribute)
35//!
36//! ## Usage
37//!
38//! Add this to your `Cargo.toml`:
39//!
40//! ```toml
41//! [dependencies]
42//! ndhistogram = "0.10.0"
43//! ```
44//!
45//! See the [change log](https://github.com/davehadley/ndhistogram/blob/main/ndhistogram/CHANGELOG.md)
46//! for differences between releases.
47//! Please report any bugs in the [issues tracker](https://github.com/davehadley/ndhistogram/issues).
48//!
49//! ## Quick-start
50//!
51//! ```rust
52//! use ndhistogram::{Histogram, ndhistogram, axis::Uniform};
53//!
54//! # fn main() -> Result<(), ndhistogram::Error> {
55//! // create a 1D histogram with 10 equally sized bins between -5 and 5
56//! let mut hist = ndhistogram!(Uniform::new(10, -5.0, 5.0)?);
57//! // fill this histogram with a single value
58//! hist.fill(&1.0);
59//! // fill this histogram with weights
60//! hist.fill_with(&2.0, 4.0);
61//! // read the histogram values
62//! let x1 = hist.value(&1.0);
63//! let also_x1 = hist.value_at_index(7);
64//! assert_eq!(x1, also_x1);
65//! // iterate the histogram values
66//! for item in hist.iter() {
67//!     println!("{}, {}, {}", item.index, item.bin, item.value)
68//! }
69//! // print the histogram to stdout
70//! println!("{}", hist);
71//! # Ok(()) }
72//! ```
73//!
74//! ## Overview
75//!
76//! A [Histogram] is composed of two components:
77//!
78//! - The [Axes] which is a set of [Axis](axis::Axis) corresponding to each dimension of the histogram.
79//!   The [Axes] and [Axis](axis::Axis) define the binning of the histogram and are responsible for mapping from coordinate space (eg \[x,y,z\]) to an integer bin number.
80//! - The histogram bin value storage. Valid bin value types include any integer and floating number type as well as user defined types that implement [Fill], [FillWith] or [FillWithWeighted].
81//!
82//! ### Histogram Implementations
83//!
84//! - [VecHistogram]: bin values are stored in a [Vec].
85//!   Created with the [ndhistogram] macro.
86//!   This is the recommended implementation for most use cases.
87//!   However, as memory is allocated even for empty bins,
88//!   this may not be practical for very high dimension histograms.
89//! - [HashHistogram]: bin values are stored in a [HashMap](std::collections::HashMap).
90//!   Created with the [sparsehistogram] macro.
91//!   Useful for high dimension, mostly empty, histograms as empty bins
92//!   take up no memory.
93//!
94//! Alternative implementations are possible by implementing the [Histogram] trait.
95//!
96//! ### Axis Implementations
97//!
98//! - [Uniform](axis::Uniform)/[UniformNoFlow](axis::UniformNoFlow): equally sized bins in a some range with optional underflow/overflow bins.
99//! - [Variable](axis::Variable)/[VariableNoFlow](axis::VariableNoFlow): variable sized bins with optional underflow/overflow bins.
100//! - [UniformCyclic](axis::UniformCyclic)/[VariableCyclic](axis::VariableCyclic): cyclic or periodic versions of the Uniform and Variable axes.
101//! - [Category](axis::Category)/[CategoryNoFlow](axis::CategoryNoFlow): a finite set of discrete values with optional overflow bin.
102//!
103//! User defined axes types are possible by implementing the [Axis](axis::Axis) trait.
104//!
105//! ### Histogram Bin Values
106//!
107//! Histograms may be filled with values of the following types:
108//!
109//! - Primitive floating point and integer number types.
110//! - All types that implement [Fill]
111//! - All types that implement [FillWith]
112//! - All types that implement [FillWithWeighted]
113//! - All types that implement [AddAssign](std::ops::AddAssign) (as they are also [FillWith]).
114//! - All types that implement [AddAssign](std::ops::AddAssign) and [One](num_traits::One) (as they are also [Fill]).
115//!
116//! This crate defines the following bin value types:
117//!
118//! - [Sum](value::Sum) : a simple bin count that counts the number of times it has been filled.
119//! - [WeightedSum](value::WeightedSum) : as Sum but with weighted fills.
120//! - [Mean](value::Mean) : computes the mean of the values it is filled with.
121//! - [WeightedMean](value::WeightedMean) : as Mean but with weighted fills.
122//!
123//! User defined bin value types are possible by implementing the [Fill], [FillWith] or [FillWithWeighted] traits.
124//!
125//! ## How to Guide
126//!
127//! ### Customize the Bin Value Type
128//!
129//! ```rust
130//! use ndhistogram::{Histogram, ndhistogram, axis::Uniform, value::Mean};
131//! # fn main() -> Result<(), ndhistogram::Error> {
132//! // Create a histogram whose bin values are i32
133//! let mut hist = ndhistogram!(Uniform::new(10, -5.0, 5.0)?; i32);
134//! hist.fill_with(&1.0, 2);
135//! let value: Option<&i32> = hist.value(&1.0);
136//! assert_eq!(value, Some(&2));
137//!
138//! // More complex value types beyond primitives are available
139//! // "Mean" calculates the average of values it is filled with
140//! let mut hist = ndhistogram!(Uniform::new(10, -5.0, 5.0)?; Mean);
141//! hist.fill_with(&1.0, 1.0);
142//! hist.fill_with(&1.0, 3.0);
143//! assert_eq!(hist.value(&1.0).unwrap().mean(), 2.0);
144//!
145//! // for other examples see the documentation of Sum, WeightedSum and WeightedMean
146//!
147//! // user defined value types are possible by implementing
148//! // Fill, FillWith or FillWithWeighted traits
149//! # Ok(()) }
150//! ```
151//!
152//! ### Create and Use a 2D Histogram
153//!
154//! ```rust
155//! use ndhistogram::{Histogram, ndhistogram, axis::Uniform};
156//! # fn main() -> Result<(), ndhistogram::Error> {
157//! // create a 2D histogram
158//! let mut hist = ndhistogram!(Uniform::new(10, -5.0, 5.0)?, Uniform::new(10, -5.0, 5.0)?);
159//! // fill 2D histogram
160//! hist.fill(&(1.0, 2.0));
161//! // read back the histogram values
162//! let x1_y2 = hist.value(&(1.0, 2.0));
163//! // higher dimensions are possible with additional arguments to ndhistogram
164//! # Ok(()) }
165//! ```
166//!
167//! ### Create a Histogram with a Discrete Axis
168//!
169//! ```rust
170//! use ndhistogram::{Histogram, ndhistogram, axis::Category};
171//! # fn main() -> Result<(), ndhistogram::Error> {
172//! let mut hist = ndhistogram!(Category::new(vec![0, 2, 4]));
173//! hist.fill_with(&2, 42.0);
174//! hist.fill_with(&1, 128.0);
175//! assert_eq!(hist.value(&2), Some(&42.0));
176//! assert_eq!(hist.value(&1), Some(&128.0));
177//! assert_eq!(hist.value(&3), Some(&128.0));
178//! // 1 and 3 give the same answer as they are both mapped to the overflow bin
179//! // For a version with no overflow bins use CategoryNoFlow
180//!
181//! // The Category type can be any hashable type, for example string
182//! let mut hist = ndhistogram!(Category::new(vec!["Red", "Blue", "Green"]));
183//! hist.fill(&"Red");
184//! assert_eq!(hist.value(&"Red"), Some(&1.0));
185//! # Ok(()) }
186//! ```
187//!
188//! ### Create a Histogram with Variable Sized Bins
189//!
190//! ```rust
191//! use ndhistogram::{Histogram, ndhistogram, axis::Variable};
192//! # fn main() -> Result<(), ndhistogram::Error> {
193//! let mut hist = ndhistogram!(Variable::new(vec![0.0, 1.0, 3.0, 6.0])?);
194//! for x in 0..6 {
195//!     hist.fill(&f64::from(x));
196//! }
197//! assert_eq!(hist.value(&0.0), Some(&1.0));
198//! assert_eq!(hist.value(&1.0), Some(&2.0));
199//! assert_eq!(hist.value(&3.0), Some(&3.0));
200//! # Ok(()) }
201//! ```
202//!
203//! ### Create a Histogram with a Periodic or Cyclic Axis
204//!
205//! ```rust
206//! use std::f64::consts::PI;
207//! use ndhistogram::{Histogram, ndhistogram, axis::UniformCyclic};
208//! # fn main() -> Result<(), ndhistogram::Error> {
209//! let mut hist = ndhistogram!(UniformCyclic::<f64>::new(10, 0.0, 2.0*PI)?);
210//! hist.fill(&PI);
211//! hist.fill(&-PI);
212//! // +pi and -pi are mapped onto the same value
213//! assert_eq!(hist.value(&-PI), Some(&2.0));
214//! assert_eq!(hist.value(&PI), Some(&2.0));
215//! # Ok(()) }
216//! ```
217//!
218//! ### Create a Sparse Histogram
219//!
220//! ```rust
221//! use ndhistogram::{Histogram, sparsehistogram, axis::Uniform};
222//! # fn main() -> Result<(), ndhistogram::Error> {
223//! // This histogram has 1e18 bins, too many to allocate with a normal histogram
224//! let mut histogram_with_lots_of_bins = sparsehistogram!(
225//!     Uniform::new(1_000_000, -5.0, 5.0)?,
226//!     Uniform::new(1_000_000, -5.0, 5.0)?,
227//!     Uniform::new(1_000_000, -5.0, 5.0)?
228//! );
229//! histogram_with_lots_of_bins.fill(&(1.0, 2.0, 3.0));
230//! // read back the filled value
231//! assert_eq!(histogram_with_lots_of_bins.value(&(1.0, 2.0, 3.0)).unwrap(), &1.0);
232//! // unfilled bins will return None
233//! assert!(histogram_with_lots_of_bins.value(&(0.0, 0.0, 0.0)).is_none());
234//! # Ok(()) }
235//! ```
236//!
237//! ### Merge Histograms
238//!
239//! ```rust
240//! use ndhistogram::{Histogram, ndhistogram, axis::Uniform};
241//! # fn main() -> Result<(), ndhistogram::Error> {
242//! let mut hist1 = ndhistogram!(Uniform::<f64>::new(10, -5.0, 5.0)?);
243//! let mut hist2 = ndhistogram!(Uniform::<f64>::new(10, -5.0, 5.0)?);
244//! hist1.fill_with(&0.0, 2.0);
245//! hist2.fill_with(&0.0, 3.0);
246//! let combined_hist = (hist1 + &hist2).expect("Axes are compatible");
247//! # assert_eq!(combined_hist.value(&0.0).unwrap(), &5.0);
248//! # Ok(()) }
249//! ```
250//!
251//! ### Iterate over Histogram Bins in Parallel
252//!
253//! ```rust
254//! # fn main() -> Result<(), ndhistogram::Error> {
255//! #[cfg(feature = "rayon")] {
256//! use rayon::prelude::*;
257//! use ndhistogram::{Histogram, ndhistogram, axis::Uniform};
258//! let mut histogram = ndhistogram!(Uniform::<f64>::new(10, -5.0, 5.0)?);
259//! let sum: f64 = histogram.par_iter().map(|bin| bin.value).sum();
260//! // see also: par_iter_mut, par_values, par_values_mut.
261//! assert_eq!(sum, 0.0);
262//! # }
263//! # Ok(()) }
264//! ```
265//!
266//! Requires "rayon" feature enabled.
267//!
268//! ## Crate Feature Flags
269//!
270//! All cargo features of this crate are off by default.
271//! The following features can be enabled in your `Cargo.toml`:
272//!
273//! - [serde] : enable support for histogram serialization and deserialization.
274//! - [rayon] : enable parallel iteration over histograms.
275//!
276//! ## How to contribute
277//!
278//! If you discover a bug in this crate or a mistake in the documentation please either
279//! [open an issue](https://github.com/davehadley/ndhistogram/issues) or
280//! [submit a pull request](https://github.com/davehadley/ndhistogram/pulls).
281//!
282//! If you want to request or add a new feature please
283//! [open an issue](https://github.com/davehadley/ndhistogram/issues).
284
285#![doc(issue_tracker_base_url = "https://github.com/davehadley/ndhistogram/issues")]
286#![doc(html_root_url = "https://docs.rs/ndhistogram/0.10.0")]
287#![cfg_attr(
288    debug_assertions,
289    warn(
290        missing_debug_implementations,
291        rust_2018_idioms,
292        unreachable_pub,
293        unused_import_braces,
294    ),
295    deny(unsafe_code, macro_use_extern_crate),
296    warn(
297        missing_docs,
298        rustdoc::missing_crate_level_docs,
299        rustdoc::broken_intra_doc_links,
300    )
301)]
302
303mod axes;
304pub mod axis;
305mod histogram;
306
307pub mod value;
308
309pub use axes::Axes;
310pub use axes::AxesTuple;
311pub use histogram::fill::Fill;
312pub use histogram::fill::FillWith;
313pub use histogram::fill::FillWithWeighted;
314pub use histogram::hashhistogram::HashHistogram;
315pub use histogram::histogram::Histogram;
316pub use histogram::histogram::Item;
317pub use histogram::vechistogram::VecHistogram;
318
319/// Type alias for 1D [Histogram]s returned by [ndhistogram].
320pub type Hist1D<X, V = f64> = VecHistogram<AxesTuple<(X,)>, V>;
321
322/// Type alias for 2D [Histogram]s returned by [ndhistogram].
323pub type Hist2D<X, Y, V = f64> = VecHistogram<AxesTuple<(X, Y)>, V>;
324
325/// Type alias for 3D [Histogram]s returned by [ndhistogram].
326pub type Hist3D<X, Y, Z, V = f64> = VecHistogram<AxesTuple<(X, Y, Z)>, V>;
327
328/// Type alias for ND [Histogram]s returned by [ndhistogram].
329pub type HistND<A, V = f64> = VecHistogram<AxesTuple<A>, V>;
330
331/// Type alias for 1D [Histogram]s returned by [sparsehistogram].
332pub type SparseHist1D<X, V = f64> = HashHistogram<AxesTuple<(X,)>, V>;
333
334/// Type alias for 2D [Histogram]s returned by [sparsehistogram].
335pub type SparseHist2D<X, Y, V = f64> = HashHistogram<AxesTuple<(X, Y)>, V>;
336
337/// Type alias for 3D [Histogram]s returned by [sparsehistogram].
338pub type SparseHist3D<X, Y, Z, V = f64> = HashHistogram<AxesTuple<(X, Y, Z)>, V>;
339
340/// Type alias for ND [Histogram]s returned by [sparsehistogram].
341pub type SparseHistND<A, V = f64> = HashHistogram<AxesTuple<A>, V>;
342
343/// Provides errors that may be returned by [Histogram]s.
344pub mod error;
345
346pub use error::Error;
347
348#[macro_use]
349mod macros;