Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 1x | import { ckmeans } from "simple-statistics"
import { range, quantile } from "d3-array"
 
import {
    excludeUndefined,
    uniq,
    last,
    roundSigFig,
    first,
} from "../../clientUtils/Util"
import { BinningStrategy } from "./BinningStrategy"
 
/** Human-readable labels for the binning strategies */
export const binningStrategyLabels: Record<BinningStrategy, string> = {
    equalInterval: "Equal-interval",
    quantiles: "Quantiles",
    ckmeans: "Ckmeans",
    manual: "Manual",
}
 
function calcEqualIntervalStepSize(
    sortedValues: number[],
    binCount: number,
    minBinValue: number
): number {
    if (!sortedValues.length) return 10
    const stepSizeInitial = (last(sortedValues)! - minBinValue) / binCount
    return roundSigFig(stepSizeInitial, 1)
}
 
interface GetBinMaximumsWithStrategyArgs {
    binningStrategy: BinningStrategy
    sortedValues: number[]
    binCount: number
    /** `minBinValue` is only used in the `equalInterval` binning strategy. */
    minBinValue?: number
}
 
// Some algorithms can create bins that start & end at the same value.
// This also means the first bin can both start and end at the same value – the minimum
// value. This is why we uniq() and why we remove any values <= minimum value.
function normalizeBinValues(
    binValues: (number | undefined)[],
    minBinValue?: number
): any {
    const values = uniq(excludeUndefined(binValues))
    return minBinValue !== undefined
        ? values.filter((v) => v > minBinValue)
        : values
}
 
export function getBinMaximums(args: GetBinMaximumsWithStrategyArgs): number[] {
    const { binningStrategy, sortedValues, binCount, minBinValue } = args
    const valueCount = sortedValues.length
 
    if (valueCount < 1 || binCount < 1) return []
 
    if (binningStrategy === BinningStrategy.ckmeans) {
        const clusters = ckmeans(
            sortedValues,
            binCount > valueCount ? valueCount : binCount
        )
        return normalizeBinValues(clusters.map(last), minBinValue)
    } else if (binningStrategy === BinningStrategy.quantiles) {
        return normalizeBinValues(
            range(1, binCount + 1).map((v) =>
                quantile(sortedValues, v / binCount)
            ),
            minBinValue
        )
    } else {
        // Equal-interval strategy by default
        const minValue = minBinValue ?? first(sortedValues) ?? 0
        const binStepSize = calcEqualIntervalStepSize(
            sortedValues,
            binCount,
            minValue
        )
        return normalizeBinValues(
            range(1, binCount + 1).map((n) => minValue + n * binStepSize),
            minBinValue
        )
    }
}
  |