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

tri.timeseries.TimeSeriesAggregate.kt Maven / Gradle / Ivy

/*-
 * #%L
 * coda-data
 * --
 * Copyright (C) 2020 - 2021 Elisha Peterson
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package tri.timeseries

import java.time.LocalDate
import tri.util.DateRange
import tri.util.minus

/**
 * Provides various ways to aggregate date/value data into a [TimeSeries].
 */
enum class TimeSeriesAggregate(private val aggregator: (List>, LocalDate?) -> Map) {
    /** Sums entries. */
    SUM({ pairs, finalDate ->
        val dateValues = pairs.groupBy { it.first }.mapValues { it.value.map { it.second } }.toSortedMap()
        if (dateValues.isEmpty()) mapOf()
        else DateRange(dateValues.keys.first()..(finalDate ?: dateValues.keys.last())).map {
            val values = dateValues[it] ?: listOf()
            val integers = values.all { it is Int }
            val sum: Number = if (integers) values.sumBy { it?.toInt() ?: 0 } else values.sumByDouble { it?.toDouble() ?: 0.0 }
            it to sum
        }.toMap()
    }),

    /** Fills latest value, ensuring gaps between missing entries are all filled. */
    FILL_WITH_LATEST_VALUE({ pairs, date ->
        val dateValues = pairs.map { it.first to it.second }.toMap().toSortedMap()
        if (dateValues.isEmpty()) mapOf()
        else DateRange(dateValues.keys.first()..(date ?: dateValues.keys.last())).map {
            it to if (it in dateValues.keys) dateValues[it]!!
            else dateValues.headMap(it).values.last()!!
        }.toMap()
    }),

    /** Fills latest value, but does not allow filling forward more than 7 days. */
    FILL_WITH_LATEST_VALUE_UP_TO_7({ pairs, date ->
        val sortedDates = pairs.map { it.first to it.second?.toDouble() }.toMap().toSortedMap()
        if (sortedDates.isEmpty()) mapOf()
        else {
            val first = sortedDates.keys.first()
            val last = sortedDates.keys.last()
            val dates = DateRange(first..last).toList()
            val values = dates.map { sortedDates[it] }
            var lastValueIndex = 0
            var lastValue = 0.0
            val adjustedValues = values.mapIndexed { i, value ->
                dates[i] to when {
                    value != null -> {
                        lastValueIndex = i
                        lastValue = value
                        value
                    }
                    i - lastValueIndex <= 7 -> lastValue
                    else -> 0.0
                }
            }.toMap()
            adjustedValues
        }
    });

    operator fun invoke(entries: List>, date: LocalDate?) = aggregator(entries, date)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy