commonMain.jetbrains.datalore.plot.base.stat.BinStatUtil.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lets-plot-common Show documentation
Show all versions of lets-plot-common Show documentation
Lets-Plot JVM package without rendering part
/*
* Copyright (c) 2019. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/
package jetbrains.datalore.plot.base.stat
import jetbrains.datalore.plot.base.DataFrame
import jetbrains.datalore.plot.base.data.TransformVar
import jetbrains.datalore.plot.base.util.MutableDouble
import jetbrains.datalore.plot.common.data.SeriesUtil
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.min
object BinStatUtil {
private const val MAX_BIN_COUNT = 500
fun weightAtIndex(data: DataFrame): (Int) -> Double {
if (data.has(TransformVar.WEIGHT)) {
val weights = data.getNumeric(TransformVar.WEIGHT)
return { index ->
val weight = weights[index]
SeriesUtil.asFinite(weight, 0.0)
}
}
return { 1.0 }
}
// ToDo: need to deal fith n/a values (see DensityStat)
fun weightVector(dataLength: Int, data: DataFrame): List {
return if (data.has(TransformVar.WEIGHT)) {
data.getNumeric(TransformVar.WEIGHT)
} else List(dataLength) { 1.0 }
}
fun binCountAndWidth(dataRange: Double, binOptions: BinOptions): CountAndWidth {
var binCount = binOptions.binCount
val binWidth: Double
if (binOptions.hasBinWidth()) {
binWidth = binOptions.binWidth!!
var count = dataRange / binWidth
count = min(MAX_BIN_COUNT.toDouble(), count)
binCount = ceil(count).toInt()
} else {
binWidth = dataRange / binCount
}
return CountAndWidth(binCount, binWidth)
}
fun computeBins(
valuesX: List,
startX: Double,
binCount: Int,
binWidth: Double,
weightAtIndex: (Int) -> Double,
densityNormalizingFactor: Double
): BinsData {
var totalCount = 0.0
val countByBinIndex = HashMap()
// val dataIndicesByBinIndex = HashMap>()
for (dataIndex in valuesX.indices) {
val x = valuesX[dataIndex]
if (!SeriesUtil.isFinite(x)) {
continue
}
val weight = weightAtIndex(dataIndex)
totalCount += weight
val binIndex = floor((x!! - startX) / binWidth).toInt()
if (!countByBinIndex.containsKey(binIndex)) {
countByBinIndex[binIndex] = MutableDouble(0.0)
}
countByBinIndex[binIndex]!!.getAndAdd(weight)
// if (!dataIndicesByBinIndex.containsKey(binIndex)) {
// dataIndicesByBinIndex[binIndex] = ArrayList()
// }
// dataIndicesByBinIndex[binIndex]!!.add(dataIndex)
}
val x = ArrayList()
val counts = ArrayList()
val densities = ArrayList()
val x0 = startX + binWidth / 2
for (i in 0 until binCount) {
x.add(x0 + i * binWidth)
var count = 0.0
// some bins are left empty (not excluded from map)
if (countByBinIndex.containsKey(i)) {
count = countByBinIndex[i]!!.get()
}
counts.add(count)
val density = count / totalCount * densityNormalizingFactor
densities.add(density)
}
// return BinsData(x, counts, densities, dataIndicesByBinIndex)
return BinsData(x, counts, densities)
}
class BinOptions(
binCount: Int, val binWidth: Double? // optional
) {
val binCount: Int = min(MAX_BIN_COUNT, max(1, binCount))
fun hasBinWidth(): Boolean {
return binWidth != null && binWidth > 0
}
}
class CountAndWidth(val count: Int, val width: Double)
class BinsData(
internal val x: List,
internal val count: List,
internal val density: List
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy