Skip to content

Commit a4e8c5d

Browse files
Merge pull request #5 from jturner314/bulk-quantiles
Improve bulk quantiles
2 parents 1d8c671 + 8f9f0b6 commit a4e8c5d

File tree

8 files changed

+424
-303
lines changed

8 files changed

+424
-303
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ itertools = { version = "0.7.0", default-features = false }
2323
indexmap = "1.0"
2424

2525
[dev-dependencies]
26+
criterion = "0.2"
2627
quickcheck = { version = "0.8.1", default-features = false }
2728
ndarray-rand = "0.9"
2829
approx = "0.3"
2930
quickcheck_macros = "0.8"
31+
32+
[[bench]]
33+
name = "sort"
34+
harness = false

benches/sort.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
extern crate criterion;
2+
extern crate ndarray;
3+
extern crate ndarray_stats;
4+
extern crate rand;
5+
6+
use criterion::{
7+
black_box, criterion_group, criterion_main, AxisScale, BatchSize, Criterion,
8+
ParameterizedBenchmark, PlotConfiguration,
9+
};
10+
use ndarray::prelude::*;
11+
use ndarray_stats::Sort1dExt;
12+
use rand::prelude::*;
13+
14+
fn get_from_sorted_mut(c: &mut Criterion) {
15+
let lens = vec![10, 100, 1000, 10000];
16+
let benchmark = ParameterizedBenchmark::new(
17+
"get_from_sorted_mut",
18+
|bencher, &len| {
19+
let mut rng = StdRng::seed_from_u64(42);
20+
let mut data: Vec<_> = (0..len).collect();
21+
data.shuffle(&mut rng);
22+
let indices: Vec<_> = (0..len).step_by(len / 10).collect();
23+
bencher.iter_batched(
24+
|| Array1::from(data.clone()),
25+
|mut arr| {
26+
for &i in &indices {
27+
black_box(arr.get_from_sorted_mut(i));
28+
}
29+
},
30+
BatchSize::SmallInput,
31+
)
32+
},
33+
lens,
34+
)
35+
.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic));
36+
c.bench("get_from_sorted_mut", benchmark);
37+
}
38+
39+
fn get_many_from_sorted_mut(c: &mut Criterion) {
40+
let lens = vec![10, 100, 1000, 10000];
41+
let benchmark = ParameterizedBenchmark::new(
42+
"get_many_from_sorted_mut",
43+
|bencher, &len| {
44+
let mut rng = StdRng::seed_from_u64(42);
45+
let mut data: Vec<_> = (0..len).collect();
46+
data.shuffle(&mut rng);
47+
let indices: Vec<_> = (0..len).step_by(len / 10).collect();
48+
bencher.iter_batched(
49+
|| Array1::from(data.clone()),
50+
|mut arr| {
51+
black_box(arr.get_many_from_sorted_mut(&indices));
52+
},
53+
BatchSize::SmallInput,
54+
)
55+
},
56+
lens,
57+
)
58+
.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic));
59+
c.bench("get_many_from_sorted_mut", benchmark);
60+
}
61+
62+
criterion_group! {
63+
name = benches;
64+
config = Criterion::default();
65+
targets = get_from_sorted_mut, get_many_from_sorted_mut
66+
}
67+
criterion_main!(benches);

src/histogram/strategies.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,8 @@ where
308308
let n_points = a.len();
309309

310310
let mut a_copy = a.to_owned();
311-
let first_quartile = a_copy.quantile_mut::<Nearest>(n64(0.25)).unwrap();
312-
let third_quartile = a_copy.quantile_mut::<Nearest>(n64(0.75)).unwrap();
311+
let first_quartile = a_copy.quantile_mut(n64(0.25), &Nearest).unwrap();
312+
let third_quartile = a_copy.quantile_mut(n64(0.75), &Nearest).unwrap();
313313
let iqr = third_quartile - first_quartile;
314314

315315
let bin_width = FreedmanDiaconis::compute_bin_width(n_points, iqr);

src/quantile/interpolate.rs

Lines changed: 13 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
//! Interpolation strategies.
2-
use ndarray::azip;
3-
use ndarray::prelude::*;
42
use noisy_float::types::N64;
53
use num_traits::{Float, FromPrimitive, NumOps, ToPrimitive};
64

@@ -45,14 +43,7 @@ pub trait Interpolate<T> {
4543
/// **Panics** if `None` is provided for the lower value when it's needed
4644
/// or if `None` is provided for the higher value when it's needed.
4745
#[doc(hidden)]
48-
fn interpolate<D>(
49-
lower: Option<Array<T, D>>,
50-
higher: Option<Array<T, D>>,
51-
q: N64,
52-
len: usize,
53-
) -> Array<T, D>
54-
where
55-
D: Dimension;
46+
fn interpolate(lower: Option<T>, higher: Option<T>, q: N64, len: usize) -> T;
5647
}
5748

5849
/// Select the higher value.
@@ -75,12 +66,7 @@ impl<T> Interpolate<T> for Higher {
7566
fn needs_higher(_q: N64, _len: usize) -> bool {
7667
true
7768
}
78-
fn interpolate<D>(
79-
_lower: Option<Array<T, D>>,
80-
higher: Option<Array<T, D>>,
81-
_q: N64,
82-
_len: usize,
83-
) -> Array<T, D> {
69+
fn interpolate(_lower: Option<T>, higher: Option<T>, _q: N64, _len: usize) -> T {
8470
higher.unwrap()
8571
}
8672
}
@@ -92,12 +78,7 @@ impl<T> Interpolate<T> for Lower {
9278
fn needs_higher(_q: N64, _len: usize) -> bool {
9379
false
9480
}
95-
fn interpolate<D>(
96-
lower: Option<Array<T, D>>,
97-
_higher: Option<Array<T, D>>,
98-
_q: N64,
99-
_len: usize,
100-
) -> Array<T, D> {
81+
fn interpolate(lower: Option<T>, _higher: Option<T>, _q: N64, _len: usize) -> T {
10182
lower.unwrap()
10283
}
10384
}
@@ -109,12 +90,7 @@ impl<T> Interpolate<T> for Nearest {
10990
fn needs_higher(q: N64, len: usize) -> bool {
11091
!<Self as Interpolate<T>>::needs_lower(q, len)
11192
}
112-
fn interpolate<D>(
113-
lower: Option<Array<T, D>>,
114-
higher: Option<Array<T, D>>,
115-
q: N64,
116-
len: usize,
117-
) -> Array<T, D> {
93+
fn interpolate(lower: Option<T>, higher: Option<T>, q: N64, len: usize) -> T {
11894
if <Self as Interpolate<T>>::needs_lower(q, len) {
11995
lower.unwrap()
12096
} else {
@@ -133,24 +109,11 @@ where
133109
fn needs_higher(_q: N64, _len: usize) -> bool {
134110
true
135111
}
136-
fn interpolate<D>(
137-
lower: Option<Array<T, D>>,
138-
higher: Option<Array<T, D>>,
139-
_q: N64,
140-
_len: usize,
141-
) -> Array<T, D>
142-
where
143-
D: Dimension,
144-
{
112+
fn interpolate(lower: Option<T>, higher: Option<T>, _q: N64, _len: usize) -> T {
145113
let denom = T::from_u8(2).unwrap();
146-
let mut lower = lower.unwrap();
114+
let lower = lower.unwrap();
147115
let higher = higher.unwrap();
148-
azip!(
149-
mut lower, ref higher in {
150-
*lower = lower.clone() + (higher.clone() - lower.clone()) / denom.clone()
151-
}
152-
);
153-
lower
116+
lower.clone() + (higher.clone() - lower.clone()) / denom.clone()
154117
}
155118
}
156119

@@ -164,23 +127,12 @@ where
164127
fn needs_higher(_q: N64, _len: usize) -> bool {
165128
true
166129
}
167-
fn interpolate<D>(
168-
lower: Option<Array<T, D>>,
169-
higher: Option<Array<T, D>>,
170-
q: N64,
171-
len: usize,
172-
) -> Array<T, D>
173-
where
174-
D: Dimension,
175-
{
130+
fn interpolate(lower: Option<T>, higher: Option<T>, q: N64, len: usize) -> T {
176131
let fraction = float_quantile_index_fraction(q, len).to_f64().unwrap();
177-
let mut a = lower.unwrap();
178-
let b = higher.unwrap();
179-
azip!(mut a, ref b in {
180-
let a_f64 = a.to_f64().unwrap();
181-
let b_f64 = b.to_f64().unwrap();
182-
*a = a.clone() + T::from_f64(fraction * (b_f64 - a_f64)).unwrap();
183-
});
184-
a
132+
let lower = lower.unwrap();
133+
let higher = higher.unwrap();
134+
let lower_f64 = lower.to_f64().unwrap();
135+
let higher_f64 = higher.to_f64().unwrap();
136+
lower.clone() + T::from_f64(fraction * (higher_f64 - lower_f64)).unwrap()
185137
}
186138
}

0 commit comments

Comments
 (0)