@tracerbench/stats
Advanced tools
Comparing version 4.5.5 to 5.0.0
@@ -45,2 +45,3 @@ "use strict"; | ||
const meanU = maxU / 2; | ||
// subtract every control data point to every experiment data point | ||
const deltas = a | ||
@@ -50,15 +51,17 @@ .map((a) => b.map((b) => a - b)) | ||
.sort((a, b) => a - b); | ||
// count the number of "wins" a > b and 0.5 for a tie if a == b | ||
const U = deltas.reduce((accum, value) => accum + (value < 0 ? 1 : value == 0 ? 0.5 : 0), 0); | ||
const lowerTail = U <= meanU; | ||
const standadDeviationU = Math.sqrt((maxU * (aLength + bLength + 1)) / 12); | ||
const standardDeviationU = Math.sqrt((maxU * (aLength + bLength + 1)) / 12); | ||
// we are estimating a discrete distribution so bias the mean depending on which tail | ||
// we are computing the pValue for | ||
// we are computing the pValue for. literally just +/- 0.5 | ||
const continuityCorrection = lowerTail ? 0.5 : -0.5; | ||
const zScore = (U - meanU + continuityCorrection) / standadDeviationU; | ||
// z is symmetrical, so use lower tail and double the cumulative for each tail | ||
// since this is a two tailed test | ||
// how many standard deviations the result is given the null hypothesis is true | ||
const zScore = (U - meanU + continuityCorrection) / standardDeviationU; | ||
// z is symmetrical, so use lower tail and double the cumulative of U for each tail | ||
// since this is a two tailed test. normal cumulative distribution function | ||
const pValue = jStat.normal.cdf(-Math.abs(zScore), 0, 1) * 2; | ||
const alpha = 1 - confidence; | ||
const lowerU = Math.round(jStat.normal.inv(alpha / 2, meanU + 0.5, standadDeviationU)); | ||
const upperU = Math.round(jStat.normal.inv(1 - alpha / 2, meanU + 0.5, standadDeviationU)); | ||
const lowerU = Math.round(jStat.normal.inv(alpha / 2, meanU + 0.5, standardDeviationU)); | ||
const upperU = Math.round(jStat.normal.inv(1 - alpha / 2, meanU + 0.5, standardDeviationU)); | ||
return { | ||
@@ -68,4 +71,4 @@ lower: deltas[lowerU], | ||
upper: deltas[upperU], | ||
zScore, | ||
pValue, | ||
zScore: +zScore.toPrecision(4), | ||
pValue: +pValue.toPrecision(4), | ||
U | ||
@@ -72,0 +75,0 @@ }; |
import { cartesianProduct, confidenceInterval } from './confidence-interval'; | ||
import { IConfidenceInterval, IOutliers, ISevenFigureSummary, IStatsOptions, Stats } from './stats'; | ||
import { convertMicrosecondsToMS, convertMSToMicroseconds, toNearestHundreth } from './utils'; | ||
import { convertMicrosecondsToMS, convertMSToMicroseconds, roundFloatAndConvertMicrosecondsToMS, toNearestHundreth } from './utils'; | ||
import { getWilcoxonRankSumTest } from './wilcoxon-rank-sum'; | ||
import { getWilcoxonSignedRankTest } from './wilcoxon-signed-rank'; | ||
export { cartesianProduct, confidenceInterval, Stats, convertMicrosecondsToMS, convertMSToMicroseconds, toNearestHundreth, getWilcoxonRankSumTest, getWilcoxonSignedRankTest, ISevenFigureSummary, IOutliers, IStatsOptions, IConfidenceInterval }; | ||
export { cartesianProduct, confidenceInterval, Stats, convertMicrosecondsToMS, convertMSToMicroseconds, toNearestHundreth, getWilcoxonRankSumTest, getWilcoxonSignedRankTest, ISevenFigureSummary, IOutliers, IStatsOptions, IConfidenceInterval, roundFloatAndConvertMicrosecondsToMS }; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getWilcoxonSignedRankTest = exports.getWilcoxonRankSumTest = exports.toNearestHundreth = exports.convertMSToMicroseconds = exports.convertMicrosecondsToMS = exports.Stats = exports.confidenceInterval = exports.cartesianProduct = void 0; | ||
exports.roundFloatAndConvertMicrosecondsToMS = exports.getWilcoxonSignedRankTest = exports.getWilcoxonRankSumTest = exports.toNearestHundreth = exports.convertMSToMicroseconds = exports.convertMicrosecondsToMS = exports.Stats = exports.confidenceInterval = exports.cartesianProduct = void 0; | ||
const confidence_interval_1 = require("./confidence-interval"); | ||
@@ -12,2 +12,3 @@ Object.defineProperty(exports, "cartesianProduct", { enumerable: true, get: function () { return confidence_interval_1.cartesianProduct; } }); | ||
Object.defineProperty(exports, "convertMSToMicroseconds", { enumerable: true, get: function () { return utils_1.convertMSToMicroseconds; } }); | ||
Object.defineProperty(exports, "roundFloatAndConvertMicrosecondsToMS", { enumerable: true, get: function () { return utils_1.roundFloatAndConvertMicrosecondsToMS; } }); | ||
Object.defineProperty(exports, "toNearestHundreth", { enumerable: true, get: function () { return utils_1.toNearestHundreth; } }); | ||
@@ -14,0 +15,0 @@ const wilcoxon_rank_sum_1 = require("./wilcoxon-rank-sum"); |
@@ -9,3 +9,5 @@ export interface Bucket { | ||
} | ||
export interface ISevenFigureSummary { | ||
export declare type ISevenFigureSummary = { | ||
[key in string | number]: number; | ||
} & { | ||
min: number; | ||
@@ -18,3 +20,3 @@ max: number; | ||
90: number; | ||
} | ||
}; | ||
export interface IOutliers { | ||
@@ -32,10 +34,16 @@ IQR: number; | ||
} | ||
export interface IConfidenceInterval { | ||
export declare type IConfidenceInterval = { | ||
[key in string]: number | boolean; | ||
} & { | ||
min: number; | ||
max: number; | ||
isSig: boolean; | ||
} | ||
median: number; | ||
zScore: number; | ||
pValue: number; | ||
U: number; | ||
}; | ||
export declare class Stats { | ||
readonly name: string; | ||
readonly estimator: number; | ||
estimator: number; | ||
readonly sparkLine: { | ||
@@ -45,11 +53,11 @@ control: string; | ||
}; | ||
readonly confidenceIntervals: { | ||
confidenceIntervals: { | ||
[key: number]: IConfidenceInterval; | ||
}; | ||
readonly confidenceInterval: IConfidenceInterval; | ||
readonly sevenFigureSummary: { | ||
confidenceInterval: IConfidenceInterval; | ||
sevenFigureSummary: { | ||
control: ISevenFigureSummary; | ||
experiment: ISevenFigureSummary; | ||
}; | ||
readonly outliers: { | ||
outliers: { | ||
control: IOutliers; | ||
@@ -62,18 +70,17 @@ experiment: IOutliers; | ||
}; | ||
readonly experimentMS: number[]; | ||
readonly controlMS: number[]; | ||
readonly experimentSortedMS: number[]; | ||
readonly controlSortedMS: number[]; | ||
readonly buckets: Bucket[]; | ||
readonly range: { | ||
experimentSorted: number[]; | ||
controlSorted: number[]; | ||
buckets: Bucket[]; | ||
range: { | ||
min: number; | ||
max: number; | ||
}; | ||
readonly populationVariance: { | ||
populationVariance: { | ||
control: number; | ||
experiment: number; | ||
}; | ||
readonly control: number[]; | ||
readonly experiment: number[]; | ||
constructor(options: IStatsOptions); | ||
control: number[]; | ||
experiment: number[]; | ||
constructor(options: IStatsOptions, unitConverterFn?: (n: number) => number); | ||
private convertAllUnits; | ||
private getOutliers; | ||
@@ -80,0 +87,0 @@ private getSevenFigureSummary; |
@@ -8,51 +8,105 @@ "use strict"; | ||
const utils_1 = require("./utils"); | ||
// ! all stats assume microseconds from tracerbench and round to milliseconds | ||
class Stats { | ||
constructor(options) { | ||
constructor(options, unitConverterFn) { | ||
const { name, control, experiment, confidenceLevel } = options; | ||
this.control = control; | ||
this.experiment = experiment; | ||
// explicitly for NOT sorted | ||
this.controlMS = control.map((x) => Math.round(utils_1.convertMicrosecondsToMS(x))); | ||
this.experimentMS = experiment.map((x) => Math.round(utils_1.convertMicrosecondsToMS(x))); | ||
// explicitly for sortedMS | ||
const controlSortedMS = control.map((x) => Math.round(utils_1.convertMicrosecondsToMS(x))); | ||
const experimentSortedMS = experiment.map((x) => Math.round(utils_1.convertMicrosecondsToMS(x))); | ||
this.controlSortedMS = controlSortedMS.sort((a, b) => a - b); | ||
this.experimentSortedMS = experimentSortedMS.sort((a, b) => a - b); | ||
const controlSorted = control; | ||
const experimentSorted = experiment; | ||
this.controlSorted = controlSorted.sort((a, b) => a - b); | ||
this.experimentSorted = experimentSorted.sort((a, b) => a - b); | ||
this.name = name; | ||
this.sampleCount = { | ||
control: this.controlSortedMS.length, | ||
experiment: this.experimentSortedMS.length | ||
control: this.controlSorted.length, | ||
experiment: this.experimentSorted.length | ||
}; | ||
this.range = this.getRange(this.controlSortedMS, this.experimentSortedMS); | ||
this.range = this.getRange(this.controlSorted, this.experimentSorted); | ||
this.sparkLine = { | ||
control: this.getSparkline(this.getHistogram(this.range, this.controlSortedMS)), | ||
experiment: this.getSparkline(this.getHistogram(this.range, this.experimentSortedMS)) | ||
control: this.getSparkline(this.getHistogram(this.range, this.controlSorted)), | ||
experiment: this.getSparkline(this.getHistogram(this.range, this.experimentSorted)) | ||
}; | ||
this.confidenceIntervals = { | ||
80: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.8), | ||
85: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.85), | ||
90: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.9), | ||
95: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.95), | ||
99: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.99), | ||
995: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.995), | ||
999: this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, 0.999) | ||
80: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.8), | ||
85: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.85), | ||
90: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.9), | ||
95: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.95), | ||
99: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.99), | ||
995: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.995), | ||
999: this.getConfidenceInterval(this.controlSorted, this.experimentSorted, 0.999) | ||
}; | ||
this.confidenceInterval = this.getConfidenceInterval(this.controlSortedMS, this.experimentSortedMS, confidenceLevel); | ||
this.estimator = Math.round(this.getHodgesLehmann(this.controlSortedMS, this.experimentSortedMS)); | ||
this.confidenceInterval = this.getConfidenceInterval(this.controlSorted, this.experimentSorted, confidenceLevel); | ||
this.estimator = Math.round(this.getHodgesLehmann(this.controlSorted, this.experimentSorted)); | ||
this.sevenFigureSummary = { | ||
control: this.getSevenFigureSummary(this.controlSortedMS), | ||
experiment: this.getSevenFigureSummary(this.experimentSortedMS) | ||
control: this.getSevenFigureSummary(this.controlSorted), | ||
experiment: this.getSevenFigureSummary(this.experimentSorted) | ||
}; | ||
this.outliers = { | ||
control: this.getOutliers(this.controlSortedMS, this.sevenFigureSummary.control), | ||
experiment: this.getOutliers(this.experimentSortedMS, this.sevenFigureSummary.experiment) | ||
control: this.getOutliers(this.controlSorted, this.sevenFigureSummary.control), | ||
experiment: this.getOutliers(this.experimentSorted, this.sevenFigureSummary.experiment) | ||
}; | ||
this.buckets = this.getBuckets(this.controlSortedMS, this.experimentSortedMS); | ||
this.buckets = this.getBuckets(this.controlSorted, this.experimentSorted); | ||
this.populationVariance = { | ||
control: this.getPopulationVariance(this.controlSortedMS), | ||
experiment: this.getPopulationVariance(this.experimentSortedMS) | ||
control: this.getPopulationVariance(this.controlSorted), | ||
experiment: this.getPopulationVariance(this.experimentSorted) | ||
}; | ||
// when passed will convert all units **after** statistical computation | ||
// its critical this happens after computation since we need to correctly handle ties | ||
if (unitConverterFn) { | ||
this.convertAllUnits(unitConverterFn); | ||
} | ||
} | ||
convertAllUnits(unitConverterFn) { | ||
this.estimator = unitConverterFn(this.estimator); | ||
this.experiment = this.experiment.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.experimentSorted = this.experimentSorted.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.control = this.control.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.controlSorted = this.controlSorted.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.range.min = unitConverterFn(this.range.min); | ||
this.range.max = unitConverterFn(this.range.max); | ||
this.populationVariance.control = unitConverterFn(this.populationVariance.control); | ||
this.populationVariance.experiment = unitConverterFn(this.populationVariance.experiment); | ||
this.outliers.control.IQR = unitConverterFn(this.outliers.control.IQR); | ||
this.outliers.control.lowerOutlier = unitConverterFn(this.outliers.control.lowerOutlier); | ||
this.outliers.control.upperOutlier = unitConverterFn(this.outliers.control.upperOutlier); | ||
this.outliers.control.outliers = this.outliers.control.outliers.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.outliers.experiment.IQR = unitConverterFn(this.outliers.experiment.IQR); | ||
this.outliers.experiment.lowerOutlier = unitConverterFn(this.outliers.experiment.lowerOutlier); | ||
this.outliers.experiment.upperOutlier = unitConverterFn(this.outliers.experiment.upperOutlier); | ||
this.outliers.experiment.outliers = this.outliers.experiment.outliers.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
for (const k in this.confidenceInterval) { | ||
if (k === 'min' || k === 'max' || k === 'median') { | ||
this.confidenceInterval[k] = unitConverterFn(this.confidenceInterval[k]); | ||
} | ||
} | ||
for (const k in this.sevenFigureSummary.control) { | ||
this.sevenFigureSummary.control[k] = unitConverterFn(this.sevenFigureSummary.control[k]); | ||
} | ||
for (const k in this.sevenFigureSummary.experiment) { | ||
this.sevenFigureSummary.experiment[k] = unitConverterFn(this.sevenFigureSummary.experiment[k]); | ||
} | ||
for (const k in this.confidenceIntervals) { | ||
for (const kk in this.confidenceIntervals[k]) { | ||
if (kk === 'min' || kk === 'max' || kk === 'median') { | ||
this.confidenceIntervals[k][kk] = unitConverterFn(this.confidenceIntervals[k][kk]); | ||
} | ||
} | ||
} | ||
this.buckets = this.buckets.map((o) => { | ||
o.min = unitConverterFn(o.min); | ||
o.max = unitConverterFn(o.max); | ||
return o; | ||
}); | ||
} | ||
getOutliers(a, sevenFigSum) { | ||
@@ -95,3 +149,7 @@ const IQR = sevenFigSum[75] - sevenFigSum[25]; | ||
max: Math.round(Math.ceil(ci.upper * 100) / 100), | ||
isSig | ||
isSig, | ||
median: ci.median, | ||
zScore: ci.zScore, | ||
pValue: ci.pValue, | ||
U: ci.U | ||
}; | ||
@@ -138,3 +196,3 @@ } | ||
} | ||
getBuckets(controlSortedMS, experimentSortedMS, bucketCount = 12) { | ||
getBuckets(controlSorted, experimentSorted, bucketCount = 12) { | ||
const { min, max } = this.range; | ||
@@ -162,3 +220,3 @@ const buffer = 1; | ||
buckets.map((bucket) => { | ||
controlSortedMS.map((sample) => { | ||
controlSorted.map((sample) => { | ||
if (sample >= bucket.min && sample < bucket.max) { | ||
@@ -168,3 +226,3 @@ bucket.count.control++; | ||
}); | ||
experimentSortedMS.map((sample) => { | ||
experimentSorted.map((sample) => { | ||
if (sample >= bucket.min && sample < bucket.max) { | ||
@@ -171,0 +229,0 @@ bucket.count.experiment++; |
export declare function convertMicrosecondsToMS(ms: string | number): number; | ||
export declare function convertMSToMicroseconds(ms: string | number): number; | ||
export declare function toNearestHundreth(n: number): number; | ||
export declare function roundFloatAndConvertMicrosecondsToMS(ms: string | number): number; | ||
export declare function fillArray(arrLngth: number, incr?: number, strt?: number): number[]; | ||
//# sourceMappingURL=utils.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.fillArray = exports.toNearestHundreth = exports.convertMSToMicroseconds = exports.convertMicrosecondsToMS = void 0; | ||
exports.fillArray = exports.roundFloatAndConvertMicrosecondsToMS = exports.toNearestHundreth = exports.convertMSToMicroseconds = exports.convertMicrosecondsToMS = void 0; | ||
function convertMicrosecondsToMS(ms) { | ||
@@ -18,2 +18,8 @@ ms = typeof ms === 'string' ? parseInt(ms, 10) : ms; | ||
exports.toNearestHundreth = toNearestHundreth; | ||
function roundFloatAndConvertMicrosecondsToMS(ms) { | ||
ms = typeof ms === 'string' ? parseInt(ms, 10) : ms; | ||
ms = Math.floor(ms * 100) / 100000; | ||
return Math.round(ms); | ||
} | ||
exports.roundFloatAndConvertMicrosecondsToMS = roundFloatAndConvertMicrosecondsToMS; | ||
function fillArray(arrLngth, incr = 1, strt = 0) { | ||
@@ -20,0 +26,0 @@ const a = []; |
{ | ||
"name": "@tracerbench/stats", | ||
"version": "4.5.5", | ||
"version": "5.0.0", | ||
"description": "Stats class written in TS-Node", | ||
@@ -66,3 +66,3 @@ "keywords": [ | ||
"engine": "node >= 10", | ||
"gitHead": "fc452a997ad2db56e451bcf4b98650336e975c80" | ||
"gitHead": "a2256a42d9ed92cff29639cb614e0cea8097d2b3" | ||
} |
@@ -59,2 +59,3 @@ import { median } from 'd3-array'; | ||
// subtract every control data point to every experiment data point | ||
const deltas = a | ||
@@ -65,2 +66,3 @@ .map((a) => b.map((b) => a - b)) | ||
// count the number of "wins" a > b and 0.5 for a tie if a == b | ||
const U = deltas.reduce( | ||
@@ -73,12 +75,13 @@ (accum, value) => accum + (value < 0 ? 1 : value == 0 ? 0.5 : 0), | ||
const standadDeviationU = Math.sqrt((maxU * (aLength + bLength + 1)) / 12); | ||
const standardDeviationU = Math.sqrt((maxU * (aLength + bLength + 1)) / 12); | ||
// we are estimating a discrete distribution so bias the mean depending on which tail | ||
// we are computing the pValue for | ||
// we are computing the pValue for. literally just +/- 0.5 | ||
const continuityCorrection = lowerTail ? 0.5 : -0.5; | ||
const zScore = (U - meanU + continuityCorrection) / standadDeviationU; | ||
// how many standard deviations the result is given the null hypothesis is true | ||
const zScore = (U - meanU + continuityCorrection) / standardDeviationU; | ||
// z is symmetrical, so use lower tail and double the cumulative for each tail | ||
// since this is a two tailed test | ||
// z is symmetrical, so use lower tail and double the cumulative of U for each tail | ||
// since this is a two tailed test. normal cumulative distribution function | ||
const pValue = jStat.normal.cdf(-Math.abs(zScore), 0, 1) * 2; | ||
@@ -89,6 +92,6 @@ | ||
const lowerU = Math.round( | ||
jStat.normal.inv(alpha / 2, meanU + 0.5, standadDeviationU) | ||
jStat.normal.inv(alpha / 2, meanU + 0.5, standardDeviationU) | ||
); | ||
const upperU = Math.round( | ||
jStat.normal.inv(1 - alpha / 2, meanU + 0.5, standadDeviationU) | ||
jStat.normal.inv(1 - alpha / 2, meanU + 0.5, standardDeviationU) | ||
); | ||
@@ -100,6 +103,6 @@ | ||
upper: deltas[upperU], | ||
zScore, | ||
pValue, | ||
zScore: +zScore.toPrecision(4), | ||
pValue: +pValue.toPrecision(4), | ||
U | ||
}; | ||
} |
@@ -12,2 +12,3 @@ import { cartesianProduct, confidenceInterval } from './confidence-interval'; | ||
convertMSToMicroseconds, | ||
roundFloatAndConvertMicrosecondsToMS, | ||
toNearestHundreth | ||
@@ -30,3 +31,4 @@ } from './utils'; | ||
IStatsOptions, | ||
IConfidenceInterval | ||
IConfidenceInterval, | ||
roundFloatAndConvertMicrosecondsToMS | ||
}; |
243
src/stats.ts
@@ -5,3 +5,3 @@ import { cross, histogram, mean, quantile } from 'd3-array'; | ||
import { confidenceInterval } from './confidence-interval'; | ||
import { convertMicrosecondsToMS, toNearestHundreth } from './utils'; | ||
import { toNearestHundreth } from './utils'; | ||
@@ -17,3 +17,5 @@ export interface Bucket { | ||
export interface ISevenFigureSummary { | ||
export type ISevenFigureSummary = { | ||
[key in string | number]: number; | ||
} & { | ||
min: number; | ||
@@ -26,3 +28,3 @@ max: number; | ||
90: number; | ||
} | ||
}; | ||
@@ -43,20 +45,24 @@ export interface IOutliers { | ||
export interface IConfidenceInterval { | ||
export type IConfidenceInterval = { | ||
[key in string]: number | boolean; | ||
} & { | ||
min: number; | ||
max: number; | ||
isSig: boolean; | ||
} | ||
// ! all stats assume microseconds from tracerbench and round to milliseconds | ||
median: number; | ||
zScore: number; | ||
pValue: number; | ||
U: number; | ||
}; | ||
export class Stats { | ||
public readonly name: string; | ||
public readonly estimator: number; | ||
public estimator: number; | ||
public readonly sparkLine: { control: string; experiment: string }; | ||
public readonly confidenceIntervals: { [key: number]: IConfidenceInterval }; | ||
public readonly confidenceInterval: IConfidenceInterval; | ||
public readonly sevenFigureSummary: { | ||
public confidenceIntervals: { [key: number]: IConfidenceInterval }; | ||
public confidenceInterval: IConfidenceInterval; | ||
public sevenFigureSummary: { | ||
control: ISevenFigureSummary; | ||
experiment: ISevenFigureSummary; | ||
}; | ||
public readonly outliers: { | ||
public outliers: { | ||
control: IOutliers; | ||
@@ -66,43 +72,32 @@ experiment: IOutliers; | ||
public readonly sampleCount: { control: number; experiment: number }; | ||
public readonly experimentMS: number[]; | ||
public readonly controlMS: number[]; | ||
public readonly experimentSortedMS: number[]; | ||
public readonly controlSortedMS: number[]; | ||
public readonly buckets: Bucket[]; | ||
public readonly range: { min: number; max: number }; | ||
public readonly populationVariance: { control: number; experiment: number }; | ||
public readonly control: number[]; | ||
public readonly experiment: number[]; | ||
constructor(options: IStatsOptions) { | ||
public experimentSorted: number[]; | ||
public controlSorted: number[]; | ||
public buckets: Bucket[]; | ||
public range: { min: number; max: number }; | ||
public populationVariance: { control: number; experiment: number }; | ||
public control: number[]; | ||
public experiment: number[]; | ||
constructor(options: IStatsOptions, unitConverterFn?: (n: number) => number) { | ||
const { name, control, experiment, confidenceLevel } = options; | ||
this.control = control; | ||
this.experiment = experiment; | ||
// explicitly for NOT sorted | ||
this.controlMS = control.map((x) => Math.round(convertMicrosecondsToMS(x))); | ||
this.experimentMS = experiment.map((x) => | ||
Math.round(convertMicrosecondsToMS(x)) | ||
); | ||
// explicitly for sortedMS | ||
const controlSortedMS = control.map((x) => | ||
Math.round(convertMicrosecondsToMS(x)) | ||
); | ||
const experimentSortedMS = experiment.map((x) => | ||
Math.round(convertMicrosecondsToMS(x)) | ||
); | ||
this.controlSortedMS = controlSortedMS.sort((a, b) => a - b); | ||
this.experimentSortedMS = experimentSortedMS.sort((a, b) => a - b); | ||
const controlSorted = control; | ||
const experimentSorted = experiment; | ||
this.controlSorted = controlSorted.sort((a, b) => a - b); | ||
this.experimentSorted = experimentSorted.sort((a, b) => a - b); | ||
this.name = name; | ||
this.sampleCount = { | ||
control: this.controlSortedMS.length, | ||
experiment: this.experimentSortedMS.length | ||
control: this.controlSorted.length, | ||
experiment: this.experimentSorted.length | ||
}; | ||
this.range = this.getRange(this.controlSortedMS, this.experimentSortedMS); | ||
this.range = this.getRange(this.controlSorted, this.experimentSorted); | ||
this.sparkLine = { | ||
control: this.getSparkline( | ||
this.getHistogram(this.range, this.controlSortedMS) | ||
this.getHistogram(this.range, this.controlSorted) | ||
), | ||
experiment: this.getSparkline( | ||
this.getHistogram(this.range, this.experimentSortedMS) | ||
this.getHistogram(this.range, this.experimentSorted) | ||
) | ||
@@ -112,34 +107,34 @@ }; | ||
80: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.8 | ||
), | ||
85: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.85 | ||
), | ||
90: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.9 | ||
), | ||
95: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.95 | ||
), | ||
99: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.99 | ||
), | ||
995: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.995 | ||
), | ||
999: this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
0.999 | ||
@@ -149,36 +144,122 @@ ) | ||
this.confidenceInterval = this.getConfidenceInterval( | ||
this.controlSortedMS, | ||
this.experimentSortedMS, | ||
this.controlSorted, | ||
this.experimentSorted, | ||
confidenceLevel | ||
); | ||
this.estimator = Math.round( | ||
this.getHodgesLehmann( | ||
this.controlSortedMS, | ||
this.experimentSortedMS | ||
) as number | ||
this.getHodgesLehmann(this.controlSorted, this.experimentSorted) as number | ||
); | ||
this.sevenFigureSummary = { | ||
control: this.getSevenFigureSummary(this.controlSortedMS), | ||
experiment: this.getSevenFigureSummary(this.experimentSortedMS) | ||
control: this.getSevenFigureSummary(this.controlSorted), | ||
experiment: this.getSevenFigureSummary(this.experimentSorted) | ||
}; | ||
this.outliers = { | ||
control: this.getOutliers( | ||
this.controlSortedMS, | ||
this.controlSorted, | ||
this.sevenFigureSummary.control | ||
), | ||
experiment: this.getOutliers( | ||
this.experimentSortedMS, | ||
this.experimentSorted, | ||
this.sevenFigureSummary.experiment | ||
) | ||
}; | ||
this.buckets = this.getBuckets( | ||
this.controlSortedMS, | ||
this.experimentSortedMS | ||
); | ||
this.buckets = this.getBuckets(this.controlSorted, this.experimentSorted); | ||
this.populationVariance = { | ||
control: this.getPopulationVariance(this.controlSortedMS), | ||
experiment: this.getPopulationVariance(this.experimentSortedMS) | ||
control: this.getPopulationVariance(this.controlSorted), | ||
experiment: this.getPopulationVariance(this.experimentSorted) | ||
}; | ||
// when passed will convert all units **after** statistical computation | ||
// its critical this happens after computation since we need to correctly handle ties | ||
if (unitConverterFn) { | ||
this.convertAllUnits(unitConverterFn); | ||
} | ||
} | ||
private convertAllUnits(unitConverterFn: (n: number) => number): void { | ||
this.estimator = unitConverterFn(this.estimator); | ||
this.experiment = this.experiment.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.experimentSorted = this.experimentSorted.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.control = this.control.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.controlSorted = this.controlSorted.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.range.min = unitConverterFn(this.range.min); | ||
this.range.max = unitConverterFn(this.range.max); | ||
this.populationVariance.control = unitConverterFn( | ||
this.populationVariance.control | ||
); | ||
this.populationVariance.experiment = unitConverterFn( | ||
this.populationVariance.experiment | ||
); | ||
this.outliers.control.IQR = unitConverterFn(this.outliers.control.IQR); | ||
this.outliers.control.lowerOutlier = unitConverterFn( | ||
this.outliers.control.lowerOutlier | ||
); | ||
this.outliers.control.upperOutlier = unitConverterFn( | ||
this.outliers.control.upperOutlier | ||
); | ||
this.outliers.control.outliers = this.outliers.control.outliers.map((n) => { | ||
return unitConverterFn(n); | ||
}); | ||
this.outliers.experiment.IQR = unitConverterFn( | ||
this.outliers.experiment.IQR | ||
); | ||
this.outliers.experiment.lowerOutlier = unitConverterFn( | ||
this.outliers.experiment.lowerOutlier | ||
); | ||
this.outliers.experiment.upperOutlier = unitConverterFn( | ||
this.outliers.experiment.upperOutlier | ||
); | ||
this.outliers.experiment.outliers = this.outliers.experiment.outliers.map( | ||
(n) => { | ||
return unitConverterFn(n); | ||
} | ||
); | ||
for (const k in this.confidenceInterval) { | ||
if (k === 'min' || k === 'max' || k === 'median') { | ||
this.confidenceInterval[k] = unitConverterFn( | ||
this.confidenceInterval[k] | ||
); | ||
} | ||
} | ||
for (const k in this.sevenFigureSummary.control) { | ||
this.sevenFigureSummary.control[k] = unitConverterFn( | ||
this.sevenFigureSummary.control[k] | ||
); | ||
} | ||
for (const k in this.sevenFigureSummary.experiment) { | ||
this.sevenFigureSummary.experiment[k] = unitConverterFn( | ||
this.sevenFigureSummary.experiment[k] | ||
); | ||
} | ||
for (const k in this.confidenceIntervals) { | ||
for (const kk in this.confidenceIntervals[k]) { | ||
if (kk === 'min' || kk === 'max' || kk === 'median') { | ||
this.confidenceIntervals[k][kk] = unitConverterFn( | ||
this.confidenceIntervals[k][kk] | ||
); | ||
} | ||
} | ||
} | ||
this.buckets = this.buckets.map((o) => { | ||
o.min = unitConverterFn(o.min); | ||
o.max = unitConverterFn(o.max); | ||
return o; | ||
}); | ||
} | ||
private getOutliers( | ||
@@ -233,3 +314,7 @@ a: number[], | ||
max: Math.round(Math.ceil(ci.upper * 100) / 100), | ||
isSig | ||
isSig, | ||
median: ci.median, | ||
zScore: ci.zScore, | ||
pValue: ci.pValue, | ||
U: ci.U | ||
}; | ||
@@ -303,4 +388,4 @@ } | ||
private getBuckets( | ||
controlSortedMS: number[], | ||
experimentSortedMS: number[], | ||
controlSorted: number[], | ||
experimentSorted: number[], | ||
bucketCount = 12 | ||
@@ -331,3 +416,3 @@ ): Bucket[] { | ||
buckets.map((bucket) => { | ||
controlSortedMS.map((sample) => { | ||
controlSorted.map((sample) => { | ||
if (sample >= bucket.min && sample < bucket.max) { | ||
@@ -337,3 +422,3 @@ bucket.count.control++; | ||
}); | ||
experimentSortedMS.map((sample) => { | ||
experimentSorted.map((sample) => { | ||
if (sample >= bucket.min && sample < bucket.max) { | ||
@@ -358,8 +443,2 @@ bucket.count.experiment++; | ||
} | ||
// private getPower(): number { | ||
// // increase sample size, increases power | ||
// // decrease variance, increases power | ||
// } | ||
} |
@@ -15,2 +15,10 @@ export function convertMicrosecondsToMS(ms: string | number): number { | ||
export function roundFloatAndConvertMicrosecondsToMS( | ||
ms: string | number | ||
): number { | ||
ms = typeof ms === 'string' ? parseInt(ms, 10) : ms; | ||
ms = Math.floor(ms * 100) / 100000; | ||
return Math.round(ms); | ||
} | ||
export function fillArray(arrLngth: number, incr = 1, strt = 0): number[] { | ||
@@ -17,0 +25,0 @@ const a = []; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
232496
1397