forked from kscience/kmath
Part 1
This commit is contained in:
parent
47a9bf0e9a
commit
2a2c5e8765
@ -8,8 +8,6 @@ package space.kscience.kmath.series
|
|||||||
import space.kscience.kmath.interpolation.SplineInterpolator
|
import space.kscience.kmath.interpolation.SplineInterpolator
|
||||||
import space.kscience.kmath.interpolation.interpolate
|
import space.kscience.kmath.interpolation.interpolate
|
||||||
import space.kscience.kmath.operations.*
|
import space.kscience.kmath.operations.*
|
||||||
import space.kscience.kmath.operations.Float64BufferOps.Companion.div
|
|
||||||
import space.kscience.kmath.operations.Float64BufferOps.Companion.pow
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.asBuffer
|
import space.kscience.kmath.structures.asBuffer
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
@ -29,13 +27,13 @@ import kotlin.math.sign
|
|||||||
* @param nModes how many modes should be extracted at most. The algorithm may return fewer modes if it was not
|
* @param nModes how many modes should be extracted at most. The algorithm may return fewer modes if it was not
|
||||||
* possible to extract more modes from the signal.
|
* possible to extract more modes from the signal.
|
||||||
*/
|
*/
|
||||||
public class EmpiricalModeDecomposition<BA, L: Number> (
|
public class EmpiricalModeDecomposition<A: Field<Double>, BA, L: Number> (
|
||||||
private val seriesAlgebra: SeriesAlgebra<Double, *, BA, L>,
|
private val seriesAlgebra: SeriesAlgebra<Double, A, BA, L>,
|
||||||
private val sConditionThreshold: Int = 15,
|
private val sConditionThreshold: Int = 15,
|
||||||
private val maxSiftIterations: Int = 20,
|
private val maxSiftIterations: Int = 20,
|
||||||
private val siftingDelta: Double = 1e-2,
|
private val siftingDelta: Double = 1e-2,
|
||||||
private val nModes: Int = 6
|
private val nModes: Int = 6
|
||||||
) where BA: BufferAlgebra<Double, *>, BA: RingOps<Buffer<Double>> {
|
) where BA: BufferAlgebra<Double, A>, BA: FieldOps<Buffer<Double>> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take a signal, construct an upper and a lower envelopes, find the mean value of two,
|
* Take a signal, construct an upper and a lower envelopes, find the mean value of two,
|
||||||
@ -46,22 +44,22 @@ public class EmpiricalModeDecomposition<BA, L: Number> (
|
|||||||
* the signal does not have enough extrema to construct envelopes.
|
* the signal does not have enough extrema to construct envelopes.
|
||||||
*/
|
*/
|
||||||
private fun findMean(signal: Series<Double>): Series<Double>? = (seriesAlgebra) {
|
private fun findMean(signal: Series<Double>): Series<Double>? = (seriesAlgebra) {
|
||||||
val interpolator = SplineInterpolator(Float64Field)
|
val interpolator = SplineInterpolator(elementAlgebra)
|
||||||
fun generateEnvelope(extrema: List<Int>, paddedExtremeValues: DoubleArray): Series<Double> {
|
fun generateEnvelope(extrema: List<Int>, paddedExtremeValues: Array<Double>): Series<Double> {
|
||||||
val envelopeFunction = interpolator.interpolate(
|
val envelopeFunction = interpolator.interpolate(
|
||||||
Buffer(extrema.size) { signal.labels[extrema[it]].toDouble() },
|
Buffer(extrema.size) { signal.labels[extrema[it]] as Double },
|
||||||
paddedExtremeValues.asBuffer()
|
paddedExtremeValues.asBuffer()
|
||||||
)
|
)
|
||||||
return signal.mapWithLabel { _, label ->
|
return signal.mapWithLabel { _, label ->
|
||||||
// For some reason PolynomialInterpolator is exclusive and the right boundary
|
// For some reason PolynomialInterpolator is exclusive and the right boundary
|
||||||
// TODO Notify interpolator authors
|
// TODO Notify interpolator authors
|
||||||
envelopeFunction(label.toDouble()) ?: paddedExtremeValues.last()
|
envelopeFunction(label as Double) ?: paddedExtremeValues.last()
|
||||||
// need to make the interpolator yield values outside boundaries?
|
// need to make the interpolator yield values outside boundaries?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Extrema padding (experimental) TODO padding needs a dedicated function
|
// Extrema padding (experimental) TODO padding needs a dedicated function
|
||||||
val maxima = listOf(0) + signal.peaks() + (signal.size - 1)
|
val maxima = listOf(0) + signal.peaks() + (signal.size - 1)
|
||||||
val maxValues = DoubleArray(maxima.size) { signal[maxima[it]] }
|
val maxValues = Array(maxima.size) { signal[maxima[it]] }
|
||||||
if (maxValues[0] < maxValues[1]) {
|
if (maxValues[0] < maxValues[1]) {
|
||||||
maxValues[0] = maxValues[1]
|
maxValues[0] = maxValues[1]
|
||||||
}
|
}
|
||||||
@ -70,7 +68,7 @@ public class EmpiricalModeDecomposition<BA, L: Number> (
|
|||||||
}
|
}
|
||||||
|
|
||||||
val minima = listOf(0) + signal.troughs() + (signal.size - 1)
|
val minima = listOf(0) + signal.troughs() + (signal.size - 1)
|
||||||
val minValues = DoubleArray(minima.size) { signal[minima[it]] }
|
val minValues = Array(minima.size) { signal[minima[it]] }
|
||||||
if (minValues[0] > minValues[1]) {
|
if (minValues[0] > minValues[1]) {
|
||||||
minValues[0] = minValues[1]
|
minValues[0] = minValues[1]
|
||||||
}
|
}
|
||||||
@ -157,13 +155,13 @@ public class EmpiricalModeDecomposition<BA, L: Number> (
|
|||||||
* @param nModes how many modes should be extracted at most. The algorithm may return fewer modes if it was not
|
* @param nModes how many modes should be extracted at most. The algorithm may return fewer modes if it was not
|
||||||
* possible to extract more modes from the signal.
|
* possible to extract more modes from the signal.
|
||||||
*/
|
*/
|
||||||
public fun <L: Number, BA> SeriesAlgebra<Double, *, BA, L>.empiricalModeDecomposition(
|
public fun <L: Number, A: Field<Double>, BA> SeriesAlgebra<Double, A, BA, L>.empiricalModeDecomposition(
|
||||||
sConditionThreshold: Int = 15,
|
sConditionThreshold: Int = 15,
|
||||||
maxSiftIterations: Int = 20,
|
maxSiftIterations: Int = 20,
|
||||||
siftingDelta: Double = 1e-2,
|
siftingDelta: Double = 1e-2,
|
||||||
nModes: Int = 3
|
nModes: Int = 3
|
||||||
): EmpiricalModeDecomposition<BA, L>
|
): EmpiricalModeDecomposition<A, BA, L>
|
||||||
where BA: BufferAlgebra<Double, *>, BA: RingOps<Buffer<Double>> = EmpiricalModeDecomposition(
|
where BA: BufferAlgebra<Double, A>, BA: FieldOps<Buffer<Double>> = EmpiricalModeDecomposition(
|
||||||
seriesAlgebra = this,
|
seriesAlgebra = this,
|
||||||
sConditionThreshold = sConditionThreshold,
|
sConditionThreshold = sConditionThreshold,
|
||||||
maxSiftIterations = maxSiftIterations,
|
maxSiftIterations = maxSiftIterations,
|
||||||
@ -188,13 +186,14 @@ private fun Series<Double>.countZeros(): Int {
|
|||||||
/**
|
/**
|
||||||
* Compute relative difference of two series.
|
* Compute relative difference of two series.
|
||||||
*/
|
*/
|
||||||
private fun <BA> SeriesAlgebra<Double, *, BA, *>.relativeDifference(
|
private fun <T, A: Ring<T>, BA> SeriesAlgebra<T, A, BA, *>.relativeDifference(
|
||||||
current: Series<Double>,
|
current: Series<T>,
|
||||||
previous: Series<Double>
|
previous: Series<T>
|
||||||
):Double where BA: BufferAlgebra<Double, *>, BA: RingOps<Buffer<Double>> =
|
):T where BA: BufferAlgebra<T, A>, BA: FieldOps<Buffer<T>> = (bufferAlgebra) {
|
||||||
(current - previous).pow(2)
|
((current - previous) * (current - previous))
|
||||||
.div(previous pow 2)
|
.div(previous * previous)
|
||||||
.fold(0.0) { acc, d -> acc + d } // TODO replace with Series<>.sum() method when it's implemented
|
.fold(elementAlgebra.zero) { acc, it -> acc + it}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Brute force count all extrema of a series.
|
* Brute force count all extrema of a series.
|
||||||
|
Loading…
Reference in New Issue
Block a user