Data visualization tasks are closely related to educational, scientific and business issues. There is a great number of graphic libraries, usually written and available from languages, designed for data science issues. But few of them could be used from Kotlin, that prevents many people from using it for statistical computing and graphics. One of the most popular browser graphic libraries is
Plotly
available from most languages (JavaScript itslef, Python, R, etc). That’s why it was decided to create Kotlin wrapper for Plotly, which uses JVM backend for drawing in browser some static HTML-page or starting its own web-server. The original Plotly API was preserved as much as possible and some new features and abilites were added.
New Plotly.kt logo symbolizes the relationship between the Kotlin language and initial Plotly library. Therefore, gradient colors and the shape of the letter “K” have been taken from Kotlin
logo
, while white histogram and cerulean dots are borrowed from Plotly
logo
to illustrate that a greate variety of types of fully styled plots can be created with
Plotly.kt
!
Our version of Plotly.kt library contains different classes for each supported type of plot, such as Histogram, Heatmap or Pie Chart, inherited from Trace, which is the main class containing all nonspecific methods and parameters. It was made in this way to flatten library model structure and to avoid duplication of large chunks of code, which is one of the problems of Plotly library itself, in which it is nearly impossible even to download all the
documentation
. Each class contains an
init
block where the trace type is set:
class Pie : Trace() {
init {
type = TraceType.pie
}
}
At present Plotly.kt supports all major trace types which could be combined in one plot:
enum class TraceType {
bar,
box,
contour,
heatmap,
heatmapgl,
histogram,
histogram2d,
histogram2dcontour,
pie,
scatter,
scattergl,
violin
}
Many other trace types will be supported in the future, such as 3D graphics, polar plots and geo maps. Calling one of the not supported types will cause an error.
All traces subclasses (Bar, Box, etc), Trace itself and additional layout and trace classes (Font, Title, Line, etc) contain a big number of different methods and parameters, and all of them are, in fact, delegates:
class Domain : Scheme() {
/**
* Sets the horizontal domain of this pie trace (in plot fraction).
* Default: [0, 1]
*/
var x by numberList()
/**
* Sets the vertical domain of this pie trace (in plot fraction).
* Default: [0, 1]
*/
var y by numberList()
}
These classes are inherited from
Scheme
, which allows to store the values of all parameters as a dynamic tree. It is important for a dynamic update of the plot, therefore now real-time updates are available.
Moreover, if you need one of the unsupported methods, you can change the trace configuration, which is similarly available owing to the dynamic tree structure:
val trace = Trace.invoke(x, y) {
name = "sin"
/** The hover text definition is currently not supported.
* We are applying it directly to configuration.
* It is still observable in the same way as other properties but is not type safe.
*/
configure {
set("text", x.map { "label for $it" })
}
}
Plot description is divided into two parts as in the original Plotly library:
-
traces
: data values, type of the plot, common parameters and specific parameters (e.g. bins width and color for Bar chart), text
-
layout
: frame settings including axes, annotations, legend and shapes
Unfortunately, sometimes it’s not easy to make out what type the object is (a trace or a layout) and it is the next problem of the original Plotly API. If you have any problems with that, please check Plotly
documentation
But let’s start with creating a simple scatter plot:
Plotly.plot {
scatter {
x(1, 2, 3, 4)
y(12, 5, 2, 12)
mode = ScatterMode.`lines+markers`
}
layout {
title = "Line and Scatter Plot"
}
}.makeFile()
This version of Plotly.kt library contains several modules, which are necessary for supporting some important features:
1.
Static page
is an isolated whole HTML page, which can contain custom headers, separators and any other Kotlin-html elements. Plot description is built in the created page in JSON format. After calling
plot.makeFile()
function corresponding HTML file saves to a temporary file, which can be customized.
For example, support of LaTeX rendering with
MathJax
can be added by using custom headers. But you need to remember that some font properties have no effect while using LaTeX, that is why you need to change font size or width by means of LaTeX itself:
Plotly.page(mathJaxHeader) {
plot {
scatter {
x.set(xValues)
y.set(yValues)
name = "\$\\Large{y = \\mathrm{sin}\\,x}\$" // increasing fontsize
}
}
}.makeFile()
The resulting graph looks as follows.
Full tutorial of drawing the sinus picture is available
here
, while the source code can be found
here
2.
HtmlFragment
is a fragment of Kotlin-html page, which also can be displayed in the browser after calling
.makefile()
.
For example, if you need to download plot in SVG format instead of PNG, you can directly change configuration of the plot and change Plotly browser button from “download png” to “download svg”:
Plotly.fragment {
val plotConfig = PlotlyConfig {
saveAsSvg()
}
plot(config = plotConfig) {...}
}.makeFile()
After applying this configuration the result will be as follows.
Example code is available
here
3.
Static plot
is created after inserting resulting JSON with the plot description into a template HTML page. If you don’t need any specific settings using static plots is the easiest way to get an image.
Full source code is available
here
4.
Dynamic plot
collects updates in the Collector, which flushes updates once in a while (50 ms by default). After flushing takes place only modified parts of the plot are updated.
5.
Jupyter
is now supported in an experimental mode. There are two modules in Plotly.kt repository for Kotlin-Jupyter: static (call
%use plotly
) and server (call
%use plotly-server
). The server is needed to make dynamic charts with real-time updates. For the time being integration with JupyterLab works stably, but there are some problems with Jupyter Classic because of the properties, connected with files and scripts loading.
%use plotly
import kotlin.math.*
Plotly.plot {
scatter {
x.set((0..100).map { it.toDouble() / 100.0 })
y.set((0..100).map { sin(2.0 * PI * (it.toDouble() / 100.0)) })
}
layout {
title = "$\\text{TeX is Number} \\displaystyle\\sum_{n=1}^\\infty \\frac{-e^{i\\pi}}{2^n}!$"
}
}
MathJax is loaded automatically, so LaTeX rendering works without any custom headers. More examples with Jupyter notebooks, plotly.json and plotly-server.json are available here
A plot object contains flexible
traces
and
layout
sections and you need to specify at least one of them to make an image. Don’t forget to call
makeFile()
!
val plot = Plotly.plot {
scatter {
x(1, 2, 3, 4, 5)
y("a", "b", "c", "d")
}
layout {
title = "Graph name"
}
}
plot.makeFile()
There is a great number of customizable parameters and methods.
Let’s have a look at some cases:
axis.type
parameter to one of the available values (linear, log, date, category or multicategory).
layout {
xaxis {
type = AxisType.log
autorange = true
}
yaxis {
type = AxisType.log
autorange = true
}
}
plot {
heatmap {
colorscale = Value.of(listOf(listOf(0, "#B1D1FC"), listOf(1, "#7E1E9C"))
}
}
bar.orientation
parameter.
plot {
bar {
orientation = Orientation.h
}
}
Vertical axes labels show some css named colors.
For more examples please check our GitHub page. Thank you for your attention!