forked from kscience/kmath
[WIP] Another histogram refactor
This commit is contained in:
parent
39640498fc
commit
29369cd6d7
@ -9,16 +9,21 @@ import space.kscience.kmath.linear.Point
|
|||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>) : DoubleDomain {
|
public abstract class Domain1D<T : Comparable<T>>(public val range: ClosedRange<T>) : Domain<T> {
|
||||||
override val dimension: Int get() = 1
|
override val dimension: Int get() = 1
|
||||||
|
|
||||||
public operator fun contains(d: Double): Boolean = range.contains(d)
|
public operator fun contains(value: T): Boolean = range.contains(value)
|
||||||
|
|
||||||
override operator fun contains(point: Point<Double>): Boolean {
|
override operator fun contains(point: Point<T>): Boolean {
|
||||||
require(point.size == 0)
|
require(point.size == 0)
|
||||||
return contains(point[0])
|
return contains(point[0])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public class DoubleDomain1D(
|
||||||
|
@Suppress("CanBeParameter") public val doubleRange: ClosedFloatingPointRange<Double>,
|
||||||
|
) : Domain1D<Double>(doubleRange), DoubleDomain {
|
||||||
override fun getLowerBound(num: Int): Double {
|
override fun getLowerBound(num: Int): Double {
|
||||||
require(num == 0)
|
require(num == 0)
|
||||||
return range.start
|
return range.start
|
||||||
@ -31,3 +36,7 @@ public class UnivariateDomain(public val range: ClosedFloatingPointRange<Double>
|
|||||||
|
|
||||||
override fun volume(): Double = range.endInclusive - range.start
|
override fun volume(): Double = range.endInclusive - range.start
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public val DoubleDomain1D.center: Double
|
||||||
|
get() = (range.endInclusive + range.start) / 2
|
@ -6,6 +6,7 @@
|
|||||||
package space.kscience.kmath.histogram
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
import space.kscience.kmath.domains.HyperSquareDomain
|
import space.kscience.kmath.domains.HyperSquareDomain
|
||||||
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
@ -31,7 +32,7 @@ public class DoubleHistogramSpace(
|
|||||||
public val dimension: Int get() = lower.size
|
public val dimension: Int get() = lower.size
|
||||||
|
|
||||||
override val shape: IntArray = IntArray(binNums.size) { binNums[it] + 2 }
|
override val shape: IntArray = IntArray(binNums.size) { binNums[it] + 2 }
|
||||||
override val histogramValueSpace: DoubleFieldND = DoubleField.ndAlgebra(*shape)
|
override val histogramValueAlgebra: DoubleFieldND = DoubleField.ndAlgebra(*shape)
|
||||||
|
|
||||||
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }
|
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }
|
||||||
|
|
||||||
@ -70,21 +71,20 @@ public class DoubleHistogramSpace(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public val Bin<Double>.domain: HyperSquareDomain
|
override fun produceBin(index: IntArray, value: Double): DomainBin<Double, Double> {
|
||||||
get() = (this as? DomainBin<Double>)?.domain as? HyperSquareDomain
|
|
||||||
?: error("Im a teapot. This is not my bin")
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
override fun produceBin(index: IntArray, value: Double): DomainBin<Double> {
|
|
||||||
val domain = getDomain(index)
|
val domain = getDomain(index)
|
||||||
return DomainBin(domain, value)
|
return DomainBin(domain, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun produce(builder: HistogramBuilder<Double>.() -> Unit): IndexedHistogram<Double, Double> {
|
override fun produce(builder: HistogramBuilder<Double, Double>.() -> Unit): IndexedHistogram<Double, Double> {
|
||||||
val ndCounter = StructureND.auto(shape) { Counter.double() }
|
val ndCounter = StructureND.auto(shape) { Counter.double() }
|
||||||
val hBuilder = HistogramBuilder<Double> { point, value ->
|
val hBuilder = object : HistogramBuilder<Double, Double> {
|
||||||
|
override val defaultValue: Double get() = 1.0
|
||||||
|
|
||||||
|
override fun putValue(point: Point<out Double>, value: Double) {
|
||||||
val index = getIndex(point)
|
val index = getIndex(point)
|
||||||
ndCounter[index].add(value.toDouble())
|
ndCounter[index].add(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hBuilder.apply(builder)
|
hBuilder.apply(builder)
|
||||||
val values: BufferND<Double> = ndCounter.mapToBuffer { it.value }
|
val values: BufferND<Double> = ndCounter.mapToBuffer { it.value }
|
||||||
|
@ -13,14 +13,14 @@ import space.kscience.kmath.structures.asBuffer
|
|||||||
/**
|
/**
|
||||||
* The binned data element. Could be a histogram bin with a number of counts or an artificial construct.
|
* The binned data element. Could be a histogram bin with a number of counts or an artificial construct.
|
||||||
*/
|
*/
|
||||||
public interface Bin<in T : Any> : Domain<T> {
|
public interface Bin<in T : Any, out V> : Domain<T> {
|
||||||
/**
|
/**
|
||||||
* The value of this bin.
|
* The value of this bin.
|
||||||
*/
|
*/
|
||||||
public val value: Number
|
public val value: V
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Histogram<in T : Any, out B : Bin<T>> {
|
public interface Histogram<in T : Any, out V, out B : Bin<T, V>> {
|
||||||
/**
|
/**
|
||||||
* Find existing bin, corresponding to given coordinates
|
* Find existing bin, corresponding to given coordinates
|
||||||
*/
|
*/
|
||||||
@ -32,29 +32,38 @@ public interface Histogram<in T : Any, out B : Bin<T>> {
|
|||||||
public val dimension: Int
|
public val dimension: Int
|
||||||
|
|
||||||
public val bins: Iterable<B>
|
public val bins: Iterable<B>
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
//A discoverability root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun interface HistogramBuilder<in T : Any> {
|
public interface HistogramBuilder<in T : Any, V : Any> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment appropriate bin
|
* The default value increment for a bin
|
||||||
*/
|
*/
|
||||||
public fun putValue(point: Point<out T>, value: Number)
|
public val defaultValue: V
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment appropriate bin with given value
|
||||||
|
*/
|
||||||
|
public fun putValue(point: Point<out T>, value: V = defaultValue)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any, B : Bin<T>> HistogramBuilder<T>.put(point: Point<out T>): Unit = putValue(point, 1.0)
|
public fun <T : Any> HistogramBuilder<T, *>.put(point: Point<out T>): Unit = putValue(point)
|
||||||
|
|
||||||
public fun <T : Any> HistogramBuilder<T>.put(vararg point: T): Unit = put(point.asBuffer())
|
public fun <T : Any> HistogramBuilder<T, *>.put(vararg point: T): Unit = put(point.asBuffer())
|
||||||
|
|
||||||
public fun HistogramBuilder<Double>.put(vararg point: Number): Unit =
|
public fun HistogramBuilder<Double, *>.put(vararg point: Number): Unit =
|
||||||
put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray()))
|
put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray()))
|
||||||
|
|
||||||
public fun HistogramBuilder<Double>.put(vararg point: Double): Unit = put(DoubleBuffer(point))
|
public fun HistogramBuilder<Double, *>.put(vararg point: Double): Unit = put(DoubleBuffer(point))
|
||||||
public fun <T : Any> HistogramBuilder<T>.fill(sequence: Iterable<Point<T>>): Unit = sequence.forEach { put(it) }
|
public fun <T : Any> HistogramBuilder<T, *>.fill(sequence: Iterable<Point<T>>): Unit = sequence.forEach { put(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass a sequence builder into histogram
|
* Pass a sequence builder into histogram
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> HistogramBuilder<T>.fill(block: suspend SequenceScope<Point<T>>.() -> Unit): Unit =
|
public fun <T : Any> HistogramBuilder<T, *>.fill(block: suspend SequenceScope<Point<T>>.() -> Unit): Unit =
|
||||||
fill(sequence(block).asIterable())
|
fill(sequence(block).asIterable())
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2021 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
|
import space.kscience.kmath.domains.Domain1D
|
||||||
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.operations.asSequence
|
||||||
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A univariate bin based on a range
|
||||||
|
*
|
||||||
|
* @property value The value of histogram including weighting
|
||||||
|
* @property standardDeviation Standard deviation of the bin value. Zero or negative if not applicable
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public class Bin1D<T : Comparable<T>, out V>(
|
||||||
|
public val domain: Domain1D<T>,
|
||||||
|
override val value: V,
|
||||||
|
) : Bin<T, V>, ClosedRange<T> by domain.range {
|
||||||
|
|
||||||
|
override val dimension: Int get() = 1
|
||||||
|
|
||||||
|
override fun contains(point: Buffer<T>): Boolean = point.size == 1 && contains(point[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
public interface Histogram1D<T : Comparable<T>, V> : Histogram<T, V, Bin1D<T, V>> {
|
||||||
|
override val dimension: Int get() = 1
|
||||||
|
public operator fun get(value: T): Bin1D<T, V>?
|
||||||
|
override operator fun get(point: Buffer<T>): Bin1D<T, V>? = get(point[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public interface Histogram1DBuilder<in T : Any, V : Any> : HistogramBuilder<T, V> {
|
||||||
|
/**
|
||||||
|
* Thread safe put operation
|
||||||
|
*/
|
||||||
|
public fun putValue(at: T, value: V = defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun Histogram1DBuilder<Double, *>.fill(items: Iterable<Double>): Unit =
|
||||||
|
items.forEach(this::putValue)
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun Histogram1DBuilder<Double, *>.fill(array: DoubleArray): Unit =
|
||||||
|
array.forEach(this::putValue)
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun Histogram1DBuilder<Double, *>.fill(buffer: Buffer<Double>): Unit =
|
||||||
|
buffer.asSequence().forEach(this::putValue)
|
@ -18,24 +18,28 @@ import space.kscience.kmath.operations.invoke
|
|||||||
/**
|
/**
|
||||||
* A simple histogram bin based on domain
|
* A simple histogram bin based on domain
|
||||||
*/
|
*/
|
||||||
public data class DomainBin<in T : Comparable<T>>(
|
public data class DomainBin<in T : Comparable<T>, out V>(
|
||||||
public val domain: Domain<T>,
|
public val domain: Domain<T>,
|
||||||
override val value: Number,
|
override val value: V,
|
||||||
) : Bin<T>, Domain<T> by domain
|
) : Bin<T, V>, Domain<T> by domain
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param T the type of the argument space
|
||||||
|
* @param V the type of bin value
|
||||||
|
*/
|
||||||
public class IndexedHistogram<T : Comparable<T>, V : Any>(
|
public class IndexedHistogram<T : Comparable<T>, V : Any>(
|
||||||
public val histogramSpace: IndexedHistogramSpace<T, V>,
|
public val histogramSpace: IndexedHistogramSpace<T, V>,
|
||||||
public val values: StructureND<V>,
|
public val values: StructureND<V>,
|
||||||
) : Histogram<T, Bin<T>> {
|
) : Histogram<T, V, DomainBin<T, V>> {
|
||||||
|
|
||||||
override fun get(point: Point<T>): Bin<T>? {
|
override fun get(point: Point<T>): DomainBin<T, V>? {
|
||||||
val index = histogramSpace.getIndex(point) ?: return null
|
val index = histogramSpace.getIndex(point) ?: return null
|
||||||
return histogramSpace.produceBin(index, values[index])
|
return histogramSpace.produceBin(index, values[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
override val dimension: Int get() = histogramSpace.shape.size
|
override val dimension: Int get() = histogramSpace.shape.size
|
||||||
|
|
||||||
override val bins: Iterable<Bin<T>>
|
override val bins: Iterable<DomainBin<T, V>>
|
||||||
get() = DefaultStrides(histogramSpace.shape).asSequence().map {
|
get() = DefaultStrides(histogramSpace.shape).asSequence().map {
|
||||||
histogramSpace.produceBin(it, values[it])
|
histogramSpace.produceBin(it, values[it])
|
||||||
}.asIterable()
|
}.asIterable()
|
||||||
@ -46,9 +50,8 @@ public class IndexedHistogram<T : Comparable<T>, V : Any>(
|
|||||||
*/
|
*/
|
||||||
public interface IndexedHistogramSpace<T : Comparable<T>, V : Any>
|
public interface IndexedHistogramSpace<T : Comparable<T>, V : Any>
|
||||||
: Group<IndexedHistogram<T, V>>, ScaleOperations<IndexedHistogram<T, V>> {
|
: Group<IndexedHistogram<T, V>>, ScaleOperations<IndexedHistogram<T, V>> {
|
||||||
//public val valueSpace: Space<V>
|
|
||||||
public val shape: Shape
|
public val shape: Shape
|
||||||
public val histogramValueSpace: FieldND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
|
public val histogramValueAlgebra: FieldND<V, *> //= NDAlgebra.space(valueSpace, Buffer.Companion::boxing, *shape),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve index of the bin including given [point]
|
* Resolve index of the bin including given [point]
|
||||||
@ -60,19 +63,19 @@ public interface IndexedHistogramSpace<T : Comparable<T>, V : Any>
|
|||||||
*/
|
*/
|
||||||
public fun getDomain(index: IntArray): Domain<T>?
|
public fun getDomain(index: IntArray): Domain<T>?
|
||||||
|
|
||||||
public fun produceBin(index: IntArray, value: V): Bin<T>
|
public fun produceBin(index: IntArray, value: V): DomainBin<T, V>
|
||||||
|
|
||||||
public fun produce(builder: HistogramBuilder<T>.() -> Unit): IndexedHistogram<T, V>
|
public fun produce(builder: HistogramBuilder<T, V>.() -> Unit): IndexedHistogram<T, V>
|
||||||
|
|
||||||
override fun add(left: IndexedHistogram<T, V>, right: IndexedHistogram<T, V>): IndexedHistogram<T, V> {
|
override fun add(left: IndexedHistogram<T, V>, right: IndexedHistogram<T, V>): IndexedHistogram<T, V> {
|
||||||
require(left.histogramSpace == this) { "Can't operate on a histogram produced by external space" }
|
require(left.histogramSpace == this) { "Can't operate on a histogram produced by external space" }
|
||||||
require(right.histogramSpace == this) { "Can't operate on a histogram produced by external space" }
|
require(right.histogramSpace == this) { "Can't operate on a histogram produced by external space" }
|
||||||
return IndexedHistogram(this, histogramValueSpace { left.values + right.values })
|
return IndexedHistogram(this, histogramValueAlgebra { left.values + right.values })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun scale(a: IndexedHistogram<T, V>, value: Double): IndexedHistogram<T, V> {
|
override fun scale(a: IndexedHistogram<T, V>, value: Double): IndexedHistogram<T, V> {
|
||||||
require(a.histogramSpace == this) { "Can't operate on a histogram produced by external space" }
|
require(a.histogramSpace == this) { "Can't operate on a histogram produced by external space" }
|
||||||
return IndexedHistogram(this, histogramValueSpace { a.values * value })
|
return IndexedHistogram(this, histogramValueAlgebra { a.values * value })
|
||||||
}
|
}
|
||||||
|
|
||||||
override val zero: IndexedHistogram<T, V> get() = produce { }
|
override val zero: IndexedHistogram<T, V> get() = produce { }
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2021 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
|
//class UniformDoubleHistogram1D: DoubleHistogram1D {
|
||||||
|
//}
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2021 KMath contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package space.kscience.kmath.histogram
|
|
||||||
|
|
||||||
import space.kscience.kmath.domains.UnivariateDomain
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.asSequence
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
|
||||||
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public val UnivariateDomain.center: Double
|
|
||||||
get() = (range.endInclusive + range.start) / 2
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A univariate bin based on a range
|
|
||||||
*
|
|
||||||
* @property value The value of histogram including weighting
|
|
||||||
* @property standardDeviation Standard deviation of the bin value. Zero or negative if not applicable
|
|
||||||
*/
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public class UnivariateBin(
|
|
||||||
public val domain: UnivariateDomain,
|
|
||||||
override val value: Double,
|
|
||||||
public val standardDeviation: Double,
|
|
||||||
) : Bin<Double>, ClosedFloatingPointRange<Double> by domain.range {
|
|
||||||
|
|
||||||
override val dimension: Int get() = 1
|
|
||||||
|
|
||||||
override fun contains(point: Buffer<Double>): Boolean = point.size == 1 && contains(point[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
public interface UnivariateHistogram : Histogram<Double, UnivariateBin> {
|
|
||||||
public operator fun get(value: Double): UnivariateBin?
|
|
||||||
override operator fun get(point: Buffer<Double>): UnivariateBin? = get(point[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public interface UnivariateHistogramBuilder : HistogramBuilder<Double> {
|
|
||||||
/**
|
|
||||||
* Thread safe put operation
|
|
||||||
*/
|
|
||||||
public fun putValue(at: Double, value: Double = 1.0)
|
|
||||||
|
|
||||||
override fun putValue(point: Buffer<Double>, value: Number)
|
|
||||||
}
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun UnivariateHistogramBuilder.fill(items: Iterable<Double>): Unit = items.forEach(this::putValue)
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun UnivariateHistogramBuilder.fill(array: DoubleArray): Unit = array.forEach(this::putValue)
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun UnivariateHistogramBuilder.fill(buffer: Buffer<Double>): Unit = buffer.asSequence().forEach(this::putValue)
|
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.histogram
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
import space.kscience.kmath.domains.UnivariateDomain
|
import space.kscience.kmath.domains.DoubleDomain1D
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.operations.Group
|
import space.kscience.kmath.operations.Group
|
||||||
import space.kscience.kmath.operations.ScaleOperations
|
import space.kscience.kmath.operations.ScaleOperations
|
||||||
@ -15,7 +15,7 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
private fun <B : ClosedFloatingPointRange<Double>> TreeMap<Double, B>.getBin(value: Double): B? {
|
private fun <B : ClosedRange<Double>> TreeMap<Double, B>.getBin(value: Double): B? {
|
||||||
// check ceiling entry and return it if it is what needed
|
// check ceiling entry and return it if it is what needed
|
||||||
val ceil = ceilingEntry(value)?.value
|
val ceil = ceilingEntry(value)?.value
|
||||||
if (ceil != null && value in ceil) return ceil
|
if (ceil != null && value in ceil) return ceil
|
||||||
@ -28,19 +28,18 @@ private fun <B : ClosedFloatingPointRange<Double>> TreeMap<Double, B>.getBin(val
|
|||||||
|
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public class TreeHistogram(
|
public class TreeHistogram(
|
||||||
private val binMap: TreeMap<Double, out UnivariateBin>,
|
private val binMap: TreeMap<Double, out Bin1D<Double, Double>>,
|
||||||
) : UnivariateHistogram {
|
) : Histogram1D<Double, Double> {
|
||||||
override fun get(value: Double): UnivariateBin? = binMap.getBin(value)
|
override fun get(value: Double): Bin1D<Double, Double>? = binMap.getBin(value)
|
||||||
override val dimension: Int get() = 1
|
override val bins: Collection<Bin1D<Double, Double>> get() = binMap.values
|
||||||
override val bins: Collection<UnivariateBin> get() = binMap.values
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal class TreeHistogramBuilder(val binFactory: (Double) -> UnivariateDomain) : UnivariateHistogramBuilder {
|
internal class TreeHistogramBuilder(val binFactory: (Double) -> DoubleDomain1D) : Histogram1DBuilder<Double, Double> {
|
||||||
|
|
||||||
internal class BinCounter(val domain: UnivariateDomain, val counter: Counter<Double> = Counter.double()) :
|
internal class BinCounter(val domain: DoubleDomain1D, val counter: Counter<Double> = Counter.double()) :
|
||||||
ClosedFloatingPointRange<Double> by domain.range
|
ClosedRange<Double> by domain.range
|
||||||
|
|
||||||
private val bins: TreeMap<Double, BinCounter> = TreeMap()
|
private val bins: TreeMap<Double, BinCounter> = TreeMap()
|
||||||
|
|
||||||
@ -64,15 +63,15 @@ internal class TreeHistogramBuilder(val binFactory: (Double) -> UnivariateDomain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putValue(point: Buffer<Double>, value: Number) {
|
override fun putValue(point: Buffer<Double>, value: Double) {
|
||||||
require(point.size == 1) { "Only points with single value could be used in univariate histogram" }
|
require(point.size == 1) { "Only points with single value could be used in univariate histogram" }
|
||||||
putValue(point[0], value.toDouble())
|
putValue(point[0], value.toDouble())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(): TreeHistogram {
|
fun build(): TreeHistogram {
|
||||||
val map = bins.mapValuesTo(TreeMap<Double, UnivariateBin>()) { (_, binCounter) ->
|
val map = bins.mapValuesTo(TreeMap<Double, Bin1D<Double,Double>>()) { (_, binCounter) ->
|
||||||
val count = binCounter.counter.value
|
val count = binCounter.counter.value
|
||||||
UnivariateBin(binCounter.domain, count, sqrt(count))
|
Bin1D(binCounter.domain, count, sqrt(count))
|
||||||
}
|
}
|
||||||
return TreeHistogram(map)
|
return TreeHistogram(map)
|
||||||
}
|
}
|
||||||
@ -83,23 +82,23 @@ internal class TreeHistogramBuilder(val binFactory: (Double) -> UnivariateDomain
|
|||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public class TreeHistogramSpace(
|
public class TreeHistogramSpace(
|
||||||
@PublishedApi internal val binFactory: (Double) -> UnivariateDomain,
|
@PublishedApi internal val binFactory: (Double) -> DoubleDomain1D,
|
||||||
) : Group<UnivariateHistogram>, ScaleOperations<UnivariateHistogram> {
|
) : Group<Histogram1D<Double,Double>>, ScaleOperations<Histogram1D<Double,Double>> {
|
||||||
|
|
||||||
public inline fun fill(block: UnivariateHistogramBuilder.() -> Unit): UnivariateHistogram =
|
public inline fun fill(block: Histogram1DBuilder<Double,Double>.() -> Unit): Histogram1D<Double,Double> =
|
||||||
TreeHistogramBuilder(binFactory).apply(block).build()
|
TreeHistogramBuilder(binFactory).apply(block).build()
|
||||||
|
|
||||||
override fun add(
|
override fun add(
|
||||||
left: UnivariateHistogram,
|
left: Histogram1D<Double,Double>,
|
||||||
right: UnivariateHistogram,
|
right: Histogram1D<Double,Double>,
|
||||||
): UnivariateHistogram {
|
): Histogram1D<Double,Double> {
|
||||||
// require(a.context == this) { "Histogram $a does not belong to this context" }
|
// require(a.context == this) { "Histogram $a does not belong to this context" }
|
||||||
// require(b.context == this) { "Histogram $b does not belong to this context" }
|
// require(b.context == this) { "Histogram $b does not belong to this context" }
|
||||||
val bins = TreeMap<Double, UnivariateBin>().apply {
|
val bins = TreeMap<Double, Bin1D<Double,Double>>().apply {
|
||||||
(left.bins.map { it.domain } union right.bins.map { it.domain }).forEach { def ->
|
(left.bins.map { it.domain } union right.bins.map { it.domain }).forEach { def ->
|
||||||
put(
|
put(
|
||||||
def.center,
|
def.center,
|
||||||
UnivariateBin(
|
Bin1D(
|
||||||
def,
|
def,
|
||||||
value = (left[def.center]?.value ?: 0.0) + (right[def.center]?.value ?: 0.0),
|
value = (left[def.center]?.value ?: 0.0) + (right[def.center]?.value ?: 0.0),
|
||||||
standardDeviation = (left[def.center]?.standardDeviation
|
standardDeviation = (left[def.center]?.standardDeviation
|
||||||
@ -111,12 +110,12 @@ public class TreeHistogramSpace(
|
|||||||
return TreeHistogram(bins)
|
return TreeHistogram(bins)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun scale(a: UnivariateHistogram, value: Double): UnivariateHistogram {
|
override fun scale(a: Histogram1D<Double,Double>, value: Double): Histogram1D<Double,Double> {
|
||||||
val bins = TreeMap<Double, UnivariateBin>().apply {
|
val bins = TreeMap<Double, Bin1D<Double,Double>>().apply {
|
||||||
a.bins.forEach { bin ->
|
a.bins.forEach { bin ->
|
||||||
put(
|
put(
|
||||||
bin.domain.center,
|
bin.domain.center,
|
||||||
UnivariateBin(
|
Bin1D(
|
||||||
bin.domain,
|
bin.domain,
|
||||||
value = bin.value * value,
|
value = bin.value * value,
|
||||||
standardDeviation = abs(bin.standardDeviation * value)
|
standardDeviation = abs(bin.standardDeviation * value)
|
||||||
@ -128,38 +127,38 @@ public class TreeHistogramSpace(
|
|||||||
return TreeHistogram(bins)
|
return TreeHistogram(bins)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun UnivariateHistogram.unaryMinus(): UnivariateHistogram = this * (-1)
|
override fun Histogram1D<Double,Double>.unaryMinus(): Histogram1D<Double,Double> = this * (-1)
|
||||||
|
|
||||||
override val zero: UnivariateHistogram by lazy { fill { } }
|
override val zero: Histogram1D<Double,Double> by lazy { fill { } }
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
/**
|
/**
|
||||||
* Build and fill a [UnivariateHistogram]. Returns a read-only histogram.
|
* Build and fill a [DoubleHistogram1D]. Returns a read-only histogram.
|
||||||
*/
|
*/
|
||||||
public inline fun uniform(
|
public inline fun uniform(
|
||||||
binSize: Double,
|
binSize: Double,
|
||||||
start: Double = 0.0,
|
start: Double = 0.0,
|
||||||
builder: UnivariateHistogramBuilder.() -> Unit,
|
builder: Histogram1DBuilder<Double,Double>.() -> Unit,
|
||||||
): UnivariateHistogram = uniform(binSize, start).fill(builder)
|
): Histogram1D<Double,Double> = uniform(binSize, start).fill(builder)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and fill a histogram with custom borders. Returns a read-only histogram.
|
* Build and fill a histogram with custom borders. Returns a read-only histogram.
|
||||||
*/
|
*/
|
||||||
public inline fun custom(
|
public inline fun custom(
|
||||||
borders: DoubleArray,
|
borders: DoubleArray,
|
||||||
builder: UnivariateHistogramBuilder.() -> Unit,
|
builder: Histogram1DBuilder<Double,Double>.() -> Unit,
|
||||||
): UnivariateHistogram = custom(borders).fill(builder)
|
): Histogram1D<Double,Double> = custom(borders).fill(builder)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and fill a [UnivariateHistogram]. Returns a read-only histogram.
|
* Build and fill a [DoubleHistogram1D]. Returns a read-only histogram.
|
||||||
*/
|
*/
|
||||||
public fun uniform(
|
public fun uniform(
|
||||||
binSize: Double,
|
binSize: Double,
|
||||||
start: Double = 0.0,
|
start: Double = 0.0,
|
||||||
): TreeHistogramSpace = TreeHistogramSpace { value ->
|
): TreeHistogramSpace = TreeHistogramSpace { value ->
|
||||||
val center = start + binSize * floor((value - start) / binSize + 0.5)
|
val center = start + binSize * floor((value - start) / binSize + 0.5)
|
||||||
UnivariateDomain((center - binSize / 2)..(center + binSize / 2))
|
DoubleDomain1D((center - binSize / 2)..(center + binSize / 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,11 +169,11 @@ public class TreeHistogramSpace(
|
|||||||
|
|
||||||
return TreeHistogramSpace { value ->
|
return TreeHistogramSpace { value ->
|
||||||
when {
|
when {
|
||||||
value < sorted.first() -> UnivariateDomain(
|
value < sorted.first() -> DoubleDomain1D(
|
||||||
Double.NEGATIVE_INFINITY..sorted.first()
|
Double.NEGATIVE_INFINITY..sorted.first()
|
||||||
)
|
)
|
||||||
|
|
||||||
value > sorted.last() -> UnivariateDomain(
|
value > sorted.last() -> DoubleDomain1D(
|
||||||
sorted.last()..Double.POSITIVE_INFINITY
|
sorted.last()..Double.POSITIVE_INFINITY
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -182,7 +181,7 @@ public class TreeHistogramSpace(
|
|||||||
val index = sorted.indices.first { value > sorted[it] }
|
val index = sorted.indices.first { value > sorted[it] }
|
||||||
val left = sorted[index]
|
val left = sorted[index]
|
||||||
val right = sorted[index + 1]
|
val right = sorted[index + 1]
|
||||||
UnivariateDomain(left..right)
|
DoubleDomain1D(left..right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user