{"cells":[{"cell_type":"markdown","source":["# Advanced interactive graphics"],"metadata":{"id":"epe_wojsFp4P"}},{"cell_type":"markdown","source":["In your other DS courses, you've learned how to create static graphics\n","uses R, ggplot, matplotlib, seaborn ... You've probably also learned\n","how to create client side interactive graphics using libraries like\n","plotly and maybe also learned client-server interactivity with shiny,\n","dash, streamlet, etcetera.\n","\n","In this section we're going to dig deeper into client side graphics,\n","which are almost always done via html, css, javascript and a\n","javascript plotting library. We're going to focus on d3.js, a well\n","known javascript library for creating interactive data\n","visulalizations.\n","\n","Tools like d3 are mostly for creating professional data web\n","graphics. So, most of our daily graphics use will just use\n","python/R/julia/matlab ... or plotting libraries like plotly. Usually,\n","you want to prototype graphics outside of d3. Here, we'll give you a\n","smidge of using d3 to get you started if your goal is to become a\n","graphics expert.\n","\n","Let's get started. I'm going to assume that you have a basic knowledge\n","of html, css and a little bit of javascript. D3 works by manipulating\n","html elements. Let's select every paragraph element in a document. Try uncommenting each of the three `let pselect` lines (and commenting the others) to see what they do."],"metadata":{"id":"cg_gylyKHUPe"}},{"cell_type":"raw","metadata":{"id":"nOPkAyLY_SJk"},"source":["\n","```\n","\n","\n","\n"," \n","\n","\n","\n","

Advanced

\n","

Data science

\n"," \n"," \n","\n","```"]},{"cell_type":"markdown","source":["```{raw} html\n","
\n","\n","
\n","```"],"metadata":{"id":"J0mIrP2hC8kl"}},{"cell_type":"markdown","metadata":{"id":"5qebAAR4_SJl"},"source":["\n","Note, working with ``%%html`` in a jupyter notebook is a little wonky. So, if you really want to see this in action, save it to a file and open it in a web browser.\n","\n","\n","+ The command `` loads d3 from a CDN. You could also download it locally if you'd like.\n","+ The script `let pselect = d3.selectAll(\"p\").style(\"color\", \"green\");` creates a variable `pselect` that is all of the html paragraph elements\n","+ Try doing this, loading the web page, then try uncommenting each other script line in turn and refreshing\n","+ In chrome do Ctrl-shift-i to get the developer console and inspect the variable pselect.\n","+ Nesting `select` or `selectAll` will select elements within the selected elements.\n","+ You can also select by id or class."]},{"cell_type":"markdown","source":["# A simple example\n","\n","Let's go through an example where we plot brain volumetric ROI data on the log scale using D3. (Note from here on hout we're omitting the html basics of the web page and we're not putting them in a jupyter code block.)\n","\n","```\n","\n","\n"," \n"," \n","```\n","+ The `data(roiDat)` selects our dataset\n","+ The `enter()` and `append('div')` commands add `div` elements to the html document, one per data element.\n","+ The `attr` method considers our `bar` stylesheet style\n","+ The `style` method changes the style so that the bars have the width of our data.\n","The notation `(d) => {return d.volume * .001 + \"px\"}` is a function that selects the\n","ROI element of the data, multiplies it by .001 then converts it to text with `px` at the end.\n","+ The `text` method at the end appends the text to our plot\n","+ The `on` methods say what to do when one mouses over and off the bars. You can see now that\n","they turn orange then back. Remove the mouseout `.on` call and see what happens.\n","\n","The output looks like this. Hover over a bar to test. (Look at the file in [d3/roi1.html](https://github.com/smart-stats/advanced_ds4bio_book/blob/main/qbook/d3/roi1.html))\n"],"metadata":{"id":"OiJ_fNtLuhiX"}},{"cell_type":"markdown","source":["```{raw} html\n","
\n","\n","
\n","```"],"metadata":{"id":"1uRU5URdDlUI"}},{"cell_type":"markdown","source":["# Working through a realistic example\n","\n","Under `assets/kirby_pivot.csv` is a dataset with the kirby 21 data pivoted to have regions as columns. Let's work through a d3 example of ploting right versus left asymmetry in the telencephalon (the largest area of the brain including the cortex and central white matter).\n","\n","Here's the scatterplot that I've got so far. For HW, add text labels to the point, or a tooltip that gives point information when you hover over it. The file rendered below is [here](https://github.com/smart-stats/advanced_ds4bio_book/blob/main/qbook/d3/roi2.html).\n","\n","```{raw} html\n","
\n","\n","
\n","```"],"metadata":{"id":"KJJhH_-dvE-p"}},{"cell_type":"markdown","source":["Let's go over some of the main parts of the d3 code here. First, we set up the graphic\n","\n","```\n","const h = 500\n","const w = 500\n","\n","// create the background\n","let svg = d3.select(\"body\")\n"," .append(\"svg\")\n"," .attr(\"width\" , h)\n"," .attr(\"height\", w);\n","```\n","\n","Next we load in the data. First, we create a function that does a little row processing for us. Honestly, it's probably better to just do this in python/R/julia ... beforehand, but it's worth showing here. We create variables for the log ratio between the right and left hemispheres and the log of the geometric mean. We'll use this to create a Tukey mean/difference plot of the log of the volumes.\n","\n","```\n","//create the variables we're interested in\n","let rowConverter = function(d) {\n"," return {\n"," id : d.id,\n"," //y is going to be the log difference R-L\n"," logratio : Math.log(parseFloat(d.Telencephalon_R)) - Math.log(parseFloat(d.Telencephalon_L)),\n"," //x is going to be the average log\n"," loggm : (Math.log(parseFloat(d.Telencephalon_L)) + Math.log(parseFloat(d.Telencephalon_R))) * .5\n"," };\n"," }\n","\n","//the location where I'm pulling the csv from\n","let dataloc = \"https://raw.githubusercontent.com/smart-stats/advanced_ds4bio_book/main/qbook/assets/kirby_pivot.csv\"\n","\n","//read in the data and parse the rows\n","kirby_pivot = d3.csv(dataloc, rowConverter)\n","```\n","\n","Modern js uses something called 'promises', which alllows for asynchronous evaluation. When we read in our csv file, it gets created as a promise and not an array like we need. The result is that our plotting commands need to then be called as a method from the promise object. The reason for this is so that it only uses the data when the data is actually loaded (i.e. promise fulfilled.) So, the plotting commmands for us look like this.\n","\n","```\n","kirby_pivot.then(dat => {\n"," PLOTTING COMMANDS\n","})\n","```\n","\n","Just a reminder that the notation `d => g(d)` is JS shorthand for `function(d) {return g(d);}` and is used heavily in d3 coding. Now let's fill in `PLOTTING COMMANDS`. First, let's fill in some utility functions. We get the range of our x and y values to help set up our axes. d3 `scales` map our function values to a range we want. So let's create scale maps for x, y and color and then also set up axes using those scales. We'll also go ahead on plot our axes so they're on the bottom.\n","\n","```\n","maxx = d3.max(dat, d => d.loggm)\n","minx = d3.min(dat, d => d.loggm)\n","maxy = d3.max(dat, d => d.logratio)\n","miny = d3.min(dat, d => d.logratio)\n","\n","//fudge is the boundary otherwise points get chopped off\n","let fudge = 50\n","\n","let yScale = d3.scaleLinear()\n"," .domain([miny, maxy])\n"," .range([h-fudge, fudge])\n","\n","let pointScale = d3.scaleLinear()\n"," .domain([miny, maxy])\n"," .range([5, 10])\n","\n","let colorScale = d3.scaleLinear()\n"," .domain([miny, maxy])\n"," .range([0, 1])\n","\n","\n","let xScale = d3.scaleLinear()\n"," .domain([minx, maxx])\n"," .range([w-fudge, fudge]);\n","\n","// define the axes\n","let xaxis = d3.axisBottom().scale(xScale)\n","let yaxis = d3.axisLeft().scale(yScale)\n","svg.append(\"g\")\n"," .attr(\"class\", \"axis\")\n"," .attr(\"transform\", \"translate(0,\" + (h - fudge) + \")\")\n"," .call(xaxis)\n","\n","svg.append(\"g\")\n"," .attr(\"class\", \"axis\")\n"," .attr(\"transform\", \"translate(\" + fudge + \",0)\")\n"," .call(yaxis)\n","```\n","Now let's create the plot. We're going to add circles at each location, which is attributes `cx` and `cy`. Notice we use our previous defined scales to give their locations. Also, we'll set the color and size relative to the logratio. Finally, when we mouseover a point, let's change the radius then change it back when we mouseoff.\n","\n","```\n","svg.selectAll(\"circle\")\n"," .data(dat)\n"," .enter()\n"," .append(\"circle\")\n"," .attr(\"cy\", d => yScale(d.logratio))\n"," .attr(\"cx\", d => xScale(d.loggm))\n"," .attr(\"r\", d => pointScale(d.logratio))\n"," .attr(\"fill\", d => d3.interpolateWarm(colorScale(d.logratio)))\n"," .attr(\"stroke\", \"black\")\n"," .on(\"mouseover\", function() {\n"," d3.select(this)\n"," .attr(\"r\", 30)\n"," })\n"," .on(\"mouseout\", function() {\n"," d3.select(this)\n"," .attr(\"r\", d => pointScale(d.logratio))\n"," })\n","```\n","\n","Obviously, this is a lot of work for a simple scatterplot. The difference is that here you have total control over plotting and interactivity elements.\n"],"metadata":{"id":"8E25eajp0C_B"}},{"cell_type":"markdown","source":["# Observable\n","\n","Observerable is a notebook for working with d3. It’s quite neat since mixing javascript coding in a web notebook, which itself is written in javascript, makes for an interesting setup. Typically, one would do the data preprocessing in R, python, julia … then do the advanced graphing in d3. In addition to accepting d3 as inputs, observable has a slightly higher set of utility functions called observable plot.\n","\n","## Links\n","+ [Observable is not javascript](https://observablehq.com/@observablehq/observables-not-javascript)\n","+ [d3 tutorial](https://www.tutorialsteacher.com/d3js/select-dom-element-using-d3js).\n","+ [d3 gallery](https://observablehq.com/@d3/gallery)\n"],"metadata":{"id":"4JU3FvVY69QD"}}],"metadata":{"kernelspec":{"name":"python3","language":"python","display_name":"Python 3"},"colab":{"provenance":[],"collapsed_sections":["KJJhH_-dvE-p"]}},"nbformat":4,"nbformat_minor":0}