All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.jetbrains.datalore.plot.common.data.SeriesUtil.kt Maven / Gradle / Ivy

There is a newer version: 4.5.3-alpha1
Show newest version
/*
 * 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.common.data

import jetbrains.datalore.base.gcommon.base.Preconditions.checkState
import jetbrains.datalore.base.gcommon.collect.ClosedRange
import jetbrains.datalore.base.gcommon.collect.Iterables
import jetbrains.datalore.base.gcommon.collect.Iterables.all
import jetbrains.datalore.base.gcommon.collect.Iterables.filter
import jetbrains.datalore.base.gcommon.collect.Ordering
import kotlin.math.max
import kotlin.math.min

object SeriesUtil {
    const val TINY = 1e-50

    private val REAL_NUMBER = { it: Double? -> isFinite(it) }

    val NEGATIVE_NUMBER = { input: Double -> input < 0 }

    fun isSubTiny(value: Double): Boolean {
        return value < TINY
    }

    fun isSubTiny(range: ClosedRange): Boolean {
        return isFinite(range) && span(range) < TINY
    }

    fun checkedDoubles(values: Iterable<*>): CheckedDoubleIterable {
        return CheckedDoubleIterable(values)
    }

    fun checkedDoubles(values: List<*>): CheckedDoubleList {
        return CheckedDoubleList(values)
    }

    fun isFinite(v: Double?): Boolean {
        return v != null && v.isFinite()
    }

    fun asFinite(v: Double?, defaultValue: Double): Double {
        return if (v != null && v.isFinite())
            v
        else
            defaultValue
    }

    fun isFinite(v: Double): Boolean {
        return v.isFinite()
    }

    fun allFinite(v0: Double?, v1: Double?): Boolean {
        return isFinite(v0) && isFinite(v1)
    }

    fun allFinite(v0: Double?, v1: Double?, v2: Double?): Boolean {
        return allFinite(
            v0,
            v1
        ) && isFinite(v2)
    }

    fun allFinite(v0: Double?, v1: Double?, v2: Double?, v3: Double?): Boolean {
        return allFinite(
            v0,
            v1,
            v2
        ) && isFinite(v3)
    }

    fun filterFinite(l0: List, l1: List): List> {
        checkState(l0.size == l1.size)

        val l0Copy = ArrayList()
        val l1Copy = ArrayList()
        var copy: Boolean = false
        for ((i, v0) in l0.withIndex()) {
            val v1 = l1[i]
            if (!allFinite(v0, v1)) {
                if (!copy) {
                    // copy already checked elements
                    @Suppress("UNCHECKED_CAST")
                    l0Copy.addAll(l0.take(i).toList() as List)
                    @Suppress("UNCHECKED_CAST")
                    l1Copy.addAll(l1.take(i).toList() as List)
                    copy = true
                }
                continue
            }

            if (copy) {
                l0Copy.add(v0 as Double)
                l1Copy.add(v1 as Double)
            }
        }

        @Suppress("UNCHECKED_CAST")
        return when (copy) {
            true -> listOf(l0Copy, l1Copy)
            false -> listOf(l0 as List, l1 as List)
        }
    }

    fun range(values: Iterable): ClosedRange? {
        var min = 0.0
        var max = 0.0
        var inited = false
        for (v in values) {
            if (isFinite(v)) {
                if (inited) {
                    min = min(min, v!!)
                    max = max(max, v)
                } else {
                    max = v!!
                    min = max
                    inited = true
                }
            }
        }
        return if (inited)
            ClosedRange(min, max)
        else
            null
    }

    fun resolution(values: Iterable, naValue: Double): Double {

        // check if this is a row of a regular grid
        val rowDetector = RegularMeshDetector.tryRow(values)
        if (rowDetector.isMesh) {
            return rowDetector.resolution
        }

        // check if this is a column of a regular grid
        val columnDetector = RegularMeshDetector.tryColumn(values)
        return if (columnDetector.isMesh) {
            columnDetector.resolution
        } else {
            // use brut force method to find data resolution
            resolutionFullScan(values, naValue)
        }
    }

    private fun resolutionFullScan(values: Iterable, naValue: Double): Double {
        @Suppress("UNCHECKED_CAST")
        val goodDataVector = filter(values, REAL_NUMBER) as Iterable
        if (Iterables.isEmpty(goodDataVector)) {
            return naValue
        }

        val copy = Ordering.natural().sortedCopy(goodDataVector)
        if (copy.size < 2) {
            return naValue
        }

        val it = copy.iterator()
        var resolution = naValue
        var allZero = true
        var prev = it.next()
        while (it.hasNext()) {
            val curr = it.next()
            val dist = curr - prev
            if (dist > 0 && (dist < resolution || allZero)) {
                allZero = false
                resolution = dist
            }

            prev = curr
        }

        return resolution
    }

    fun ensureApplicableRange(range: ClosedRange?): ClosedRange {
        if (range == null) {
            return ClosedRange(-0.5, 0.5)
        }
        if (isSubTiny(range)) {
            val median = range.lowerEnd
            return ClosedRange(median - 0.5, median + 0.5)
        }
        return range
    }

    fun span(range: ClosedRange): Double {
        require(isFinite(range)) { "range must be finite: $range" }
        return range.upperEnd - range.lowerEnd
    }

    fun span(range0: ClosedRange?, range1: ClosedRange?): ClosedRange? {
        if (range0 == null) return range1
        return if (range1 == null) range0 else range0.span(range1)
    }

    fun expand(range: ClosedRange, newSpan: Double): ClosedRange {
        val expand = (newSpan - span(range)) / 2
        return expand(range, expand, expand)
    }

    fun expand(range: ClosedRange, lowerExpand: Double, upperExpand: Double): ClosedRange {
        return ClosedRange(range.lowerEnd - lowerExpand, range.upperEnd + upperExpand)
    }

    fun isFinite(range: ClosedRange): Boolean {
        return !(range.lowerEnd.isInfinite() || range.upperEnd.isInfinite())
    }

    fun matchingIndices(list: List<*>, matchedValue: Any?): MutableList {
        val result = ArrayList()
        for (i in list.indices) {
            if (matchedValue == list[i]) {
                result.add(i)
            }
        }
        return result
    }

    fun matchingIndices(list: List<*>, matchedValues: Set<*>): List {
        val result = ArrayList()
        for (i in list.indices) {
            if (matchedValues.contains(list[i])) {
                result.add(i)
            }
        }
        return result
    }

    fun  pickAtIndices(list: List, indices: List): List {
        val initialCapacity = if (indices.size > 10) indices.size else 10
        val result = ArrayList(initialCapacity)
        for (index in indices) {
            if (index < list.size) {
                result.add(list[index])
            }
        }
        return result
    }

    fun  pickAtIndices(list: List, indices: Set): List {
        val result = ArrayList(list.size)
        for (i in list.indices) {
            if (indices.contains(i)) {
                result.add(list[i])
            }
        }
        return result
    }

    fun  skipAtIndices(list: List, indices: Set): List {
        val result = ArrayList(list.size)
        for (i in list.indices) {
            if (!indices.contains(i)) {
                result.add(list[i])
            }
        }
        return result
    }

    fun  firstNotNull(list: List, defaultValue: T): T {
        for (v in list) {
            if (v != null) {
                return v
            }
        }
        return defaultValue
    }

    fun mean(values: List, defaultValue: Double?): Double? {
        var result = 0.0
        var i = -1.0
        for (value in values) {
            if (value != null && value.isFinite()) {
                i++
                result = value / (i + 1) + result * (i / (i + 1))
            }
        }
        return if (i >= 0) result else defaultValue
    }

    fun sum(values: List): Double {
        var result = 0.0
        for (value in values) {
            if (value != null && value.isFinite()) {
                result += value
            }
        }
        return result
    }

    fun toDoubleList(l: List<*>?): List? {
        return if (l == null)
            null
        else
            CheckedDoubleList(l).cast()
    }

    class CheckedDoubleList(list: List<*>) : CheckedDoubleIterable(list) {

        override fun cast(): List {
            return super.cast() as List
        }
    }

    open class CheckedDoubleIterable(private val myIterable: Iterable<*>) {
        private val myEmpty: Boolean = Iterables.isEmpty(myIterable)
        private val myCanBeCast: Boolean

        init {
            myCanBeCast = if (myEmpty) {
                true
            } else {
                all(filter(myIterable) { it != null }) { input -> input is Double }
            }
        }

        fun notEmptyAndCanBeCast(): Boolean {
            return !myEmpty && myCanBeCast
        }

        fun canBeCast(): Boolean {
            return myCanBeCast
        }

        open fun cast(): Iterable {
            checkState(myCanBeCast, "Can't cast to collection of numbers")
            // Safe cast: all values were checked
            @Suppress("UNCHECKED_CAST")
            return myIterable as Iterable
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy