diff --git a/docs/examples/jupyter-notebooks-dev/as_discrete.ipynb b/docs/examples/jupyter-notebooks-dev/as_discrete.ipynb
new file mode 100644
index 00000000000..2cbeaa12857
--- /dev/null
+++ b/docs/examples/jupyter-notebooks-dev/as_discrete.ipynb
@@ -0,0 +1,886 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from lets_plot import *\n",
+ "import lets_plot.mapping as pm"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ "
Lets-Plot JS is embedded.
\n",
+ " "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "LetsPlot.setup_html()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df = {\n",
+ " 'x': [0, 5, 10, 15],\n",
+ " 'y': [0, 5, 10, 15],\n",
+ " 'a': [1, 2, 3, 4],\n",
+ " 'b': [4, 5, 6, 7]\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# factor, no scale\n",
+ "p = ggplot(df, aes(x='x', y='y')) + geom_point(aes(color=pm.as_discrete('a')), size=9)\n",
+ "p"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# factor, scale\n",
+ "p = ggplot(df, aes(x='x', y='y')) \\\n",
+ " + geom_point(aes(color='a', fill=pm.as_discrete('b')), shape=21, size=9, stroke=5) \\\n",
+ " + scale_color_discrete()\n",
+ "p"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# data in mappings, scale_color_discrete\n",
+ "p = ggplot() + geom_point(aes(x=[0, 5, 10], y=[0, 5, 10], color=[1, 2, 4]), size=9) + scale_color_discrete()\n",
+ "p"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Issue 1\n",
+ "\n",
+ "`factor` is not working when used in \"ggplot()\" mapping."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "p3 = ggplot(df, aes(x='x', y='y', color=pm.as_discrete('a'))) + geom_point(size=9)\n",
+ "p3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Issue 2\n",
+ "`factor` doesn't create groups the way discrete variable does."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df = {\n",
+ " 'x': [0, 5, 10, 15],\n",
+ " 'y': [0, 5, 10, 15],\n",
+ " 'a': [0, 0, 1, 1],\n",
+ " 'c': ['a', 'a', 'b', 'b']\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "p4 = ggplot(df, aes(x='x', y='y')) + geom_line(aes(color=pm.as_discrete('a')), size=3)\n",
+ "p4"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# expected: 2 lines ('c' is a discrete variable)\n",
+ "p5 = ggplot(df, aes(x='x', y='y')) + geom_line(aes(color='c'), size=3)\n",
+ "p5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# expected: 2 lines (`group` is defined manually)\n",
+ "p6 = ggplot(df, aes(x='x', y='y')) + geom_line(aes(color='a', group='a'), size=3)\n",
+ "p6"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Issue 2a\n",
+ "Also about groups but with `stat` this time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "mpg_df = pd.read_csv('https://jetbrains.bintray.com/lets-plot/mpg.csv')\n",
+ "mpg_plot = ggplot(mpg_df, aes(x='displ', y='hwy'))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "cyl_factor = pm.as_discrete('cyl')\n",
+ "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
+ "+ geom_smooth(aes(color=cyl_factor), method='lm', size=1, se=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# expected: seperate regression line for each group\n",
+ "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
+ "+ geom_smooth(aes(color='cyl', group='cyl'), method='lm', size=1, se=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Issue 3\n",
+ "\"Not an aesthetic 'group'\" error when used with `factor`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
+ "+ geom_smooth(aes(color=cyl_factor, group='cyl'), method='lm', size=1, se=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Issue 4\n",
+ "Nice to have parameter `ordered`. Owerwise have to use `scale_discrete(breaks=[...])` to order groups by number of cylinders:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
+ "+ scale_color_discrete(breaks=[4, 5, 6, 8])"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/examples/jupyter-notebooks-dev/factor.ipynb b/docs/examples/jupyter-notebooks-dev/factor.ipynb
deleted file mode 100644
index 04308a4fc77..00000000000
--- a/docs/examples/jupyter-notebooks-dev/factor.ipynb
+++ /dev/null
@@ -1,860 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from lets_plot import *\n",
- "import lets_plot.mapping as pm"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- " \n",
- " Lets-Plot JS is embedded.
\n",
- " "
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "load_lets_plot_js()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "df = {\n",
- " 'x': [0, 5, 10, 15],\n",
- " 'y': [0, 5, 10, 15],\n",
- " 'a': [1, 2, 3, 4],\n",
- " 'b': [4, 5, 6, 7]\n",
- "}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# factor, no scale\n",
- "p = ggplot(df, aes(x='x', y='y')) + geom_point(aes(color=pm.factor('a')), size=9)\n",
- "p"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# factor, scale\n",
- "p = ggplot(df, aes(x='x', y='y')) \\\n",
- " + geom_point(aes(color='a', fill=pm.factor('b')), shape=21, size=9, stroke=5) \\\n",
- " + scale_color_discrete()\n",
- "p"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# data in mappings, scale_color_discrete\n",
- "p = ggplot() + geom_point(aes(x=[0, 5, 10], y=[0, 5, 10], color=[1, 2, 4]), size=9) + scale_color_discrete()\n",
- "p"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Issue 1\n",
- "\n",
- "`factor` is not working when used in \"ggplot()\" mapping."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 7,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "p3 = ggplot(df, aes(x='x', y='y', color=pm.factor('a'))) + geom_point(size=9)\n",
- "p3"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Issue 2\n",
- "`factor` doesn't create groups the way discrete variable does."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "df = {\n",
- " 'x': [0, 5, 10, 15],\n",
- " 'y': [0, 5, 10, 15],\n",
- " 'a': [0, 0, 1, 1],\n",
- " 'c': ['a', 'a', 'b', 'b']\n",
- "}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 14,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "p4 = ggplot(df, aes(x='x', y='y')) + geom_line(aes(color=pm.factor('a')), size=3)\n",
- "p4"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# expected: 2 lines ('c' is a discrete variable)\n",
- "p5 = ggplot(df, aes(x='x', y='y')) + geom_line(aes(color='c'), size=3)\n",
- "p5"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 27,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# expected: 2 lines (`group` is defined manually)\n",
- "p6 = ggplot(df, aes(x='x', y='y')) + geom_line(aes(color='a', group='a'), size=3)\n",
- "p6"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Issue 2a\n",
- "Also about groups but with `stat` this time"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pandas as pd\n",
- "mpg_df = pd.read_csv('https://jetbrains.bintray.com/lets-plot/mpg.csv')\n",
- "mpg_plot = ggplot(mpg_df, aes(x='displ', y='hwy'))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 23,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "cyl_factor = pm.factor('cyl')\n",
- "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
- "+ geom_smooth(aes(color=cyl_factor), method='lm', size=1, se=False)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 32,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# expected: seperate regression line for each group\n",
- "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
- "+ geom_smooth(aes(color='cyl', group='cyl'), method='lm', size=1, se=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Issue 3\n",
- "\"Not an aesthetic 'group'\" error when used with `factor`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 33,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
- "+ geom_smooth(aes(color=cyl_factor, group='cyl'), method='lm', size=1, se=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Issue 4\n",
- "Nice to have parameter `ordered`. Owerwise have to use `scale_discrete(breaks=[...])` to order groups by number of cylinders:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- " \n",
- " "
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 40,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "mpg_plot + geom_point(aes(color=cyl_factor)) \\\n",
- "+ scale_color_discrete(breaks=[4, 5, 6, 8])"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.7.5"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/DataFrame.kt b/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/DataFrame.kt
index 41a745e517f..4b1faaa1657 100644
--- a/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/DataFrame.kt
+++ b/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/DataFrame.kt
@@ -146,7 +146,8 @@ class DataFrame private constructor(builder: Builder) {
class Variable @JvmOverloads constructor(
val name: String,
val source: Source = Source.ORIGIN,
- val label: String = name) {
+ val label: String = name
+ ) {
val isOrigin: Boolean
get() = source == Source.ORIGIN
@@ -205,6 +206,12 @@ class DataFrame private constructor(builder: Builder) {
return this
}
+ fun putDiscrete(variable: Variable, v: List<*>): Builder {
+ putIntern(variable, v)
+ myIsNumeric[variable] = false
+ return this
+ }
+
fun putIntern(variable: Variable, v: List<*>) {
myVectorByVar[variable] = ArrayList(v)
}
diff --git a/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/data/DataFrameUtil.kt b/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/data/DataFrameUtil.kt
index 4edc12da8f0..ef237e54528 100644
--- a/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/data/DataFrameUtil.kt
+++ b/plot-base-portable/src/commonMain/kotlin/jetbrains/datalore/plot/base/data/DataFrameUtil.kt
@@ -95,43 +95,31 @@ object DataFrameUtil {
return LinkedHashSet(data[variable])
}
- fun hasValues(data: DataFrame, `var`: DataFrame.Variable): Boolean {
- return data.has(`var`) && !data[`var`].isEmpty()
- }
-
- fun valuesOrNull(data: DataFrame, `var`: DataFrame.Variable): List<*>? {
- return if (data.has(`var`)) {
- data[`var`]
- } else null
- }
-
fun sortedCopy(variables: Iterable): List {
val ordering = Ordering.from(Comparator { o1, o2 -> o1.name.compareTo(o2.name) })
return ordering.sortedCopy(variables)
}
fun variables(df: DataFrame): Map {
- val vars = HashMap()
- for (`var` in df.variables()) {
- vars[`var`.name] = `var`
- }
- return vars
+ return df.variables().associateBy { it.name }
}
fun appendReplace(df0: DataFrame, df1: DataFrame): DataFrame {
- val df0Vars = variables(df0)
-
- val builder = df0.builder()
- for (df1Var in df1.variables()) {
- var resultVar = df1Var
- if (df0Vars.containsKey(df1Var.name)) {
- val df0Var = df0Vars[df1Var.name]!!
- builder.remove(df0Var)
- resultVar = df0Var
+ fun DataFrame.Builder.put(destVars: Collection, df: DataFrame) = apply {
+ destVars.forEach { destVar ->
+ val srcVar = findVariableOrFail(df, destVar.name)
+ when (df.isNumeric(srcVar)) {
+ true -> putNumeric(destVar, df.getNumeric(srcVar))
+ false -> putDiscrete(destVar, df[srcVar])
+ }
}
- builder.put(resultVar, df1[df1Var])
}
- return builder.build()
+
+ return DataFrame.Builder()
+ .put(df0.variables().filter { it.name !in variables(df1) }, df0) // df0 - df1, keep vars from df0
+ .put(df0.variables().filter { it.name in variables(df1) }, df1) // df0 & df1, keep vars from df0
+ .put(df1.variables().filter { it.name !in variables(df0) }, df1) // df1 - df0, new vars from df1
+ .build()
}
fun toMap(df: DataFrame): Map> {
@@ -155,16 +143,12 @@ object DataFrameUtil {
@JvmOverloads
fun createVariable(name: String, label: String = name): DataFrame.Variable {
- val variable: DataFrame.Variable
- when {
- TransformVar.isTransformVar(name) -> variable = TransformVar[name]
- Stats.isStatVar(name) -> variable = Stats.statVar(name)
- Dummies.isDummyVar(name) -> return Dummies.newDummy(
- name
- )
- else -> variable = DataFrame.Variable(name, DataFrame.Variable.Source.ORIGIN, label)
+ return when {
+ TransformVar.isTransformVar(name) -> TransformVar[name]
+ Stats.isStatVar(name) -> Stats.statVar(name)
+ Dummies.isDummyVar(name) -> Dummies.newDummy(name)
+ else -> DataFrame.Variable(name, DataFrame.Variable.Source.ORIGIN, label)
}
- return variable
}
fun getSummaryText(df: DataFrame): String {
diff --git a/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/ConfigUtil.kt b/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/ConfigUtil.kt
index 446b1a22410..fac9f8fcd87 100644
--- a/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/ConfigUtil.kt
+++ b/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/ConfigUtil.kt
@@ -30,6 +30,7 @@ object ConfigUtil {
}
}
+
internal fun createDataFrame(rawData: Any?): DataFrame {
val varNameMap = asVarNameMap(rawData)
return updateDataFrame(DataFrame.Builder.emptyFrame(), varNameMap)
@@ -96,20 +97,18 @@ object ConfigUtil {
val varNameMap = HashMap>()
if (data is Map<*, *>) {
- val mapData = data as Map<*, *>?
- for (k in mapData!!.keys) {
- val v = mapData[k]
+ for (k in data.keys) {
+ val v = data[k]
if (v is List<*>) {
varNameMap[k.toString()] = v
}
}
} else if (data is List<*>) {
- val list = data as List<*>?
// check if this is a matrix - all elements are lists of the same size
var matrix = true
var rowSize = -1
- for (row in list!!) {
+ for (row in data) {
if (row is List<*>) {
if (rowSize < 0 || row.size == rowSize) {
rowSize = row.size
@@ -121,9 +120,9 @@ object ConfigUtil {
}
if (matrix) {
- val dummyNames = Dummies.dummyNames(list.size)
- for (i in list.indices) {
- varNameMap[dummyNames[i]] = list[i] as List<*>
+ val dummyNames = Dummies.dummyNames(data.size)
+ for (i in data.indices) {
+ varNameMap[dummyNames[i]] = data[i] as List<*>
}
} else {
// simple data vector
@@ -140,30 +139,20 @@ object ConfigUtil {
private fun updateDataFrame(df: DataFrame, data: Map>): DataFrame {
val dfVars = DataFrameUtil.variables(df)
val b = df.builder()
- for (varName in data.keys) {
- val variable: DataFrame.Variable
- if (dfVars.containsKey(varName)) {
- variable = dfVars[varName]!!
- } else {
- variable = DataFrameUtil.createVariable(varName)
- }
-
- b.put(variable, toList(data[varName]!!))
+ for ((varName, values) in data) {
+ val variable = dfVars[varName] ?: DataFrameUtil.createVariable(varName)
+ b.put(variable, values)
}
return b.build()
}
private fun toList(o: Any): List<*> {
- if (o is List<*>) {
- return o
- }
- if (o is Number) {
- return listOf(o.toDouble())
- }
- if (o is Iterable<*>) {
- throw IllegalArgumentException("Can't cast/transform to list: " + o::class.simpleName)
+ return when (o) {
+ is List<*> -> o
+ is Number -> listOf(o.toDouble())
+ is Iterable<*> -> throw IllegalArgumentException("Can't cast/transform to list: " + o::class.simpleName)
+ else -> listOf(o.toString())
}
- return listOf(o.toString())
}
internal fun createAesMapping(data: DataFrame, mapping: Map<*, *>?): Map, DataFrame.Variable> {
diff --git a/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/DataMetaUtil.kt b/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/DataMetaUtil.kt
new file mode 100644
index 00000000000..737fda14983
--- /dev/null
+++ b/plot-config-portable/src/commonMain/kotlin/jetbrains/datalore/plot/config/DataMetaUtil.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020. JetBrains s.r.o.
+ * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
+ */
+
+package jetbrains.datalore.plot.config
+
+import jetbrains.datalore.plot.base.DataFrame
+import jetbrains.datalore.plot.base.data.DataFrameUtil
+import jetbrains.datalore.plot.base.data.DataFrameUtil.createVariable
+import jetbrains.datalore.plot.base.data.DataFrameUtil.findVariableOrFail
+import jetbrains.datalore.plot.config.Option.Meta.MappingAnnotation
+import jetbrains.datalore.plot.config.Option.Meta.MappingAnnotation.DISCRETE
+
+object DataMetaUtil {
+ private const val prefix = "@as_discrete@"
+
+ private fun isDiscrete(variable: String) = variable.startsWith(prefix)
+
+ public fun toDiscrete(variable: String): String {
+ require(!isDiscrete(variable)) { "toDiscrete() - variable already encoded: $variable" }
+ return "$prefix$variable"
+ }
+
+ private fun fromDiscrete(variable: String): String {
+ require(isDiscrete(variable)) { "fromDiscrete() - variable is not encoded: $variable" }
+ return variable.removePrefix(prefix)
+ }
+
+ /**
+ @returns Map
+ */
+ private fun getAesMappingAnnotations(options: Map<*, *>): Map {
+ return options
+ .getMaps(MappingAnnotation.TAG)
+ ?.associate { it.read(MappingAnnotation.AES) as String to it.read(MappingAnnotation.ANNOTATION) as String }
+ ?: emptyMap()
+ }
+
+ /**
+ @returns Set of discrete aes
+ */
+ fun getAsDiscreteAesSet(options: Map<*, *>): Set {
+ return getAesMappingAnnotations(options).filterValues(DISCRETE::equals).keys
+ }
+
+ fun createScaleSpecs(plotOptions: Map): List> {
+ val plotDiscreteAes = plotOptions
+ .getMap(Option.Meta.DATA_META)
+ ?.let(DataMetaUtil::getAsDiscreteAesSet)
+ ?: emptySet()
+
+ val layersDiscreteAes = plotOptions
+ .getMaps(Option.Plot.LAYERS)?.asSequence()
+ ?.mapNotNull { it.getMap(Option.Meta.DATA_META) }
+ ?.map(DataMetaUtil::getAsDiscreteAesSet) // diascrete aes from all layers
+ ?.flatten()?.toSet() // List> -> Set
+ ?: emptySet()
+
+
+ return (plotDiscreteAes + layersDiscreteAes).map { aes ->
+ mutableMapOf(
+ Option.Scale.AES to aes,
+ Option.Scale.DISCRETE_DOMAIN to true
+ )
+ }
+ }
+
+ /**
+ * returns mappings and DataFrame extended with auto-generated discrete mappings and variables
+ */
+ fun createDataFrame(
+ options: OptionsAccessor,
+ commonData: DataFrame,
+ commonDiscreteAes: Set,
+ commonMappings: Map<*, *>,
+ isClientSide: Boolean
+ ): Pair