From 2789798ab3533995fa7bf266cdad6c91bea33e34 Mon Sep 17 00:00:00 2001 From: Tom Wanzek Date: Mon, 18 Jul 2016 20:05:59 -0400 Subject: [PATCH] Updated d3-array and related tests. Definitions and tests up-to-date with release 1.12.0 including complete d3-array and d3-quadtree. --- d3-array/d3-array-tests.ts | 154 +++++++++++++++++++++++++++++++++---- d3-array/index.d.ts | 81 +++++++++++++------ 2 files changed, 194 insertions(+), 41 deletions(-) diff --git a/d3-array/d3-array-tests.ts b/d3-array/d3-array-tests.ts index 5fbd4e18c7..c2b8c82b43 100644 --- a/d3-array/d3-array-tests.ts +++ b/d3-array/d3-array-tests.ts @@ -7,6 +7,8 @@ */ import * as d3 from 'd3-array'; +import { scaleTime } from 'd3-scale'; +import { timeYear } from 'd3-time'; // ----------------------------------------------------------------------------- // Preparatory Steps @@ -252,7 +254,7 @@ num = d3.variance(mixedObjectArray, function (datum, index, array) { }); // ----------------------------------------------------------------------------- -// Test Searching Arrrays +// Test Searching Arrays // ----------------------------------------------------------------------------- // scan() ---------------------------------------------------------------------- @@ -263,19 +265,72 @@ num = d3.scan(mixedObjectArray, function (a, b) { // bisectLeft() ---------------------------------------------------------------- -// TODO: complete +num = d3.bisectLeft([0, 2, 3, 4, 7, 8], 4); +num = d3.bisectLeft([0, 2, 3, 4, 7, 8], 4, 1); +num = d3.bisectLeft([0, 2, 3, 4, 7, 8], 4, 1, 4); -// bisect() -------------------------------------------------------------------- +num = d3.bisectLeft(['0', '2', '3', '4', '7', '8'], '21'); +num = d3.bisectLeft(['0', '2', '3', '4', '7', '8'], '21', 1); +num = d3.bisectLeft(['0', '2', '3', '4', '7', '8'], '21', 1, 4); -// TODO: complete +num = d3.bisectLeft([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1)); +num = d3.bisectLeft([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1), 1); +num = d3.bisectLeft([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1), 1, 2); // bisectRight() --------------------------------------------------------------- -// TODO: complete +num = d3.bisectRight([0, 2, 3, 4, 7, 8], 4); +num = d3.bisectRight([0, 2, 3, 4, 7, 8], 4, 1); +num = d3.bisectRight([0, 2, 3, 4, 7, 8], 4, 1, 4); + +num = d3.bisectRight(['0', '2', '3', '4', '7', '8'], '21'); +num = d3.bisectRight(['0', '2', '3', '4', '7', '8'], '21', 1); +num = d3.bisectRight(['0', '2', '3', '4', '7', '8'], '21', 1, 4); + +num = d3.bisectRight([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1)); +num = d3.bisectRight([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1), 1); +num = d3.bisectRight([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1), 1, 2); + +// bisect() -------------------------------------------------------------------- + +num = d3.bisect([0, 2, 3, 4, 7, 8], 4); +num = d3.bisect([0, 2, 3, 4, 7, 8], 4, 1); +num = d3.bisect([0, 2, 3, 4, 7, 8], 4, 1, 4); + +num = d3.bisect(['0', '2', '3', '4', '7', '8'], '21'); +num = d3.bisect(['0', '2', '3', '4', '7', '8'], '21', 1); +num = d3.bisect(['0', '2', '3', '4', '7', '8'], '21', 1, 4); + +num = d3.bisect([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1)); +num = d3.bisect([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1), 1); +num = d3.bisect([new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1)], new Date(2011, 2, 1), 1, 2); // bisector() ------------------------------------------------------------------ -// TODO: complete +mixedObjectArray.sort(function (a, b) { return a.date.valueOf() - b.date.valueOf(); }); + +let mixedObjectDateBisectorObject: d3.Bisector; + +// define using accessor +mixedObjectDateBisectorObject = d3.bisector(function (el) { + return el.date; +}); + +// define using comparator +mixedObjectDateBisectorObject = d3.bisector(function (el, x) { + return el.date.valueOf() - x.valueOf(); +}); + +// bisect left +num = mixedObjectDateBisectorObject.left(mixedObjectArray, new Date(2015, 3, 14)); +num = mixedObjectDateBisectorObject.left(mixedObjectArray, new Date(2015, 3, 14), 1); +num = mixedObjectDateBisectorObject.left(mixedObjectArray, new Date(2015, 3, 14), 3, 4); + +// bisect right +num = mixedObjectDateBisectorObject.right(mixedObjectArray, new Date(2015, 3, 14)); +num = mixedObjectDateBisectorObject.right(mixedObjectArray, new Date(2015, 3, 14), 1); +num = mixedObjectDateBisectorObject.right(mixedObjectArray, new Date(2015, 3, 14), 3, 4); + // ascending() ----------------------------------------------------------------- @@ -395,35 +450,102 @@ testArrays = d3.zip( // Test Histogram // ----------------------------------------------------------------------------- +let tScale = scaleTime(); + // Create histogram generator ================================================== -let defaultHistogram: d3.HistogramGenerator; +let defaultHistogram: d3.HistogramGenerator; defaultHistogram = d3.histogram(); -let testHistogram: d3.HistogramGenerator; -testHistogram = d3.histogram(); +let testHistogram: d3.HistogramGenerator; +testHistogram = d3.histogram(); // Configure histogram generator =============================================== // value(...) ------------------------------------------------------------------ +testHistogram = testHistogram.value(function (d, i, data) { + let datum: MixedObject = d; // d is of type MixedObject + let index: number = i; // i is number + let array: MixedObject[] = data; // data is of type MixedObject[] + return datum.date; +}); + +let valueAccessorFn: (d: MixedObject, i: number, data: MixedObject[]) => Date; +valueAccessorFn = testHistogram.value(); // domain(...) ----------------------------------------------------------------- +// test with array +testHistogram = testHistogram.domain([new Date(2014, 3, 15), new Date(2017, 4, 15)]); + +// usage with scale domain: +let domain = tScale.domain(); + +testHistogram = testHistogram.domain([domain[0], domain[domain.length]]); + +// testHistogram = testHistogram.domain(tScale.domain()); // fails, as scale domain is an array with possibly more than the two elements expected by histogram + +// use with accessor function +testHistogram = testHistogram.domain(function (values) { + return [values[0], values[values.length]]; +}); + +// get current domain accessor function +let domainAccessorFn: (values: Date[]) => [Date, Date]; +domainAccessorFn = testHistogram.domain(); // thresholds(...) ------------------------------------------------------------- +// with count constant +defaultHistogram = defaultHistogram.thresholds(3); + +// with threshold count generator +defaultHistogram = defaultHistogram.thresholds(d3.thresholdScott); + +// with thresholds value array + +testHistogram = testHistogram.thresholds([new Date(2015, 11, 15), new Date(2016, 6, 1), new Date(2016, 8, 30)]); + +// with thresholds value array accessors +testHistogram = testHistogram.thresholds(function (values: Date[], min: Date, max: Date) { + let thresholds: Date[]; + thresholds = [values[0], values[2], values[4]]; + return thresholds; +}); + +testHistogram = testHistogram.thresholds(tScale.ticks(timeYear)); // Use histogram generator ===================================================== -let bins: Array>; -bins = testHistogram(mixedObjectArray); +let defaultBins: Array>; +defaultBins = defaultHistogram([-1, 0, 1, 1, 3, 20, 234]); + +let defaultBin: d3.Bin; +defaultBin = defaultBins[0]; + +num = defaultBin.length; // defaultBin is array +num = defaultBin[0]; // with element type number +num = defaultBin.x0; // bin lower bound is number +num = defaultBin.x1; // bin upper bound is number + +let testBins: Array>; +testBins = testHistogram(mixedObjectArray); + +let testBin: d3.Bin; +testBin = testBins[0]; + +num = testBin.length; // defaultBin is array +let mixedObject: MixedObject = testBin[0]; // with element type MixedObject +date = testBin.x0; // bin lower bound is Date +date = testBin.x1; // bin upper bound is Date + -// Note currently bins returned are as follows: !!! -// first bin.x0:T bin.x1:number, -// interior bin.x0:number, bin.x1:number -// last bin.x0:number, bin.x1:T // Histogram Tresholds ========================================================= -// TODO: Complete \ No newline at end of file +num = d3.thresholdFreedmanDiaconis([-1, 0, 1, 1, 3, 20, 234], -1, 234); + +num = d3.thresholdScott([-1, 0, 1, 1, 3, 20, 234], -1, 234); + +num = d3.thresholdSturges([-1, 0, 1, 1, 3, 20, 234]); \ No newline at end of file diff --git a/d3-array/index.d.ts b/d3-array/index.d.ts index 819d38b3f7..f5de581b1b 100644 --- a/d3-array/index.d.ts +++ b/d3-array/index.d.ts @@ -175,27 +175,29 @@ export function variance(array: T[], accessor: (datum: T, index: number, arra // -------------------------------------------------------------------------------------- -// Searching Arrrays +// Searching Arrays // -------------------------------------------------------------------------------------- export function scan(array: T[], comparator: (a: T, b: T) => number): number; export function bisectLeft(array: number[], x: number, lo?: number, hi?: number): number; export function bisectLeft(array: string[], x: string, lo?: number, hi?: number): number; +export function bisectLeft(array: Date[], x: Date, lo?: number, hi?: number): number; + +export function bisectRight(array: number[], x: number, lo?: number, hi?: number): number; +export function bisectRight(array: string[], x: string, lo?: number, hi?: number): number; +export function bisectRight(array: Date[], x: Date, lo?: number, hi?: number): number; export var bisect: typeof bisectRight; -export function bisectRight(array: T[], x: T, lo?: number, hi?: number): number; - -export function bisector(accessor: (x: T) => U): { +export interface Bisector { left: (array: T[], x: U, lo?: number, hi?: number) => number; right: (array: T[], x: U, lo?: number, hi?: number) => number; } -export function bisector(comparator: (a: T, b: U) => number): { - left: (array: T[], x: U, lo?: number, hi?: number) => number; - right: (array: T[], x: U, lo?: number, hi?: number) => number; -} +export function bisector(accessor: (x: T) => U): Bisector; + +export function bisector(comparator: (a: T, b: U) => number): Bisector // NB. this is limited to primitive values due to D3's use of the <, >, and >= operators. Results get weird for object instances. /** @@ -281,9 +283,9 @@ export function zip(...arrays: T[][]): T[][]; // Histogram // -------------------------------------------------------------------------------------- -export interface Bin extends Array { - x0: number; - x1: number; +export interface Bin extends Array { + x0: Value; + x1: Value; } /** @@ -294,25 +296,54 @@ export type ThresholdCountGenerator = (values: number[], min?: number, max?: num /** * Type definition for threshold generator which returns an array of recommended thresholds */ -export type ThresholdArrayGenerator = (values: number[], min?: number, max?: number) => number[]; +export type ThresholdArrayGenerator = (values: Value[], min?: Value, max?: Value) => Value[]; -export interface HistogramGenerator { - (data: T[]): Array>; - value(): (d: T, i: number, data: T[]) => number; - value(valueAccessor: (d: T, i: number, data: T[]) => number): HistogramGenerator; - domain(): (values: number[]) => [number, number]; - domain(domainAccessor: (values: number[]) => [number, number]): HistogramGenerator; - thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator; - thresholds(count: number): HistogramGenerator; - thresholds(thresholds: number[]): HistogramGenerator; - thresholds(thresholds: ThresholdCountGenerator): HistogramGenerator; - thresholds(thresholds: ThresholdArrayGenerator): HistogramGenerator; +export interface HistogramGenerator { + (data: Datum[]): Array>; + value(): (d: Datum, i: number, data: Datum[]) => Value; + value(valueAccessor: (d: Datum, i: number, data: Datum[]) => Value): this; + domain(): (values: Value[]) => [Value, Value]; + domain(domain: [Value, Value]): this; + domain(domainAccessor: (values: Value[]) => [Value, Value]): this; + thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator; + /** + * Divide the domain uniformly into approximately count bins. IMPORTANT: This threshold + * setting approach only works, when the materialized values are numbers! + * + * @param count The desired number of uniform bins. + */ + thresholds(count: number): this; + /** + * Set a threshold accessor function, which returns the desired number of bins. + * Divides the domain uniformly into approximately count bins. IMPORTANT: This threshold + * setting approach only works, when the materialized values are numbers! + * + * @param count A function which accepts as arguments the array of materialized values, and + * optionally the domain minimum and maximum. The function calcutates and returns the suggested + * number of bins. + */ + thresholds(count: ThresholdCountGenerator): this; + /** + * Set the array of values to be used as thresholds in determining the bins. + * @param thresholds Array of threshold values used for binning. The elements must + * be of the same type as the materialized values of the histogram. + */ + thresholds(thresholds: Value[]): this; + /** + * Set a threshold accessor function, which returns the array of values to be used as + * thresholds in determining the bins. + * + * @param thresholds A function which accepts as arguments the array of materialized values, and + * optionally the domain minimum and maximum. The function calcutates and returns the array of values to be used as + * thresholds in determining the bins. + */ + thresholds(thresholds: ThresholdArrayGenerator): this; } -export function histogram(): HistogramGenerator; -export function histogram(): HistogramGenerator; +export function histogram(): HistogramGenerator; +export function histogram(): HistogramGenerator; // -------------------------------------------------------------------------------------- // Histogram Thresholds