Skip to content

Commit

Permalink
continued kdf migration (0.9 SNAPSHOT)
Browse files Browse the repository at this point in the history
  • Loading branch information
holgerbrandl committed Feb 2, 2023
1 parent b696c1e commit fcc740d
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 58 deletions.
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ dependencies {
// implementation "org.jetbrains.kotlin:kotlin-stdlib"
compileOnly "org.jetbrains.kotlin:kotlin-reflect:1.8.0"

// tagged version for release
// note krangl is still needed for the example data
api 'com.github.holgerbrandl:krangl:0.18.4'
implementation 'com.github.holgerbrandl:kdfutils:1.1'
api 'com.github.holgerbrandl:kdfutils:1.1'

api( 'org.jetbrains.kotlinx:dataframe-core:0.9.1')
api 'org.jetbrains.kotlinx:dataframe-core:0.9.1'

// compile "com.github.holgerbrandl:krangl:0.17-SNAPSHOT"
// api "ml.dmlc:xgboost4j:0.80"
Expand All @@ -40,7 +40,7 @@ dependencies {
// see https://github.com/codecentric/javafxsvg
// compile 'de.codecentric.centerdevice:javafxsvg:1.3.0'

testImplementation group: 'junit', name: 'junit', version: '4.13'
testImplementation group: 'junit', name: 'junit', version: '4.13.1'
// testCompile "io.kotlintest:kotlintest-runner-junit5:3.1.9"
testImplementation 'io.kotest:kotest-assertions-core:5.0.3'

Expand Down
93 changes: 53 additions & 40 deletions src/main/kotlin/kravis/GGPlot2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,30 @@ import kravis.render.EngineAutodetect
import kravis.render.PlotFormat
import kravis.render.RenderEngine
import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.jupyter.api.session.JupyterSessionInfo
import java.awt.Dimension
import java.io.File
import java.nio.file.Path
import java.util.*
import kotlin.io.path.extension

/**
* Various Settings to finetune rendering and behavior of kravis. Those settins are not persisted and need to be configured on a per session basis
* Various Settings to finetune rendering and behavior of kravis. Those settings are not persisted and need to be configured on a per-session basis
*/
object SessionPrefs {

private val AUTO_DETECT_DEVICE by lazy {
try {
// if(JupyterSessionInfo.isRunWithKernel()) {
// Note: not part of the library dependencies but just provided at runtime
infoMsg("Detecting output device...")

JupyterSessionInfo.isRunWithKernel()
// blocked by https://github.com/Kotlin/kotlin-jupyter/issues/352
// Class.forName("org.jetbrains.kotlinx.jupyter.api.session.JupyterSessionProvider")
// infoMsg("Using jupyter plotting device")
infoMsg("Using jupyter plotting device")
JupyterDevice()
} catch (e: ClassNotFoundException) {
} catch(e: Throwable) {
// it's not jupyter so default back to swing
// infoMsg("Using swing plotting device")
infoMsg("Using swing plotting device")
SwingPlottingDevice()
}
}
Expand All @@ -46,12 +50,14 @@ object SessionPrefs {
try {
this.javaClass.classLoader.loadClass("FormPreviewFrame") != null
true
} catch (e: ClassNotFoundException) {
} catch(e: ClassNotFoundException) {
false
}
}

/** Render plots when toString is invoked. This makes it more convenient in a termimal setting. */
/**
* Render plots when toString is invoked. This makes it more convenient in a terminal setting.
*/
var SHOW_TO_STRING = !(OUTPUT_DEVICE is JupyterDevice) && !isDebugSession
}

Expand All @@ -73,7 +79,7 @@ fun DataFrame<*>.plot(
yend: String? = null,
weight: String? = null,
label: String? = null,
group: String? = null
group: String? = null,
): GGPlot {
val mapping = listOf<Pair<String, Aesthetic>>()
.addNonNull(x, Aesthetic.x)
Expand All @@ -92,7 +98,8 @@ fun DataFrame<*>.plot(
.addNonNull(label, Aesthetic.label)
.addNonNull(group, Aesthetic.group)

val aes = Aes(*mapping.toTypedArray()
val aes = Aes(
*mapping.toTypedArray()
)
return GGPlot(this, aes)
}
Expand All @@ -105,7 +112,7 @@ fun DataFrame<*>.plot(vararg aes: Pair<String, Aesthetic>) = GGPlot(this, Aes(*a
class GGPlot(
data: DataFrame<*>? = null,
mapping: Aes? = null,
environment: String? = null
environment: String? = null,
) {
internal val preambble = emptyList<String>().toMutableList()

Expand Down Expand Up @@ -151,7 +158,7 @@ class GGPlot(


fun registerDataset(data: DataFrame<*>?): VarName? {
if (data == null) {
if(data == null) {
return null
}

Expand Down Expand Up @@ -181,10 +188,10 @@ class GGPlot(
}

override fun toString(): String {
if (SessionPrefs.SHOW_TO_STRING) {
if(SessionPrefs.SHOW_TO_STRING) {
try {
show()
} catch (e: Exception) {
} catch(e: Exception) {
System.err.println("Plot pendering failed with " + e)
}
}
Expand Down Expand Up @@ -242,7 +249,7 @@ class GGPlot(
shape: String? = null,
size: String? = null,
stroke: String? = null,
) = appendSpec {
) = appendSpec {
val args = arg2string(
"title" to title,
"caption" to caption,
Expand Down Expand Up @@ -284,32 +291,33 @@ class Aes(vararg val aes: Pair<String, Aesthetic>) {
yend: String? = null,
weight: String? = null,
label: String? = null,
group: String? = null
group: String? = null,
) :
this(*listOf<Pair<String, Aesthetic>>()
.addNonNull(x, Aesthetic.x)
.addNonNull(y, Aesthetic.y)
.addNonNull(alpha, Aesthetic.alpha)
.addNonNull(color, Aesthetic.color)
.addNonNull(fill, Aesthetic.fill)
.addNonNull(shape, Aesthetic.shape)
.addNonNull(size, Aesthetic.size)
.addNonNull(stroke, Aesthetic.size)
.addNonNull(ymin, Aesthetic.ymin)
.addNonNull(ymax, Aesthetic.ymax)
.addNonNull(xend, Aesthetic.xend)
.addNonNull(yend, Aesthetic.yend)
.addNonNull(weight, Aesthetic.weight)
.addNonNull(label, Aesthetic.label)
.addNonNull(group, Aesthetic.group)
.toTypedArray()
)
this(
*listOf<Pair<String, Aesthetic>>()
.addNonNull(x, Aesthetic.x)
.addNonNull(y, Aesthetic.y)
.addNonNull(alpha, Aesthetic.alpha)
.addNonNull(color, Aesthetic.color)
.addNonNull(fill, Aesthetic.fill)
.addNonNull(shape, Aesthetic.shape)
.addNonNull(size, Aesthetic.size)
.addNonNull(stroke, Aesthetic.size)
.addNonNull(ymin, Aesthetic.ymin)
.addNonNull(ymax, Aesthetic.ymax)
.addNonNull(xend, Aesthetic.xend)
.addNonNull(yend, Aesthetic.yend)
.addNonNull(weight, Aesthetic.weight)
.addNonNull(label, Aesthetic.label)
.addNonNull(group, Aesthetic.group)
.toTypedArray()
)

fun stringify(): VarName? {
if (aes.isEmpty()) return null
if(aes.isEmpty()) return null

val map = aes.map { (expr, aesthetic) ->
val quoted = if (expr.isRExpression) {
val quoted = if(expr.isRExpression) {
expr.removePrefix(EXPRESSION_PREFIX)
} else {
"`$expr`"
Expand All @@ -322,15 +330,15 @@ class Aes(vararg val aes: Pair<String, Aesthetic>) {
}

private fun List<Pair<String, Aesthetic>>.addNonNull(x: String?, aes: Aesthetic): List<Pair<String, Aesthetic>> {
return if (x != null) this + Pair(x, aes) else this
return if(x != null) this + Pair(x, aes) else this
}

enum class Aesthetic {
x, y, color, fill, yintercept, xintercept, size, alpha, shape, stroke,

ymin,

// The group aesthetic is useful if there are only two or three groups and there is a summary statistic that should be calculated per group and displayed in one chart.
// The group aesthetic is useful if there are only two or three groups and there is a summary statistic that should be calculated per group and displayed in one chart.
group,

ymax,
Expand Down Expand Up @@ -364,8 +372,13 @@ object OrderUtils {
*
* The levels of `f` are reordered so that the values of `fun(orderAttribute)` (for fct_reorder()) are in ascending order.
*/
fun reorder(f: String, orderAttribute: String, orderFun: OrderFun = OrderFun.mean, ascending: Boolean = true): String {
return (if (ascending) {
fun reorder(
f: String,
orderAttribute: String,
orderFun: OrderFun = OrderFun.mean,
ascending: Boolean = true,
): String {
return (if(ascending) {
"fct_reorder($f, $orderAttribute, .fun=$orderFun)"
} else {
"fct_rev(fct_reorder($f, $orderAttribute, .fun=$orderFun))"
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/kravis/Geoms.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fun GGPlot.geomPoint(
addSpec("geom_point(${args})")
}

internal fun requireZeroOne(d: Double?) = d?.also { require(it >= 0 && it <= 1) { "alpha must be [0,1] but was $it." } }
internal fun requireZeroOne(d: Double?) = d?.also { require(it in 0.0..1.0) { "alpha must be [0,1] but was $it." } }


/**
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/kravis/IteratorApi.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package kravis

import com.github.holgerbrandl.kdfutils.toKotlinDF
import krangl.deparseRecords
import org.jetbrains.kotlinx.dataframe.datasets.sleepData
import org.jetbrains.kotlinx.dataframe.api.asKotlinDF
import skipNull
import kotlin.reflect.KProperty0
import kotlin.reflect.KProperty1
Expand Down Expand Up @@ -32,7 +32,7 @@ inline fun <reified T> Iterable<T>.plot(vararg aes2data: Pair<Aesthetic, PropExt
val df = this.deparseRecords(*rulez.toList().toTypedArray())


return GGPlot(data = df.asKotlinDF(), mapping = Aes(*aes.toTypedArray()))
return GGPlot(data = df.toKotlinDF(), mapping = Aes(*aes.toTypedArray()))
}

/**
Expand Down Expand Up @@ -131,7 +131,7 @@ inline fun <reified T> Iterable<T>.plot(
name to deparseFormula
}

val deparsedReceiver = deparseRecords(*deparseFormulae.toTypedArray()).asKotlinDF()
val deparsedReceiver = deparseRecords(*deparseFormulae.toTypedArray()).toKotlinDF()


// build new mapping
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/kravis/render/Docker.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package kravis.render

import com.github.holgerbrandl.kdfutils.writeTSV
import kravis.GGPlot
import kravis.SessionPrefs
import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.api.writeTSV
import java.awt.Dimension
import java.io.File
import java.nio.file.Path
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/kravis/render/LocalR.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kravis.render

import com.github.holgerbrandl.kdfutils.writeTSV
import kravis.GGPlot
import org.jetbrains.kotlinx.dataframe.api.writeTSV
import java.awt.Dimension
import java.io.File
import java.nio.file.Path
Expand Down
1 change: 0 additions & 1 deletion src/test/kotlin/kravis/ggplot/AbstractSvgPlotRegression.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import kravis.SessionPrefs
import kravis.render.Docker
import kravis.render.saveTempFile
import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.api.asKotlinDF
import org.jetbrains.kotlinx.dataframe.io.readTSV
import org.junit.Assert.assertTrue
import org.junit.Before
Expand Down
11 changes: 8 additions & 3 deletions src/test/kotlin/kravis/ggplot/GeomRegressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,14 @@ class GeomRegressions : AbstractSvgPlotRegression() {
@Test
fun `create factor ordered barchart with error bars`() {
val plot = irisData.groupBy("Species")
.summarizeAt({ listOf("Sepal.Length") }, mean, sd)
.addColumn("ymax") { it["Sepal.Length.mean"] + it["Sepal.Length.sd"] }
.addColumn("ymin") { it["Sepal.Length.mean"] - it["Sepal.Length.sd"] }
.aggregate {
mean() into "mean"
std() into "sd"
}

// .summarizeAt({ listOf("Sepal.Length") }, mean, sd)
.add("ymax") { "Sepal.Length.mean"<Double>() + "Sepal.Length.sd"<Double>() }
.add("ymin") { "Sepal.Length.mean"<Double>() - "Sepal.Length.sd"<Double>() }
.plot(
x = reorder("Species", "Sepal.Length.mean", ascending = false),
y = "Sepal.Length.mean",
Expand Down
8 changes: 4 additions & 4 deletions src/test/kotlin/kravis/samples/SleepPatternsExample.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ internal object SleepPatternsExample {

// KProperty2
// submitted as https://youtrack.jetbrains.com/issue/KT-56095/Good-code-is-red-property-references
sleepPatterns.ggplot3(
x = { ::conservation },
y = { ::bodywt }
)
// sleepPatterns.ggplot3(
// x = { ::conservation },
// y = { ::bodywt }
// )
}
}

Expand Down

0 comments on commit fcc740d

Please sign in to comment.