{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[this doc on github](https://github.com/dotnet/interactive/tree/master/samples/notebooks/fsharp/Docs)\n", "\n", "# Charts with XPlot\n", "Charts can be rendered using [Xplot.Plotly](https://fslab.org/XPlot/). \n", "We will cover some example on how to use XPlot in a notebook with the .NET Kernel.\n", "\n", "First, import the `XPlot.Plotly` namespace:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "#i \"nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json\" \r\n", "#i \"nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json\" \r\n", "\r\n", "#r \"nuget: XPlot.Plotly.Interactive, 4.0.2\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "open XPlot.Plotly" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next cell sets up some helpers for data generation." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let generator = new Random()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Rendering Scatter plots\n", "One of the most commonly used type of chart to explore data set. Use the type `Scatter`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let openSeries =\n", " Scatter(\n", " name = \"Open\",\n", " x = [1; 2; 3; 4],\n", " y = [10; 15; 13; 17])\n", "\n", "let closeSeries =\n", " Scatter(\n", " name = \"Close\",\n", " x = [2; 3; 4; 5],\n", " y = [16; 5; 11; 9])\n", "\n", "[openSeries; closeSeries]\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Open vs Close\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's change it to be markers style, so more like a scatter plot." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "openSeries.mode <- \"markers\"\n", "closeSeries.mode <- \"markers\"\n", "\n", "[openSeries; closeSeries]\n", "|> Chart.Plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Scatter` can also produce polar charts by setting the radial property `r` and angular proeprty `t`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let openSeries =\n", " Scatter(\n", " name = \"Open\",\n", " r = [1.; 2.; 3.; 4.],\n", " t = [45.; 100.; 150.; 290.])\n", "\n", "let closeSeries =\n", " Scatter(\n", " name = \"Close\",\n", " r = [2.; 3.; 4.; 5. ],\n", " t = [16.; 45.; 118.; 90.])\n", "\n", "[openSeries; closeSeries]\n", "|> Chart.Plot\n", "|> Chart.WithLayout(Layout(orientation = -90.))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Large scatter plots and performance\n", "It is not uncommong to have scatter plots with a large dataset, it is a common scenario at the beginning of a data exploration process. Using the default `svg` based rendering will create performace issues as the dom will become very large.\n", "We can then use `web-gl` support to address the problem." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "#!time\n", "\n", "let series =\n", " [|\n", " for a in 1 .. 10 ->\n", " Scattergl(\n", " name = sprintf \"Series %i\" a, \n", " mode = \"markers\", \n", " x = [ for ax in 1 .. 100000 -> generator.Next(-200, 200) * 1000 * generator.Next(-2000, 2000)], \n", " y = [ for ay in 1 .. 100000 -> generator.Next(-200, 200) * 1000 * generator.Next(-2000, 2000)])\n", " |]\n", " \n", "series\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Large Dataset\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Can provide custom marker `colour`, `size` and `colorscale` to display even more information to the user." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let generatePoint () = generator.Next(-200, 200) * 1000 * generator.Next(-2000, 2000)\n", " \n", "let sizes =\n", " [ for s in 1..100 ->\n", " if generator.NextDouble() < 0.75 then\n", " generator.Next(1, 5)\n", " else\n", " generator.Next(10, 15) ]\n", " \n", "let temperatures = sizes |> Seq.map (fun x -> x * 10 - 100)\n", "\n", "let series =\n", " [|\n", " for a in 1 .. 10 -> \n", " Scattergl(\n", " name = sprintf \"Series %i\" a,\n", " mode = \"markers\",\n", " x = [ for _ in 1 .. 100 -> generatePoint () ],\n", " y = [ for ay in 1 .. 100 -> generatePoint () ],\n", " marker = \n", " Marker(\n", " colorscale = \"hot\",\n", " color = temperatures,\n", " size = sizes))\n", " |]\n", " \n", "series\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Size and Colour\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Color Scales\n", "\n", "Plotly provides some additional `color scales` to use. Note that we use `display` explicitly to display each graph with separate titles, rather than a single chart." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "for s in series do s.marker.colorscale <- \"Viridis\"\n", "\n", "series\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Viridis scale\"\n", "|> display\n", "\n", "for s in series do s.marker.colorscale <- \"Hot\"\n", "\n", "series\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Hot scale\"\n", "|> display\n", "\n", "for s in series do s.marker.colorscale <- \"Jet\"\n", "\n", "series\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Jet scale\"\n", "|> display" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Rendering Histograms\n", "Let's have a look at using histograms, the next cell sets up some generators." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let count = 20\n", "let dates = [for d in 1 .. count -> DateTime.Now.AddMinutes(float(generator.Next(d, d + 30)))]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's define histogram traces:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let openByTime =\n", " Histogram(\n", " x = dates,\n", " y = [for y in 1 .. count -> generator.Next(0, 200)],\n", " name = \"Open\")\n", " \n", "let closeByTime =\n", " Histogram(\n", " x = dates,\n", " y = [for y in 1 .. count -> generator.Next(0, 200)],\n", " name = \"Close\")\n", " \n", "[openByTime; closeByTime]\n", "|> Chart.Plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Histogram generator will automatically count the number of items per bin. \n", "\n", "Setting `histfunc` to `\"sum\"` we can now add up all the values contained in each bin.\n", "Note that we are creatng bin using the `x` data point and we are using bydefault autobinx" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let openByTime =\n", " Histogram(\n", " x = dates,\n", " y = [for y in 1 .. count -> generator.Next(0, 200)],\n", " name = \"Open\",\n", " histfunc = \"sum\")\n", " \n", "let closeByTime =\n", " Histogram(\n", " x = dates,\n", " y = [for y in 1 .. count -> generator.Next(0, 200)],\n", " name = \"Close\",\n", " histfunc = \"sum\")\n", " \n", "[openByTime; closeByTime]\n", "|> Chart.Plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Area chart and Polar Area chart" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By populating hte property `fill` of a `Scatter` trace the chart will render as area chart.\n", "\n", "Here is set to `\"tozeroy\"` which will create a fill zone underneath the line reachin to the 0 of the y axis." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let openSeries =\n", " Scatter(\n", " name = \"Open\",\n", " x = [1; 2; 3; 4],\n", " y = [10; 15; 13; 17],\n", " fill = \"tozeroy\",\n", " mode= \"lines\")\n", " \n", "let closeSeries =\n", " Scatter(\n", " name = \"Close\",\n", " x = [1; 2; 3; 4],\n", " y = [3; 5; 11; 9],\n", " fill = \"tozeroy\",\n", " mode= \"lines\")\n", "\n", "[openSeries; closeSeries]\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Open vs Close\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With one `fill` set to `\"tonexty\"` the cahrt will fill the aread between traces." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "openSeries.fill <- None\n", "closeSeries.fill <- \"tonexty\"\n", "\n", "[openSeries; closeSeries]\n", "|> Chart.Plot\n", "|> Chart.WithTitle \"Open vs Close\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using `Area` traces we can generate radial area chart. In this example we are using cardinal points to xpress angular values.\n", "The list `[\"North\"; \"N-E\"; \"East\"; \"S-E\"; \"South\"; \"S-W\"; \"West\"; \"N-W\"]` will be autoimatically translated to angular values." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "fsharp" } }, "outputs": [], "source": [ "let winDirections = [\"North\"; \"N-E\"; \"East\"; \"S-E\"; \"South\"; \"S-W\"; \"West\"; \"N-W\"]\n", "\n", "let areaTrace1 =\n", " Area(\n", " r = [77.5; 72.5; 70.0; 45.0; 22.5; 42.5; 40.0; 62.5],\n", " t = winDirections,\n", " name = \"11-14 m/s\",\n", " marker = Marker(color = \"rgb(106,81,163)\"))\n", "\n", "let areaTrace2 =\n", " Area(\n", " r = [57.49999999999999; 50.0; 45.0; 35.0; 20.0; 22.5; 37.5; 55.00000000000001],\n", " t = winDirections,\n", " name = \"8-11 m/s\",\n", " marker = Marker(color = \"rgb(158,154,200)\"))\n", "\n", "let areaTrace3 = \n", " Area(\n", " r = [40.0; 30.0; 30.0; 35.0; 7.5; 7.5; 32.5; 40.0],\n", " t = winDirections,\n", " name = \"5-8 m/s\",\n", " marker = Marker(color = \"rgb(203,201,226)\"))\n", "\n", "let areaTrace4 =\n", " Area(\n", " r = [20.0; 7.5; 15.0; 22.5; 2.5; 2.5; 12.5; 22.5],\n", " t = winDirections,\n", " name = \"< 5 m/s\",\n", " marker = Marker(color = \"rgb(242,240,247)\"))\n", "\n", "let areaLayout =\n", " Layout(\n", " title = \"Wind Speed Distribution in Laurel, NE\",\n", " font = Font(size = 16.),\n", " legend = Legend(font = Font(size = 16.)),\n", " radialaxis = Radialaxis(ticksuffix = \"%\"),\n", " orientation = 270.)\n", " \n", "[areaTrace1; areaTrace2; areaTrace3; areaTrace4]\n", "|> Chart.Plot\n", "|> Chart.WithLayout areaLayout" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (F#)", "language": "F#", "name": ".net-fsharp" }, "language_info": { "file_extension": ".fs", "mimetype": "text/x-fsharp", "name": "C#", "pygments_lexer": "fsharp", "version": "4.5" } }, "nbformat": 4, "nbformat_minor": 4 }