in plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/stat/DensityStat.kt [41:114]
override fun apply(data: DataFrame, statCtx: StatContext, messageConsumer: (s: String) -> Unit): DataFrame {
if (!hasRequiredValues(data, Aes.X)) {
return withEmptyStatValues()
}
val xs: List<Double>
val weights: List<Double>
if (data.has(TransformVar.WEIGHT)) {
val filtered = SeriesUtil.filterFinite(
data.getNumeric(TransformVar.X),
data.getNumeric(TransformVar.WEIGHT)
)
val xsFiltered = filtered[0]
val weightsFiltered = filtered[1]
val (xsSorted, weightsSorted) = xsFiltered
.zip(weightsFiltered).sortedBy { it.first }
.unzip()
xs = xsSorted
weights = weightsSorted
} else {
xs = data.getNumeric(TransformVar.X)
.filterNotNull().filter { it.isFinite() }
.sorted()
weights = List(xs.size) { 1.0 }
}
if (xs.isEmpty()) return withEmptyStatValues()
val rangeX = if (trim) {
val xSummary = FiveNumberSummary(xs)
DoubleSpan(xSummary.min, xSummary.max)
} else {
statCtx.overallXRange() ?: DoubleSpan(-0.5, 0.5)
}
val statX = DensityStatUtil.createStepValues(rangeX, n)
val statDensity = ArrayList<Double>()
val statCount = ArrayList<Double>()
val statScaled = ArrayList<Double>()
val densityFunction = DensityStatUtil.densityFunction(
xs, weights,
bandWidth, bandWidthMethod, adjust, kernel, fullScanMax
)
val nTotal = weights.sum()
for (x in statX) {
val d = densityFunction(x)
statCount.add(d)
statDensity.add(d / nTotal)
}
val maxm = statCount.maxOrNull()!!
for (d in statCount) {
statScaled.add(d / maxm)
}
val statQuantile = DensityStatUtil.calculateStatQuantile(statX, statCount, quantiles)
val statData = DensityStatUtil.expandByGroupEnds(mapOf(
Stats.X to statX,
Stats.DENSITY to statDensity,
Stats.COUNT to statCount,
Stats.SCALED to statScaled,
Stats.QUANTILE to statQuantile
), Stats.X, Stats.QUANTILE)
val builder = DataFrame.Builder()
for ((variable, series) in statData) {
builder.putNumeric(variable, series)
}
return builder.build()
}