{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "While Matplotlib is powerful, it requires a steep learning curve. Moreover, I found it often requires more lines of code for the type of visualisations I wish to produce than various *.js libriaries. Ever since I found out about IPython Notebook and d3js I wanted to combine the two and replace Matplotlib in my workflow. IPython offers a great interactive way of using Python. D3js coupled with developer tools in Chrome/Firefox does the same to graphics." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Due to security concerns there is currently no easy way to execute Javascript in a cell. With this script you can create the d3 code from within (I)Python but render them later on with PhantomJs. Becasue of that one can easily switch between producing interactive SVG visualisations by just copying the resulting HTML code from the rendered script file, or saving a static version in PNG, PDF, etc.\n", "\n", "The main ingredients are coded as a [class in Python](http://gist.github.com/4484816)\n", "\n", "Dependencies:\n", "\n", "* PhantomJs (in `PATH`)\n", "* `titlecase`\n", "* NumPy" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from ipyD3 import *\n", "from IPython.display import display\n", "d3 = d3object()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "All arguments are kwargs, and you can set:\n", "\n", "* `height` of your object\n", "* `width` of your object\n", "* html that will preceed yout object (`topHtml`)\n", "* html that will follow yout object (`bottomHtml`)\n", "* a predefined `style` that already includes `topHtml`, `botttomHtml`, and css\n", "* weather to execute the scripts in your notebook (`publish`)\n", "* how precise should be the data on conversion to Javascript variables (`precision`)\n", "* path to the PhantomJs executable (`phantomExec`)\n", "* path to directory where you want to permanently story otherwise temporary ipyD3 files (`keepTempDir`)\n", "* previous `d3` objects if you wish to create a multi-page report" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##Figures\n", "\n", "You start by initializing the object." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np\n", "d3 = d3object(width=800,\n", " height=200,\n", " style='JFFigure',\n", " number=1,\n", " d3=None,\n", " precision=100,\n", " title='Example figure with d3js',\n", " desc='Standrad normal distribution')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Variables are passed through `addVar` function. It will convert the basic types to `Javascript`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "d3.addVar(\n", "interpolation1='basis',\n", "interpolation2='step-before',\n", "resolution=80,\n", "domainRange=[-4,4],\n", "imposeMax=0\n", ")" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can add your own `Javascript` and `CSS`. Note, that all variables specified with `addVar` are pasted at the beginning of the script, so if you need to change a variable's value within the script you need to use `addJs`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "d3.addJs('''\n", " var svg = d3.select(\"#\"+d3ObjId)\n", " .append(\"svg\")\n", " .attr(\"width\", width)\n", " .attr(\"height\", height)\n", " .append(\"g\").attr(\"id\", \"#\"+d3ObjId+'InnerG')\n", "\n", " // A formatter for counts.\n", " var formatCount = d3.format(\",.0f\");\n", " \n", " var margin = {top: 10, right: 30, bottom: 30, left: 30},\n", " width = width - margin.left - margin.right,\n", " height = height - margin.top - margin.bottom;\n", " \n", " var x = d3.scale.linear()\n", " //.domain(domainRange)\n", " .range([0, width]);\n", " \n", " // Generate a histogram using twenty uniformly-spaced bins.\n", " var xAxis = d3.svg.axis()\n", " .ticks(4)\n", " .scale(x)\n", " .orient(\"bottom\");\n", "\n", " function appendHistogram(series, domainSeries, height, width, x_offset, y_offset, interpolation){\n", " var svg2 = svg.append(\"g\")\n", " y_offset=Math.round(y_offset)\n", " height=Math.round(height)\n", " height+=y_offset\n", " width+=x_offset\n", "\n", " x.range([x_offset, width])\n", " .domain(domainSeries)\n", "\n", " var data = d3.layout.histogram()\n", " .bins(x.ticks(resolution))\n", " .frequency(0)\n", " (series);\n", " \n", " var seriesMax=d3.max([imposeMax, d3.max(data, function(d) { return d.y; })]);\n", " var y = d3.scale.linear()\n", " //.domain([0, d3.max(data, function(d) { return d.y; })])\n", " .domain([0, seriesMax])\n", " .range([height, y_offset]);\n", " \n", "\n", " var area = d3.svg.area()\n", " .interpolate(interpolation)\n", " .x(function(d) { \n", " if(interpolation==\"step-before\")\n", " return x(d.x+d.dx/2)\n", " return x(d.x); \n", " })\n", " .y0(height)\n", " .y1(function(d) { return d3.max([y(d.y),y(seriesMax)]); });\n", " \n", " svg2.append(\"path\")\n", " .datum(data)\n", " .attr(\"class\", \"area\")\n", " .attr(\"d\", area);\n", " \n", " svg2.append(\"g\")\n", " .attr(\"class\", \"axis\")\n", " .attr(\"transform\", \"translate(0,\" + height + \")\")\n", " .call(xAxis);\n", " \n", " \n", " }\n", "\n", "appendHistogram(data, domainRange, height, width/2-15, 10, 0, interpolation1)\n", "appendHistogram(data, domainRange, height, width/2-15, width/2+20, 0, interpolation2)\n", "''')\n", "\n", "d3.addCss('''\n", " .polyline{\n", " stroke: #000;\n", " shape-rendering: crispEdges;\n", " }\n", " .area{\n", " shape-rendering: geometricPrecision;\n", " stroke: #000 !important;\n", " stroke-width:1 !important;\n", " fill: #ddd !important;\n", " }\n", " \n", " .axis path, .axis line {\n", " fill: none;\n", " stroke: #000;\n", " stroke-width: 1px;\n", " color-rendering: optimizeQuality !important;\n", " shape-rendering: crispEdges !important;\n", " text-rendering: geometricPrecision !important; \n", " \n", " }\n", "''')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "E.g. dsitribution of standard normal. D3js allows for quick swtiching between histograms and approx. shape of the distribution.\n", "\n", "With too few observations:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "d3.addVar(data=np.random.randn(1000))\n", "html=d3.render(mode=('show','html'))\n", "display(html)" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\r\n", "
\r\n", "