diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt index 44638c29b..e2811602c 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogram1D.kt @@ -81,6 +81,30 @@ public class UniformHistogram1DGroup( builder.block() return UniformHistogram1D(this, map) } + + /** + * Re-bin given histogram to be compatible if exiting bin fully falls inside existing bin, this bin value + * is increased by one. If not, all bins including values from this bin are increased by fraction + * (conserving the norming). + */ + @UnstableKMathAPI + public fun produceFrom(histogram: Histogram1D): UniformHistogram1D = + if ((histogram as? UniformHistogram1D)?.group == this) histogram + else { + val map = HashMap() + histogram.bins.forEach { bin -> + val range = bin.domain.range + val indexOfLeft = getIndex(range.start) + val indexOfRight = getIndex(range.endInclusive) + val numBins = indexOfRight - indexOfLeft + 1 + for (i in indexOfLeft..indexOfRight) { + map[indexOfLeft] = with(valueAlgebra) { + (map[indexOfLeft] ?: zero) + bin.binValue / numBins + } + } + } + UniformHistogram1D(this, map) + } } public fun Histogram.Companion.uniform1D( diff --git a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt index d23afdb8b..8d5cb8fb1 100644 --- a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt @@ -12,14 +12,15 @@ import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.nextBuffer +import kotlin.native.concurrent.ThreadLocal import kotlin.test.Test import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class, UnstableKMathAPI::class) internal class UniformHistogram1DTest { + @Test fun normal() = runTest { - val generator = RandomGenerator.default(123) val distribution = NormalDistribution(0.0, 1.0) with(Histogram.uniform1D(DoubleField, 0.1)) { val h1 = produce(distribution.nextBuffer(generator, 10000)) @@ -31,4 +32,17 @@ internal class UniformHistogram1DTest { assertEquals(60000, h3.bins.sumOf { it.binValue }.toInt()) } } + + @Test + fun rebin() = runTest { + val h1 = Histogram.uniform1D(DoubleField, 0.1).produce(generator.nextDoubleBuffer(10000)) + val h2 = Histogram.uniform1D(DoubleField,0.3).produceFrom(h1) + + assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt()) + } + + @ThreadLocal + companion object{ + private val generator = RandomGenerator.default(123) + } } \ No newline at end of file