{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[this doc on github](https://github.com/dotnet/interactive/tree/master/samples/notebooks/polyglot)\n", "\n", "# Visualizing data using d3js\n", "\n", "**This is a work in progress.** It doesn't work yet in [Binder](https://mybinder.org/v2/gh/dotnet/interactive/master?urlpath=lab) because it relies on HTTP communication between the kernel and the Jupyter frontend.\n", "\n", "This notebooks uses directly [d3.js](https://d3js.org/) library to perform custom data visualisation." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" } }, "outputs": [], "source": [ "var rnd = new Random();\n", "var a = Enumerable.Range(1,rnd.Next(4,12)).Select( t => rnd.Next(t, t*10)).ToArray();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using [RequireJS](https://requirejs.org/) we import [d3.js](https://d3js.org/). We setup the rendering code, some SVG filter inspiredy by [Visual Cinnamon](https://www.visualcinnamon.com/) article on [gooey effect](https://www.visualcinnamon.com/2016/06/fun-data-visualizations-svg-gooey-effect).\n", "\n", "Using `interactive.csharp.getVariable` we fetch the variable `a` value." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "javascript" } }, "outputs": [], "source": [ "#!javascript\r\n", "\r\n", "if (typeof (notebookScope.interval) !== 'undefined') {\r\n", " clearInterval(notebookScope.interval);\r\n", "}\r\n", "\r\n", "notebookScope.plot = (sgvSelector, variableName) => {\r\n", " let dtreeLoader = interactive.configureRequire({\r\n", " paths: {\r\n", " d3: \"https://d3js.org/d3.v6.min\"\r\n", " }\r\n", " });\r\n", " dtreeLoader([\"d3\"], function (d3) {\r\n", " let svg = d3.\r\n", " select(sgvSelector);\r\n", " svg.selectAll(\"defs\").remove();\r\n", " svg.selectAll(\"g\").remove();\r\n", "\r\n", " let defs = svg.append(\"defs\");\r\n", "\r\n", " let filter = defs.append(\"filter\").attr(\"id\", \"gooeyCodeFilter\");\r\n", "\r\n", " filter.append(\"feGaussianBlur\")\r\n", " .attr(\"in\", \"SourceGraphic\")\r\n", " .attr(\"stdDeviation\", \"10\")\r\n", " .attr(\"color-interpolation-filters\", \"sRGB\")\r\n", " .attr(\"result\", \"blur\");\r\n", "\r\n", " filter.append(\"feColorMatrix\")\r\n", " .attr(\"in\", \"blur\")\r\n", " .attr(\"mode\", \"matrix\")\r\n", " .attr(\"values\", \"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9\")\r\n", " .attr(\"result\", \"gooey\");\r\n", "\r\n", " let container = d3\r\n", " .select(sgvSelector)\r\n", " .append(\"g\")\r\n", " .style(\"filter\", \"url(#gooeyCodeFilter)\");\r\n", "\r\n", "\r\n", " console.log(container);\r\n", "\r\n", " let updateD3Rendering = () => interactive.csharp.getVariable(variableName)\r\n", " .then(data => {\r\n", " var i = 0;\r\n", " var p = container\r\n", " .selectAll(\".points\")\r\n", " .data(data, (d, i) => i);\r\n", "\r\n", " p.transition()\r\n", " .duration(2000)\r\n", " .style(\"fill\", d => d3.interpolateTurbo(d / 80))\r\n", " .attr(\"r\", d => Math.max(0, d));\r\n", "\r\n", " p.enter()\r\n", " .append(\"circle\")\r\n", " .attr(\"class\", \"points\")\r\n", " .attr(\"cy\", 80)\r\n", " .attr(\"cx\", (d,i) => ((i) + 1) * 60)\r\n", " .transition()\r\n", " .duration(2000)\r\n", " .style(\"fill\", d => d3.interpolateTurbo(d / 80))\r\n", " .ease(d3.easeElasticOut.period(1.00))\r\n", " .attr(\"r\", d => Math.max(0, d)),\r\n", "\r\n", " p.exit()\r\n", " .transition()\r\n", " .duration(1000)\r\n", " .attr(\"r\", 0)\r\n", " .remove();\r\n", " });\r\n", " notebookScope.interval = setInterval(() => updateD3Rendering(), 3000);\r\n", " });\r\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the `setInterval` call near the end of the previous cell. This rechecks the data in the kernel and updates the plot.\n", "\n", "Back on the kernel, we can now update the data so that the kernel can see it.\n", "\n", "Yes, this is a contrived example, and we're planning to support true streaming data, but it's a start." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "html" } }, "outputs": [], "source": [ "#!html\n", "\n", "\n", "#!js\n", "notebookScope.plot(\"svg#dataPlot1\", \"a\");\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" } }, "outputs": [], "source": [ "#!csharp\n", "for(var i = 0; i < 10; i++){\n", " await Task.Delay(1000);\n", " var limit = rnd.Next(4,12);\n", " a = Enumerable.Range(1,limit).Select( t => rnd.Next(30, 80)).ToArray();\n", "}" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "file_extension": ".cs", "mimetype": "text/x-csharp", "name": "C#", "pygments_lexer": "csharp", "version": "8.0" } }, "nbformat": 4, "nbformat_minor": 4 }