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

org.kalasim.plot.letsplot.LetsPlotVis.kt Maven / Gradle / Ivy

The newest version!
package org.kalasim.plot.letsplot

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.api.*
import org.jetbrains.kotlinx.dataframe.api.util.unfold
import org.jetbrains.letsPlot.Stat
import org.jetbrains.letsPlot.facet.facetWrap
import org.jetbrains.letsPlot.geom.*
import org.jetbrains.letsPlot.intern.GenericAesMapping
import org.jetbrains.letsPlot.intern.Plot
import org.jetbrains.letsPlot.label.*
import org.jetbrains.letsPlot.letsPlot
import org.jetbrains.letsPlot.scale.scaleXDateTime
import org.kalasim.*
import org.kalasim.analysis.ResourceActivityEvent
import org.kalasim.monitors.*

fun DataFrame<*>.letsPlot(mapping: GenericAesMapping.() -> Unit = {}) = org.jetbrains.letsPlot.letsPlot(toMap(), mapping)


fun  MetricTimeline.display(
    title: String = name,
    from: SimTime? = null,
    to: SimTime? = null,
//    forceTickAxis: Boolean = false,
): Plot {
    val data = stepFun()
        .filter { from == null || it.time >= from }
        .filter { to == null || it.time <= to }

    return data.toDataFrame()
        .convertTick2Double("time")
        .letsPlot() +
            geomStep { x = "time"; y = "value" } + ggtitle(title)
}

private fun DataFrame<*>.convertTick2Double(colName: String): DataFrame<*> {
//    return add(colName) { colName().epochSeconds }
    return convert { colName() }.with { it.epochSeconds }
}


fun NumericStatisticMonitor.display(title: String = name): Plot {
    val data: Map> = mapOf("values" to values.toList())

    return letsPlot(data) + geomHistogram { x = "values" } + ggtitle(title)
}

fun  FrequencyTable.display(title: String? = null): Plot {
    var plot = toList().toDataFrame().letsPlot() +
            geomBar(stat = Stat.identity) { x = "first"; y = "second" }

    if(title != null) plot += ggtitle(title)

    return plot
}


fun  CategoryTimeline.display(
    title: String = name,
    forceTickAxis: Boolean = false,
): Plot {
    val nlmStatsData = statsData()
    val stepFun = nlmStatsData.stepFun()

    data class Segment(val value: T, val start: SimTime, val end: SimTime)

    val df = stepFun.zipWithNext().map {
        Segment(
            it.first.value,
            it.first.time,
            it.second.time
        )
    }.toDataFrame()

    println(df)

    val segments = df
        .convertTick2Double("start")
        .convertTick2Double("end")
//        .addColumn("start") { expr -> expr["start"].map { wtTransform(TickTime(it)) } }
//        .addColumn("end") { expr -> expr["end"].map { wtTransform(TickTime(it)) } }
        .convert { "value"()}.with { it?.toString() }


    // why cant we use "x".asDiscreteVariable here?
    return segments.letsPlot() +
            geomSegment {
                x = "start"
                y = "value"
                xend = "end"
                yend = "value"
            } +
            geomPoint() +
            xlab("time") +
            ylab("") +
            ggtitle(title)
                .also { if(!forceTickAxis) it + scaleXDateTime() }
}


fun List.display(
    title: String? = null,
    forceTickAxis: Boolean = false,
): Plot {

    val plotData = toDataFrame()
        //
        .unfold("resource", listOf("name"))
        .add("activity") { "activity"() ?: "Other" }
        .convertTick2Double("requested")
        .convertTick2Double("released")
//        .addColumn("start") { expr -> expr["start"].map { it.value } }
//        .addColumn("end") { expr -> expr["end"].map { it.value } }

//    require(!forceTickAxis) { "tick axis not support. pls file an issue on github." }

    return plotData.letsPlot() +
            geomSegment(size = 10) {
                y = "name"
                yend = "name"
                x = if(forceTickAxis) "requestedTT" else "requested"
                xend = if(forceTickAxis) "releasedTT" else "released"
                color = "activity"

            } +
            ylab("") +
            xlab("Time")
                .also { if(title != null) ggtitle(title) }
                .also { if(!forceTickAxis) it + scaleXDateTime() }
}


fun List.display(
    title: String? = null,
    exclude: List = listOf(
        ResourceMetric.Capacity,
        ResourceMetric.Occupancy,
        ResourceMetric.Availability
    ),
    forceTickAxis: Boolean = false,
): Plot {
    return filter { it.metric !in exclude }.toDataFrame()
//        .addColumn("start") { expr -> expr["start"].map { it.value } }
        .convertTick2Double("start")
        .letsPlot() +
            geomStep {

                x = if(forceTickAxis) "start" else "startTT"
                y = "value"
                color = "metric"
            } +
            // scales arg not yet supported https://github.com/JetBrains/lets-plot/issues/479
            facetWrap("color", ncol = 1)
                .also { if(title != null) ggtitle(title) }
                .also { if(!forceTickAxis) it + scaleXDateTime() }
}


//
// Components
//

internal fun List.clistTimeline() = flatMap { eqn ->
    eqn.stateTimeline
        .statsData().asList().map { eqn to it }
}

fun List.displayStateTimeline(
    title: String? = null,
    componentName: String = "Component",
    forceTickAxis: Boolean = false,
): Plot {
    val df = clistTimeline()
        .toDataFrame()
        //
        .unfold("first", listOf("name"))
        //>
        .unfold>("second", listOf("timestamp", "duration", "value"))
//        .addColumn("start") { expr -> expr["timestamp"].map { wtTransform(TickTime(it)) } }
//        .addColumn("end") { expr -> (expr["timestamp"] + expr["timestamp"]).map { wtTransform(TickTime(it)) } }
        .convertTick2Double("start")
        .convertTick2Double("end")
        .add("value") { "value"()?.toString() }


    return df.letsPlot() + geomSegment {
        y = "name"
        yend = "name"
        x = "start"
        xend = "end"
        color = "value"
    } + xlab(componentName)
        .also { if(title != null) ggtitle(title) }
        .also { if(!forceTickAxis) it + scaleXDateTime() }
}


fun List.displayStateProportions(
    title: String? = null,
): Plot {
    val df = clistTimeline()
        .toDataFrame()
        .unfold("first", listOf("name"))
        .unfold>("second", listOf("timestamp", "duration", "value"))

    return df.letsPlot() + geomBar {
        y = "name"
        fill = "value"
        weight = "duration"
    } + xlab("State Proportion")
        .also { if(title != null) ggtitle(title) }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy