Skip to content

Commit 142f4e6

Browse files
committed
quantileIndex, medianIndex
closes #140
1 parent 1d1e460 commit 142f4e6

File tree

6 files changed

+61
-12
lines changed

6 files changed

+61
-12
lines changed

README.md

+19-7
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,17 @@ const m = d3.min(array);
5757

5858
## API Reference
5959

60-
* [Statistics](#statistics)
61-
* [Search](#search)
62-
* [Transformations](#transformations)
63-
* [Iterables](#iterables)
64-
* [Sets](#sets)
65-
* [Bins](#bins)
66-
* [Interning](#interning)
60+
- [d3-array](#d3-array)
61+
- [Installing](#installing)
62+
- [API Reference](#api-reference)
63+
- [Statistics](#statistics)
64+
- [Search](#search)
65+
- [Transformations](#transformations)
66+
- [Iterables](#iterables)
67+
- [Sets](#sets)
68+
- [Bins](#bins)
69+
- [Bin Thresholds](#bin-thresholds)
70+
- [Interning](#interning)
6771

6872
### Statistics
6973

@@ -117,6 +121,10 @@ Returns the mean of the given *iterable* of numbers. If the iterable contains no
117121

118122
Returns the median of the given *iterable* of numbers using the [R-7 method](https://en.wikipedia.org/wiki/Quantile#Estimating_quantiles_from_a_sample). If the iterable contains no numbers, returns undefined. An optional *accessor* function may be specified, which is equivalent to calling Array.from before computing the median. This method ignores undefined and NaN values; this is useful for ignoring missing data.
119123

124+
<a name="medianIndex" href="#medianIndex">#</a> d3.<b>medianIndex</b>(<i>array</i>, <i>p</i>[, <i>accessor</i>]) [Source](https://github.com/d3/d3-array/blob/main/src/median.js "Source")
125+
126+
Similar to *median*, but returns the index of the element to the left of the median.
127+
120128
<a name="cumsum" href="#cumsum">#</a> d3.<b>cumsum</b>(<i>iterable</i>[, <i>accessor</i>]) · [Source](https://github.com/d3/d3-array/blob/main/src/cumsum.js), [Examples](https://observablehq.com/@d3/d3-cumsum)
121129

122130
Returns the cumulative sum of the given *iterable* of numbers, as a Float64Array of the same length. If the iterable contains no numbers, returns zeros. An optional *accessor* function may be specified, which is equivalent to calling Array.from before computing the cumulative sum. This method ignores undefined and NaN values; this is useful for ignoring missing data.
@@ -137,6 +145,10 @@ d3.quantile(a, 0.1); // 2
137145

138146
An optional *accessor* function may be specified, which is equivalent to calling *array*.map(*accessor*) before computing the quantile.
139147

148+
<a name="quantileIndex" href="#quantileIndex">#</a> d3.<b>quantileIndex</b>(<i>array</i>, <i>p</i>[, <i>accessor</i>]) [Source](https://github.com/d3/d3-array/blob/main/src/quantile.js "Source")
149+
150+
Similar to *quantile*, but returns the index to the left of *p*.
151+
140152
<a name="quantileSorted" href="#quantileSorted">#</a> d3.<b>quantileSorted</b>(<i>array</i>, <i>p</i>[, <i>accessor</i>]) · [Source](https://github.com/d3/d3-array/blob/main/src/quantile.js), [Examples](https://observablehq.com/@d3/d3-mean-d3-median-and-friends)
141153

142154
Similar to *quantile*, but expects the input to be a **sorted** *array* of values. In contrast with *quantile*, the accessor is only called on the elements needed to compute the quantile.

src/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ export {default as thresholdSturges} from "./threshold/sturges.js";
1717
export {default as max} from "./max.js";
1818
export {default as maxIndex} from "./maxIndex.js";
1919
export {default as mean} from "./mean.js";
20-
export {default as median} from "./median.js";
20+
export {default as median, medianIndex} from "./median.js";
2121
export {default as merge} from "./merge.js";
2222
export {default as min} from "./min.js";
2323
export {default as minIndex} from "./minIndex.js";
2424
export {default as mode} from "./mode.js";
2525
export {default as nice} from "./nice.js";
2626
export {default as pairs} from "./pairs.js";
2727
export {default as permute} from "./permute.js";
28-
export {default as quantile, quantileSorted} from "./quantile.js";
28+
export {default as quantile, quantileIndex, quantileSorted} from "./quantile.js";
2929
export {default as quickselect} from "./quickselect.js";
3030
export {default as range} from "./range.js";
3131
export {default as rank} from "./rank.js";

src/median.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import quantile from "./quantile.js";
1+
import quantile, {quantileIndex} from "./quantile.js";
22

33
export default function median(values, valueof) {
44
return quantile(values, 0.5, valueof);
55
}
6+
7+
export function medianIndex(values, valueof) {
8+
return quantileIndex(values, 0.5, valueof);
9+
}

src/quantile.js

+14
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,17 @@ export function quantileSorted(values, p, valueof = number) {
2727
value1 = +valueof(values[i0 + 1], i0 + 1, values);
2828
return value0 + (value1 - value0) * (i - i0);
2929
}
30+
31+
export function quantileIndex(values, p, valueof) {
32+
if (valueof) values = Float64Array.from(numbers(values, valueof));
33+
const q = quantile(values, p);
34+
35+
let index, v = -Infinity;
36+
for (let i = 0; i < values.length; i++) {
37+
const x = values[i];
38+
if (x <= q) {
39+
if (x > v) index = i, v = x;
40+
}
41+
}
42+
return index;
43+
}

test/median-test.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import assert from "assert";
2-
import {median} from "../src/index.js";
2+
import {median, medianIndex} from "../src/index.js";
33
import {OneTimeNumber} from "./OneTimeNumber.js";
44

55
it("median(array) returns the median value for numbers", () => {
@@ -98,6 +98,16 @@ it("median(array, f) uses the undefined context", () => {
9898
assert.deepStrictEqual(results, [undefined, undefined]);
9999
});
100100

101+
it("medianIndex(array) returns the index", () => {
102+
assert.deepEqual(medianIndex([1, 2]), 0);
103+
assert.deepEqual(medianIndex([1, 2, 3]), 1);
104+
assert.deepEqual(medianIndex([1, 3, 2]), 2);
105+
assert.deepEqual(medianIndex([2, 3, 1]), 0);
106+
assert.deepEqual(medianIndex([1]), 0);
107+
assert.deepEqual(medianIndex([]), undefined);
108+
});
109+
110+
101111
function box(value) {
102112
return {value: value};
103113
}

test/quantile-test.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import assert from "assert";
2-
import {quantile, quantileSorted} from "../src/index.js";
2+
import {quantile, quantileIndex, quantileSorted} from "../src/index.js";
33

44
it("quantileSorted(array, p) requires sorted numeric input, quantile doesn't", () => {
55
assert.strictEqual(quantileSorted([1, 2, 3, 4], 0), 1);
@@ -83,6 +83,15 @@ it("quantile(array, p, f) observes the specified accessor", () => {
8383
assert.strictEqual(quantile([], 1, unbox), undefined);
8484
});
8585

86+
it("quantileIndex(array) returns the index", () => {
87+
assert.deepEqual(quantileIndex([1, 2], 0.2), 0);
88+
assert.deepEqual(quantileIndex([1, 2, 3], 0.2), 0);
89+
assert.deepEqual(quantileIndex([1, 3, 2], 0.2), 0);
90+
assert.deepEqual(quantileIndex([2, 3, 1], 0.2), 2);
91+
assert.deepEqual(quantileIndex([1], 0.2), 0);
92+
assert.deepEqual(quantileIndex([], 0.2), undefined);
93+
});
94+
8695
function box(value) {
8796
return {value: value};
8897
}

0 commit comments

Comments
 (0)