-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Basic version of the eCDF stat. * Refactor ECDFStat. * Add an interpolation to ECDF. * Add autotests for the ECDFStat. * Fix ECDFStat (case when in one group there is only NaN's). * Add 'pad' parameter to the StepGeom (for the eCDF). * Add stat_ecdf() function. Tiny fixes in other stat functions. * Specify limits for the stat_ecdf() function. * Add docstrings for stat_ecdf(). * Small fixes in StepGeom::getPads because of sampling. * Move scaling of the Y axis inside the step geometry. * Add demo notebook for the stat_ecdf(). * Mention stat_ecdf() in the future_changes.md file. * Small refactor in the StepGeom. * Add pads to the ECDFStat. * Add another one demo case for the eCDF.
- Loading branch information
1 parent
8689b7c
commit 7ec91aa
Showing
12 changed files
with
884 additions
and
7 deletions.
There are no files selected for viewing
127 changes: 127 additions & 0 deletions
127
demo/plot-common/src/commonMain/kotlin/demo/plot/common/model/plotConfig/ECDF.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Copyright (c) 2023. JetBrains s.r.o. | ||
* Use of this source code is governed by the MIT license that can be found in the LICENSE file. | ||
*/ | ||
|
||
package demo.plot.common.model.plotConfig | ||
|
||
import demo.plot.common.data.Iris | ||
import demoAndTestShared.parsePlotSpec | ||
|
||
class ECDF { | ||
fun plotSpecList(): List<MutableMap<String, Any>> { | ||
return listOf( | ||
basic(), | ||
withInterpolation(), | ||
withGrouping(), | ||
withOrientationChange(), | ||
) | ||
} | ||
|
||
private fun basic(): MutableMap<String, Any> { | ||
val spec = """ | ||
{ | ||
'kind': 'plot', | ||
'mapping': { | ||
'x': 'sepal length (cm)' | ||
}, | ||
'ggtitle': { | ||
'text': 'Basic demo' | ||
}, | ||
'layers': [ | ||
{ | ||
'geom': 'step', | ||
'stat': 'ecdf', | ||
'pad': true | ||
} | ||
] | ||
} | ||
""".trimIndent() | ||
|
||
val plotSpec = HashMap(parsePlotSpec(spec)) | ||
plotSpec["data"] = Iris.df | ||
return plotSpec | ||
|
||
} | ||
|
||
private fun withInterpolation(): MutableMap<String, Any> { | ||
val spec = """ | ||
{ | ||
'kind': 'plot', | ||
'mapping': { | ||
'x': 'sepal length (cm)' | ||
}, | ||
'ggtitle': { | ||
'text': 'Interpolation' | ||
}, | ||
'layers': [ | ||
{ | ||
'geom': 'step', | ||
'stat': 'ecdf', | ||
'n': 10, | ||
'pad': true | ||
} | ||
] | ||
} | ||
""".trimIndent() | ||
|
||
val plotSpec = HashMap(parsePlotSpec(spec)) | ||
plotSpec["data"] = Iris.df | ||
return plotSpec | ||
|
||
} | ||
|
||
private fun withGrouping(): MutableMap<String, Any> { | ||
val spec = """ | ||
{ | ||
'kind': 'plot', | ||
'mapping': { | ||
'x': 'sepal length (cm)', | ||
'color': 'target' | ||
}, | ||
'ggtitle': { | ||
'text': 'With additional grouping' | ||
}, | ||
'layers': [ | ||
{ | ||
'geom': 'step', | ||
'stat': 'ecdf', | ||
'pad': true | ||
} | ||
] | ||
} | ||
""".trimIndent() | ||
|
||
val plotSpec = HashMap(parsePlotSpec(spec)) | ||
plotSpec["data"] = Iris.df | ||
return plotSpec | ||
|
||
} | ||
|
||
private fun withOrientationChange(): MutableMap<String, Any> { | ||
val spec = """ | ||
{ | ||
'kind': 'plot', | ||
'mapping': { | ||
'y': 'sepal length (cm)' | ||
}, | ||
'ggtitle': { | ||
'text': 'Orientation changed' | ||
}, | ||
'layers': [ | ||
{ | ||
'geom': 'step', | ||
'stat': 'ecdf', | ||
'pad': true, | ||
'orientation': 'y' | ||
} | ||
] | ||
} | ||
""".trimIndent() | ||
|
||
val plotSpec = HashMap(parsePlotSpec(spec)) | ||
plotSpec["data"] = Iris.df | ||
return plotSpec | ||
|
||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
demo/plot/src/jvmBatikMain/kotlin/demo/plot/batik/plotConfig/ECDFBatik.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* Copyright (c) 2023. JetBrains s.r.o. | ||
* Use of this source code is governed by the MIT license that can be found in the LICENSE file. | ||
*/ | ||
|
||
package demo.plot.batik.plotConfig | ||
|
||
import demo.plot.common.model.plotConfig.ECDF | ||
import demo.common.batik.demoUtils.PlotSpecsDemoWindowBatik | ||
|
||
fun main() { | ||
with(ECDF()) { | ||
PlotSpecsDemoWindowBatik( | ||
"ECDF plot", | ||
plotSpecList() | ||
).open() | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
plot-base/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/base/stat/ECDFStat.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* Copyright (c) 2023. JetBrains s.r.o. | ||
* Use of this source code is governed by the MIT license that can be found in the LICENSE file. | ||
*/ | ||
|
||
package org.jetbrains.letsPlot.core.plot.base.stat | ||
|
||
import org.jetbrains.letsPlot.core.plot.base.Aes | ||
import org.jetbrains.letsPlot.core.plot.base.DataFrame | ||
import org.jetbrains.letsPlot.core.plot.base.StatContext | ||
import org.jetbrains.letsPlot.core.plot.base.data.TransformVar | ||
|
||
class ECDFStat( | ||
private val n: Int?, | ||
private val padded: Boolean | ||
) : BaseStat(DEF_MAPPING) { | ||
|
||
override fun consumes(): List<Aes<*>> { | ||
return listOf(Aes.X) | ||
} | ||
|
||
override fun apply(data: DataFrame, statCtx: StatContext, messageConsumer: (s: String) -> Unit): DataFrame { | ||
if (!hasRequiredValues(data, Aes.X)) { | ||
return withEmptyStatValues() | ||
} | ||
|
||
val statData = buildStat(data.getNumeric(TransformVar.X)) | ||
|
||
return DataFrame.Builder() | ||
.putNumeric(Stats.X, statData.getValue(Stats.X)) | ||
.putNumeric(Stats.Y, statData.getValue(Stats.Y)) | ||
.build() | ||
} | ||
|
||
private fun buildStat( | ||
xs: List<Double?> | ||
): Map<DataFrame.Variable, List<Double>> { | ||
val xValues = xs.filter { it?.isFinite() ?: false }.map { it!! } | ||
if (xValues.isEmpty()) { | ||
return mapOf( | ||
Stats.X to emptyList(), | ||
Stats.Y to emptyList(), | ||
) | ||
} | ||
|
||
val ecdf: (Double) -> Double = { t -> xValues.count { x -> x <= t }.toDouble() / xValues.size } | ||
val statX = if (n == null) { | ||
xValues.distinct() | ||
} else { | ||
linspace(xValues.min(), xValues.max(), n) | ||
} | ||
val statY = statX.map { ecdf(it) } | ||
val padX = if (padded) { | ||
listOf(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY) | ||
} else { | ||
emptyList() | ||
} | ||
val padY = if (padded) { | ||
listOf(0.0, 1.0) | ||
} else { | ||
emptyList() | ||
} | ||
|
||
return mapOf( | ||
Stats.X to statX + padX, | ||
Stats.Y to statY + padY, | ||
) | ||
} | ||
|
||
private fun linspace(start: Double, stop: Double, num: Int): List<Double> { | ||
if (num <= 0) return emptyList() | ||
if (num == 1) return listOf(start) | ||
val step = (stop - start) / (num - 1) | ||
return List(num) { start + it * step } | ||
} | ||
|
||
companion object { | ||
const val DEF_PADDED = true | ||
|
||
private val DEF_MAPPING: Map<Aes<*>, DataFrame.Variable> = mapOf( | ||
Aes.X to Stats.X, | ||
Aes.Y to Stats.Y | ||
) | ||
} | ||
} |
Oops, something went wrong.