d3-array: strict histogram proposition [BREAKING CHANGE] (#25450)

* Use $ExpectError
Rename some variables

* More typing

* More tests

And update d3-contour

* Add deprecated interface

* Update TypeScript minimal version

Oups :/

* d3-chord TS version update

* Linting errors.

* Strict function type for statistics functions

* Strict function type for histogram

* Update d3-contour

* Ts Lint

* Fix accessor type inference

* Arrays for accessors are ArrayLike, in stat fct
This commit is contained in:
denisname
2018-10-08 17:35:40 +02:00
committed by Andy
parent 7a5dfdb5f6
commit 8799abfabc
5 changed files with 407 additions and 101 deletions

View File

@@ -40,12 +40,14 @@ class MixedObject {
}
let num: number;
let date: Date;
let undef: undefined;
let mixedObject: MixedObject;
let numOrUndefined: number | undefined;
let strOrUndefined: string | undefined;
let numericOrUndefined: NumCoercible | undefined;
let dateOrUndefined: Date | undefined;
let mixedObjectOrUndefined: MixedObject | undefined;
let numOrUndefinedExtent: [number, number] | [undefined, undefined];
let strOrUndefinedExtent: [string, string] | [undefined, undefined];
let numericOrUndefinedExtent: [NumCoercible, NumCoercible] | [undefined, undefined];
@@ -68,6 +70,7 @@ const mixedObjectArray = [
];
const mixedObjectOrUndefinedArray = [...mixedObjectArray, undefined];
const mixedObjectArrayLike = mixedObjectArray as ArrayLike<MixedObject>;
let typedArray = Uint8Array.from(numbersArray);
let readonlyNumbersArray = numbersArray as ReadonlyArray<number>;
@@ -76,32 +79,60 @@ const readonlyStringyNumbersArray = stringyNumbersArray as ReadonlyArray<string>
const readonlyNumericArray = numericArray as ReadonlyArray<NumCoercible>;
const readonlyDateArray = dateArray as ReadonlyArray<Date>;
const readonlyMixedObjectArray = mixedObjectArray as ReadonlyArray<MixedObject>;
const readonlyMixedObjectOrUndefinedArray = mixedObjectOrUndefinedArray as ReadonlyArray<MixedObject>;
const readonlyMixedObjectOrUndefinedArray = mixedObjectOrUndefinedArray as ReadonlyArray<MixedObject | undefined>;
function accessorMixedObjectToNum(datum: MixedObject, index: number, array: MixedObject[]): number {
function accessorMixedObjectToNum(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): number {
return datum.num;
}
function accessorMixedObjectToStr(datum: MixedObject, index: number, array: MixedObject[]): string {
function accessorMixedObjectToStr(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): string {
return datum.str;
}
function accessorMixedObjectToNumeric(datum: MixedObject, index: number, array: MixedObject[]): NumCoercible {
function accessorMixedObjectToNumeric(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): NumCoercible {
return datum.numeric;
}
function accessorMixedObjectToDate(datum: MixedObject, index: number, array: MixedObject[]): Date {
function accessorMixedObjectToDate(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): Date {
return datum.date;
}
function accessorMixedObjectToNumOrUndefined(datum: MixedObject | undefined, index: number, array: Array<MixedObject | undefined>): number | undefined | null {
function accessorMixedObjectToNumOrUndefined(datum: MixedObject | undefined, index: number, array: ArrayLike<MixedObject | undefined>): number | undefined | null {
return datum ? datum.num : undefined;
}
function accessorMixedObjectToStrOrUndefined(datum: MixedObject | undefined, index: number, array: MixedObject[]): string | undefined | null {
function accessorMixedObjectToStrOrUndefined(datum: MixedObject | undefined, index: number, array: ArrayLike<MixedObject>): string | undefined | null {
return datum ? datum.str : undefined;
}
function accessorLikeMixedObjectToNum(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): number {
return datum.num;
}
function accessorLikeMixedObjectToStr(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): string {
return datum.str;
}
function accessorLikeMixedObjectToNumeric(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): NumCoercible {
return datum.numeric;
}
function accessorLikeMixedObjectToDate(datum: MixedObject, index: number, array: ArrayLike<MixedObject>): Date {
return datum.date;
}
function accessorLikeMixedObjectToNumOrUndefined(datum: MixedObject | undefined, index: number, array: ArrayLike<MixedObject | undefined>): number | undefined | null {
return datum ? datum.num : undefined;
}
function accessorLikeMixedObjectToStrOrUndefined(datum: MixedObject | undefined, index: number, array: ArrayLike<MixedObject>): string | undefined | null {
return datum ? datum.str : undefined;
}
function accessorReadOnlyMixedObjectToNumOrUndefined(datum: MixedObject | undefined, index: number, array: ArrayLike<MixedObject | undefined>): number | undefined | null {
return datum ? datum.num : undefined;
}
// -----------------------------------------------------------------------------
// Test Statistics
// -----------------------------------------------------------------------------
@@ -131,7 +162,24 @@ numericOrUndefined = d3Array.max(mixedObjectArray, accessorMixedObjectToNumeric)
dateOrUndefined = d3Array.max(mixedObjectArray, accessorMixedObjectToDate);
numOrUndefined = d3Array.max(mixedObjectArray, accessorMixedObjectToNumOrUndefined);
strOrUndefined = d3Array.max(mixedObjectArray, accessorMixedObjectToStrOrUndefined);
numOrUndefined = d3Array.max(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.max(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.max(mixedObjectArrayLike, accessorLikeMixedObjectToNum);
numOrUndefined = d3Array.max(mixedObjectArray, accessorLikeMixedObjectToNum);
numOrUndefined = d3Array.max(mixedObjectArray, accessorReadOnlyMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.max(mixedObjectArray, (d) => {
const l: MixedObject = d;
return l.num;
});
strOrUndefined = d3Array.max(mixedObjectArray, (d) => {
const l: MixedObject = d;
return l.str;
});
// $ExpectError
numOrUndefined = d3Array.max(readonlyNumbersArray, (d, i, a) => { a.push(3); return 0; });
// min() -----------------------------------------------------------------------
@@ -158,7 +206,7 @@ numericOrUndefined = d3Array.min(mixedObjectArray, accessorMixedObjectToNumeric)
dateOrUndefined = d3Array.min(mixedObjectArray, accessorMixedObjectToDate);
numOrUndefined = d3Array.min(mixedObjectArray, accessorMixedObjectToNumOrUndefined);
strOrUndefined = d3Array.min(mixedObjectArray, accessorMixedObjectToStrOrUndefined);
numOrUndefined = d3Array.max(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.min(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// extent() --------------------------------------------------------------------
@@ -185,7 +233,7 @@ mixedOrUndefinedExtent = d3Array.extent(mixedObjectArray, accessorMixedObjectToN
dateMixedOrUndefined = d3Array.extent(mixedObjectArray, accessorMixedObjectToDate);
numOrUndefinedExtent = d3Array.extent(mixedObjectArray, accessorMixedObjectToNumOrUndefined);
strOrUndefinedExtent = d3Array.extent(mixedObjectArray, accessorMixedObjectToStrOrUndefined);
numOrUndefinedExtent = d3Array.extent(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefinedExtent = d3Array.extent(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// mean() ----------------------------------------------------------------------
@@ -200,7 +248,7 @@ numOrUndefined = d3Array.mean(readonlyNumbersOrUndefinedArray);
numOrUndefined = d3Array.mean(mixedObjectArray, accessorMixedObjectToNum);
numOrUndefined = d3Array.mean(mixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.mean(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.mean(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// median() --------------------------------------------------------------------
@@ -215,7 +263,7 @@ numOrUndefined = d3Array.median(readonlyNumbersOrUndefinedArray);
numOrUndefined = d3Array.median(mixedObjectArray, accessorMixedObjectToNum);
numOrUndefined = d3Array.median(mixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.median(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.median(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// quantile() ------------------------------------------------------------------
@@ -230,7 +278,7 @@ numOrUndefined = d3Array.quantile(readonlyNumbersOrUndefinedArray, 0.5);
numOrUndefined = d3Array.quantile(mixedObjectArray, 0.5, accessorMixedObjectToNum);
numOrUndefined = d3Array.quantile(mixedObjectOrUndefinedArray, 0.5, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.quantile(readonlyMixedObjectOrUndefinedArray, 0.5, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.quantile(readonlyMixedObjectOrUndefinedArray, 0.5, accessorReadOnlyMixedObjectToNumOrUndefined);
// sum() -----------------------------------------------------------------------
@@ -245,7 +293,7 @@ numOrUndefined = d3Array.sum(readonlyNumbersOrUndefinedArray);
numOrUndefined = d3Array.sum(mixedObjectArray, accessorMixedObjectToNum);
numOrUndefined = d3Array.sum(mixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.sum(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.sum(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// deviation() -----------------------------------------------------------------
@@ -260,7 +308,7 @@ numOrUndefined = d3Array.deviation(readonlyNumbersOrUndefinedArray);
numOrUndefined = d3Array.deviation(mixedObjectArray, accessorMixedObjectToNum);
numOrUndefined = d3Array.deviation(mixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.deviation(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.deviation(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// variance() ------------------------------------------------------------------
@@ -275,7 +323,7 @@ numOrUndefined = d3Array.variance(readonlyNumbersOrUndefinedArray);
numOrUndefined = d3Array.variance(mixedObjectArray, accessorMixedObjectToNum);
numOrUndefined = d3Array.variance(mixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.variance(readonlyMixedObjectOrUndefinedArray, accessorMixedObjectToNumOrUndefined);
numOrUndefined = d3Array.variance(readonlyMixedObjectOrUndefinedArray, accessorReadOnlyMixedObjectToNumOrUndefined);
// -----------------------------------------------------------------------------
// Test Searching Arrays
@@ -480,8 +528,10 @@ let mergedArray: MixedObject[];
mergedArray = d3Array.merge(testArrays); // inferred type
mergedArray = d3Array.merge<MixedObject>(testArrays); // explicit type
// mergedArray = d3Array.merge<MixedObject>([[10, 40, 30], [15, 30]]); // fails, type mismatch
// mergedArray = d3Array.merge([testArray1, [15, 30]]); // fails, type mismatch
// $ExpectError
mergedArray = d3Array.merge<MixedObject>([[10, 40, 30], [15, 30]]); // fails, type mismatch
// $ExpectError
mergedArray = d3Array.merge([testArray1, [15, 30]]); // fails, type mismatch
mergedArray = d3Array.merge(readonlyTestArrays); // inferred type
mergedArray = d3Array.merge<MixedObject>(readonlyTestArrays); // explicit type
@@ -569,7 +619,8 @@ numbersArray = d3Array.range(1, 10, 0.5);
mergedArray = d3Array.shuffle(mergedArray);
mergedArray = d3Array.shuffle(mergedArray, 1);
mergedArray = d3Array.shuffle(mergedArray, 1, 3);
// mergedArray = d3Array.shuffle(readonlyMergedArray); // fails, shuffle mutates input array in-place
// $ExpectError
mergedArray = d3Array.shuffle(readonlyMergedArray); // fails, shuffle mutates input array in-place
// Test each TypedArray explicitly. Can't use ArrayLike in this case because shuffle is mutable and ArrayLike would include ReadonlyArray
const resultInt8: Int8Array = d3Array.shuffle(new Int8Array(numbersArray));
@@ -608,96 +659,259 @@ testArrays = d3Array.zip(readonlyTestArray1, readonlyTestArray2);
// Test Histogram
// -----------------------------------------------------------------------------
const tScale = scaleTime();
const timeScale = scaleTime();
// Create histogram generator ==================================================
let defaultHistogram: d3Array.HistogramGenerator<number, number>;
defaultHistogram = d3Array.histogram();
// number - number
let histoNumber_Number: d3Array.HistogramGeneratorNumber<number, number>;
histoNumber_Number = d3Array.histogram();
histoNumber_Number = d3Array.histogram<number, number>();
let testHistogram: d3Array.HistogramGenerator<MixedObject, Date>;
testHistogram = d3Array.histogram<MixedObject, Date>();
// MixedObject - number | undefined
let histoMixed_NumberOrUndefined: d3Array.HistogramGeneratorNumber<MixedObject, number | undefined>;
histoMixed_NumberOrUndefined = d3Array.histogram<MixedObject, number | undefined>();
// MixedObject | undefined - number | undefined
let histoMixedOrUndefined_NumberOrUndefined: d3Array.HistogramGeneratorNumber<MixedObject | undefined, number | undefined>;
histoMixedOrUndefined_NumberOrUndefined = d3Array.histogram<MixedObject | undefined, number | undefined>();
// MixedObject | undefined - number
let histoMixedOrUndefined_Number: d3Array.HistogramGeneratorNumber<MixedObject | undefined, number>;
histoMixedOrUndefined_Number = d3Array.histogram<MixedObject | undefined, number>();
// MixedObject - Date
let histoMixedObject_Date: d3Array.HistogramGeneratorDate<MixedObject, Date>;
histoMixedObject_Date = d3Array.histogram<MixedObject, Date>();
// MixedObject - Date | undefined
let histoMixedObject_DateOrUndefined: d3Array.HistogramGeneratorDate<MixedObject, Date | undefined>;
histoMixedObject_DateOrUndefined = d3Array.histogram<MixedObject, Date | undefined>();
let defaultHistogram: d3Array.HistogramGeneratorNumber<number, number>;
defaultHistogram = d3Array.histogram();
// Configure histogram generator ===============================================
// value(...) ------------------------------------------------------------------
testHistogram = testHistogram.value((d, i, data) => {
const datum: MixedObject = d; // d is of type MixedObject
const index: number = i; // i is number
const array: ArrayLike<MixedObject> = data; // data is of type MixedObject[]
return datum.date;
let valueAccessorFn: (d: MixedObject, i: number, data: MixedObject[]) => Date;
valueAccessorFn = histoMixedObject_Date.value();
type valueAccessor<D, V> = (d: D, i: number, data: D[]) => V;
// number - number
const valueFnNumber_Number: valueAccessor<number, number> = histoNumber_Number.value();
histoNumber_Number = histoNumber_Number.value((d: number, i: number, data: ArrayLike<number>) => {
return d - num;
});
let valueAccessorFn: (d: MixedObject, i: number, data: MixedObject[]) => Date;
valueAccessorFn = testHistogram.value();
// MixedObject - number | undefined
const valueFnMixedObject_NumberOrUndefined: valueAccessor<MixedObject, number | undefined> = histoMixed_NumberOrUndefined.value();
histoMixed_NumberOrUndefined = histoMixed_NumberOrUndefined.value((d: MixedObject, i: number, data: ArrayLike<MixedObject>) => {
return d.str === "NA" ? undefined : d.num;
});
// MixedObject | undefined - number | undefined
const valueFnMixedOrUndefined_NumberOrUndefined: valueAccessor<MixedObject | undefined, number | undefined> = histoMixedOrUndefined_NumberOrUndefined.value();
histoMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined.value((d: MixedObject | undefined, i: number, data: ArrayLike<MixedObject | undefined>) => {
return d ? d.num : undefined;
});
// MixedObject | undefined - number
const valueFnMixedOrUndefined_Number: valueAccessor<MixedObject | undefined, number> = histoMixedOrUndefined_Number.value();
histoMixedOrUndefined_Number = histoMixedOrUndefined_Number.value((d: MixedObject | undefined, i: number, data: ArrayLike<MixedObject | undefined>) => {
return d ? d.num : 0;
});
// MixedObject - Date
const valueFnMixedObject_Date: valueAccessor<MixedObject, Date> = histoMixedObject_Date.value();
histoMixedObject_Date = histoMixedObject_Date.value((d: MixedObject, i: number, data: ArrayLike<MixedObject>) => {
return d.date;
});
// MixedObject - Date | undefined
const valueFnMixedObject_DateOrUndefined: valueAccessor<MixedObject, Date | undefined> = histoMixedObject_DateOrUndefined.value();
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.value((d: MixedObject, i: number, data: ArrayLike<MixedObject>) => {
return d.date;
});
// domain(...) -----------------------------------------------------------------
// test with array
testHistogram = testHistogram.domain([new Date(2014, 3, 15), new Date(2017, 4, 15)]);
const domain = timeScale.domain();
let domainFnNumber: (array: number[]) => [number, number] | [undefined, undefined];
let domainFnNumberOrUndef: (array: Array<number | undefined>) => [number, number] | [undefined, undefined];
let domainFnDate: (values: Date[]) => [Date, Date];
// usage with scale domain:
const domain = tScale.domain();
// number - number
domainFnNumber = histoNumber_Number.domain();
histoNumber_Number = histoNumber_Number.domain([0, 100]);
histoNumber_Number = histoNumber_Number.domain(d3Array.extent);
testHistogram = testHistogram.domain([domain[0], domain[domain.length]]);
// MixedObject - number | undefined
domainFnNumber = histoNumber_Number.domain();
histoMixed_NumberOrUndefined = histoMixed_NumberOrUndefined.domain([0, 100]);
histoMixed_NumberOrUndefined = histoMixed_NumberOrUndefined.domain(d3Array.extent);
// testHistogram = testHistogram.domain(tScale.domain()); // fails, as scale domain is an array with possibly more than the two elements expected by histogram
// MixedObject | undefined - number | undefined
domainFnNumberOrUndef = histoMixedOrUndefined_NumberOrUndefined.domain();
histoMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined.domain([0, 100]);
histoMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined.domain(d3Array.extent);
// use with accessor function
testHistogram = testHistogram.domain(values => [values[0], values[values.length]]);
// MixedObject | undefined - number
domainFnNumber = histoMixedOrUndefined_Number.domain();
histoMixedOrUndefined_Number = histoMixedOrUndefined_Number.domain([0, 100]);
histoMixedOrUndefined_Number = histoMixedOrUndefined_Number.domain(d3Array.extent);
// get current domain accessor function
let domainAccessorFn: (values: Date[]) => [Date, Date];
domainAccessorFn = testHistogram.domain();
// MixedObject - Date
domainFnDate = histoMixedObject_Date.domain();
histoMixedObject_Date = histoMixedObject_Date.domain([new Date(2014, 3, 15), new Date(2017, 4, 15)]);
histoMixedObject_Date = histoMixedObject_Date.domain([domain[0], domain[domain.length]]);
histoMixedObject_Date = histoMixedObject_Date.domain((values) => [values[0], values[values.length]]);
// $ExpectError
histoMixedObject_Date = histoMixedObject_Date.domain(timeScale.domain()); // fails, as scale domain is an array with possibly more than the two elements expected by histogram
// MixedObject - Date | undefined
domainFnDate = histoMixedObject_Date.domain();
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.domain([new Date(2014, 3, 15), new Date(2017, 4, 15)]);
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.domain([domain[0], domain[domain.length]]);
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.domain((values) => [values[0]!, values[values.length]!]);
// thresholds(...) -------------------------------------------------------------
// with count constant
defaultHistogram = defaultHistogram.thresholds(3);
type thresholdsWithUndefinedCont = d3Array.ThresholdCountGenerator;
type thresholdsWithUndefinedArray = d3Array.ThresholdNumberArrayGenerator<number | undefined>;
// with threshold count generator
defaultHistogram = defaultHistogram.thresholds(d3Array.thresholdScott);
let thresholds: d3Array.ThresholdCountGenerator<number> | d3Array.ThresholdNumberArrayGenerator<number>;
let thresholdsWithUndefined: thresholdsWithUndefinedCont | thresholdsWithUndefinedArray;
const thresholdsArray: d3Array.ThresholdNumberArrayGenerator<number> = (x: ArrayLike<number>) => [5, 10, 20];
// with thresholds value array
let thresholdsDate: d3Array.ThresholdDateArrayGenerator<Date>;
let thresholdsDateOrUndefined: d3Array.ThresholdDateArrayGenerator<Date | undefined>;
testHistogram = testHistogram.thresholds([new Date(2015, 11, 15), new Date(2016, 6, 1), new Date(2016, 8, 30)]);
// number - number
thresholds = histoNumber_Number.thresholds();
histoNumber_Number = histoNumber_Number.thresholds(3);
histoNumber_Number = histoNumber_Number.thresholds([5, 10, 20]);
histoNumber_Number = histoNumber_Number.thresholds(d3Array.thresholdScott);
histoNumber_Number = histoNumber_Number.thresholds(thresholdsArray);
// with thresholds value array accessors
testHistogram = testHistogram.thresholds((values: Date[], min: Date, max: Date) => {
let thresholds: Date[];
thresholds = [values[0], values[2], values[4]];
// MixedObject - number | undefined
thresholdsWithUndefined = histoMixed_NumberOrUndefined.thresholds();
histoMixed_NumberOrUndefined = histoMixed_NumberOrUndefined.thresholds(3);
histoMixed_NumberOrUndefined = histoMixed_NumberOrUndefined.thresholds([5, 10, 20]);
histoMixed_NumberOrUndefined = histoMixed_NumberOrUndefined.thresholds(d3Array.thresholdScott);
// MixedObject | undefined - number | undefined
thresholdsWithUndefined = histoMixedOrUndefined_NumberOrUndefined.thresholds();
histoMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined.thresholds(3);
histoMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined.thresholds([5, 10, 20]);
histoMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined.thresholds(d3Array.thresholdScott);
// MixedObject | undefined - number
thresholds = histoMixedOrUndefined_Number.thresholds();
histoMixedOrUndefined_Number = histoMixedOrUndefined_Number.thresholds(5);
histoMixedOrUndefined_Number = histoMixedOrUndefined_Number.thresholds([5, 10, 20]);
histoMixedOrUndefined_Number = histoMixedOrUndefined_Number.thresholds(d3Array.thresholdSturges);
// MixedObject - Date
thresholdsDate = histoMixedObject_Date.thresholds();
histoMixedObject_Date = histoMixedObject_Date.thresholds([new Date(2015, 11, 15), new Date(2016, 6, 1), new Date(2016, 8, 30)]);
histoMixedObject_Date = histoMixedObject_Date.thresholds(timeScale.ticks(timeYear));
histoMixedObject_Date = histoMixedObject_Date.thresholds((values: ArrayLike<Date>) => [new Date(2015, 11, 15), new Date(2016, 6, 1), new Date(2016, 8, 30)]);
histoMixedObject_Date = histoMixedObject_Date.thresholds((values: ArrayLike<Date>, min: Date, max: Date) => {
const thresholds: Date[] = [values[0], values[2], values[4]];
return thresholds;
});
// $ExpectError
histoMixedObject_Date = histoMixedObject_Date.thresholds(d3Array.thresholdScott);
// MixedObject - Date | undefined
thresholdsDateOrUndefined = histoMixedObject_DateOrUndefined.thresholds();
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.thresholds([new Date(2015, 11, 15), new Date(2016, 6, 1), new Date(2016, 8, 30)]);
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.thresholds(timeScale.ticks(timeYear));
histoMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined.thresholds((values: ArrayLike<Date | undefined>, min: Date, max: Date) => {
const thresholds: Date[] = [values[0]!, new Date(2015, 11, 15), values[values.length]!];
return thresholds;
});
testHistogram = testHistogram.thresholds(tScale.ticks(timeYear));
// Use histogram generator =====================================================
numbersArray = [-1, 0, 1, 1, 3, 20, 234];
undef = d3Array.histogram()([])[0].x0 as undefined;
undef = d3Array.histogram<number | undefined, number | undefined>()([undefined])[0].x0 as undefined;
let defaultBins: Array<d3Array.Bin<number, number>>;
defaultBins = defaultHistogram(numbersArray);
// number - number
let binsNumber_Number: Array<d3Array.Bin<number, number>>;
binsNumber_Number = histoNumber_Number([-1, 0, 1, 1, 3, 20, 234]);
let defaultBin: d3Array.Bin<number, number>;
defaultBin = defaultBins[0];
let binNumber_Number: d3Array.Bin<number, number>;
binNumber_Number = binsNumber_Number[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
num = binNumber_Number.length;
num = binNumber_Number[0];
numOrUndefined = binNumber_Number.x0;
numOrUndefined = binNumber_Number.x1;
let testBins: Array<d3Array.Bin<MixedObject, Date>>;
testBins = testHistogram(mixedObjectArray);
testBins = testHistogram(readonlyMixedObjectArray);
// MixedObject - number | undefined
let binsNumberMixed_NumberOrUndefined: Array<d3Array.Bin<MixedObject, number | undefined>>;
binsNumberMixed_NumberOrUndefined = histoMixed_NumberOrUndefined(mixedObjectArray);
let testBin: d3Array.Bin<MixedObject, Date>;
testBin = testBins[0];
let binNumberMixed_NumberOrUndefined: d3Array.Bin<MixedObject, number | undefined>;
binNumberMixed_NumberOrUndefined = binsNumberMixed_NumberOrUndefined[0];
num = testBin.length; // defaultBin is array
const mixedObject: MixedObject = testBin[0]; // with element type MixedObject
date = testBin.x0; // bin lower bound is Date
date = testBin.x1; // bin upper bound is Date
num = binNumberMixed_NumberOrUndefined.length;
mixedObject = binNumberMixed_NumberOrUndefined[0];
numOrUndefined = binNumberMixed_NumberOrUndefined.x0;
numOrUndefined = binNumberMixed_NumberOrUndefined.x1;
// MixedObject | undefined - number | undefined
let binsNumberMixedOrUndefined_NumberOrUndefined: Array<d3Array.Bin<MixedObject | undefined, number | undefined>>;
binsNumberMixedOrUndefined_NumberOrUndefined = histoMixedOrUndefined_NumberOrUndefined(mixedObjectArray);
let binNumberMixedOrUndefined_NumberOrUndefined: d3Array.Bin<MixedObject | undefined, number | undefined>;
binNumberMixedOrUndefined_NumberOrUndefined = binsNumberMixedOrUndefined_NumberOrUndefined[0];
num = binNumberMixedOrUndefined_NumberOrUndefined.length;
mixedObjectOrUndefined = binNumberMixedOrUndefined_NumberOrUndefined[0];
numOrUndefined = binNumberMixedOrUndefined_NumberOrUndefined.x0;
numOrUndefined = binNumberMixedOrUndefined_NumberOrUndefined.x1;
// MixedObject | undefined - number
let binsNumberMixedOrUndefined_Number: Array<d3Array.Bin<MixedObject | undefined, number>>;
binsNumberMixedOrUndefined_Number = histoMixedOrUndefined_Number(mixedObjectArray);
let binNumberMixedOrUndefined_Number: d3Array.Bin<MixedObject | undefined, number>;
binNumberMixedOrUndefined_Number = binsNumberMixedOrUndefined_Number[0];
num = binNumberMixedOrUndefined_Number.length;
mixedObjectOrUndefined = binNumberMixedOrUndefined_Number[0];
numOrUndefined = binNumberMixedOrUndefined_Number.x0;
numOrUndefined = binNumberMixedOrUndefined_Number.x1;
// MixedObject - Date
let binsMixedObject_Date: Array<d3Array.Bin<MixedObject, Date>>;
binsMixedObject_Date = histoMixedObject_Date(mixedObjectArray);
binsMixedObject_Date = histoMixedObject_Date(readonlyMixedObjectArray);
let binMixedObject_Date: d3Array.Bin<MixedObject, Date>;
binMixedObject_Date = binsMixedObject_Date[0];
num = binMixedObject_Date.length;
mixedObject = binMixedObject_Date[0];
dateOrUndefined = binMixedObject_Date.x0;
dateOrUndefined = binMixedObject_Date.x1;
// MixedObject - Date | undefined
let binsMixedObject_DateOrUndefined: Array<d3Array.Bin<MixedObject, Date | undefined>>;
binsMixedObject_DateOrUndefined = histoMixedObject_DateOrUndefined(mixedObjectArray);
let binMixedObject_DateOrUndefined: d3Array.Bin<MixedObject, Date | undefined>;
binMixedObject_DateOrUndefined = binsMixedObject_DateOrUndefined[0];
num = binMixedObject_DateOrUndefined.length;
mixedObject = binMixedObject_DateOrUndefined[0];
dateOrUndefined = binMixedObject_DateOrUndefined.x0;
dateOrUndefined = binMixedObject_DateOrUndefined.x1;
// Histogram Tresholds =========================================================
@@ -716,3 +930,8 @@ num = d3Array.thresholdScott(readonlyNumbersArray, -1, 234);
num = d3Array.thresholdSturges(numbersArray);
num = d3Array.thresholdSturges(typedArray);
num = d3Array.thresholdSturges(readonlyNumbersArray);
// Deprecated ==================================================================
const histDeprecatedNumber: d3Array.HistogramGenerator<MixedObject, number> = d3Array.histogram<MixedObject, number>();
const histDeprecatedDate: d3Array.HistogramGenerator<MixedObject, Date> = d3Array.histogram<MixedObject, Date>();

View File

@@ -1,10 +1,13 @@
// Type definitions for D3JS d3-array module 1.2
// Project: https://github.com/d3/d3-array
// Definitions by: Alex Ford <https://github.com/gustavderdrache>, Boris Yankov <https://github.com/borisyankov>, Tom Wanzek <https://github.com/tomwanzek>
// Definitions by: Alex Ford <https://github.com/gustavderdrache>
// Boris Yankov <https://github.com/borisyankov>
// Tom Wanzek <https://github.com/tomwanzek>
// denisname <https://github.com/denisname>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// Last module patch version validated against: 1.2.0
// Last module patch version validated against: 1.2.1
// --------------------------------------------------------------------------
// Shared Types and Interfaces
@@ -90,18 +93,27 @@ export function extent<T, U extends Numeric>(array: ArrayLike<T>, accessor: (dat
* Return the mean of an array of numbers
*/
export function mean<T extends Numeric>(array: ArrayLike<T | undefined | null>): number | undefined;
/**
* Return the mean of an array of numbers
*/
export function mean<T>(array: ArrayLike<T>, accessor: (datum: T, index: number, array: ArrayLike<T>) => number | undefined | null): number | undefined;
/**
* Return the median of an array of numbers
*/
export function median<T extends Numeric>(array: ArrayLike<T | undefined | null>): number | undefined;
/**
* Return the median of an array of numbers
*/
export function median<T>(array: ArrayLike<T>, accessor: (element: T, i: number, array: ArrayLike<T>) => number | undefined | null): number | undefined;
/**
* Returns the p-quantile of an array of numbers
*/
export function quantile<T extends Numeric>(array: ArrayLike<T | undefined | null>, p: number): number | undefined;
export function quantile<T>(array: ArrayLike<T>, p: number, accessor: (element: T, i: number, array: ArrayLike<T>) => number | undefined | null): number | undefined;
/**
@@ -312,29 +324,97 @@ export function zip<T>(...arrays: Array<ArrayLike<T>>): T[][];
// Histogram
// --------------------------------------------------------------------------------------
export interface Bin<Datum, Value extends number | Date> extends Array<Datum> {
x0: Value;
x1: Value;
export interface Bin<Datum, Value extends number | Date | undefined> extends Array<Datum> {
x0: Value | undefined;
x1: Value | undefined;
}
/**
* Type definition for threshold generator which returns the count of recommended thresholds
*/
export type ThresholdCountGenerator = (values: ArrayLike<number>, min?: number, max?: number) => number;
export type ThresholdCountGenerator<Value extends number | undefined = number | undefined> =
(values: ArrayLike<Value>, min: number, max: number) => number;
/**
* Type definition for threshold generator which returns an array of recommended thresholds
* Type definition for threshold generator which returns an array of recommended numbers thresholds
*/
export type ThresholdArrayGenerator<Value extends number | Date> = (values: ArrayLike<Value>, min?: Value, max?: Value) => Value[];
export type ThresholdNumberArrayGenerator<Value extends number | undefined> =
(values: ArrayLike<Value>, min: number, max: number) => Value[];
export interface HistogramGenerator<Datum, Value extends number | Date> {
/**
* Type definition for threshold generator which returns an array of recommended dates thresholds
*/
export type ThresholdDateArrayGenerator<Value extends Date | undefined> =
(values: ArrayLike<Value>, min: Date, max: Date) => Value[];
/**
* @deprecated Use `HistogramGeneratorNumber<Datum, Value>` for `number` values and `HistogramGeneratorDate<Datum, Value> for `Date` values.
*/
export interface HistogramGenerator<Datum, Value extends number | Date | undefined> {
(data: ArrayLike<Datum>): Array<Bin<Datum, Value>>;
value(): (d: Datum, i: number, data: ArrayLike<Datum>) => Value;
value(valueAccessor: (d: Datum, i: number, data: ArrayLike<Datum>) => Value): this;
domain(): (values: ArrayLike<Value>) => [Value, Value];
domain(): (values: ArrayLike<Value>) => [Value, Value] | [undefined, undefined];
domain(domain: [Value, Value]): this;
domain(domainAccessor: (values: ArrayLike<Value>) => [Value, Value]): this;
thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator<Value>;
domain(domainAccessor: (values: ArrayLike<Value>) => [Value, Value] | [undefined, undefined]): this;
/**
* Set the array of values to be used as thresholds in determining the bins.
*
* Any threshold values outside the domain are ignored. The first bin.x0 is always equal to the minimum domain value,
* and the last bin.x1 is always equal to the maximum domain value.
*
* @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: ArrayLike<Value>): this;
}
export interface HistogramCommon<Datum, Value extends number | Date | undefined> {
(data: ArrayLike<Datum>): Array<Bin<Datum, Value>>;
value(): (d: Datum, i: number, data: ArrayLike<Datum>) => Value;
value(valueAccessor: (d: Datum, i: number, data: ArrayLike<Datum>) => Value): this;
}
export interface HistogramGeneratorDate<Datum, Value extends Date | undefined> extends HistogramCommon<Datum, Date> {
domain(): (values: ArrayLike<Value>) => [Date, Date];
domain(domain: [Date, Date]): this;
domain(domainAccessor: (values: ArrayLike<Value>) => [Date, Date]): this;
thresholds(): ThresholdDateArrayGenerator<Value>;
/**
* Set the array of values to be used as thresholds in determining the bins.
*
* Any threshold values outside the domain are ignored. The first bin.x0 is always equal to the minimum domain value,
* and the last bin.x1 is always equal to the maximum domain value.
*
* @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: ArrayLike<Value>): this;
/**
* Set a threshold accessor function, which returns the array of values to be used as
* thresholds in determining the bins.
*
* Any threshold values outside the domain are ignored. The first bin.x0 is always equal to the minimum domain value,
* and the last bin.x1 is always equal to the maximum domain value.
*
* @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: ThresholdDateArrayGenerator<Value>): this;
}
export interface HistogramGeneratorNumber<Datum, Value extends number | undefined> extends HistogramCommon<Datum, Value> {
domain(): (values: ArrayLike<Value>) => [number, number] | [undefined, undefined];
domain(domain: [number, number]): this;
domain(domainAccessor: (values: ArrayLike<Value>) => [number, number] | [undefined, undefined]): this;
thresholds(): ThresholdCountGenerator<Value> | ThresholdNumberArrayGenerator<Value>;
/**
* Divide the domain uniformly into approximately count bins. IMPORTANT: This threshold
* setting approach only works, when the materialized values are numbers!
@@ -357,7 +437,7 @@ export interface HistogramGenerator<Datum, Value extends number | Date> {
* optionally the domain minimum and maximum. The function calcutates and returns the suggested
* number of bins.
*/
thresholds(count: ThresholdCountGenerator): this;
thresholds(count: ThresholdCountGenerator<Value>): this;
/**
* Set the array of values to be used as thresholds in determining the bins.
*
@@ -379,18 +459,25 @@ export interface HistogramGenerator<Datum, Value extends number | Date> {
* 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<Value>): this;
thresholds(thresholds: ThresholdNumberArrayGenerator<Value>): this;
}
export function histogram(): HistogramGenerator<number, number>;
export function histogram<Datum, Value extends number | Date>(): HistogramGenerator<Datum, Value>;
export function histogram(): HistogramGeneratorNumber<number, number>;
export function histogram<Datum, Value extends number | undefined>(): HistogramGeneratorNumber<Datum, Value>;
export function histogram<Datum, Value extends Date | undefined>(): HistogramGeneratorDate<Datum, Value>;
/**
* @deprecated Do not use Value generic which mixes number and Date types. Use either number or Date
* (in combination with undefined, as applicable) to obtain a type-specific histogram generator.
*/
export function histogram<Datum, Value extends number | Date | undefined>(): HistogramGenerator<Datum, Value>;
// --------------------------------------------------------------------------------------
// Histogram Thresholds
// --------------------------------------------------------------------------------------
export function thresholdFreedmanDiaconis(values: ArrayLike<number>, min: number, max: number): number; // of type ThresholdCountGenerator
export function thresholdFreedmanDiaconis(values: ArrayLike<number | undefined>, min: number, max: number): number; // of type ThresholdCountGenerator
export function thresholdScott(values: ArrayLike<number>, min: number, max: number): number; // of type ThresholdCountGenerator
export function thresholdScott(values: ArrayLike<number | undefined>, min: number, max: number): number; // of type ThresholdCountGenerator
export function thresholdSturges(values: ArrayLike<number>): number; // of type ThresholdCountGenerator
export function thresholdSturges(values: ArrayLike<number | undefined>): number; // of type ThresholdCountGenerator

View File

@@ -8,7 +8,7 @@
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": false,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"

View File

@@ -10,7 +10,7 @@ import * as d3Contour from 'd3-contour';
import {
range,
thresholdSturges,
ThresholdArrayGenerator,
ThresholdNumberArrayGenerator,
ThresholdCountGenerator
} from 'd3-array';
import { geoPath } from 'd3-geo';
@@ -39,13 +39,13 @@ function goldsteinPrice(x: number, y: number) {
let size: [number, number];
let boolFlag: boolean;
const thresholdArrayGen: ThresholdArrayGenerator<number> = (values: ArrayLike<number>, min?: number, max?: number) => {
const thresholdArrayGen: ThresholdNumberArrayGenerator<number> = (values: ArrayLike<number>, min?: number, max?: number) => {
let thresholds: number[];
thresholds = [values[1], values[2], values[4]];
return thresholds;
};
let thresholdGenerator: ThresholdArrayGenerator<number> | ThresholdCountGenerator;
let thresholdGenerator: ThresholdNumberArrayGenerator<number> | ThresholdCountGenerator<number>;
let pathStringMaybe: string | null;
let num: number;

View File

@@ -7,7 +7,7 @@
// Last module patch version validated against: 1.2.0
import { MultiPolygon } from 'geojson';
import { ThresholdArrayGenerator, ThresholdCountGenerator } from 'd3-array';
import { ThresholdNumberArrayGenerator, ThresholdCountGenerator } from 'd3-array';
/**
* An extended GeoJSON MultiPolygon representing a contour.
@@ -78,7 +78,7 @@ export interface Contours {
/**
* Returns the current threshold generator, which by default implements Sturges formula.
*/
thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator<number>;
thresholds(): ThresholdCountGenerator<number> | ThresholdNumberArrayGenerator<number>;
/**
* Sets the threshold generator to use the specified count and returns this contour generator.
* The input values extent will be uniformly divided into approximately count bins.
@@ -110,7 +110,7 @@ export interface Contours {
* @param thresholds A threshold generator function. The threshold generator function is passed the array of input values
* as its argument and returns either an array of calculated thresholds, or the count of thresholds to use.
*/
thresholds(thresholds: ThresholdCountGenerator | ThresholdArrayGenerator<number>): this;
thresholds(thresholds: ThresholdCountGenerator<number> | ThresholdNumberArrayGenerator<number>): this;
}
/**
@@ -196,7 +196,7 @@ export interface ContourDensity<Datum = [number, number]> {
/**
* Returns the current threshold generator, which by default generates about twenty nicely-rounded density thresholds.
*/
thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator<number>;
thresholds(): ThresholdCountGenerator<number> | ThresholdNumberArrayGenerator<number>;
/**
* Sets the threshold generator to use the specified count and returns this density contour estimator.
* Approximately count uniformly-spaced nicely-rounded thresholds will be generated.
@@ -228,7 +228,7 @@ export interface ContourDensity<Datum = [number, number]> {
* @param thresholds A threshold generator function. The threshold generator function is passed the array of input values
* as its argument and returns either an array of calculated thresholds, or the count of thresholds to use.
*/
thresholds(thresholds: ThresholdCountGenerator | ThresholdArrayGenerator<number>): this;
thresholds(thresholds: ThresholdCountGenerator<number> | ThresholdNumberArrayGenerator<number>): this;
/**
* Returns the current bandwidth, which defaults to 20.4939….