diff --git a/demo/jvm-batik/src/main/kotlin/frontendContextDemo/scripts/Waterfall.kt b/demo/jvm-batik/src/main/kotlin/frontendContextDemo/scripts/Waterfall.kt new file mode 100644 index 000000000..9f5894a38 --- /dev/null +++ b/demo/jvm-batik/src/main/kotlin/frontendContextDemo/scripts/Waterfall.kt @@ -0,0 +1,74 @@ +package frontendContextDemo.scripts + +import frontendContextDemo.ScriptInBatikContext +import org.jetbrains.letsPlot.bistro.waterfall.waterfallPlot +import org.jetbrains.letsPlot.label.ggtitle +import org.jetbrains.letsPlot.themes.elementLine +import org.jetbrains.letsPlot.themes.elementText +import org.jetbrains.letsPlot.tooltips.layerTooltips + +object Waterfall { + @JvmStatic + fun main(args: Array) { + ScriptInBatikContext.eval("Waterfall plot") { + run { + val data = mapOf( + "cat" to listOf("A", "B", "C", "D"), + "val" to listOf(1, 3, -2, 1) + ) + + val p = waterfallPlot(data, "cat", "val") + ggtitle("Basic demo") + p.show() + } + + run { + val data = mapOf( + "cat" to listOf("A", "B", "C", "D", "E"), + "val" to listOf(100, 200, -400, 500, -200) + ) + + val p = waterfallPlot( + data, "cat", "val", + color = "flow_type", + fill = "lightgrey", + size = 3.0, + alpha = 0.75, + linetype = "dotted", + width = 0.4, + showLegend = true, + relativeTooltips = layerTooltips() + .title("Category: @..xlabel.. (#@..x..)") + .minWidth(200) + .anchor("top_center") + .line("ymax|@..ymax..") + .line("ymin|^ymin") + .format("@..x..", "d") + .format("@..ymax..", ".3f") + .format("ymin", "d") + .disableSplitting(), + calcTotal = false, + totalTitle = "result", + sortedValue = true, + maxValues = 3, + hline = elementLine(color = "magenta", size = 5), + hlineOntop = false, + connector = elementLine(color = "cyan", size = 1.5), + label = elementText(color = "flow_type", family = "Times", face = "bold", size = 5, angle = 45), + labelFormat = "d" + ) + ggtitle("With parameters") + p.show() + } + + run { + val data = mapOf( + "cat" to listOf("A", "B", "C", "D", "T1", "A", "B", "C", null, "E", "T2"), + "val" to listOf(1.2, 2.2, -0.4, 1.5, null, -2.0, 1.3, -0.8, 1.0, 1.0, 0.0), + "m" to listOf("absolute", "relative", "relative", "relative", "total", "relative", "relative", "relative", "relative", null, "total") + ) + + val p = waterfallPlot(data, "cat", "val", measure = "m", showLegend = true) + ggtitle("With measure") + p.show() + } + } + } +} \ No newline at end of file diff --git a/docs/examples/jupyter-notebooks/dev/waterfall.ipynb b/docs/examples/jupyter-notebooks/dev/waterfall.ipynb new file mode 100644 index 000000000..bdeadbff5 --- /dev/null +++ b/docs/examples/jupyter-notebooks/dev/waterfall.ipynb @@ -0,0 +1,11377 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "df665d24-f3ff-4f9b-951c-0831027434a4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "Lets-Plot Kotlin API v.0.0.0-SNAPSHOT. Frontend: Notebook with dynamically loaded JS. Lets-Plot JS v.4.4.0." + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%useLatestDescriptors\n", + "\n", + "LetsPlot.getInfo()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6b451621-29e1-446d-ab57-11557cc7001f", + "metadata": {}, + "outputs": [], + "source": [ + "val data1 = mapOf(\n", + " \"x\" to listOf(\"A\", \"B\", \"C\", \"D\", \"E\"),\n", + " \"y\" to listOf(300, -100, -400, 300, 200),\n", + ")\n", + "\n", + "val data2 = mapOf(\n", + " \"x\" to listOf(\"A\", \"B\", \"C\", \"D\", \"T1\", \"A\", \"B\", \"C\", \"D\", \"T2\"),\n", + " \"y\" to listOf(100, 100, -300, 500, null, -200, 300, 100, -300, 0.0),\n", + " \"m\" to listOf(\"relative\", \"relative\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"relative\", \"relative\", \"total\"),\n", + ")\n", + "\n", + "val data3 = mapOf(\n", + " \"x\" to listOf(\"A\", \"B\", \"C\", \"D\", \"E\", \"A\", \"B\", \"C\", \"D\", \"T\"),\n", + " \"y\" to listOf(100, 100, -300, 500, 300, -200, 300, 100, -300, 0.0),\n", + " \"m\" to listOf(\"absolute\", \"relative\", \"relative\", \"relative\", \"absolute\", \"relative\", \"relative\", \"relative\", \"relative\", \"total\"),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c07f677e-77e0-4dc1-a75c-23d9019108ae", + "metadata": {}, + "source": [ + "## Default" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7644c9c6-6d53-48ae-b1dc-4bf8f69948ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\") + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\") + ggtitle(\"With measure\", \"Without absolute\"),\n", + " waterfallPlot(data3, \"x\", \"y\", measure = \"m\") + ggtitle(\"With measure\", \"With absolute\"),\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "b9d7af9a-90f5-4df6-b013-d9699bdb1bb8", + "metadata": {}, + "source": [ + "## Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "70c8d128-ac15-48ea-97c0-4393bee16ef4", + "metadata": {}, + "source": [ + "### Aesthetics" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "edf96788-7da9-45c7-adc1-dce41a7b8ef2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// color\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", size = 1.0, color = \"magenta\") + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data1, \"x\", \"y\", size = 1.0, color = \"flow_type\", fill = \"lightgrey\") + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", size = 1.0, color = \"magenta\") + ggtitle(\"With measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", size = 1.0, color = \"flow_type\", fill=\"lightgrey\") + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "82e01445-ff41-4ff0-9a1f-502dc66fb3ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// fill\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", size = 1.0, fill = \"magenta\") + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", size = 1.0, fill = \"magenta\") + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "6e9ea88a-ac8a-4734-b691-79eda73a8088", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// size\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", size = 2.0) + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", size = 2.0) + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "78833028-8bb0-458a-8766-209652949fe3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// alpha\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", alpha = 0.5) + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", alpha = 0.5) + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "82652fb9-fb77-4594-9c2b-4d8482408e38", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// linetype\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", size = 1.0, linetype = \"dashed\") + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", size = 1.0, linetype = \"dashed\") + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7f890eb2-9ad0-4f13-835f-7724f55166ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// width\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", width = 0.4) + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.4) + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "markdown", + "id": "5b1b6836-d21d-42b1-ba1e-0a453db9ecdb", + "metadata": {}, + "source": [ + "### Standard parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "423e6840-fbfd-4a06-9ed1-2d5334860cde", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// showLegend\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", showLegend = true, calcTotal = true) + ggtitle(\"Without measure\", \"Default calcTotal\"),\n", + " waterfallPlot(data1, \"x\", \"y\", showLegend = true, calcTotal = false) + ggtitle(\"Without measure\", \"calcTotal = false\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", showLegend = true) + ggtitle(\"With measure\", \"Without absolute\"),\n", + " waterfallPlot(data3, \"x\", \"y\", measure = \"m\", showLegend = true) + ggtitle(\"With measure\", \"With absolute\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b83ed533-4bef-4942-8fd1-07c1d68bbaa6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// tooltips\n", + "val relativeTooltips = layerTooltips().title(\"@..xlabel.. (#@..x..)\").line(\"@{..flow_type..}d from @..initial.. to @..value..\").disableSplitting()\n", + "val absoluteTooltips = layerTooltips().title(\"@..xlabel.. (#@..x..)\").line(\"Absolute value|@..value..\")\n", + "\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\") + ggtitle(\"Without measure\", \"Default tooltips\"),\n", + " waterfallPlot(data1, \"x\", \"y\", relativeTooltips = \"none\", absoluteTooltips = \"none\") + ggtitle(\"Without measure\", \"tooltips = 'none'\"),\n", + " waterfallPlot(data1, \"x\", \"y\", relativeTooltips = \"detailed\", absoluteTooltips = \"detailed\") + ggtitle(\"Without measure\", \"tooltips = 'detailed'\"),\n", + " waterfallPlot(data1, \"x\", \"y\", relativeTooltips = relativeTooltips, absoluteTooltips = absoluteTooltips) + ggtitle(\"Without measure\", \"Custom tooltips\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\") + ggtitle(\"With measure\", \"Default tooltips\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", relativeTooltips = \"none\", absoluteTooltips = \"none\") + ggtitle(\"With measure\", \"tooltips = 'none'\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", relativeTooltips = \"detailed\", absoluteTooltips = \"detailed\") + ggtitle(\"With measure\", \"tooltips = 'detailed'\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", relativeTooltips = relativeTooltips, absoluteTooltips = absoluteTooltips) + ggtitle(\"With measure\", \"Custom tooltips\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "markdown", + "id": "17e7b87f-3624-40ff-9364-67a375514a52", + "metadata": {}, + "source": [ + "### Waterfall-specific parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "482493d1-d1d8-4478-900d-eae6b4d7efac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// sortedValue\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", sortedValue = true) + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", sortedValue = true) + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "7ca5b72f-4e97-4e59-b328-9fa3f4d34fd1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// threshold\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", threshold = 200.0) + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", threshold = 200.0) + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7e8e058a-7665-4b7d-aa75-396519d0124e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// maxValues\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", maxValues = 2) + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", maxValues = 2) + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "ab513acb-a1ac-4dd8-836c-63bfddfa9dce", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// Use threshold to skip zeros\n", + "val dataWithZeros = mapOf(\n", + " \"x\" to listOf(\"a\", \"b\", \"c\", \"d\", \"t\", \"a\", \"b\", \"t\"),\n", + " \"y\" to listOf(1, -2, 3, 0, null, 0, 2, null),\n", + " \"m\" to listOf(\"relative\", \"relative\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"total\"),\n", + ")\n", + "\n", + "gggrid(listOf(\n", + " waterfallPlot(dataWithZeros, \"x\", \"y\", measure = \"m\"),\n", + " waterfallPlot(dataWithZeros, \"x\", \"y\", measure = \"m\", threshold = 0.0),\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "27315df6-acba-4c7e-81a8-382d172c0c5b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// calcTotal\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\") + ggtitle(\"Without measure\", \"Default calcTotal\"),\n", + " waterfallPlot(data1, \"x\", \"y\", calcTotal = false) + ggtitle(\"Without measure\", \"calcTotal = false\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\") + ggtitle(\"With measure\", \"Default calcTotal\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", calcTotal = false) + ggtitle(\"With measure\", \"calcTotal = false\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8dde4088-12f9-46ee-a7ee-24d403f4fbec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// totalTitle\n", + "gggrid(listOf(\n", + " waterfallPlot(data1, \"x\", \"y\", totalTitle = \"Result\", showLegend = true, absoluteTooltips = \"detailed\") + ggtitle(\"Without measure\"),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", totalTitle = \"Result\", showLegend = true, absoluteTooltips = \"detailed\") + ggtitle(\"With measure\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "markdown", + "id": "931fa8e1-a41c-441b-80f3-1f989c93cb49", + "metadata": {}, + "source": [ + "### Control additional geometries" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "23011d78-8ae8-4fdd-b464-63bc180ac33e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// hline\n", + "gggrid(listOf(\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementLine()),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementLine(blank = true)),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementBlank()),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = \"blank\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "45dc28f2-6201-4b78-a762-1c21f5f375a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// hlineOntop\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementLine(), hlineOntop = false)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "89e4ade6-097d-4a7e-9808-0813bf0d13b8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// hline color\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementLine(color = \"magenta\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "d2315516-af71-4c85-b130-52152e552932", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// hline size\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementLine(size = 2.0))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e576db23-4b5f-4769-b89a-d64c0fa9e0a2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// hline lineType\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", hline = elementLine(linetype = \"solid\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "9b2a2807-1dd7-48eb-a86f-71b1060d5740", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// connector\n", + "gggrid(listOf(\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = elementLine()),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = elementLine(blank = true)),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = elementBlank()),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = \"blank\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "a1de59d7-2937-4797-b13e-39e92e655fb6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// connector color\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = elementLine(color = \"magenta\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "07bd40bb-5d39-4631-92b6-30e8a68cae57", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// connector size\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = elementLine(size = 2.0))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "9bc7f4b5-aa16-48ab-b78e-ba14aad2878e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// connector linetype\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", width = 0.5, connector = elementLine(linetype = \"dotted\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "a22632ff-5c60-4f73-824c-b11bce7d8d74", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label\n", + "gggrid(listOf(\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText()),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(blank = true)),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementBlank()),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = \"blank\"),\n", + "), ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "36cdc00c-0c1b-4e12-9209-d47780e8d67d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label color\n", + "gggrid(listOf(\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(color = \"yellow\")),\n", + " waterfallPlot(data2, \"x\", \"y\", measure = \"m\", fill = \"lightgray\", label = elementText(color = \"flow_type\")),\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "aa2003e8-c4ab-4c62-b21f-085c95f27223", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label family\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(family = \"Courier\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "19451de0-2047-468d-87f6-49b79e403186", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label face\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(face = \"bold_italic\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "804a2d19-ae3c-4240-9f15-7e4a47c216ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label size\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(size = 10))" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "6216a61b-07c9-4aa0-b9b3-576ec0891585", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label angle\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(angle = 45))" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "24bf8d68-5c24-4652-936b-1fc08af776d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// label hjust/vjust\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", label = elementText(hjust = 0, vjust = 0))" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "941519f9-f44f-4d27-a67d-7fb51b7ccc0e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// labelFormat\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\", labelFormat = \"({.1f})\")" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "b9a522c9-a3f0-4dd9-94bd-ce0e97893115", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// group\n", + "val dataWithGroups = mapOf(\n", + " \"x\" to listOf(\"A\", \"C\", \"T1\", \"A\", \"B\", \"C\", \"T2\"),\n", + " \"y\" to listOf(2, -1, null, 1, 3, -2, 0.0),\n", + " \"m\" to listOf(\"absolute\", \"relative\", \"total\", \"absolute\", \"relative\", \"relative\", \"total\"),\n", + " \"g\" to listOf(\"a\", \"a\", \"a\", \"b\", \"b\", \"b\", \"b\"),\n", + ")\n", + "\n", + "waterfallPlot(dataWithGroups, \"x\", \"y\", measure = \"m\", group = \"g\") + facetWrap(facets = \"g\", scales = \"free_x\")" + ] + }, + { + "cell_type": "markdown", + "id": "f6445e7d-ff37-421e-807e-3ea6f040e927", + "metadata": {}, + "source": [ + "## Other Customizations" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "dc4d3c7c-d580-48fc-b3f1-cbb3cc233ac1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// fill and color\n", + "gggrid(listOf(\n", + " waterfallPlot(data3, \"x\", \"y\", measure = \"m\", showLegend = true, width = 0.7, size = 1.0, color = \"#777777\", label = elementText(color = \"#777777\")) +\n", + " scaleFillManual(mapOf(\"Increase\" to \"white\", \"Decrease\" to \"black\", \"Absolute\" to \"green\", \"Total\" to \"yellow\")) +\n", + " ggtitle(\"Custom scaleFillManual()\"),\n", + " waterfallPlot(data3, \"x\", \"y\", measure = \"m\", showLegend = true, width = 0.7, fill = \"black\", label = elementText(color = \"flow_type\")) +\n", + " scaleColorManual(mapOf(\"Increase\" to \"green\", \"Decrease\" to \"yellow\", \"Absolute\" to \"red\", \"Total\" to \"#bbbbff\")) +\n", + " ggtitle(\"Custom scaleColorManual()\"),\n", + " waterfallPlot(data3, \"x\", \"y\", measure = \"m\", showLegend = true, width = 0.7, color = \"#777777\", label = elementText(color = \"#777777\")) +\n", + " scaleFillManual(mapOf(\"Increase\" to \"green\", \"Decrease\" to \"red\", \"Absolute\" to \"cyan\", \"Total\" to \"yellow\"),\n", + " labels = listOf(\"Up\", \"Down\", \"From zero\", \"Result\")) +\n", + " ggtitle(\"Custom flow type names\"),\n", + "), ncol = 1) + ggsize(1000, 800)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "77a4c4f3-ec15-4afd-84a5-1827701cc87f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// flip coordinates\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\") + coordFlip()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "27b85478-d5bb-4a71-8923-41f847e33a79", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// custom theme\n", + "waterfallPlot(data2, \"x\", \"y\", measure = \"m\") + themeBW() + flavorDarcula()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Kotlin", + "language": "kotlin", + "name": "kotlin" + }, + "language_info": { + "codemirror_mode": "text/x-kotlin", + "file_extension": ".kt", + "mimetype": "text/x-kotlin", + "name": "kotlin", + "nbconvert_exporter": "", + "pygments_lexer": "kotlin", + "version": "1.9.23" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/examples/jupyter-notebooks/f-4.7.4/waterfall_plot.ipynb b/docs/examples/jupyter-notebooks/f-4.7.4/waterfall_plot.ipynb new file mode 100644 index 000000000..79b5b8a50 --- /dev/null +++ b/docs/examples/jupyter-notebooks/f-4.7.4/waterfall_plot.ipynb @@ -0,0 +1,1900 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "63396492-2899-4862-8cfb-84e812a844cc", + "metadata": {}, + "source": [ + "# Waterfall Plot\n", + "\n", + "A waterfall plot shows the cumulative effect of sequentially introduced positive or negative values.\n", + "\n", + "To use it, you need to import the 'bistro' module." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b1b75d26-0fde-4591-af27-677b5d807b5f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%useLatestDescriptors" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7137dd20-92ce-4b54-87d1-a1088285f1d0", + "metadata": {}, + "outputs": [], + "source": [ + "val dataMap = mapOf(\n", + " \"Accounts\" to listOf(\"Product revenue\", \"Services revenue\", \"Fixed costs\", \"Variable costs\"),\n", + " \"Values\" to listOf(830_000, 290_000, -360_000, -150_000),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1f861d4a-b5df-429e-b4e0-a2a925c4cb28", + "metadata": {}, + "source": [ + "## Default View" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "201f8210-dfc1-4df2-b1eb-3e6c5cc1a92b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "waterfallPlot(dataMap, \"Accounts\", \"Values\")" + ] + }, + { + "cell_type": "markdown", + "id": "e3de1d7d-641f-4403-befe-2846b74e4368", + "metadata": {}, + "source": [ + "## Improved View" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cfaa985d-f86a-407c-a1be-b545201c2492", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "waterfallPlot(dataMap, \"Accounts\", \"Values\",\n", + " size = 0.75, alpha = 0.15, width = 0.8, totalTitle = \"Profit\",\n", + " hline = elementLine(linetype = \"solid\", size = 1.0),\n", + " connector = elementLine(linetype = \"dotted\"),\n", + " label = elementText(size = 10.0, family = \"Courier\", face = \"bold\", color = \"flow_type\"),\n", + " labelFormat = \"$,.1~s\") +\n", + " scaleYContinuous(name = \"Values\", format = \"$,.1~s\") +\n", + " ggtitle(\"Company Profit (in USD)\") +\n", + " ggsize(1000, 500) +\n", + " themeMinimal() +\n", + " theme(plotTitle = elementText(size = 20.0, face = \"bold\", hjust = 0.5))" + ] + }, + { + "cell_type": "markdown", + "id": "5ea2b8fb-b0f1-4183-966a-ddc10d0253c6", + "metadata": {}, + "source": [ + "## Additional Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "9dd5211f-3797-4c3c-a51d-515c48d3eff3", + "metadata": {}, + "source": [ + "### `measure` and `group`" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b20767fb-fa6e-4dfe-b186-49706ff1a757", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "val groupSize = 7\n", + "val dataWithGroupsMap = mapOf(\n", + " \"Company\" to List(groupSize) { \"Badgersoft\" } + List(groupSize) { \"AIlien Co.\" },\n", + " \"Accounts\" to listOf(\"initial\", \"revenue\", \"costs\", \"Q1\", \"revenue\", \"costs\", \"Q2\").let { it + it },\n", + " \"Values\" to listOf(200, 200, -100, null, 250, -100, null,\n", + " 150, 50, -100, null, 100, -100, null),\n", + " \"Measure\" to listOf(\"absolute\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"total\").let { it + it },\n", + ")\n", + "\n", + "waterfallPlot(dataWithGroupsMap, \"Accounts\", \"Values\", measure = \"Measure\", group = \"Company\") +\n", + " facetGrid(x = \"Company\", scales = \"free_x\")" + ] + }, + { + "cell_type": "markdown", + "id": "ea6ca346-cec9-4cd6-ae8d-698b9c7375e7", + "metadata": {}, + "source": [ + "### `calcTotal`\n", + "\n", + "`calcTotal = false` disables the calculation of the total.\n", + "\n", + "If the `measure` serie is specified however, the `calcTotal` setting has no effect." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5559c152-b59f-45b3-a12d-2a3266d8d6d3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gggrid(listOf(\n", + " waterfallPlot(dataMap, \"Accounts\", \"Values\", calcTotal = false),\n", + " waterfallPlot(dataWithGroupsMap.entries.associate { (k, v) -> k to v.subList(0, groupSize) },\n", + " \"Accounts\", \"Values\", measure = \"Measure\", calcTotal = false),\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "ad245ee8-c6f7-4e28-8cee-25c13691da1a", + "metadata": {}, + "source": [ + "### Tooltips\n", + "\n", + "Tooltips for relative and absolute measures should be specified independently." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "72425275-b5da-41e0-84d3-bd2d1cf36e6a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "val relativeTooltips = layerTooltips()\n", + " .title(\"Account: @..xlabel..\")\n", + " .format(\"@..initial..\", \" $,.3~s\")\n", + " .format(\"@..value..\", \" $,.3~s\")\n", + " .line(\"@{..flow_type..}d from @..initial.. to @..value..\")\n", + " .disableSplitting()\n", + "\n", + "gggrid(listOf(\n", + " waterfallPlot(dataMap, \"Accounts\", \"Values\", relativeTooltips = \"detailed\", absoluteTooltips = \"detailed\") + ggtitle(\"'detailed' tooltips\"),\n", + " waterfallPlot(dataMap, \"Accounts\", \"Values\", relativeTooltips = relativeTooltips, absoluteTooltips = \"none\") + ggtitle(\"Custom tooltips\"),\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "747094ce-a7a4-4ce9-b3f1-0794f3436cf9", + "metadata": {}, + "source": [ + "### `sortedValue`" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "2484b0c4-0389-4bd2-b8d2-f78af0bc15bb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "waterfallPlot(dataMap, \"Accounts\", \"Values\", sortedValue = true)" + ] + }, + { + "cell_type": "markdown", + "id": "047022ef-8363-4363-ae61-e4441b518c12", + "metadata": {}, + "source": [ + "### `threshold`/`maxValues`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "86a9457e-98de-4800-8f5a-ad1860722685", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gggrid(listOf(\n", + " waterfallPlot(dataMap, \"Accounts\", \"Values\") + ggtitle(\"Default\"),\n", + " waterfallPlot(dataMap, \"Accounts\", \"Values\", threshold = 300_000.0) + ggtitle(\"Specified threshold\"),\n", + " waterfallPlot(dataMap, \"Accounts\", \"Values\", maxValues = 2) + ggtitle(\"Specified maxValues\"),\n", + "))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Kotlin", + "language": "kotlin", + "name": "kotlin" + }, + "language_info": { + "codemirror_mode": "text/x-kotlin", + "file_extension": ".kt", + "mimetype": "text/x-kotlin", + "name": "kotlin", + "nbconvert_exporter": "", + "pygments_lexer": "kotlin", + "version": "1.9.23" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/future_changes.md b/future_changes.md index 71ba963d3..f45ef8289 100644 --- a/future_changes.md +++ b/future_changes.md @@ -2,6 +2,10 @@ ### Added +- Waterfall plot [[#975](https://github.com/JetBrains/lets-plot/issues/975)]: + + See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot-kotlin/blob/master/docs/examples/jupyter-notebooks/f-4.7.4/waterfall_plot.ipynb). + - Legend title in `guideLegend()` and `guideColorbar()`. See: [example notebook](https://nbviewer.org/github/JetBrains/lets-plot-kotlin/blob/master/docs/examples/jupyter-notebooks/f-4.7.4/legend_title.ipynb). diff --git a/plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/bistro/waterfall/WaterfallPlot.kt b/plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/bistro/waterfall/WaterfallPlot.kt new file mode 100644 index 000000000..93af3816e --- /dev/null +++ b/plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/bistro/waterfall/WaterfallPlot.kt @@ -0,0 +1,129 @@ +package org.jetbrains.letsPlot.bistro.waterfall + +/** + * A waterfall plot shows the cumulative effect of sequentially introduced positive or negative values. + * + * ## Examples + * + * - [waterfall_plot.ipynb](https://nbviewer.org/github/JetBrains/lets-plot-docs/blob/master/source/kotlin_examples/cookbook/waterfall_plot.ipynb) + * + * @param data The data to be displayed in this layer. + * @param x Name of a variable. + * @param y Name of a numeric variable. + * @param measure Kind of a calculation. + * Values in 'measure' column could be: + * - "absolute" - the value is shown as is; + * - "relative" - the value is shown as a difference from the previous value; + * - "total" - the value is shown as a cumulative sum of all previous values. + * + * @param group Grouping variable. + * Each group calculates its own statistics. + * @param color Color of the box boundary lines. + * Use "flow_type" to color lines by the direction of the flow. + * @param fill Fill color of the boxes. + * Use "flow_type" to color boxes by the direction of the flow. + * @param size default = 0.0. + * Line width of the box boundary lines. + * @param alpha Transparency level of the boxes. Understands numbers between 0 and 1. + * @param linetype Int or String. + * Type of the box boundary lines. + * Codes and names: 0 = "blank", 1 = "solid", 2 = "dashed", 3 = "dotted", 4 = "dotdash", + * 5 = "longdash", 6 = "twodash". + * @param width default = 0.9. + * Width of the boxes. Typically range between 0 and 1. + * Values that are greater than 1 lead to overlapping of the boxes. + * @param showLegend default = false. + * true - show the legend. + * @param relativeTooltips Result of the call to the `layerTooltips()` function. + * Tooltips for boxes with relative values. + * Specifies appearance, style and content. + * When "none", tooltips are not shown. + * When "detailed", a more detailed (compared to the default) version of the tooltips is shown. + * @param absoluteTooltips Result of the call to the `layerTooltips()` function. + * Tooltips for boxes with absolute values. + * Specifies appearance, style and content. + * When "none", tooltips are not shown. + * When "detailed", a more detailed (compared to the default) version of the tooltips is shown. + * @param sortedValue default = false. + * Sorts categories by absolute value of the changes. + * @param threshold Groups all categories under a certain threshold value into "Other" category. + * @param maxValues Groups all categories with the smallest changes, except the first `maxValues`, into "Other" category. + * @param calcTotal default = true. + * Setting the `calcTotal` to true will put the final cumulative sum into a new separate box. + * Taken into account only if the "measure" column isn't provided. + * @param totalTitle The header of the last box with the final cumulative sum, if "measure" column isn't provided. + * Also used as a title in the legend for columns of type "total". + * @param hline Horizontal line passing through 0. + * Set "blank" or result of `elementBlank()` to draw nothing. + * Set `elementLine()` to specify parameters. + * @param hlineOntop default = true. + * Option to place horizontal line over the other layers. + * @param connector Line between neighbouring boxes connecting the end of the previous box and the beginning of the next box. + * Set "blank" or result of `elementBlank()` to draw nothing. + * Set `elementLine()` to specify parameters. + * @param label Label on the box. + * Shows change value. + * Set "blank" or result of `elementBlank()` to draw nothing. + * Set `elementText()` to specify parameters. + * Use "flow_type" for `color` parameter of the `elementText()` to color labels by the direction of the flow. + * @param labelFormat Format used to transform label mapping values to a string. + * For more info see: [formats.html](https://lets-plot.org/kotlin/formats.html) + * Note: the "$" must be escaped as "\$". + * + * Examples: + * - ".2f" -> "12.45" + * - "Score: {.2f}" -> "Score: 12.45" + * - "Score: {}" -> "Score: 12.454789" + * + */ +fun waterfallPlot( + data: Map<*, *>, + x: String, + y: String, + measure: String? = null, + group: String? = null, + color: String? = null, + fill: String? = null, + size: Number? = null, + alpha: Number? = null, + linetype: Any? = null, + width: Number? = null, + showLegend: Boolean? = null, + relativeTooltips: Any? = null, + absoluteTooltips: Any? = null, + calcTotal: Boolean? = null, + totalTitle: String? = null, + sortedValue: Boolean? = null, + threshold: Number? = null, + maxValues: Int? = null, + hline: Any? = null, + hlineOntop: Boolean? = null, + connector: Any? = null, + label: Any? = null, + labelFormat: String? = null +) = WaterfallPlotBuilder( + data = data, + x = x, + y = y, + measure = measure, + group = group, + color = color, + fill = fill, + size = size, + alpha = alpha, + linetype = linetype, + width = width, + showLegend = showLegend, + relativeTooltips = relativeTooltips, + absoluteTooltips = absoluteTooltips, + calcTotal = calcTotal, + totalTitle = totalTitle, + sortedValue = sortedValue, + threshold = threshold, + maxValues = maxValues, + hline = hline, + hlineOntop = hlineOntop, + connector = connector, + label = label, + labelFormat = labelFormat +).build() \ No newline at end of file diff --git a/plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/bistro/waterfall/WaterfallPlotBuilder.kt b/plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/bistro/waterfall/WaterfallPlotBuilder.kt new file mode 100644 index 000000000..791de24c2 --- /dev/null +++ b/plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/bistro/waterfall/WaterfallPlotBuilder.kt @@ -0,0 +1,72 @@ +package org.jetbrains.letsPlot.bistro.waterfall + +import org.jetbrains.letsPlot.core.spec.Option.Plot.BISTRO +import org.jetbrains.letsPlot.core.spec.back.transform.bistro.waterfall.Option.Waterfall +import org.jetbrains.letsPlot.intern.* +import org.jetbrains.letsPlot.letsPlot +import org.jetbrains.letsPlot.tooltips.TooltipOptions + +internal class WaterfallPlotBuilder( + private val data: Map<*, *>, + private val x: String, + private val y: String, + private val measure: String?, + private val group: String?, + private val color: String?, + private val fill: String?, + private val size: Number?, + private val alpha: Number?, + private val linetype: Any?, + private val width: Number?, + private val showLegend: Boolean?, + private val relativeTooltips: Any?, + private val absoluteTooltips: Any?, + private val calcTotal: Boolean?, + private val totalTitle: String?, + private val sortedValue: Boolean?, + private val threshold: Number?, + private val maxValues: Int?, + private val hline: Any?, + private val hlineOntop: Boolean?, + private val connector: Any?, + private val label: Any?, + private val labelFormat: String? +) { + fun build(): Plot { + return letsPlot(data) + OptionsMap( + kind = BISTRO, + name = Waterfall.NAME, + options = mapOf( + Waterfall.X to x, + Waterfall.Y to y, + Waterfall.MEASURE to measure, + Waterfall.GROUP to group, + Waterfall.COLOR to color, + Waterfall.FILL to fill, + Waterfall.SIZE to size, + Waterfall.ALPHA to alpha, + Waterfall.LINE_TYPE to linetype, + Waterfall.WIDTH to width, + Waterfall.SHOW_LEGEND to showLegend, + Waterfall.RELATIVE_TOOLTIPS to when (relativeTooltips) { + is TooltipOptions -> relativeTooltips.options + else -> relativeTooltips + }, + Waterfall.ABSOLUTE_TOOLTIPS to when (absoluteTooltips) { + is TooltipOptions -> absoluteTooltips.options + else -> absoluteTooltips + }, + Waterfall.CALCULATE_TOTAL to calcTotal, + Waterfall.TOTAL_TITLE to totalTitle, + Waterfall.SORTED_VALUE to sortedValue, + Waterfall.THRESHOLD to threshold, + Waterfall.MAX_VALUES to maxValues, + Waterfall.H_LINE to hline, + Waterfall.H_LINE_ON_TOP to hlineOntop, + Waterfall.CONNECTOR to connector, + Waterfall.LABEL to label, + Waterfall.LABEL_FORMAT to labelFormat + ).filterNonNullValues() + ) + } +} \ No newline at end of file