Skip to content

Instantly share code, notes, and snippets.

@chriddyp
Created March 22, 2015 23:25
Show Gist options
  • Save chriddyp/2fe51027d881076e4f52 to your computer and use it in GitHub Desktop.
Save chriddyp/2fe51027d881076e4f52 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "",
"signature": "sha256:1e0d328c4f25b13c190b1b1857ead6f0ac95450a15896723b7950a0c1686c334"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `extend_traces`\n",
"### Append points to the existing traces in your graph widget\n",
"#### IPython Graph Widget with Plotly"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from IPython.display import Image\n",
"Image(url='http://i.imgur.com/qblToez.gif')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<img src=\"http://i.imgur.com/qblToez.gif\"/>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 1,
"text": [
"<IPython.core.display.Image at 0x10458e110>"
]
}
],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from plotly.widgets import GraphWidget\n",
"import time"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stderr",
"text": [
"/Users/chris/anaconda/lib/python2.7/site-packages/pandas/computation/expressions.py:21: UserWarning: The installed version of numexpr 2.0.1 is not supported in pandas and will be not be used\n",
"The minimum supported version is 2.1\n",
"\n",
" \"version is 2.1\\n\".format(ver=ver), UserWarning)\n"
]
},
{
"javascript": [
"window.genUID = function() {\n",
" return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n",
" var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);\n",
" return v.toString(16);\n",
" });\n",
"};\n",
"\n",
"var IPYTHON_VERSION = '3';\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function (widget, manager) {\n",
" if (!('DOMWidgetView' in widget)) {\n",
"\n",
" // we're in IPython2, things moved a bit from 2 --> 3.\n",
" // construct the expected IPython3 widget API\n",
" IPYTHON_VERSION = '2';\n",
" manager = {WidgetManager: widget};\n",
" widget = {DOMWidgetView: IPython.DOMWidgetView};\n",
" }\n",
"\n",
" var GraphView = widget.DOMWidgetView.extend({\n",
" render: function(){\n",
" var that = this;\n",
"\n",
" var graphId = window.genUID();\n",
" var loadingId = 'loading-'+graphId;\n",
"\n",
"\n",
" var _graph_url = that.model.get('_graph_url');\n",
"\n",
" // variable plotlyDomain in the case of enterprise\n",
" var url_parts = _graph_url.split('/');\n",
" var plotlyDomain = url_parts[0] + '//' + url_parts[2];\n",
"\n",
" if(!('plotlyDomains' in window)){\n",
" window.plotlyDomains = {};\n",
" }\n",
" window.plotlyDomains[graphId] = plotlyDomain;\n",
"\n",
" // Place IFrame in output cell div `$el`\n",
" that.$el.css('width', '100%');\n",
" that.$graph = $(['<iframe id=\"'+graphId+'\"',\n",
" 'src=\"'+_graph_url+'.embed\"',\n",
" 'seamless',\n",
" 'style=\"border: none;\"',\n",
" 'width=\"100%\"',\n",
" 'height=\"600\">',\n",
" '</iframe>'].join(' '));\n",
" that.$graph.appendTo(that.$el);\n",
"\n",
" that.$loading = $('<div id=\"'+loadingId+'\">Initializing...</div>')\n",
" .appendTo(that.$el);\n",
"\n",
" // for some reason the 'width' is being changed in IPython 3.0.0\n",
" // for the containing `div` element. There's a flicker here, but\n",
" // I was unable to fix it otherwise.\n",
" setTimeout(function () {\n",
" if (IPYTHON_VERSION === '3') {\n",
" $('#' + graphId)[0].parentElement.style.width = '100%';\n",
" }\n",
" }, 500);\n",
"\n",
" // initialize communication with the iframe\n",
" if(!('pingers' in window)){\n",
" window.pingers = {};\n",
" }\n",
"\n",
" window.pingers[graphId] = setInterval(function() {\n",
" that.graphContentWindow = $('#'+graphId)[0].contentWindow;\n",
" that.graphContentWindow.postMessage({task: 'ping'}, plotlyDomain);\n",
" }, 200);\n",
"\n",
" // Assign a message listener to the 'message' events\n",
" // from iframe's postMessage protocol.\n",
" // Filter the messages by iframe src so that the right message\n",
" // gets passed to the right widget\n",
" if(!('messageListeners' in window)){\n",
" window.messageListeners = {};\n",
" }\n",
"\n",
" window.messageListeners[graphId] = function(e) {\n",
" if(_graph_url.indexOf(e.origin)>-1) {\n",
" var frame = document.getElementById(graphId);\n",
"\n",
" if(frame === null){\n",
" // frame doesn't exist in the dom anymore, clean up it's old event listener\n",
" window.removeEventListener('message', window.messageListeners[graphId]);\n",
" clearInterval(window.pingers[graphId]);\n",
" } else if(frame.contentWindow === e.source) {\n",
" // TODO: Stop event propagation, so each frame doesn't listen and filter\n",
" var frameContentWindow = $('#'+graphId)[0].contentWindow;\n",
" var message = e.data;\n",
"\n",
" if('pong' in message && message.pong) {\n",
" $('#loading-'+graphId).hide();\n",
" clearInterval(window.pingers[graphId]);\n",
" that.send({event: 'pong', graphId: graphId});\n",
" } else if (message.type==='hover' ||\n",
" message.type==='zoom' ||\n",
" message.type==='click' ||\n",
" message.type==='unhover') {\n",
"\n",
" // click and hover events contain all of the data in the traces,\n",
" // which can be a very large object and may take a ton of time\n",
" // to pass to the python backend. Strip out the data, and require\n",
" // the user to call get_figure if they need trace information\n",
" if(message.type !== 'zoom') {\n",
" for(var i in message.points) {\n",
" delete message.points[i].data;\n",
" delete message.points[i].fullData;\n",
" }\n",
" }\n",
" that.send({event: message.type, message: message, graphId: graphId});\n",
" }\n",
" }\n",
" }\n",
" };\n",
"\n",
" window.removeEventListener('message', window.messageListeners[graphId]);\n",
" window.addEventListener('message', window.messageListeners[graphId]);\n",
"\n",
" },\n",
"\n",
" update: function() {\n",
" // Listen for messages from the graph widget in python\n",
" var jmessage = this.model.get('_message');\n",
" var message = JSON.parse(jmessage);\n",
"\n",
" // check for duplicate messages\n",
" if(!('messageIds' in window)){\n",
" window.messageIds = {};\n",
" }\n",
"\n",
" if(!(message.uid in window.messageIds)){\n",
" // message hasn't been received yet, do stuff\n",
" window.messageIds[message.uid] = true;\n",
"\n",
" var plot = $('#'+message.graphId)[0].contentWindow;\n",
" plot.postMessage(message, window.plotlyDomains[message.graphId]);\n",
" }\n",
"\n",
" return GraphView.__super__.update.apply(this);\n",
" }\n",
" });\n",
"\n",
" // Register the GraphView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('GraphView', GraphView);\n",
"\n",
"});\n",
"\n",
"//@ sourceURL=graphWidget.js\n"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x107786950>"
]
}
],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"g = GraphWidget()\n",
"g"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 3
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Extend a single trace with new x, y data\n",
"g.plot([{'x': [], 'y': []}]) # initialize a new graph with a single trace\n",
"\n",
"g.extend_traces({'x': [[1, 2, 3]], 'y': [[20,30,40]]}, indices=[0])\n",
"\n",
"# append more data\n",
"time.sleep(0.5)\n",
"g.extend_traces({'x': [[4, 5, 6]], 'y': [[70,60,50]]}, indices=[0])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Extend multiple traces\n",
"g.plot([{'x': [], 'y': []}, {'x': [], 'y': []}]) # initialize a new graph with multiple traces\n",
"g.extend_traces({\n",
" 'x': [[1,2,3], [2,3,4]],\n",
" 'y': [[20,30,40], [2,3,4]]\n",
"}, indices=[0, 1])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 5
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Live-update a graph\n",
"g.plot([{'x': [], 'y': []}])\n",
"for i in range(10):\n",
" g.extend_traces({'x': [[i]], 'y': [[i]]}, indices=[0])\n",
" time.sleep(0.5)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Update other attributes, like marker colors and sizes and text\n",
"g.plot([{'x': [], 'y': [], 'text': [], 'marker': {'size': [], 'color': []}}])\n",
"g.extend_traces({\n",
" 'x': [[1,2,3]],\n",
" 'y': [[10,20,30]],\n",
" 'text': [['A', 'B', 'C']],\n",
" 'marker.size': [[10,15,20]],\n",
" 'marker.color': [['blue', 'red', 'orange']]\n",
"}, indices=[0])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 7
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Only show the 10 most recent points\n",
"g.plot([{'x': [], 'y': []}])\n",
"for i in range(30):\n",
" g.extend_traces({\n",
" 'x': [[i]],\n",
" 'y': [[i]]\n",
" }, [0], max_points=10)\n",
" time.sleep(0.5)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 8
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Show 10 most recent points for the first trace, 20 for the second trace\n",
"g.plot([{'x': [], 'y': []}, {'x': [], 'y': []}])\n",
"for i in range(30):\n",
" g.extend_traces({'x': [[i], [i]], 'y': [[i], [i+1]]}, [0, 1], max_points={'x': [10, 20], 'y': [10, 20]})\n",
" time.sleep(0.5)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 9
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Example from the gif\n",
"\n",
"import numpy as np\n",
"\n",
"g.plot(\n",
" {\n",
" 'data': [\n",
" {'x': [], 'y': [], 'mode': 'markers'}\n",
" ],\n",
" 'layout': {\n",
" 'xaxis': {\n",
" 'autorange': False,\n",
" 'range': [0, 8*np.pi]\n",
" },\n",
" 'yaxis': {\n",
" 'autorange': False,\n",
" 'range': [-2, 2]\n",
" }\n",
" }\n",
" }) # initialize a new graph with a single trace\n",
"\n",
"x = np.linspace(0, 8*np.pi, 200)\n",
"\n",
"for xi in x:\n",
" g.extend_traces({'x': [[xi]], 'y': [[np.sin(xi)+np.random.randn(1)[0]/10.]]}, [0])"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# CSS styling within IPython notebook - feel free to re-use\n",
"from IPython.core.display import HTML\n",
"import urllib2\n",
"\n",
"HTML(urllib2.urlopen('http://bit.ly/1Bf5Hft').read())"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<style>\n",
"\n",
"html {\n",
" font-size: 62.5% !important; }\n",
"body {\n",
" font-size: 1.5em !important; /* currently ems cause chrome bug misinterpreting rems on body element */\n",
" line-height: 1.6 !important;\n",
" font-weight: 400 !important;\n",
" font-family: \"Raleway\", \"HelveticaNeue\", \"Helvetica Neue\", Helvetica, Arial, sans-serif !important;\n",
" color: #222 !important; }\n",
"\n",
"div{ border-radius: 0px !important; }\n",
"div.CodeMirror-sizer{ background: rgb(244, 244, 248) !important; }\n",
"div.input_area{ background: rgb(244, 244, 248) !important; }\n",
"\n",
"div.out_prompt_overlay:hover{ background: rgb(244, 244, 248) !important; }\n",
"div.input_prompt:hover{ background: rgb(244, 244, 248) !important; }\n",
"\n",
"h1, h2, h3, h4, h5, h6 {\n",
" color: #333 !important;\n",
" margin-top: 0 !important;\n",
" margin-bottom: 2rem !important;\n",
" font-weight: 300 !important; }\n",
"h1 { font-size: 4.0rem !important; line-height: 1.2 !important; letter-spacing: -.1rem !important;}\n",
"h2 { font-size: 3.6rem !important; line-height: 1.25 !important; letter-spacing: -.1rem !important; }\n",
"h3 { font-size: 3.0rem !important; line-height: 1.3 !important; letter-spacing: -.1rem !important; }\n",
"h4 { font-size: 2.4rem !important; line-height: 1.35 !important; letter-spacing: -.08rem !important; }\n",
"h5 { font-size: 1.8rem !important; line-height: 1.5 !important; letter-spacing: -.05rem !important; }\n",
"h6 { font-size: 1.5rem !important; line-height: 1.6 !important; letter-spacing: 0 !important; }\n",
"\n",
"@media (min-width: 550px) {\n",
" h1 { font-size: 5.0rem !important; }\n",
" h2 { font-size: 4.2rem !important; }\n",
" h3 { font-size: 3.6rem !important; }\n",
" h4 { font-size: 3.0rem !important; }\n",
" h5 { font-size: 2.4rem !important; }\n",
" h6 { font-size: 1.5rem !important; }\n",
"}\n",
"\n",
"p {\n",
" margin-top: 0 !important; }\n",
" \n",
"a {\n",
" color: #1EAEDB !important; }\n",
"a:hover {\n",
" color: #0FA0CE !important; }\n",
" \n",
"code {\n",
" padding: .2rem .5rem !important;\n",
" margin: 0 .2rem !important;\n",
" font-size: 90% !important;\n",
" white-space: nowrap !important;\n",
" background: #F1F1F1 !important;\n",
" border: 1px solid #E1E1E1 !important;\n",
" border-radius: 4px !important; }\n",
"pre > code {\n",
" display: block !important;\n",
" padding: 1rem 1.5rem !important;\n",
" white-space: pre !important; }\n",
" \n",
"button{ border-radius: 0px !important; }\n",
".navbar-inner{ background-image: none !important; }\n",
"select, textarea{ border-radius: 0px !important; }\n",
"\n",
"</style>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 11,
"text": [
"<IPython.core.display.HTML at 0x1077981d0>"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment