ModalityAnalyzer

class ModalityAnalyzer(data: DoubleArray, val numBins: Int = defaultNumBins, streamNum: Int = 0, val streamProvider: RNStreamProviderIfc = KSLRandom.DefaultRNStreamProvider) : RNStreamControlIfc(source)

How many modes the data appears to have, and whether that appearance survives smoothing.

Why this is here at all. An analyst reaching for a mixture has usually looked at a plot and counted humps. That count is the input the fitting workflow is built around, so it is worth knowing whether it is a property of the data or of the bin width that happened to be chosen.

Modes bound the component count from below, and only from below. A two-component mixture of equal-variance normals is unimodal whenever its means differ by less than twice the standard deviation, so seeing one hump does not mean one component. Seeing three humps does mean at least three components. The test here is therefore evidence about a floor rather than about the count itself.

A class rather than an object because the operations share expensive state: the binning, the mode trace, the bisection and every bootstrap replicate reuse the same sorted array and bin grid. This follows PDFModeler, which caches its histogram for the same reason and splits instance members from stateless functions over supplied data in the same way.

The stream is a property of the analyzer, not an argument to its methods. That is the library's convention — Bootstrap, DPopulation and RVariable all acquire their stream once at construction and expose RNStreamControlIfc so that repositioning is something the caller asks for explicitly. Taking a stream number per call, as this class first did, cannot be made reproducible: an explicit number hands every caller the same stream object at whatever position the last caller left it, and a stream number of zero means the next stream, so consecutive calls draw from different streams. Both were measured; five identical calls at an explicit number returned five different p-values.

Parameters

data

the observations, which need not be sorted

numBins

how many cells the data is binned into

streamNum

the random number stream number, defaults to 0, which means the next stream

streamProvider

the provider of random number streams, defaults to the library's shared provider. Supply a provider of its own to an analyzer that must be independent of everything else drawing from the shared one — which is what concurrent callers need, since a stream is not safe to share between threads.

Constructors

Link copied to clipboard
constructor(data: DoubleArray, numBins: Int = defaultNumBins, streamNum: Int = 0, streamProvider: RNStreamProviderIfc = KSLRandom.DefaultRNStreamProvider)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

If true, the stream will automatically participate in having its stream advanced to the next sub-stream via stream managers

Link copied to clipboard
open override var antithetic: Boolean

Tells the stream to start producing antithetic variates

Link copied to clipboard
Link copied to clipboard
open override var resetStartStreamOption: Boolean

If true, the stream will automatically participate in having its stream reset to its start stream via stream managers

Link copied to clipboard

The observations in non-decreasing order.

Link copied to clipboard

The number of the stream the resampling draws from. The smoothing noise draws from the next one.

Link copied to clipboard

Functions

Link copied to clipboard
open override fun advanceToNextSubStream()

Positions the RNG at the beginning of its next substream

Link copied to clipboard
fun assess(numBootstrapSamples: Int = defaultNumBootstrapSamples): ModalityAssessment

Everything the pre-fit description needs about modality, in one object.

Link copied to clipboard
fun binSensitivity(binCounts: List<Int> = defaultBinSensitivityCounts): Map<Int, Int>

How the apparent mode count varies with the number of bins.

Link copied to clipboard
fun criticalBandwidth(maxModes: Int): Double

The smallest bandwidth at which the estimate has at most this many modes.

Link copied to clipboard
fun defaultTraceBandwidths(numPoints: Int = defaultNumTracePoints): DoubleArray

A geometric sweep of bandwidths spanning the range over which the mode count changes.

Link copied to clipboard
fun density(bandwidth: Double): (Double) -> Double

The kernel density estimate at a bandwidth, as a function, so that it can be plotted by the library's existing density plot rather than by a new one.

Link copied to clipboard

The kernel density estimate at a bandwidth, over the analyzer's grid.

Link copied to clipboard

The grid the density is evaluated on.

Link copied to clipboard
fun modeCountAt(bandwidth: Double): Int

How many modes the estimate has at a bandwidth.

Link copied to clipboard
fun modesAt(bandwidth: Double): DoubleArray

Where the modes of the estimate sit at a bandwidth.

Link copied to clipboard
fun modeTrace(bandwidths: DoubleArray = defaultTraceBandwidths()): List<ModeTraceEntry>

Modes against bandwidth over a sweep, which is the display an analyst reads to see whether an apparent structure survives smoothing.

Link copied to clipboard
open override fun resetStartStream()

The resetStartStream method will position the RNG at the beginning of its stream. This is the same location in the stream as assigned when the RNG was created and initialized.

Link copied to clipboard
open override fun resetStartSubStream()

Resets the position of the RNG at the start of the current substream

Link copied to clipboard
fun test(maxModes: Int, numBootstrapSamples: Int = defaultNumBootstrapSamples): ModalityTestResult

Silverman's test of "at most this many modes", by the smoothed bootstrap.