Created
April 18, 2018 13:22
-
-
Save fbcotter/937ccd5289b2d4e15f4ec91ccddd488c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "# Softmax Analysis\n", | |
| "Comments on q2c of the past paper" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 1, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-17T18:50:35.207108Z", | |
| "start_time": "2018-04-17T18:50:34.962488Z" | |
| } | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<style>.container { width:80% !important; }</style>" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.HTML object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "%matplotlib notebook\n", | |
| "import matplotlib.pyplot as plt\n", | |
| "import numpy as np\n", | |
| "import sklearn\n", | |
| "from IPython.core.display import display, HTML\n", | |
| "display(HTML(\"<style>.container { width:80% !important; }</style>\"))\n", | |
| "np.warnings.filterwarnings('ignore')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 2, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-17T18:50:35.275289Z", | |
| "start_time": "2018-04-17T18:50:35.209706Z" | |
| }, | |
| "scrolled": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/javascript": [ | |
| "/* Put everything inside the global mpl namespace */\n", | |
| "window.mpl = {};\n", | |
| "\n", | |
| "mpl.get_websocket_type = function() {\n", | |
| " if (typeof(WebSocket) !== 'undefined') {\n", | |
| " return WebSocket;\n", | |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
| " return MozWebSocket;\n", | |
| " } else {\n", | |
| " alert('Your browser does not have WebSocket support.' +\n", | |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
| " 'Firefox 4 and 5 are also supported but you ' +\n", | |
| " 'have to enable WebSockets in about:config.');\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
| " this.id = figure_id;\n", | |
| "\n", | |
| " this.ws = websocket;\n", | |
| "\n", | |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", | |
| "\n", | |
| " if (!this.supports_binary) {\n", | |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", | |
| " if (warnings) {\n", | |
| " warnings.style.display = 'block';\n", | |
| " warnings.textContent = (\n", | |
| " \"This browser does not support binary websocket messages. \" +\n", | |
| " \"Performance may be slow.\");\n", | |
| " }\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj = new Image();\n", | |
| "\n", | |
| " this.context = undefined;\n", | |
| " this.message = undefined;\n", | |
| " this.canvas = undefined;\n", | |
| " this.rubberband_canvas = undefined;\n", | |
| " this.rubberband_context = undefined;\n", | |
| " this.format_dropdown = undefined;\n", | |
| "\n", | |
| " this.image_mode = 'full';\n", | |
| "\n", | |
| " this.root = $('<div/>');\n", | |
| " this._root_extra_style(this.root)\n", | |
| " this.root.attr('style', 'display: inline-block');\n", | |
| "\n", | |
| " $(parent_element).append(this.root);\n", | |
| "\n", | |
| " this._init_header(this);\n", | |
| " this._init_canvas(this);\n", | |
| " this._init_toolbar(this);\n", | |
| "\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " this.waiting = false;\n", | |
| "\n", | |
| " this.ws.onopen = function () {\n", | |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
| " fig.send_message(\"send_image_mode\", {});\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj.onload = function() {\n", | |
| " if (fig.image_mode == 'full') {\n", | |
| " // Full images could contain transparency (where diff images\n", | |
| " // almost always do), so we need to clear the canvas so that\n", | |
| " // there is no ghosting.\n", | |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| " }\n", | |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
| " };\n", | |
| "\n", | |
| " this.imageObj.onunload = function() {\n", | |
| " this.ws.close();\n", | |
| " }\n", | |
| "\n", | |
| " this.ws.onmessage = this._make_on_message_function(this);\n", | |
| "\n", | |
| " this.ondownload = ondownload;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_header = function() {\n", | |
| " var titlebar = $(\n", | |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
| " 'ui-helper-clearfix\"/>');\n", | |
| " var titletext = $(\n", | |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
| " 'text-align: center; padding: 3px;\"/>');\n", | |
| " titlebar.append(titletext)\n", | |
| " this.root.append(titlebar);\n", | |
| " this.header = titletext[0];\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_canvas = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var canvas_div = $('<div/>');\n", | |
| "\n", | |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
| "\n", | |
| " function canvas_keyboard_event(event) {\n", | |
| " return fig.key_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
| " this.canvas_div = canvas_div\n", | |
| " this._canvas_extra_style(canvas_div)\n", | |
| " this.root.append(canvas_div);\n", | |
| "\n", | |
| " var canvas = $('<canvas/>');\n", | |
| " canvas.addClass('mpl-canvas');\n", | |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
| "\n", | |
| " this.canvas = canvas[0];\n", | |
| " this.context = canvas[0].getContext(\"2d\");\n", | |
| "\n", | |
| " var rubberband = $('<canvas/>');\n", | |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
| "\n", | |
| " var pass_mouse_events = true;\n", | |
| "\n", | |
| " canvas_div.resizable({\n", | |
| " start: function(event, ui) {\n", | |
| " pass_mouse_events = false;\n", | |
| " },\n", | |
| " resize: function(event, ui) {\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " stop: function(event, ui) {\n", | |
| " pass_mouse_events = true;\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " });\n", | |
| "\n", | |
| " function mouse_event_fn(event) {\n", | |
| " if (pass_mouse_events)\n", | |
| " return fig.mouse_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", | |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", | |
| " // Throttle sequential mouse events to 1 every 20ms.\n", | |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
| "\n", | |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
| "\n", | |
| " canvas_div.on(\"wheel\", function (event) {\n", | |
| " event = event.originalEvent;\n", | |
| " event['data'] = 'scroll'\n", | |
| " if (event.deltaY < 0) {\n", | |
| " event.step = 1;\n", | |
| " } else {\n", | |
| " event.step = -1;\n", | |
| " }\n", | |
| " mouse_event_fn(event);\n", | |
| " });\n", | |
| "\n", | |
| " canvas_div.append(canvas);\n", | |
| " canvas_div.append(rubberband);\n", | |
| "\n", | |
| " this.rubberband = rubberband;\n", | |
| " this.rubberband_canvas = rubberband[0];\n", | |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", | |
| "\n", | |
| " this._resize_canvas = function(width, height) {\n", | |
| " // Keep the size of the canvas, canvas container, and rubber band\n", | |
| " // canvas in synch.\n", | |
| " canvas_div.css('width', width)\n", | |
| " canvas_div.css('height', height)\n", | |
| "\n", | |
| " canvas.attr('width', width);\n", | |
| " canvas.attr('height', height);\n", | |
| "\n", | |
| " rubberband.attr('width', width);\n", | |
| " rubberband.attr('height', height);\n", | |
| " }\n", | |
| "\n", | |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
| " // upon first draw.\n", | |
| " this._resize_canvas(600, 600);\n", | |
| "\n", | |
| " // Disable right mouse context menu.\n", | |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
| " return false;\n", | |
| " });\n", | |
| "\n", | |
| " function set_focus () {\n", | |
| " canvas.focus();\n", | |
| " canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " window.setTimeout(set_focus, 100);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) {\n", | |
| " // put a spacer in here.\n", | |
| " continue;\n", | |
| " }\n", | |
| " var button = $('<button/>');\n", | |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
| " 'ui-button-icon-only');\n", | |
| " button.attr('role', 'button');\n", | |
| " button.attr('aria-disabled', 'false');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| "\n", | |
| " var icon_img = $('<span/>');\n", | |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
| " icon_img.addClass(image);\n", | |
| " icon_img.addClass('ui-corner-all');\n", | |
| "\n", | |
| " var tooltip_span = $('<span/>');\n", | |
| " tooltip_span.addClass('ui-button-text');\n", | |
| " tooltip_span.html(tooltip);\n", | |
| "\n", | |
| " button.append(icon_img);\n", | |
| " button.append(tooltip_span);\n", | |
| "\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " var fmt_picker_span = $('<span/>');\n", | |
| "\n", | |
| " var fmt_picker = $('<select/>');\n", | |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
| " fmt_picker_span.append(fmt_picker);\n", | |
| " nav_element.append(fmt_picker_span);\n", | |
| " this.format_dropdown = fmt_picker[0];\n", | |
| "\n", | |
| " for (var ind in mpl.extensions) {\n", | |
| " var fmt = mpl.extensions[ind];\n", | |
| " var option = $(\n", | |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
| " fmt_picker.append(option)\n", | |
| " }\n", | |
| "\n", | |
| " // Add hover states to the ui-buttons\n", | |
| " $( \".ui-button\" ).hover(\n", | |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", | |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
| " );\n", | |
| "\n", | |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
| " // which will in turn request a refresh of the image.\n", | |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", | |
| " properties['type'] = type;\n", | |
| " properties['figure_id'] = this.id;\n", | |
| " this.ws.send(JSON.stringify(properties));\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_draw_message = function() {\n", | |
| " if (!this.waiting) {\n", | |
| " this.waiting = true;\n", | |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " var format_dropdown = fig.format_dropdown;\n", | |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
| " fig.ondownload(fig, format);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
| " var size = msg['size'];\n", | |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
| " fig._resize_canvas(size[0], size[1]);\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
| " var x0 = msg['x0'];\n", | |
| " var y0 = fig.canvas.height - msg['y0'];\n", | |
| " var x1 = msg['x1'];\n", | |
| " var y1 = fig.canvas.height - msg['y1'];\n", | |
| " x0 = Math.floor(x0) + 0.5;\n", | |
| " y0 = Math.floor(y0) + 0.5;\n", | |
| " x1 = Math.floor(x1) + 0.5;\n", | |
| " y1 = Math.floor(y1) + 0.5;\n", | |
| " var min_x = Math.min(x0, x1);\n", | |
| " var min_y = Math.min(y0, y1);\n", | |
| " var width = Math.abs(x1 - x0);\n", | |
| " var height = Math.abs(y1 - y0);\n", | |
| "\n", | |
| " fig.rubberband_context.clearRect(\n", | |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| "\n", | |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
| " // Updates the figure title.\n", | |
| " fig.header.textContent = msg['label'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
| " var cursor = msg['cursor'];\n", | |
| " switch(cursor)\n", | |
| " {\n", | |
| " case 0:\n", | |
| " cursor = 'pointer';\n", | |
| " break;\n", | |
| " case 1:\n", | |
| " cursor = 'default';\n", | |
| " break;\n", | |
| " case 2:\n", | |
| " cursor = 'crosshair';\n", | |
| " break;\n", | |
| " case 3:\n", | |
| " cursor = 'move';\n", | |
| " break;\n", | |
| " }\n", | |
| " fig.rubberband_canvas.style.cursor = cursor;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
| " fig.message.textContent = msg['message'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
| " // Request the server to send over a new figure.\n", | |
| " fig.send_draw_message();\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
| " fig.image_mode = msg['mode'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Called whenever the canvas gets updated.\n", | |
| " this.send_message(\"ack\", {});\n", | |
| "}\n", | |
| "\n", | |
| "// A function to construct a web socket function for onmessage handling.\n", | |
| "// Called in the figure constructor.\n", | |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
| " return function socket_on_message(evt) {\n", | |
| " if (evt.data instanceof Blob) {\n", | |
| " /* FIXME: We get \"Resource interpreted as Image but\n", | |
| " * transferred with MIME type text/plain:\" errors on\n", | |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", | |
| " * to be part of the websocket stream */\n", | |
| " evt.data.type = \"image/png\";\n", | |
| "\n", | |
| " /* Free the memory for the previous frames */\n", | |
| " if (fig.imageObj.src) {\n", | |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", | |
| " fig.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
| " evt.data);\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
| " fig.imageObj.src = evt.data;\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var msg = JSON.parse(evt.data);\n", | |
| " var msg_type = msg['type'];\n", | |
| "\n", | |
| " // Call the \"handle_{type}\" callback, which takes\n", | |
| " // the figure and JSON message as its only arguments.\n", | |
| " try {\n", | |
| " var callback = fig[\"handle_\" + msg_type];\n", | |
| " } catch (e) {\n", | |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " if (callback) {\n", | |
| " try {\n", | |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
| " callback(fig, msg);\n", | |
| " } catch (e) {\n", | |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
| " }\n", | |
| " }\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
| "mpl.findpos = function(e) {\n", | |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
| " var targ;\n", | |
| " if (!e)\n", | |
| " e = window.event;\n", | |
| " if (e.target)\n", | |
| " targ = e.target;\n", | |
| " else if (e.srcElement)\n", | |
| " targ = e.srcElement;\n", | |
| " if (targ.nodeType == 3) // defeat Safari bug\n", | |
| " targ = targ.parentNode;\n", | |
| "\n", | |
| " // jQuery normalizes the pageX and pageY\n", | |
| " // pageX,Y are the mouse positions relative to the document\n", | |
| " // offset() returns the position of the element relative to the document\n", | |
| " var x = e.pageX - $(targ).offset().left;\n", | |
| " var y = e.pageY - $(targ).offset().top;\n", | |
| "\n", | |
| " return {\"x\": x, \"y\": y};\n", | |
| "};\n", | |
| "\n", | |
| "/*\n", | |
| " * return a copy of an object with only non-object keys\n", | |
| " * we need this to avoid circular references\n", | |
| " * http://stackoverflow.com/a/24161582/3208463\n", | |
| " */\n", | |
| "function simpleKeys (original) {\n", | |
| " return Object.keys(original).reduce(function (obj, key) {\n", | |
| " if (typeof original[key] !== 'object')\n", | |
| " obj[key] = original[key]\n", | |
| " return obj;\n", | |
| " }, {});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
| " var canvas_pos = mpl.findpos(event)\n", | |
| "\n", | |
| " if (name === 'button_press')\n", | |
| " {\n", | |
| " this.canvas.focus();\n", | |
| " this.canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " var x = canvas_pos.x;\n", | |
| " var y = canvas_pos.y;\n", | |
| "\n", | |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", | |
| " step: event.step,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| "\n", | |
| " /* This prevents the web browser from automatically changing to\n", | |
| " * the text insertion cursor when the button is pressed. We want\n", | |
| " * to control all of the cursor setting manually through the\n", | |
| " * 'cursor' event from matplotlib */\n", | |
| " event.preventDefault();\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " // Handle any extra behaviour associated with a key event\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.key_event = function(event, name) {\n", | |
| "\n", | |
| " // Prevent repeat events\n", | |
| " if (name == 'key_press')\n", | |
| " {\n", | |
| " if (event.which === this._key)\n", | |
| " return;\n", | |
| " else\n", | |
| " this._key = event.which;\n", | |
| " }\n", | |
| " if (name == 'key_release')\n", | |
| " this._key = null;\n", | |
| "\n", | |
| " var value = '';\n", | |
| " if (event.ctrlKey && event.which != 17)\n", | |
| " value += \"ctrl+\";\n", | |
| " if (event.altKey && event.which != 18)\n", | |
| " value += \"alt+\";\n", | |
| " if (event.shiftKey && event.which != 16)\n", | |
| " value += \"shift+\";\n", | |
| "\n", | |
| " value += 'k';\n", | |
| " value += event.which.toString();\n", | |
| "\n", | |
| " this._key_event_extra(event, name);\n", | |
| "\n", | |
| " this.send_message(name, {key: value,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
| " if (name == 'download') {\n", | |
| " this.handle_save(this, null);\n", | |
| " } else {\n", | |
| " this.send_message(\"toolbar_button\", {name: name});\n", | |
| " }\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
| " this.message.textContent = tooltip;\n", | |
| "};\n", | |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
| "\n", | |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
| "\n", | |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
| " // object with the appropriate methods. Currently this is a non binary\n", | |
| " // socket, so there is still some room for performance tuning.\n", | |
| " var ws = {};\n", | |
| "\n", | |
| " ws.close = function() {\n", | |
| " comm.close()\n", | |
| " };\n", | |
| " ws.send = function(m) {\n", | |
| " //console.log('sending', m);\n", | |
| " comm.send(m);\n", | |
| " };\n", | |
| " // Register the callback with on_msg.\n", | |
| " comm.on_msg(function(msg) {\n", | |
| " //console.log('receiving', msg['content']['data'], msg);\n", | |
| " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
| " ws.onmessage(msg['content']['data'])\n", | |
| " });\n", | |
| " return ws;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", | |
| " // This is the function which gets called when the mpl process\n", | |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
| "\n", | |
| " var id = msg.content.data.id;\n", | |
| " // Get hold of the div created by the display call when the Comm\n", | |
| " // socket was opened in Python.\n", | |
| " var element = $(\"#\" + id);\n", | |
| " var ws_proxy = comm_websocket_adapter(comm)\n", | |
| "\n", | |
| " function ondownload(figure, format) {\n", | |
| " window.open(figure.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " var fig = new mpl.figure(id, ws_proxy,\n", | |
| " ondownload,\n", | |
| " element.get(0));\n", | |
| "\n", | |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
| " // web socket which is closed, not our websocket->open comm proxy.\n", | |
| " ws_proxy.onopen();\n", | |
| "\n", | |
| " fig.parent_element = element.get(0);\n", | |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
| " if (!fig.cell_info) {\n", | |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var output_index = fig.cell_info[2]\n", | |
| " var cell = fig.cell_info[0];\n", | |
| "\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
| " fig.root.unbind('remove')\n", | |
| "\n", | |
| " // Update the output cell to use the data from the current canvas.\n", | |
| " fig.push_to_output();\n", | |
| " var dataURL = fig.canvas.toDataURL();\n", | |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
| " // the notebook keyboard shortcuts fail.\n", | |
| " IPython.keyboard_manager.enable()\n", | |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
| " fig.close_ws(fig, msg);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
| " fig.send_message('closing', msg);\n", | |
| " // fig.ws.close()\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
| " // Turn the data on the canvas into data in the output cell.\n", | |
| " var dataURL = this.canvas.toDataURL();\n", | |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Tell IPython that the notebook contents must change.\n", | |
| " IPython.notebook.set_dirty(true);\n", | |
| " this.send_message(\"ack\", {});\n", | |
| " var fig = this;\n", | |
| " // Wait a second, then push the new image to the DOM so\n", | |
| " // that it is saved nicely (might be nice to debounce this).\n", | |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items){\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) { continue; };\n", | |
| "\n", | |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " // Add the status bar.\n", | |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "\n", | |
| " // Add the close button to the window.\n", | |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
| " buttongrp.append(button);\n", | |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
| " titlebar.prepend(buttongrp);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(el){\n", | |
| " var fig = this\n", | |
| " el.on(\"remove\", function(){\n", | |
| "\tfig.close_ws(fig, {});\n", | |
| " });\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
| " // this is important to make the div 'focusable\n", | |
| " el.attr('tabindex', 0)\n", | |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
| " // off when our div gets focus\n", | |
| "\n", | |
| " // location in version 3\n", | |
| " if (IPython.notebook.keyboard_manager) {\n", | |
| " IPython.notebook.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| " else {\n", | |
| " // location in version 2\n", | |
| " IPython.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " var manager = IPython.notebook.keyboard_manager;\n", | |
| " if (!manager)\n", | |
| " manager = IPython.keyboard_manager;\n", | |
| "\n", | |
| " // Check for shift+enter\n", | |
| " if (event.shiftKey && event.which == 13) {\n", | |
| " this.canvas_div.blur();\n", | |
| " // select the cell after this one\n", | |
| " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
| " IPython.notebook.select(index + 1);\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " fig.ondownload(fig, null);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.find_output_cell = function(html_output) {\n", | |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
| " // IPython event is triggered only after the cells have been serialised, which for\n", | |
| " // our purposes (turning an active figure into a static one), is too late.\n", | |
| " var cells = IPython.notebook.get_cells();\n", | |
| " var ncells = cells.length;\n", | |
| " for (var i=0; i<ncells; i++) {\n", | |
| " var cell = cells[i];\n", | |
| " if (cell.cell_type === 'code'){\n", | |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
| " var data = cell.output_area.outputs[j];\n", | |
| " if (data.data) {\n", | |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", | |
| " data = data.data;\n", | |
| " }\n", | |
| " if (data['text/html'] == html_output) {\n", | |
| " return [cell, data, j];\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "// Register the function which deals with the matplotlib target/channel.\n", | |
| "// The kernel may be null if the page has been refreshed.\n", | |
| "if (IPython.notebook.kernel != null) {\n", | |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
| "}\n" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.Javascript object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4Xu3dCZjdZX33/08SsgpBoWCVRUUporhgLaJ/6wJiXB7Xav0LKii7uFSwFW3V1gVUNhciAQXEaq27VaugFgUtWovig1ZxoQoBFFtRQJas81z35EycJJNMku94G2Ze57q8hOTcZ3md7znznt9ymBYXAgQIECBAgACBKSUwbUo9W0+WAAECBAgQIEAgAtAQECBAgAABAgSmmIAAnGIvuKdLgAABAgQIEBCAZoAAAQIECBAgMMUEBOAUe8E9XQIECBAgQICAADQDBAgQIECAAIEpJiAAp9gL7ukSIECAAAECBASgGSBAgAABAgQITDEBATjFXnBPlwABAgQIECAgAM0AAQIECBAgQGCKCQjAKfaCe7oECBAgQIAAAQFoBggQIECAAAECU0xAAE6xF9zTJUCAAAECBAgIQDNAgAABAgQIEJhiAgJwir3gni4BAgQIECBAQACaAQIECBAgQIDAFBMQgFPsBfd0CRAgQIAAAQIC0AwQIECAAAECBKaYgACcYi+4p0uAAAECBAgQEIBmgAABAgQIECAwxQQE4BR7wT1dAgQIECBAgIAANAMECBAgQIAAgSkmIACn2Avu6RIgQIAAAQIEBKAZIECAAAECBAhMMQEBOMVecE+XAAECBAgQICAAzQABAgQIECBAYIoJCMAp9oJ7ugQIECBAgAABAWgGCBAgQIAAAQJTTEAATrEX3NMlQIAAAQIECAhAM0CAAAECBAgQmGICAnCKveCeLgECBAgQIEBAAJoBAgQIECBAgMAUExCAU+wF93QJECBAgAABAgLQDBAgQIAAAQIEppiAAJxiL7inS4AAAQIECBAQgGaAAAECBAgQIDDFBATgFHvBPV0CBAgQIECAgAA0AwQIECBAgACBKSYgAKfYC+7pEiBAgAABAgQEoBkgQIAAAQIECEwxAQE4xV5wT5cAAQIECBAgIADNAAECBAgQIEBgigkIwCn2gnu6BAgQIECAAAEBaAYIECBAgAABAlNMQABOsRfc0yVAgAABAgQICEAzQIAAAQIECBCYYgICsPaCN7+7J7m5djNWEyBAgAABAp0FtklyXZKhzve7RdydAKy9DDsluaZ2E1YTIECAAAECfyCBnZNc+we67z/o3QrAGv/8JDcuXrw48+e3f3Sp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\">" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.HTML object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "x = np.concatenate([np.random.uniform(low=10,high=20, size=15), \n", | |
| " np.random.uniform(20,37, 20), \n", | |
| " [38, 40], \n", | |
| " np.random.uniform(40,80, 30), \n", | |
| " np.random.uniform(80, 100, 15)])\n", | |
| "y = np.concatenate([np.ones(15), \n", | |
| " 2*np.ones(20), \n", | |
| " [1,1],\n", | |
| " 3*np.ones(30), \n", | |
| " np.ones(15)]).astype('int')\n", | |
| "y_one_hot = np.zeros((y.shape[0], 3),dtype=bool)\n", | |
| "y_one_hot[np.arange(y.shape[0]), y-1] = True\n", | |
| "\n", | |
| "plt.figure()\n", | |
| "colors = ('b', 'g', 'r')\n", | |
| "c = [colors[a-1] for a in y]\n", | |
| "plt.scatter(x,y, c=c)\n", | |
| "plt.xlabel('x')\n", | |
| "plt.ylabel('z')\n", | |
| "plt.yticks([1,2,3]);" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Although the data appears to be 2D by the way they've drawn it, really it is only 1 dimensional (there's only $x$, not $x_1, x_2, \\ldots$), therefore decision boundaries must be vertical! Clearly this isn't going to work" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Let us use a softmax classifier. I.e. \n", | |
| "\n", | |
| "$$p(y_n = k | \\theta, x_n) = \\pi_k = \\frac{\\exp(\\mathbf{w}_k^T \\mathbf{x}_n)}{ \\sum_l \\exp(\\mathbf{w}_l^T \\mathbf{x}_n)}$$\n", | |
| "\n", | |
| "Where, $\\mathbf{x}_n = [x_n, 1]^T$ and $\\theta = \\{ \\mathbf{w}_1, \\mathbf{w}_2, \\mathbf{w}_3 \\}$. Also recall that this is classification, so\n", | |
| "\n", | |
| "$$p(y_n | \\theta, x_n) = \\prod_k \\pi_k^{I(y_n = k)}$$\n", | |
| "$$p(\\mathbf{y} | \\theta, \\mathbf{x}) = \\prod_n \\prod_k \\pi_k^{I(y_n = k)}$$\n", | |
| "$$\\log p(\\mathbf{y} | \\theta, \\mathbf{x}) = \\sum_n \\sum_k \\pi_k^{I(y_n = k)}$$\n", | |
| "\n", | |
| "Anyway, this can all be arbitrated away if we use a pre-built solver, like scikit-learn" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 24, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-18T10:12:22.137615Z", | |
| "start_time": "2018-04-18T10:12:22.115548Z" | |
| } | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Some functions for plotting decision boundaries\n", | |
| "# The idea is we create a meshgrid of the entire space and then feed these \n", | |
| "# coordinates to a classifier. \n", | |
| "# This way we can classify the entire plane and work out where the\n", | |
| "# crossover poitns are\n", | |
| "\n", | |
| "def plot_boundaries_1d(ax, clf, xx, yy, **params):\n", | |
| " \"\"\"Plot the decision boundaries for a classifier.\n", | |
| "\n", | |
| " Parameters\n", | |
| " ----------\n", | |
| " ax: matplotlib axes object\n", | |
| " clf: a classifier\n", | |
| " xx: meshgrid ndarray\n", | |
| " params: dictionary of params to pass to contourf, optional\n", | |
| " \"\"\"\n", | |
| " Z = clf.predict(xx.reshape(-1,1))\n", | |
| " Z = Z.reshape(xx.shape)\n", | |
| " out = ax.contourf(xx, yy, Z, **params)\n", | |
| " return out\n", | |
| "\n", | |
| "def plot_boundaries_2d(ax, clf, xx, yy, **params):\n", | |
| " \"\"\"Plot the decision boundaries for a classifier.\n", | |
| "\n", | |
| " Parameters\n", | |
| " ----------\n", | |
| " ax: matplotlib axes object\n", | |
| " clf: a classifier\n", | |
| " xx: meshgrid ndarray\n", | |
| " params: dictionary of params to pass to contourf, optional\n", | |
| " \"\"\"\n", | |
| " Z = clf.predict(np.stack((xx.ravel(),yy.ravel()), axis=-1))\n", | |
| " Z = Z.reshape(xx.shape)\n", | |
| " out = ax.contourf(xx, yy, Z, **params)\n", | |
| " return out\n", | |
| "\n", | |
| "def plot_boundaries_2da(ax, clf, xx, yy, **params):\n", | |
| " \"\"\"\n", | |
| " \"\"\"\n", | |
| " X = np.stack((xx.ravel(), xx.ravel()**2), axis=-1)\n", | |
| " Z = clf.predict(X)\n", | |
| " Z = Z.reshape(xx.shape)\n", | |
| " out = ax.contourf(xx, yy, Z, **params)\n", | |
| " return out" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 10, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-18T10:08:17.653765Z", | |
| "start_time": "2018-04-18T10:08:17.523211Z" | |
| }, | |
| "scrolled": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/javascript": [ | |
| "/* Put everything inside the global mpl namespace */\n", | |
| "window.mpl = {};\n", | |
| "\n", | |
| "mpl.get_websocket_type = function() {\n", | |
| " if (typeof(WebSocket) !== 'undefined') {\n", | |
| " return WebSocket;\n", | |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
| " return MozWebSocket;\n", | |
| " } else {\n", | |
| " alert('Your browser does not have WebSocket support.' +\n", | |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
| " 'Firefox 4 and 5 are also supported but you ' +\n", | |
| " 'have to enable WebSockets in about:config.');\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
| " this.id = figure_id;\n", | |
| "\n", | |
| " this.ws = websocket;\n", | |
| "\n", | |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", | |
| "\n", | |
| " if (!this.supports_binary) {\n", | |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", | |
| " if (warnings) {\n", | |
| " warnings.style.display = 'block';\n", | |
| " warnings.textContent = (\n", | |
| " \"This browser does not support binary websocket messages. \" +\n", | |
| " \"Performance may be slow.\");\n", | |
| " }\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj = new Image();\n", | |
| "\n", | |
| " this.context = undefined;\n", | |
| " this.message = undefined;\n", | |
| " this.canvas = undefined;\n", | |
| " this.rubberband_canvas = undefined;\n", | |
| " this.rubberband_context = undefined;\n", | |
| " this.format_dropdown = undefined;\n", | |
| "\n", | |
| " this.image_mode = 'full';\n", | |
| "\n", | |
| " this.root = $('<div/>');\n", | |
| " this._root_extra_style(this.root)\n", | |
| " this.root.attr('style', 'display: inline-block');\n", | |
| "\n", | |
| " $(parent_element).append(this.root);\n", | |
| "\n", | |
| " this._init_header(this);\n", | |
| " this._init_canvas(this);\n", | |
| " this._init_toolbar(this);\n", | |
| "\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " this.waiting = false;\n", | |
| "\n", | |
| " this.ws.onopen = function () {\n", | |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
| " fig.send_message(\"send_image_mode\", {});\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj.onload = function() {\n", | |
| " if (fig.image_mode == 'full') {\n", | |
| " // Full images could contain transparency (where diff images\n", | |
| " // almost always do), so we need to clear the canvas so that\n", | |
| " // there is no ghosting.\n", | |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| " }\n", | |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
| " };\n", | |
| "\n", | |
| " this.imageObj.onunload = function() {\n", | |
| " this.ws.close();\n", | |
| " }\n", | |
| "\n", | |
| " this.ws.onmessage = this._make_on_message_function(this);\n", | |
| "\n", | |
| " this.ondownload = ondownload;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_header = function() {\n", | |
| " var titlebar = $(\n", | |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
| " 'ui-helper-clearfix\"/>');\n", | |
| " var titletext = $(\n", | |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
| " 'text-align: center; padding: 3px;\"/>');\n", | |
| " titlebar.append(titletext)\n", | |
| " this.root.append(titlebar);\n", | |
| " this.header = titletext[0];\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_canvas = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var canvas_div = $('<div/>');\n", | |
| "\n", | |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
| "\n", | |
| " function canvas_keyboard_event(event) {\n", | |
| " return fig.key_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
| " this.canvas_div = canvas_div\n", | |
| " this._canvas_extra_style(canvas_div)\n", | |
| " this.root.append(canvas_div);\n", | |
| "\n", | |
| " var canvas = $('<canvas/>');\n", | |
| " canvas.addClass('mpl-canvas');\n", | |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
| "\n", | |
| " this.canvas = canvas[0];\n", | |
| " this.context = canvas[0].getContext(\"2d\");\n", | |
| "\n", | |
| " var rubberband = $('<canvas/>');\n", | |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
| "\n", | |
| " var pass_mouse_events = true;\n", | |
| "\n", | |
| " canvas_div.resizable({\n", | |
| " start: function(event, ui) {\n", | |
| " pass_mouse_events = false;\n", | |
| " },\n", | |
| " resize: function(event, ui) {\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " stop: function(event, ui) {\n", | |
| " pass_mouse_events = true;\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " });\n", | |
| "\n", | |
| " function mouse_event_fn(event) {\n", | |
| " if (pass_mouse_events)\n", | |
| " return fig.mouse_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", | |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", | |
| " // Throttle sequential mouse events to 1 every 20ms.\n", | |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
| "\n", | |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
| "\n", | |
| " canvas_div.on(\"wheel\", function (event) {\n", | |
| " event = event.originalEvent;\n", | |
| " event['data'] = 'scroll'\n", | |
| " if (event.deltaY < 0) {\n", | |
| " event.step = 1;\n", | |
| " } else {\n", | |
| " event.step = -1;\n", | |
| " }\n", | |
| " mouse_event_fn(event);\n", | |
| " });\n", | |
| "\n", | |
| " canvas_div.append(canvas);\n", | |
| " canvas_div.append(rubberband);\n", | |
| "\n", | |
| " this.rubberband = rubberband;\n", | |
| " this.rubberband_canvas = rubberband[0];\n", | |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", | |
| "\n", | |
| " this._resize_canvas = function(width, height) {\n", | |
| " // Keep the size of the canvas, canvas container, and rubber band\n", | |
| " // canvas in synch.\n", | |
| " canvas_div.css('width', width)\n", | |
| " canvas_div.css('height', height)\n", | |
| "\n", | |
| " canvas.attr('width', width);\n", | |
| " canvas.attr('height', height);\n", | |
| "\n", | |
| " rubberband.attr('width', width);\n", | |
| " rubberband.attr('height', height);\n", | |
| " }\n", | |
| "\n", | |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
| " // upon first draw.\n", | |
| " this._resize_canvas(600, 600);\n", | |
| "\n", | |
| " // Disable right mouse context menu.\n", | |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
| " return false;\n", | |
| " });\n", | |
| "\n", | |
| " function set_focus () {\n", | |
| " canvas.focus();\n", | |
| " canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " window.setTimeout(set_focus, 100);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) {\n", | |
| " // put a spacer in here.\n", | |
| " continue;\n", | |
| " }\n", | |
| " var button = $('<button/>');\n", | |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
| " 'ui-button-icon-only');\n", | |
| " button.attr('role', 'button');\n", | |
| " button.attr('aria-disabled', 'false');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| "\n", | |
| " var icon_img = $('<span/>');\n", | |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
| " icon_img.addClass(image);\n", | |
| " icon_img.addClass('ui-corner-all');\n", | |
| "\n", | |
| " var tooltip_span = $('<span/>');\n", | |
| " tooltip_span.addClass('ui-button-text');\n", | |
| " tooltip_span.html(tooltip);\n", | |
| "\n", | |
| " button.append(icon_img);\n", | |
| " button.append(tooltip_span);\n", | |
| "\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " var fmt_picker_span = $('<span/>');\n", | |
| "\n", | |
| " var fmt_picker = $('<select/>');\n", | |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
| " fmt_picker_span.append(fmt_picker);\n", | |
| " nav_element.append(fmt_picker_span);\n", | |
| " this.format_dropdown = fmt_picker[0];\n", | |
| "\n", | |
| " for (var ind in mpl.extensions) {\n", | |
| " var fmt = mpl.extensions[ind];\n", | |
| " var option = $(\n", | |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
| " fmt_picker.append(option)\n", | |
| " }\n", | |
| "\n", | |
| " // Add hover states to the ui-buttons\n", | |
| " $( \".ui-button\" ).hover(\n", | |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", | |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
| " );\n", | |
| "\n", | |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
| " // which will in turn request a refresh of the image.\n", | |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", | |
| " properties['type'] = type;\n", | |
| " properties['figure_id'] = this.id;\n", | |
| " this.ws.send(JSON.stringify(properties));\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_draw_message = function() {\n", | |
| " if (!this.waiting) {\n", | |
| " this.waiting = true;\n", | |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " var format_dropdown = fig.format_dropdown;\n", | |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
| " fig.ondownload(fig, format);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
| " var size = msg['size'];\n", | |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
| " fig._resize_canvas(size[0], size[1]);\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
| " var x0 = msg['x0'];\n", | |
| " var y0 = fig.canvas.height - msg['y0'];\n", | |
| " var x1 = msg['x1'];\n", | |
| " var y1 = fig.canvas.height - msg['y1'];\n", | |
| " x0 = Math.floor(x0) + 0.5;\n", | |
| " y0 = Math.floor(y0) + 0.5;\n", | |
| " x1 = Math.floor(x1) + 0.5;\n", | |
| " y1 = Math.floor(y1) + 0.5;\n", | |
| " var min_x = Math.min(x0, x1);\n", | |
| " var min_y = Math.min(y0, y1);\n", | |
| " var width = Math.abs(x1 - x0);\n", | |
| " var height = Math.abs(y1 - y0);\n", | |
| "\n", | |
| " fig.rubberband_context.clearRect(\n", | |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| "\n", | |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
| " // Updates the figure title.\n", | |
| " fig.header.textContent = msg['label'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
| " var cursor = msg['cursor'];\n", | |
| " switch(cursor)\n", | |
| " {\n", | |
| " case 0:\n", | |
| " cursor = 'pointer';\n", | |
| " break;\n", | |
| " case 1:\n", | |
| " cursor = 'default';\n", | |
| " break;\n", | |
| " case 2:\n", | |
| " cursor = 'crosshair';\n", | |
| " break;\n", | |
| " case 3:\n", | |
| " cursor = 'move';\n", | |
| " break;\n", | |
| " }\n", | |
| " fig.rubberband_canvas.style.cursor = cursor;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
| " fig.message.textContent = msg['message'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
| " // Request the server to send over a new figure.\n", | |
| " fig.send_draw_message();\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
| " fig.image_mode = msg['mode'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Called whenever the canvas gets updated.\n", | |
| " this.send_message(\"ack\", {});\n", | |
| "}\n", | |
| "\n", | |
| "// A function to construct a web socket function for onmessage handling.\n", | |
| "// Called in the figure constructor.\n", | |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
| " return function socket_on_message(evt) {\n", | |
| " if (evt.data instanceof Blob) {\n", | |
| " /* FIXME: We get \"Resource interpreted as Image but\n", | |
| " * transferred with MIME type text/plain:\" errors on\n", | |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", | |
| " * to be part of the websocket stream */\n", | |
| " evt.data.type = \"image/png\";\n", | |
| "\n", | |
| " /* Free the memory for the previous frames */\n", | |
| " if (fig.imageObj.src) {\n", | |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", | |
| " fig.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
| " evt.data);\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
| " fig.imageObj.src = evt.data;\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var msg = JSON.parse(evt.data);\n", | |
| " var msg_type = msg['type'];\n", | |
| "\n", | |
| " // Call the \"handle_{type}\" callback, which takes\n", | |
| " // the figure and JSON message as its only arguments.\n", | |
| " try {\n", | |
| " var callback = fig[\"handle_\" + msg_type];\n", | |
| " } catch (e) {\n", | |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " if (callback) {\n", | |
| " try {\n", | |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
| " callback(fig, msg);\n", | |
| " } catch (e) {\n", | |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
| " }\n", | |
| " }\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
| "mpl.findpos = function(e) {\n", | |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
| " var targ;\n", | |
| " if (!e)\n", | |
| " e = window.event;\n", | |
| " if (e.target)\n", | |
| " targ = e.target;\n", | |
| " else if (e.srcElement)\n", | |
| " targ = e.srcElement;\n", | |
| " if (targ.nodeType == 3) // defeat Safari bug\n", | |
| " targ = targ.parentNode;\n", | |
| "\n", | |
| " // jQuery normalizes the pageX and pageY\n", | |
| " // pageX,Y are the mouse positions relative to the document\n", | |
| " // offset() returns the position of the element relative to the document\n", | |
| " var x = e.pageX - $(targ).offset().left;\n", | |
| " var y = e.pageY - $(targ).offset().top;\n", | |
| "\n", | |
| " return {\"x\": x, \"y\": y};\n", | |
| "};\n", | |
| "\n", | |
| "/*\n", | |
| " * return a copy of an object with only non-object keys\n", | |
| " * we need this to avoid circular references\n", | |
| " * http://stackoverflow.com/a/24161582/3208463\n", | |
| " */\n", | |
| "function simpleKeys (original) {\n", | |
| " return Object.keys(original).reduce(function (obj, key) {\n", | |
| " if (typeof original[key] !== 'object')\n", | |
| " obj[key] = original[key]\n", | |
| " return obj;\n", | |
| " }, {});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
| " var canvas_pos = mpl.findpos(event)\n", | |
| "\n", | |
| " if (name === 'button_press')\n", | |
| " {\n", | |
| " this.canvas.focus();\n", | |
| " this.canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " var x = canvas_pos.x;\n", | |
| " var y = canvas_pos.y;\n", | |
| "\n", | |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", | |
| " step: event.step,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| "\n", | |
| " /* This prevents the web browser from automatically changing to\n", | |
| " * the text insertion cursor when the button is pressed. We want\n", | |
| " * to control all of the cursor setting manually through the\n", | |
| " * 'cursor' event from matplotlib */\n", | |
| " event.preventDefault();\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " // Handle any extra behaviour associated with a key event\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.key_event = function(event, name) {\n", | |
| "\n", | |
| " // Prevent repeat events\n", | |
| " if (name == 'key_press')\n", | |
| " {\n", | |
| " if (event.which === this._key)\n", | |
| " return;\n", | |
| " else\n", | |
| " this._key = event.which;\n", | |
| " }\n", | |
| " if (name == 'key_release')\n", | |
| " this._key = null;\n", | |
| "\n", | |
| " var value = '';\n", | |
| " if (event.ctrlKey && event.which != 17)\n", | |
| " value += \"ctrl+\";\n", | |
| " if (event.altKey && event.which != 18)\n", | |
| " value += \"alt+\";\n", | |
| " if (event.shiftKey && event.which != 16)\n", | |
| " value += \"shift+\";\n", | |
| "\n", | |
| " value += 'k';\n", | |
| " value += event.which.toString();\n", | |
| "\n", | |
| " this._key_event_extra(event, name);\n", | |
| "\n", | |
| " this.send_message(name, {key: value,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
| " if (name == 'download') {\n", | |
| " this.handle_save(this, null);\n", | |
| " } else {\n", | |
| " this.send_message(\"toolbar_button\", {name: name});\n", | |
| " }\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
| " this.message.textContent = tooltip;\n", | |
| "};\n", | |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
| "\n", | |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
| "\n", | |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
| " // object with the appropriate methods. Currently this is a non binary\n", | |
| " // socket, so there is still some room for performance tuning.\n", | |
| " var ws = {};\n", | |
| "\n", | |
| " ws.close = function() {\n", | |
| " comm.close()\n", | |
| " };\n", | |
| " ws.send = function(m) {\n", | |
| " //console.log('sending', m);\n", | |
| " comm.send(m);\n", | |
| " };\n", | |
| " // Register the callback with on_msg.\n", | |
| " comm.on_msg(function(msg) {\n", | |
| " //console.log('receiving', msg['content']['data'], msg);\n", | |
| " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
| " ws.onmessage(msg['content']['data'])\n", | |
| " });\n", | |
| " return ws;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", | |
| " // This is the function which gets called when the mpl process\n", | |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
| "\n", | |
| " var id = msg.content.data.id;\n", | |
| " // Get hold of the div created by the display call when the Comm\n", | |
| " // socket was opened in Python.\n", | |
| " var element = $(\"#\" + id);\n", | |
| " var ws_proxy = comm_websocket_adapter(comm)\n", | |
| "\n", | |
| " function ondownload(figure, format) {\n", | |
| " window.open(figure.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " var fig = new mpl.figure(id, ws_proxy,\n", | |
| " ondownload,\n", | |
| " element.get(0));\n", | |
| "\n", | |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
| " // web socket which is closed, not our websocket->open comm proxy.\n", | |
| " ws_proxy.onopen();\n", | |
| "\n", | |
| " fig.parent_element = element.get(0);\n", | |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
| " if (!fig.cell_info) {\n", | |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var output_index = fig.cell_info[2]\n", | |
| " var cell = fig.cell_info[0];\n", | |
| "\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
| " fig.root.unbind('remove')\n", | |
| "\n", | |
| " // Update the output cell to use the data from the current canvas.\n", | |
| " fig.push_to_output();\n", | |
| " var dataURL = fig.canvas.toDataURL();\n", | |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
| " // the notebook keyboard shortcuts fail.\n", | |
| " IPython.keyboard_manager.enable()\n", | |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
| " fig.close_ws(fig, msg);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
| " fig.send_message('closing', msg);\n", | |
| " // fig.ws.close()\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
| " // Turn the data on the canvas into data in the output cell.\n", | |
| " var dataURL = this.canvas.toDataURL();\n", | |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Tell IPython that the notebook contents must change.\n", | |
| " IPython.notebook.set_dirty(true);\n", | |
| " this.send_message(\"ack\", {});\n", | |
| " var fig = this;\n", | |
| " // Wait a second, then push the new image to the DOM so\n", | |
| " // that it is saved nicely (might be nice to debounce this).\n", | |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items){\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) { continue; };\n", | |
| "\n", | |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " // Add the status bar.\n", | |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "\n", | |
| " // Add the close button to the window.\n", | |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
| " buttongrp.append(button);\n", | |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
| " titlebar.prepend(buttongrp);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(el){\n", | |
| " var fig = this\n", | |
| " el.on(\"remove\", function(){\n", | |
| "\tfig.close_ws(fig, {});\n", | |
| " });\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
| " // this is important to make the div 'focusable\n", | |
| " el.attr('tabindex', 0)\n", | |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
| " // off when our div gets focus\n", | |
| "\n", | |
| " // location in version 3\n", | |
| " if (IPython.notebook.keyboard_manager) {\n", | |
| " IPython.notebook.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| " else {\n", | |
| " // location in version 2\n", | |
| " IPython.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " var manager = IPython.notebook.keyboard_manager;\n", | |
| " if (!manager)\n", | |
| " manager = IPython.keyboard_manager;\n", | |
| "\n", | |
| " // Check for shift+enter\n", | |
| " if (event.shiftKey && event.which == 13) {\n", | |
| " this.canvas_div.blur();\n", | |
| " // select the cell after this one\n", | |
| " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
| " IPython.notebook.select(index + 1);\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " fig.ondownload(fig, null);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.find_output_cell = function(html_output) {\n", | |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
| " // IPython event is triggered only after the cells have been serialised, which for\n", | |
| " // our purposes (turning an active figure into a static one), is too late.\n", | |
| " var cells = IPython.notebook.get_cells();\n", | |
| " var ncells = cells.length;\n", | |
| " for (var i=0; i<ncells; i++) {\n", | |
| " var cell = cells[i];\n", | |
| " if (cell.cell_type === 'code'){\n", | |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
| " var data = cell.output_area.outputs[j];\n", | |
| " if (data.data) {\n", | |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", | |
| " data = data.data;\n", | |
| " }\n", | |
| " if (data['text/html'] == html_output) {\n", | |
| " return [cell, data, j];\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "// Register the function which deals with the matplotlib target/channel.\n", | |
| "// The kernel may be null if the page has been refreshed.\n", | |
| "if (IPython.notebook.kernel != null) {\n", | |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
| "}\n" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.Javascript object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<img src=\"data:image/png;base64,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\">" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.HTML object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<matplotlib.contour.QuadContourSet at 0x7f69ad7d2e10>" | |
| ] | |
| }, | |
| "execution_count": 10, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "# Import the LogisticRegression solver. This can handle multiple classes\n", | |
| "from sklearn.linear_model import LogisticRegression\n", | |
| "\n", | |
| "# Normally this solver includes a penalty on misclassifications, i.e. a \n", | |
| "# regularizer, i.e. it is finding the MAP estimate rather than the Maximum \n", | |
| "# Likelihood. We can turn this off by setting the regularizer constant low \n", | |
| "# or setting C high.\n", | |
| "clf = LogisticRegression(C=100)\n", | |
| "\n", | |
| "# Fit the classifier \n", | |
| "clf.fit(x.reshape(-1,1), y)\n", | |
| "\n", | |
| "# Plot the data and the boundaries\n", | |
| "fig, ax = plt.subplots(1)\n", | |
| "ax.scatter(x,y, c=c)\n", | |
| "xx,yy = np.meshgrid(np.arange(0,120, 0.001), np.arange(0,4,1))\n", | |
| "plot_boundaries_1d(ax, clf, xx, yy, cmap='jet', alpha=0.4)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 5, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-17T18:50:35.749156Z", | |
| "start_time": "2018-04-17T18:50:35.743722Z" | |
| } | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Log likelihood is: -77.15861489704933\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "# Annoyingly scikit learn doesn't have a way to get the log-likelihood of the data\n", | |
| "# easily. You can however get log likelihoods for each data point at all classes.\n", | |
| "# Can sum this over the correct class to get the model log likelihood\n", | |
| "loglikelihood = np.sum(clf.predict_log_proba(x.reshape(-1,1))[y_one_hot])\n", | |
| "print('Log likelihood is: {}'.format(loglikelihood))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Clearly this can't work well on our data. However, we can project our data into a new space by taking the feature vector $\\phi = [x, x^2]^t$" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 15, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-18T10:10:21.721227Z", | |
| "start_time": "2018-04-18T10:10:21.673648Z" | |
| } | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/javascript": [ | |
| "/* Put everything inside the global mpl namespace */\n", | |
| "window.mpl = {};\n", | |
| "\n", | |
| "mpl.get_websocket_type = function() {\n", | |
| " if (typeof(WebSocket) !== 'undefined') {\n", | |
| " return WebSocket;\n", | |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
| " return MozWebSocket;\n", | |
| " } else {\n", | |
| " alert('Your browser does not have WebSocket support.' +\n", | |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
| " 'Firefox 4 and 5 are also supported but you ' +\n", | |
| " 'have to enable WebSockets in about:config.');\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
| " this.id = figure_id;\n", | |
| "\n", | |
| " this.ws = websocket;\n", | |
| "\n", | |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", | |
| "\n", | |
| " if (!this.supports_binary) {\n", | |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", | |
| " if (warnings) {\n", | |
| " warnings.style.display = 'block';\n", | |
| " warnings.textContent = (\n", | |
| " \"This browser does not support binary websocket messages. \" +\n", | |
| " \"Performance may be slow.\");\n", | |
| " }\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj = new Image();\n", | |
| "\n", | |
| " this.context = undefined;\n", | |
| " this.message = undefined;\n", | |
| " this.canvas = undefined;\n", | |
| " this.rubberband_canvas = undefined;\n", | |
| " this.rubberband_context = undefined;\n", | |
| " this.format_dropdown = undefined;\n", | |
| "\n", | |
| " this.image_mode = 'full';\n", | |
| "\n", | |
| " this.root = $('<div/>');\n", | |
| " this._root_extra_style(this.root)\n", | |
| " this.root.attr('style', 'display: inline-block');\n", | |
| "\n", | |
| " $(parent_element).append(this.root);\n", | |
| "\n", | |
| " this._init_header(this);\n", | |
| " this._init_canvas(this);\n", | |
| " this._init_toolbar(this);\n", | |
| "\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " this.waiting = false;\n", | |
| "\n", | |
| " this.ws.onopen = function () {\n", | |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
| " fig.send_message(\"send_image_mode\", {});\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj.onload = function() {\n", | |
| " if (fig.image_mode == 'full') {\n", | |
| " // Full images could contain transparency (where diff images\n", | |
| " // almost always do), so we need to clear the canvas so that\n", | |
| " // there is no ghosting.\n", | |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| " }\n", | |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
| " };\n", | |
| "\n", | |
| " this.imageObj.onunload = function() {\n", | |
| " this.ws.close();\n", | |
| " }\n", | |
| "\n", | |
| " this.ws.onmessage = this._make_on_message_function(this);\n", | |
| "\n", | |
| " this.ondownload = ondownload;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_header = function() {\n", | |
| " var titlebar = $(\n", | |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
| " 'ui-helper-clearfix\"/>');\n", | |
| " var titletext = $(\n", | |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
| " 'text-align: center; padding: 3px;\"/>');\n", | |
| " titlebar.append(titletext)\n", | |
| " this.root.append(titlebar);\n", | |
| " this.header = titletext[0];\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_canvas = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var canvas_div = $('<div/>');\n", | |
| "\n", | |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
| "\n", | |
| " function canvas_keyboard_event(event) {\n", | |
| " return fig.key_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
| " this.canvas_div = canvas_div\n", | |
| " this._canvas_extra_style(canvas_div)\n", | |
| " this.root.append(canvas_div);\n", | |
| "\n", | |
| " var canvas = $('<canvas/>');\n", | |
| " canvas.addClass('mpl-canvas');\n", | |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
| "\n", | |
| " this.canvas = canvas[0];\n", | |
| " this.context = canvas[0].getContext(\"2d\");\n", | |
| "\n", | |
| " var rubberband = $('<canvas/>');\n", | |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
| "\n", | |
| " var pass_mouse_events = true;\n", | |
| "\n", | |
| " canvas_div.resizable({\n", | |
| " start: function(event, ui) {\n", | |
| " pass_mouse_events = false;\n", | |
| " },\n", | |
| " resize: function(event, ui) {\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " stop: function(event, ui) {\n", | |
| " pass_mouse_events = true;\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " });\n", | |
| "\n", | |
| " function mouse_event_fn(event) {\n", | |
| " if (pass_mouse_events)\n", | |
| " return fig.mouse_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", | |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", | |
| " // Throttle sequential mouse events to 1 every 20ms.\n", | |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
| "\n", | |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
| "\n", | |
| " canvas_div.on(\"wheel\", function (event) {\n", | |
| " event = event.originalEvent;\n", | |
| " event['data'] = 'scroll'\n", | |
| " if (event.deltaY < 0) {\n", | |
| " event.step = 1;\n", | |
| " } else {\n", | |
| " event.step = -1;\n", | |
| " }\n", | |
| " mouse_event_fn(event);\n", | |
| " });\n", | |
| "\n", | |
| " canvas_div.append(canvas);\n", | |
| " canvas_div.append(rubberband);\n", | |
| "\n", | |
| " this.rubberband = rubberband;\n", | |
| " this.rubberband_canvas = rubberband[0];\n", | |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", | |
| "\n", | |
| " this._resize_canvas = function(width, height) {\n", | |
| " // Keep the size of the canvas, canvas container, and rubber band\n", | |
| " // canvas in synch.\n", | |
| " canvas_div.css('width', width)\n", | |
| " canvas_div.css('height', height)\n", | |
| "\n", | |
| " canvas.attr('width', width);\n", | |
| " canvas.attr('height', height);\n", | |
| "\n", | |
| " rubberband.attr('width', width);\n", | |
| " rubberband.attr('height', height);\n", | |
| " }\n", | |
| "\n", | |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
| " // upon first draw.\n", | |
| " this._resize_canvas(600, 600);\n", | |
| "\n", | |
| " // Disable right mouse context menu.\n", | |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
| " return false;\n", | |
| " });\n", | |
| "\n", | |
| " function set_focus () {\n", | |
| " canvas.focus();\n", | |
| " canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " window.setTimeout(set_focus, 100);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) {\n", | |
| " // put a spacer in here.\n", | |
| " continue;\n", | |
| " }\n", | |
| " var button = $('<button/>');\n", | |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
| " 'ui-button-icon-only');\n", | |
| " button.attr('role', 'button');\n", | |
| " button.attr('aria-disabled', 'false');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| "\n", | |
| " var icon_img = $('<span/>');\n", | |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
| " icon_img.addClass(image);\n", | |
| " icon_img.addClass('ui-corner-all');\n", | |
| "\n", | |
| " var tooltip_span = $('<span/>');\n", | |
| " tooltip_span.addClass('ui-button-text');\n", | |
| " tooltip_span.html(tooltip);\n", | |
| "\n", | |
| " button.append(icon_img);\n", | |
| " button.append(tooltip_span);\n", | |
| "\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " var fmt_picker_span = $('<span/>');\n", | |
| "\n", | |
| " var fmt_picker = $('<select/>');\n", | |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
| " fmt_picker_span.append(fmt_picker);\n", | |
| " nav_element.append(fmt_picker_span);\n", | |
| " this.format_dropdown = fmt_picker[0];\n", | |
| "\n", | |
| " for (var ind in mpl.extensions) {\n", | |
| " var fmt = mpl.extensions[ind];\n", | |
| " var option = $(\n", | |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
| " fmt_picker.append(option)\n", | |
| " }\n", | |
| "\n", | |
| " // Add hover states to the ui-buttons\n", | |
| " $( \".ui-button\" ).hover(\n", | |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", | |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
| " );\n", | |
| "\n", | |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
| " // which will in turn request a refresh of the image.\n", | |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", | |
| " properties['type'] = type;\n", | |
| " properties['figure_id'] = this.id;\n", | |
| " this.ws.send(JSON.stringify(properties));\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_draw_message = function() {\n", | |
| " if (!this.waiting) {\n", | |
| " this.waiting = true;\n", | |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " var format_dropdown = fig.format_dropdown;\n", | |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
| " fig.ondownload(fig, format);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
| " var size = msg['size'];\n", | |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
| " fig._resize_canvas(size[0], size[1]);\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
| " var x0 = msg['x0'];\n", | |
| " var y0 = fig.canvas.height - msg['y0'];\n", | |
| " var x1 = msg['x1'];\n", | |
| " var y1 = fig.canvas.height - msg['y1'];\n", | |
| " x0 = Math.floor(x0) + 0.5;\n", | |
| " y0 = Math.floor(y0) + 0.5;\n", | |
| " x1 = Math.floor(x1) + 0.5;\n", | |
| " y1 = Math.floor(y1) + 0.5;\n", | |
| " var min_x = Math.min(x0, x1);\n", | |
| " var min_y = Math.min(y0, y1);\n", | |
| " var width = Math.abs(x1 - x0);\n", | |
| " var height = Math.abs(y1 - y0);\n", | |
| "\n", | |
| " fig.rubberband_context.clearRect(\n", | |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| "\n", | |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
| " // Updates the figure title.\n", | |
| " fig.header.textContent = msg['label'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
| " var cursor = msg['cursor'];\n", | |
| " switch(cursor)\n", | |
| " {\n", | |
| " case 0:\n", | |
| " cursor = 'pointer';\n", | |
| " break;\n", | |
| " case 1:\n", | |
| " cursor = 'default';\n", | |
| " break;\n", | |
| " case 2:\n", | |
| " cursor = 'crosshair';\n", | |
| " break;\n", | |
| " case 3:\n", | |
| " cursor = 'move';\n", | |
| " break;\n", | |
| " }\n", | |
| " fig.rubberband_canvas.style.cursor = cursor;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
| " fig.message.textContent = msg['message'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
| " // Request the server to send over a new figure.\n", | |
| " fig.send_draw_message();\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
| " fig.image_mode = msg['mode'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Called whenever the canvas gets updated.\n", | |
| " this.send_message(\"ack\", {});\n", | |
| "}\n", | |
| "\n", | |
| "// A function to construct a web socket function for onmessage handling.\n", | |
| "// Called in the figure constructor.\n", | |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
| " return function socket_on_message(evt) {\n", | |
| " if (evt.data instanceof Blob) {\n", | |
| " /* FIXME: We get \"Resource interpreted as Image but\n", | |
| " * transferred with MIME type text/plain:\" errors on\n", | |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", | |
| " * to be part of the websocket stream */\n", | |
| " evt.data.type = \"image/png\";\n", | |
| "\n", | |
| " /* Free the memory for the previous frames */\n", | |
| " if (fig.imageObj.src) {\n", | |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", | |
| " fig.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
| " evt.data);\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
| " fig.imageObj.src = evt.data;\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var msg = JSON.parse(evt.data);\n", | |
| " var msg_type = msg['type'];\n", | |
| "\n", | |
| " // Call the \"handle_{type}\" callback, which takes\n", | |
| " // the figure and JSON message as its only arguments.\n", | |
| " try {\n", | |
| " var callback = fig[\"handle_\" + msg_type];\n", | |
| " } catch (e) {\n", | |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " if (callback) {\n", | |
| " try {\n", | |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
| " callback(fig, msg);\n", | |
| " } catch (e) {\n", | |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
| " }\n", | |
| " }\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
| "mpl.findpos = function(e) {\n", | |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
| " var targ;\n", | |
| " if (!e)\n", | |
| " e = window.event;\n", | |
| " if (e.target)\n", | |
| " targ = e.target;\n", | |
| " else if (e.srcElement)\n", | |
| " targ = e.srcElement;\n", | |
| " if (targ.nodeType == 3) // defeat Safari bug\n", | |
| " targ = targ.parentNode;\n", | |
| "\n", | |
| " // jQuery normalizes the pageX and pageY\n", | |
| " // pageX,Y are the mouse positions relative to the document\n", | |
| " // offset() returns the position of the element relative to the document\n", | |
| " var x = e.pageX - $(targ).offset().left;\n", | |
| " var y = e.pageY - $(targ).offset().top;\n", | |
| "\n", | |
| " return {\"x\": x, \"y\": y};\n", | |
| "};\n", | |
| "\n", | |
| "/*\n", | |
| " * return a copy of an object with only non-object keys\n", | |
| " * we need this to avoid circular references\n", | |
| " * http://stackoverflow.com/a/24161582/3208463\n", | |
| " */\n", | |
| "function simpleKeys (original) {\n", | |
| " return Object.keys(original).reduce(function (obj, key) {\n", | |
| " if (typeof original[key] !== 'object')\n", | |
| " obj[key] = original[key]\n", | |
| " return obj;\n", | |
| " }, {});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
| " var canvas_pos = mpl.findpos(event)\n", | |
| "\n", | |
| " if (name === 'button_press')\n", | |
| " {\n", | |
| " this.canvas.focus();\n", | |
| " this.canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " var x = canvas_pos.x;\n", | |
| " var y = canvas_pos.y;\n", | |
| "\n", | |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", | |
| " step: event.step,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| "\n", | |
| " /* This prevents the web browser from automatically changing to\n", | |
| " * the text insertion cursor when the button is pressed. We want\n", | |
| " * to control all of the cursor setting manually through the\n", | |
| " * 'cursor' event from matplotlib */\n", | |
| " event.preventDefault();\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " // Handle any extra behaviour associated with a key event\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.key_event = function(event, name) {\n", | |
| "\n", | |
| " // Prevent repeat events\n", | |
| " if (name == 'key_press')\n", | |
| " {\n", | |
| " if (event.which === this._key)\n", | |
| " return;\n", | |
| " else\n", | |
| " this._key = event.which;\n", | |
| " }\n", | |
| " if (name == 'key_release')\n", | |
| " this._key = null;\n", | |
| "\n", | |
| " var value = '';\n", | |
| " if (event.ctrlKey && event.which != 17)\n", | |
| " value += \"ctrl+\";\n", | |
| " if (event.altKey && event.which != 18)\n", | |
| " value += \"alt+\";\n", | |
| " if (event.shiftKey && event.which != 16)\n", | |
| " value += \"shift+\";\n", | |
| "\n", | |
| " value += 'k';\n", | |
| " value += event.which.toString();\n", | |
| "\n", | |
| " this._key_event_extra(event, name);\n", | |
| "\n", | |
| " this.send_message(name, {key: value,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
| " if (name == 'download') {\n", | |
| " this.handle_save(this, null);\n", | |
| " } else {\n", | |
| " this.send_message(\"toolbar_button\", {name: name});\n", | |
| " }\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
| " this.message.textContent = tooltip;\n", | |
| "};\n", | |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
| "\n", | |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
| "\n", | |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
| " // object with the appropriate methods. Currently this is a non binary\n", | |
| " // socket, so there is still some room for performance tuning.\n", | |
| " var ws = {};\n", | |
| "\n", | |
| " ws.close = function() {\n", | |
| " comm.close()\n", | |
| " };\n", | |
| " ws.send = function(m) {\n", | |
| " //console.log('sending', m);\n", | |
| " comm.send(m);\n", | |
| " };\n", | |
| " // Register the callback with on_msg.\n", | |
| " comm.on_msg(function(msg) {\n", | |
| " //console.log('receiving', msg['content']['data'], msg);\n", | |
| " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
| " ws.onmessage(msg['content']['data'])\n", | |
| " });\n", | |
| " return ws;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", | |
| " // This is the function which gets called when the mpl process\n", | |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
| "\n", | |
| " var id = msg.content.data.id;\n", | |
| " // Get hold of the div created by the display call when the Comm\n", | |
| " // socket was opened in Python.\n", | |
| " var element = $(\"#\" + id);\n", | |
| " var ws_proxy = comm_websocket_adapter(comm)\n", | |
| "\n", | |
| " function ondownload(figure, format) {\n", | |
| " window.open(figure.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " var fig = new mpl.figure(id, ws_proxy,\n", | |
| " ondownload,\n", | |
| " element.get(0));\n", | |
| "\n", | |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
| " // web socket which is closed, not our websocket->open comm proxy.\n", | |
| " ws_proxy.onopen();\n", | |
| "\n", | |
| " fig.parent_element = element.get(0);\n", | |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
| " if (!fig.cell_info) {\n", | |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var output_index = fig.cell_info[2]\n", | |
| " var cell = fig.cell_info[0];\n", | |
| "\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
| " fig.root.unbind('remove')\n", | |
| "\n", | |
| " // Update the output cell to use the data from the current canvas.\n", | |
| " fig.push_to_output();\n", | |
| " var dataURL = fig.canvas.toDataURL();\n", | |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
| " // the notebook keyboard shortcuts fail.\n", | |
| " IPython.keyboard_manager.enable()\n", | |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
| " fig.close_ws(fig, msg);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
| " fig.send_message('closing', msg);\n", | |
| " // fig.ws.close()\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
| " // Turn the data on the canvas into data in the output cell.\n", | |
| " var dataURL = this.canvas.toDataURL();\n", | |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Tell IPython that the notebook contents must change.\n", | |
| " IPython.notebook.set_dirty(true);\n", | |
| " this.send_message(\"ack\", {});\n", | |
| " var fig = this;\n", | |
| " // Wait a second, then push the new image to the DOM so\n", | |
| " // that it is saved nicely (might be nice to debounce this).\n", | |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items){\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) { continue; };\n", | |
| "\n", | |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " // Add the status bar.\n", | |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "\n", | |
| " // Add the close button to the window.\n", | |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
| " buttongrp.append(button);\n", | |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
| " titlebar.prepend(buttongrp);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(el){\n", | |
| " var fig = this\n", | |
| " el.on(\"remove\", function(){\n", | |
| "\tfig.close_ws(fig, {});\n", | |
| " });\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
| " // this is important to make the div 'focusable\n", | |
| " el.attr('tabindex', 0)\n", | |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
| " // off when our div gets focus\n", | |
| "\n", | |
| " // location in version 3\n", | |
| " if (IPython.notebook.keyboard_manager) {\n", | |
| " IPython.notebook.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| " else {\n", | |
| " // location in version 2\n", | |
| " IPython.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " var manager = IPython.notebook.keyboard_manager;\n", | |
| " if (!manager)\n", | |
| " manager = IPython.keyboard_manager;\n", | |
| "\n", | |
| " // Check for shift+enter\n", | |
| " if (event.shiftKey && event.which == 13) {\n", | |
| " this.canvas_div.blur();\n", | |
| " // select the cell after this one\n", | |
| " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
| " IPython.notebook.select(index + 1);\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " fig.ondownload(fig, null);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.find_output_cell = function(html_output) {\n", | |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
| " // IPython event is triggered only after the cells have been serialised, which for\n", | |
| " // our purposes (turning an active figure into a static one), is too late.\n", | |
| " var cells = IPython.notebook.get_cells();\n", | |
| " var ncells = cells.length;\n", | |
| " for (var i=0; i<ncells; i++) {\n", | |
| " var cell = cells[i];\n", | |
| " if (cell.cell_type === 'code'){\n", | |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
| " var data = cell.output_area.outputs[j];\n", | |
| " if (data.data) {\n", | |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", | |
| " data = data.data;\n", | |
| " }\n", | |
| " if (data['text/html'] == html_output) {\n", | |
| " return [cell, data, j];\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "// Register the function which deals with the matplotlib target/channel.\n", | |
| "// The kernel may be null if the page has been refreshed.\n", | |
| "if (IPython.notebook.kernel != null) {\n", | |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
| "}\n" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.Javascript object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<img src=\"data:image/png;base64,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nUKmCBgA0xcAjwcuTH0t3O2p08Cbpq4LfCNTdvhiGIcBcDGgukoFclXg8ccf56h992VSeTnhvt7a8FVLJSVsdeaZnNO/f66yOG8F0i5gAExfAJwK7Ae81GAvhesBzwNCSMrWxQCYrXvWeSmQgMArr7xCvz59mDF9OrvttRdnnXMOPXffne/Gj+fP5eWMKSnh7VateGPCBNq2bZvAiNyEArkhYABMXwAMP5m+mE/bbAm8kMXtZADM4p3r1BRYnAL//ve/2X/ffVkLKALeBjp26sQbb7/N7bffzotPP80a667Lsb17s/zyyy/OobhuBXJOwACYvgCYc82TmrABMFf3vPNWIFKg07LL8revvuKc1Hr+Ga6jyctjyPDh9OjRI3LtliugwO8JGAANgLHvEANgrKD1CuSgQHV1NUVFRUwOR/1S8/8cWAG4+uqrOf74cFm1iwIKLC4BA6ABMLa3DICxgtYrkIMC4evb2i+9NJfPnMlBqfkPBf4CjJs4se4B0C4KKLD4BAyABsDY7jIAxgpar0COCgwZMoQjDzmEw2prKUg9OmG/gw/mrrvvzlERp61AcgIGQANgbLcZAGMFrVcgywXmzJnDyJEjmTt3Lt27d2eJJZaYN+OXX36Za668km+//ZZjjj/ea/+yvBecXuYIGAANgLHdaACMFbRegSwWCN/pu/M227BkeTmFwJeFhTw+ahQbbbRRFs/aqSmQ+QIGQANgbJcaAGMFrVcgiwW223RTur7+OpfV1NTNMjwY9eE11+SN997L4lk7NQUyX8AAaACM7VIDYKyg9QpkocA333zD//73P7p06cIkYNXUHL8EwkNTw4dPy5Yts3DmTkmB5iFgADQAxnaqATBW0HoFskggXOd30rHHcuMtt0BtLSXh+zFrajgrNccngMNateKzr7+moCDc+uGigAJNIWAANADG9p0BMFbQegWySODaa6/l6n79eLi8nJWBy4ABwAlAKXBdSQnnXXIJvU8If+KigAJNJWAANADG9p4BMFbQegWySGDbjTfmwNdf5+jUnGqBDsXF/GHTTVmuTRsOOeoo7/TNov3tVJqvgAHQABjbvQbAWEHrFWjmAq+++io33ngjnTp14qWnn2ab557jjNScysO3exQXM2LMGDbZZJNmPlOHr0D2CBgADYCx3WwAjBW0XoFmLHDUkUdy+623showBajNz6ew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\">" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.HTML object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "plt.figure()\n", | |
| "# Update our feature vector\n", | |
| "x2 = x**2\n", | |
| "phi = np.stack((x, x2),axis=-1)\n", | |
| "\n", | |
| "# Plot the output - here we can no longer show the z value so we keep the colours to \n", | |
| "# indicate classes. Remember b = 1, g = 2, r = 3\n", | |
| "plt.scatter(x, x2, c=c)\n", | |
| "plt.xlabel('x')\n", | |
| "plt.ylabel('x^2')\n", | |
| "plt.yticks([0, 10000]);" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 21, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-18T10:11:33.686593Z", | |
| "start_time": "2018-04-18T10:11:33.478083Z" | |
| } | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/javascript": [ | |
| "/* Put everything inside the global mpl namespace */\n", | |
| "window.mpl = {};\n", | |
| "\n", | |
| "mpl.get_websocket_type = function() {\n", | |
| " if (typeof(WebSocket) !== 'undefined') {\n", | |
| " return WebSocket;\n", | |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
| " return MozWebSocket;\n", | |
| " } else {\n", | |
| " alert('Your browser does not have WebSocket support.' +\n", | |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
| " 'Firefox 4 and 5 are also supported but you ' +\n", | |
| " 'have to enable WebSockets in about:config.');\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
| " this.id = figure_id;\n", | |
| "\n", | |
| " this.ws = websocket;\n", | |
| "\n", | |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", | |
| "\n", | |
| " if (!this.supports_binary) {\n", | |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", | |
| " if (warnings) {\n", | |
| " warnings.style.display = 'block';\n", | |
| " warnings.textContent = (\n", | |
| " \"This browser does not support binary websocket messages. \" +\n", | |
| " \"Performance may be slow.\");\n", | |
| " }\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj = new Image();\n", | |
| "\n", | |
| " this.context = undefined;\n", | |
| " this.message = undefined;\n", | |
| " this.canvas = undefined;\n", | |
| " this.rubberband_canvas = undefined;\n", | |
| " this.rubberband_context = undefined;\n", | |
| " this.format_dropdown = undefined;\n", | |
| "\n", | |
| " this.image_mode = 'full';\n", | |
| "\n", | |
| " this.root = $('<div/>');\n", | |
| " this._root_extra_style(this.root)\n", | |
| " this.root.attr('style', 'display: inline-block');\n", | |
| "\n", | |
| " $(parent_element).append(this.root);\n", | |
| "\n", | |
| " this._init_header(this);\n", | |
| " this._init_canvas(this);\n", | |
| " this._init_toolbar(this);\n", | |
| "\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " this.waiting = false;\n", | |
| "\n", | |
| " this.ws.onopen = function () {\n", | |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
| " fig.send_message(\"send_image_mode\", {});\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj.onload = function() {\n", | |
| " if (fig.image_mode == 'full') {\n", | |
| " // Full images could contain transparency (where diff images\n", | |
| " // almost always do), so we need to clear the canvas so that\n", | |
| " // there is no ghosting.\n", | |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| " }\n", | |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
| " };\n", | |
| "\n", | |
| " this.imageObj.onunload = function() {\n", | |
| " this.ws.close();\n", | |
| " }\n", | |
| "\n", | |
| " this.ws.onmessage = this._make_on_message_function(this);\n", | |
| "\n", | |
| " this.ondownload = ondownload;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_header = function() {\n", | |
| " var titlebar = $(\n", | |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
| " 'ui-helper-clearfix\"/>');\n", | |
| " var titletext = $(\n", | |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
| " 'text-align: center; padding: 3px;\"/>');\n", | |
| " titlebar.append(titletext)\n", | |
| " this.root.append(titlebar);\n", | |
| " this.header = titletext[0];\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_canvas = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var canvas_div = $('<div/>');\n", | |
| "\n", | |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
| "\n", | |
| " function canvas_keyboard_event(event) {\n", | |
| " return fig.key_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
| " this.canvas_div = canvas_div\n", | |
| " this._canvas_extra_style(canvas_div)\n", | |
| " this.root.append(canvas_div);\n", | |
| "\n", | |
| " var canvas = $('<canvas/>');\n", | |
| " canvas.addClass('mpl-canvas');\n", | |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
| "\n", | |
| " this.canvas = canvas[0];\n", | |
| " this.context = canvas[0].getContext(\"2d\");\n", | |
| "\n", | |
| " var rubberband = $('<canvas/>');\n", | |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
| "\n", | |
| " var pass_mouse_events = true;\n", | |
| "\n", | |
| " canvas_div.resizable({\n", | |
| " start: function(event, ui) {\n", | |
| " pass_mouse_events = false;\n", | |
| " },\n", | |
| " resize: function(event, ui) {\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " stop: function(event, ui) {\n", | |
| " pass_mouse_events = true;\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " });\n", | |
| "\n", | |
| " function mouse_event_fn(event) {\n", | |
| " if (pass_mouse_events)\n", | |
| " return fig.mouse_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", | |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", | |
| " // Throttle sequential mouse events to 1 every 20ms.\n", | |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
| "\n", | |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
| "\n", | |
| " canvas_div.on(\"wheel\", function (event) {\n", | |
| " event = event.originalEvent;\n", | |
| " event['data'] = 'scroll'\n", | |
| " if (event.deltaY < 0) {\n", | |
| " event.step = 1;\n", | |
| " } else {\n", | |
| " event.step = -1;\n", | |
| " }\n", | |
| " mouse_event_fn(event);\n", | |
| " });\n", | |
| "\n", | |
| " canvas_div.append(canvas);\n", | |
| " canvas_div.append(rubberband);\n", | |
| "\n", | |
| " this.rubberband = rubberband;\n", | |
| " this.rubberband_canvas = rubberband[0];\n", | |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", | |
| "\n", | |
| " this._resize_canvas = function(width, height) {\n", | |
| " // Keep the size of the canvas, canvas container, and rubber band\n", | |
| " // canvas in synch.\n", | |
| " canvas_div.css('width', width)\n", | |
| " canvas_div.css('height', height)\n", | |
| "\n", | |
| " canvas.attr('width', width);\n", | |
| " canvas.attr('height', height);\n", | |
| "\n", | |
| " rubberband.attr('width', width);\n", | |
| " rubberband.attr('height', height);\n", | |
| " }\n", | |
| "\n", | |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
| " // upon first draw.\n", | |
| " this._resize_canvas(600, 600);\n", | |
| "\n", | |
| " // Disable right mouse context menu.\n", | |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
| " return false;\n", | |
| " });\n", | |
| "\n", | |
| " function set_focus () {\n", | |
| " canvas.focus();\n", | |
| " canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " window.setTimeout(set_focus, 100);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) {\n", | |
| " // put a spacer in here.\n", | |
| " continue;\n", | |
| " }\n", | |
| " var button = $('<button/>');\n", | |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
| " 'ui-button-icon-only');\n", | |
| " button.attr('role', 'button');\n", | |
| " button.attr('aria-disabled', 'false');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| "\n", | |
| " var icon_img = $('<span/>');\n", | |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
| " icon_img.addClass(image);\n", | |
| " icon_img.addClass('ui-corner-all');\n", | |
| "\n", | |
| " var tooltip_span = $('<span/>');\n", | |
| " tooltip_span.addClass('ui-button-text');\n", | |
| " tooltip_span.html(tooltip);\n", | |
| "\n", | |
| " button.append(icon_img);\n", | |
| " button.append(tooltip_span);\n", | |
| "\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " var fmt_picker_span = $('<span/>');\n", | |
| "\n", | |
| " var fmt_picker = $('<select/>');\n", | |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
| " fmt_picker_span.append(fmt_picker);\n", | |
| " nav_element.append(fmt_picker_span);\n", | |
| " this.format_dropdown = fmt_picker[0];\n", | |
| "\n", | |
| " for (var ind in mpl.extensions) {\n", | |
| " var fmt = mpl.extensions[ind];\n", | |
| " var option = $(\n", | |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
| " fmt_picker.append(option)\n", | |
| " }\n", | |
| "\n", | |
| " // Add hover states to the ui-buttons\n", | |
| " $( \".ui-button\" ).hover(\n", | |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", | |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
| " );\n", | |
| "\n", | |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
| " // which will in turn request a refresh of the image.\n", | |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", | |
| " properties['type'] = type;\n", | |
| " properties['figure_id'] = this.id;\n", | |
| " this.ws.send(JSON.stringify(properties));\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_draw_message = function() {\n", | |
| " if (!this.waiting) {\n", | |
| " this.waiting = true;\n", | |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " var format_dropdown = fig.format_dropdown;\n", | |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
| " fig.ondownload(fig, format);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
| " var size = msg['size'];\n", | |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
| " fig._resize_canvas(size[0], size[1]);\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
| " var x0 = msg['x0'];\n", | |
| " var y0 = fig.canvas.height - msg['y0'];\n", | |
| " var x1 = msg['x1'];\n", | |
| " var y1 = fig.canvas.height - msg['y1'];\n", | |
| " x0 = Math.floor(x0) + 0.5;\n", | |
| " y0 = Math.floor(y0) + 0.5;\n", | |
| " x1 = Math.floor(x1) + 0.5;\n", | |
| " y1 = Math.floor(y1) + 0.5;\n", | |
| " var min_x = Math.min(x0, x1);\n", | |
| " var min_y = Math.min(y0, y1);\n", | |
| " var width = Math.abs(x1 - x0);\n", | |
| " var height = Math.abs(y1 - y0);\n", | |
| "\n", | |
| " fig.rubberband_context.clearRect(\n", | |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| "\n", | |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
| " // Updates the figure title.\n", | |
| " fig.header.textContent = msg['label'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
| " var cursor = msg['cursor'];\n", | |
| " switch(cursor)\n", | |
| " {\n", | |
| " case 0:\n", | |
| " cursor = 'pointer';\n", | |
| " break;\n", | |
| " case 1:\n", | |
| " cursor = 'default';\n", | |
| " break;\n", | |
| " case 2:\n", | |
| " cursor = 'crosshair';\n", | |
| " break;\n", | |
| " case 3:\n", | |
| " cursor = 'move';\n", | |
| " break;\n", | |
| " }\n", | |
| " fig.rubberband_canvas.style.cursor = cursor;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
| " fig.message.textContent = msg['message'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
| " // Request the server to send over a new figure.\n", | |
| " fig.send_draw_message();\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
| " fig.image_mode = msg['mode'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Called whenever the canvas gets updated.\n", | |
| " this.send_message(\"ack\", {});\n", | |
| "}\n", | |
| "\n", | |
| "// A function to construct a web socket function for onmessage handling.\n", | |
| "// Called in the figure constructor.\n", | |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
| " return function socket_on_message(evt) {\n", | |
| " if (evt.data instanceof Blob) {\n", | |
| " /* FIXME: We get \"Resource interpreted as Image but\n", | |
| " * transferred with MIME type text/plain:\" errors on\n", | |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", | |
| " * to be part of the websocket stream */\n", | |
| " evt.data.type = \"image/png\";\n", | |
| "\n", | |
| " /* Free the memory for the previous frames */\n", | |
| " if (fig.imageObj.src) {\n", | |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", | |
| " fig.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
| " evt.data);\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
| " fig.imageObj.src = evt.data;\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var msg = JSON.parse(evt.data);\n", | |
| " var msg_type = msg['type'];\n", | |
| "\n", | |
| " // Call the \"handle_{type}\" callback, which takes\n", | |
| " // the figure and JSON message as its only arguments.\n", | |
| " try {\n", | |
| " var callback = fig[\"handle_\" + msg_type];\n", | |
| " } catch (e) {\n", | |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " if (callback) {\n", | |
| " try {\n", | |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
| " callback(fig, msg);\n", | |
| " } catch (e) {\n", | |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
| " }\n", | |
| " }\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
| "mpl.findpos = function(e) {\n", | |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
| " var targ;\n", | |
| " if (!e)\n", | |
| " e = window.event;\n", | |
| " if (e.target)\n", | |
| " targ = e.target;\n", | |
| " else if (e.srcElement)\n", | |
| " targ = e.srcElement;\n", | |
| " if (targ.nodeType == 3) // defeat Safari bug\n", | |
| " targ = targ.parentNode;\n", | |
| "\n", | |
| " // jQuery normalizes the pageX and pageY\n", | |
| " // pageX,Y are the mouse positions relative to the document\n", | |
| " // offset() returns the position of the element relative to the document\n", | |
| " var x = e.pageX - $(targ).offset().left;\n", | |
| " var y = e.pageY - $(targ).offset().top;\n", | |
| "\n", | |
| " return {\"x\": x, \"y\": y};\n", | |
| "};\n", | |
| "\n", | |
| "/*\n", | |
| " * return a copy of an object with only non-object keys\n", | |
| " * we need this to avoid circular references\n", | |
| " * http://stackoverflow.com/a/24161582/3208463\n", | |
| " */\n", | |
| "function simpleKeys (original) {\n", | |
| " return Object.keys(original).reduce(function (obj, key) {\n", | |
| " if (typeof original[key] !== 'object')\n", | |
| " obj[key] = original[key]\n", | |
| " return obj;\n", | |
| " }, {});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
| " var canvas_pos = mpl.findpos(event)\n", | |
| "\n", | |
| " if (name === 'button_press')\n", | |
| " {\n", | |
| " this.canvas.focus();\n", | |
| " this.canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " var x = canvas_pos.x;\n", | |
| " var y = canvas_pos.y;\n", | |
| "\n", | |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", | |
| " step: event.step,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| "\n", | |
| " /* This prevents the web browser from automatically changing to\n", | |
| " * the text insertion cursor when the button is pressed. We want\n", | |
| " * to control all of the cursor setting manually through the\n", | |
| " * 'cursor' event from matplotlib */\n", | |
| " event.preventDefault();\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " // Handle any extra behaviour associated with a key event\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.key_event = function(event, name) {\n", | |
| "\n", | |
| " // Prevent repeat events\n", | |
| " if (name == 'key_press')\n", | |
| " {\n", | |
| " if (event.which === this._key)\n", | |
| " return;\n", | |
| " else\n", | |
| " this._key = event.which;\n", | |
| " }\n", | |
| " if (name == 'key_release')\n", | |
| " this._key = null;\n", | |
| "\n", | |
| " var value = '';\n", | |
| " if (event.ctrlKey && event.which != 17)\n", | |
| " value += \"ctrl+\";\n", | |
| " if (event.altKey && event.which != 18)\n", | |
| " value += \"alt+\";\n", | |
| " if (event.shiftKey && event.which != 16)\n", | |
| " value += \"shift+\";\n", | |
| "\n", | |
| " value += 'k';\n", | |
| " value += event.which.toString();\n", | |
| "\n", | |
| " this._key_event_extra(event, name);\n", | |
| "\n", | |
| " this.send_message(name, {key: value,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
| " if (name == 'download') {\n", | |
| " this.handle_save(this, null);\n", | |
| " } else {\n", | |
| " this.send_message(\"toolbar_button\", {name: name});\n", | |
| " }\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
| " this.message.textContent = tooltip;\n", | |
| "};\n", | |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
| "\n", | |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
| "\n", | |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
| " // object with the appropriate methods. Currently this is a non binary\n", | |
| " // socket, so there is still some room for performance tuning.\n", | |
| " var ws = {};\n", | |
| "\n", | |
| " ws.close = function() {\n", | |
| " comm.close()\n", | |
| " };\n", | |
| " ws.send = function(m) {\n", | |
| " //console.log('sending', m);\n", | |
| " comm.send(m);\n", | |
| " };\n", | |
| " // Register the callback with on_msg.\n", | |
| " comm.on_msg(function(msg) {\n", | |
| " //console.log('receiving', msg['content']['data'], msg);\n", | |
| " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
| " ws.onmessage(msg['content']['data'])\n", | |
| " });\n", | |
| " return ws;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", | |
| " // This is the function which gets called when the mpl process\n", | |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
| "\n", | |
| " var id = msg.content.data.id;\n", | |
| " // Get hold of the div created by the display call when the Comm\n", | |
| " // socket was opened in Python.\n", | |
| " var element = $(\"#\" + id);\n", | |
| " var ws_proxy = comm_websocket_adapter(comm)\n", | |
| "\n", | |
| " function ondownload(figure, format) {\n", | |
| " window.open(figure.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " var fig = new mpl.figure(id, ws_proxy,\n", | |
| " ondownload,\n", | |
| " element.get(0));\n", | |
| "\n", | |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
| " // web socket which is closed, not our websocket->open comm proxy.\n", | |
| " ws_proxy.onopen();\n", | |
| "\n", | |
| " fig.parent_element = element.get(0);\n", | |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
| " if (!fig.cell_info) {\n", | |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var output_index = fig.cell_info[2]\n", | |
| " var cell = fig.cell_info[0];\n", | |
| "\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
| " fig.root.unbind('remove')\n", | |
| "\n", | |
| " // Update the output cell to use the data from the current canvas.\n", | |
| " fig.push_to_output();\n", | |
| " var dataURL = fig.canvas.toDataURL();\n", | |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
| " // the notebook keyboard shortcuts fail.\n", | |
| " IPython.keyboard_manager.enable()\n", | |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
| " fig.close_ws(fig, msg);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
| " fig.send_message('closing', msg);\n", | |
| " // fig.ws.close()\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
| " // Turn the data on the canvas into data in the output cell.\n", | |
| " var dataURL = this.canvas.toDataURL();\n", | |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Tell IPython that the notebook contents must change.\n", | |
| " IPython.notebook.set_dirty(true);\n", | |
| " this.send_message(\"ack\", {});\n", | |
| " var fig = this;\n", | |
| " // Wait a second, then push the new image to the DOM so\n", | |
| " // that it is saved nicely (might be nice to debounce this).\n", | |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items){\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) { continue; };\n", | |
| "\n", | |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " // Add the status bar.\n", | |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "\n", | |
| " // Add the close button to the window.\n", | |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
| " buttongrp.append(button);\n", | |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
| " titlebar.prepend(buttongrp);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(el){\n", | |
| " var fig = this\n", | |
| " el.on(\"remove\", function(){\n", | |
| "\tfig.close_ws(fig, {});\n", | |
| " });\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
| " // this is important to make the div 'focusable\n", | |
| " el.attr('tabindex', 0)\n", | |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
| " // off when our div gets focus\n", | |
| "\n", | |
| " // location in version 3\n", | |
| " if (IPython.notebook.keyboard_manager) {\n", | |
| " IPython.notebook.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| " else {\n", | |
| " // location in version 2\n", | |
| " IPython.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " var manager = IPython.notebook.keyboard_manager;\n", | |
| " if (!manager)\n", | |
| " manager = IPython.keyboard_manager;\n", | |
| "\n", | |
| " // Check for shift+enter\n", | |
| " if (event.shiftKey && event.which == 13) {\n", | |
| " this.canvas_div.blur();\n", | |
| " // select the cell after this one\n", | |
| " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
| " IPython.notebook.select(index + 1);\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " fig.ondownload(fig, null);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.find_output_cell = function(html_output) {\n", | |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
| " // IPython event is triggered only after the cells have been serialised, which for\n", | |
| " // our purposes (turning an active figure into a static one), is too late.\n", | |
| " var cells = IPython.notebook.get_cells();\n", | |
| " var ncells = cells.length;\n", | |
| " for (var i=0; i<ncells; i++) {\n", | |
| " var cell = cells[i];\n", | |
| " if (cell.cell_type === 'code'){\n", | |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
| " var data = cell.output_area.outputs[j];\n", | |
| " if (data.data) {\n", | |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", | |
| " data = data.data;\n", | |
| " }\n", | |
| " if (data['text/html'] == html_output) {\n", | |
| " return [cell, data, j];\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "// Register the function which deals with the matplotlib target/channel.\n", | |
| "// The kernel may be null if the page has been refreshed.\n", | |
| "if (IPython.notebook.kernel != null) {\n", | |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
| "}\n" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.Javascript object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuzdCZxVdf3/8dfcO+zLAIqAiisu4AKKlrinGZZa2uKWW+YaVkba9u/3+/n7le2ZlZSpJf0sw/q55BpqibumJO4bKqmoKIowIAgz9/4f38u5dENg7sz3nrlz577O40HBzPl87znP8x188z3n+z0NuCmggAIKKKCAAgrUlUBDXZ2tJ6uAAgoooIACCiiAAdBOoIACCiiggAIK1JmAAbDOLrinq4ACCiiggAIKGADtAwoooIACCiigQJ0JGADr7IJ7ugoooIACCiiggAHQPqCAAgoooIACCtSZgAGwzi64p6uAAgoooIACChgA7QMKKKCAAgoooECdCRgA6+yCe7oKKKCAAgoooIAB0D6ggAIKKKCAAgrUmYABsM4uuKergAIKKKCAAgoYAO0DCiiggAIKKKBAnQkYAOvsgnu6CiiggAIKKKCAAdA+oIACCiiggAIK1JmAAbDOLrinq4ACCiiggAIKGADtAwoooIACCiigQJ0JGADr7IJ7ugoooIACCiiggAHQPqCAAgoooIACCtSZgAGwzi64p6uAAgoooIACChgA7QMKKKCAAgoooECdCRgA6+yCe7oKKKCAAgoooIAB0D6ggAIKKKCAAgrUmYABsM4uuKergAIKKKCAAgoYAO0DCiiggAIKKKBAnQkYAOvsgnu6CiiggAIKKKCAAdA+oIACCiiggAIK1JmAAbDOLrinq4ACCiiggAIKGADtAwoooIACCiigQJ0JGADr7IJ7ugoooIACCiiggAHQPqCAAgoooIACCtSZgAGwzi64p6uAAgoooIACChgA7QMKKKCAAgoooECdCRgA6+yCe7oKKKCAAgoooIAB0D6ggAIKKKCAAgrUmYABsM4uuKergAIKKKCAAgoYAO0DCiiggAIKKKBAnQkYAOvsgnu6CiiggAIKKKCAAdA+oIACCiiggAIK1JmAAbDOLrinq4ACCiiggAIKGADtAwoooIACCiigQJ0JGADr7IJ7ugoooIACCiiggAHQPqCAAgoooIACCtSZgAGwzi64p6uAAgoooIACChgA7QMKKKCAAgoooECdCRgA6+yCe7oKKKCAAgoooIAB0D6ggAIKKKCAAgrUmYABMO6CB78Ngea4ZqxWQAEFFFBAgU4WGAC8AuQ7+XO7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\">" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.HTML object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<matplotlib.contour.QuadContourSet at 0x7f69ad36cfd0>" | |
| ] | |
| }, | |
| "execution_count": 21, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "# Fit our softmax with new data\n", | |
| "clf.fit(phi, y)\n", | |
| "\n", | |
| "# Plot the results\n", | |
| "fig, ax = plt.subplots(1)\n", | |
| "ax.scatter(x,x2, c=c)\n", | |
| "xx,yy = np.meshgrid(np.linspace(0,120, 1000), np.linspace(0,120**2,1000))\n", | |
| "plot_boundaries_2d(ax, clf, xx, yy, cmap='jet', alpha=0.5)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "If we like, we can plot the decision boundaries on the original diagram (i.e. showing only x)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 25, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-18T10:12:25.757487Z", | |
| "start_time": "2018-04-18T10:12:25.639522Z" | |
| } | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/javascript": [ | |
| "/* Put everything inside the global mpl namespace */\n", | |
| "window.mpl = {};\n", | |
| "\n", | |
| "mpl.get_websocket_type = function() {\n", | |
| " if (typeof(WebSocket) !== 'undefined') {\n", | |
| " return WebSocket;\n", | |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
| " return MozWebSocket;\n", | |
| " } else {\n", | |
| " alert('Your browser does not have WebSocket support.' +\n", | |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
| " 'Firefox 4 and 5 are also supported but you ' +\n", | |
| " 'have to enable WebSockets in about:config.');\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
| " this.id = figure_id;\n", | |
| "\n", | |
| " this.ws = websocket;\n", | |
| "\n", | |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", | |
| "\n", | |
| " if (!this.supports_binary) {\n", | |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", | |
| " if (warnings) {\n", | |
| " warnings.style.display = 'block';\n", | |
| " warnings.textContent = (\n", | |
| " \"This browser does not support binary websocket messages. \" +\n", | |
| " \"Performance may be slow.\");\n", | |
| " }\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj = new Image();\n", | |
| "\n", | |
| " this.context = undefined;\n", | |
| " this.message = undefined;\n", | |
| " this.canvas = undefined;\n", | |
| " this.rubberband_canvas = undefined;\n", | |
| " this.rubberband_context = undefined;\n", | |
| " this.format_dropdown = undefined;\n", | |
| "\n", | |
| " this.image_mode = 'full';\n", | |
| "\n", | |
| " this.root = $('<div/>');\n", | |
| " this._root_extra_style(this.root)\n", | |
| " this.root.attr('style', 'display: inline-block');\n", | |
| "\n", | |
| " $(parent_element).append(this.root);\n", | |
| "\n", | |
| " this._init_header(this);\n", | |
| " this._init_canvas(this);\n", | |
| " this._init_toolbar(this);\n", | |
| "\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " this.waiting = false;\n", | |
| "\n", | |
| " this.ws.onopen = function () {\n", | |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
| " fig.send_message(\"send_image_mode\", {});\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " }\n", | |
| "\n", | |
| " this.imageObj.onload = function() {\n", | |
| " if (fig.image_mode == 'full') {\n", | |
| " // Full images could contain transparency (where diff images\n", | |
| " // almost always do), so we need to clear the canvas so that\n", | |
| " // there is no ghosting.\n", | |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| " }\n", | |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
| " };\n", | |
| "\n", | |
| " this.imageObj.onunload = function() {\n", | |
| " this.ws.close();\n", | |
| " }\n", | |
| "\n", | |
| " this.ws.onmessage = this._make_on_message_function(this);\n", | |
| "\n", | |
| " this.ondownload = ondownload;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_header = function() {\n", | |
| " var titlebar = $(\n", | |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
| " 'ui-helper-clearfix\"/>');\n", | |
| " var titletext = $(\n", | |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
| " 'text-align: center; padding: 3px;\"/>');\n", | |
| " titlebar.append(titletext)\n", | |
| " this.root.append(titlebar);\n", | |
| " this.header = titletext[0];\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_canvas = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var canvas_div = $('<div/>');\n", | |
| "\n", | |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
| "\n", | |
| " function canvas_keyboard_event(event) {\n", | |
| " return fig.key_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
| " this.canvas_div = canvas_div\n", | |
| " this._canvas_extra_style(canvas_div)\n", | |
| " this.root.append(canvas_div);\n", | |
| "\n", | |
| " var canvas = $('<canvas/>');\n", | |
| " canvas.addClass('mpl-canvas');\n", | |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
| "\n", | |
| " this.canvas = canvas[0];\n", | |
| " this.context = canvas[0].getContext(\"2d\");\n", | |
| "\n", | |
| " var rubberband = $('<canvas/>');\n", | |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
| "\n", | |
| " var pass_mouse_events = true;\n", | |
| "\n", | |
| " canvas_div.resizable({\n", | |
| " start: function(event, ui) {\n", | |
| " pass_mouse_events = false;\n", | |
| " },\n", | |
| " resize: function(event, ui) {\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " stop: function(event, ui) {\n", | |
| " pass_mouse_events = true;\n", | |
| " fig.request_resize(ui.size.width, ui.size.height);\n", | |
| " },\n", | |
| " });\n", | |
| "\n", | |
| " function mouse_event_fn(event) {\n", | |
| " if (pass_mouse_events)\n", | |
| " return fig.mouse_event(event, event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", | |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", | |
| " // Throttle sequential mouse events to 1 every 20ms.\n", | |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
| "\n", | |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
| "\n", | |
| " canvas_div.on(\"wheel\", function (event) {\n", | |
| " event = event.originalEvent;\n", | |
| " event['data'] = 'scroll'\n", | |
| " if (event.deltaY < 0) {\n", | |
| " event.step = 1;\n", | |
| " } else {\n", | |
| " event.step = -1;\n", | |
| " }\n", | |
| " mouse_event_fn(event);\n", | |
| " });\n", | |
| "\n", | |
| " canvas_div.append(canvas);\n", | |
| " canvas_div.append(rubberband);\n", | |
| "\n", | |
| " this.rubberband = rubberband;\n", | |
| " this.rubberband_canvas = rubberband[0];\n", | |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", | |
| "\n", | |
| " this._resize_canvas = function(width, height) {\n", | |
| " // Keep the size of the canvas, canvas container, and rubber band\n", | |
| " // canvas in synch.\n", | |
| " canvas_div.css('width', width)\n", | |
| " canvas_div.css('height', height)\n", | |
| "\n", | |
| " canvas.attr('width', width);\n", | |
| " canvas.attr('height', height);\n", | |
| "\n", | |
| " rubberband.attr('width', width);\n", | |
| " rubberband.attr('height', height);\n", | |
| " }\n", | |
| "\n", | |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
| " // upon first draw.\n", | |
| " this._resize_canvas(600, 600);\n", | |
| "\n", | |
| " // Disable right mouse context menu.\n", | |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
| " return false;\n", | |
| " });\n", | |
| "\n", | |
| " function set_focus () {\n", | |
| " canvas.focus();\n", | |
| " canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " window.setTimeout(set_focus, 100);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) {\n", | |
| " // put a spacer in here.\n", | |
| " continue;\n", | |
| " }\n", | |
| " var button = $('<button/>');\n", | |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
| " 'ui-button-icon-only');\n", | |
| " button.attr('role', 'button');\n", | |
| " button.attr('aria-disabled', 'false');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| "\n", | |
| " var icon_img = $('<span/>');\n", | |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
| " icon_img.addClass(image);\n", | |
| " icon_img.addClass('ui-corner-all');\n", | |
| "\n", | |
| " var tooltip_span = $('<span/>');\n", | |
| " tooltip_span.addClass('ui-button-text');\n", | |
| " tooltip_span.html(tooltip);\n", | |
| "\n", | |
| " button.append(icon_img);\n", | |
| " button.append(tooltip_span);\n", | |
| "\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " var fmt_picker_span = $('<span/>');\n", | |
| "\n", | |
| " var fmt_picker = $('<select/>');\n", | |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
| " fmt_picker_span.append(fmt_picker);\n", | |
| " nav_element.append(fmt_picker_span);\n", | |
| " this.format_dropdown = fmt_picker[0];\n", | |
| "\n", | |
| " for (var ind in mpl.extensions) {\n", | |
| " var fmt = mpl.extensions[ind];\n", | |
| " var option = $(\n", | |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
| " fmt_picker.append(option)\n", | |
| " }\n", | |
| "\n", | |
| " // Add hover states to the ui-buttons\n", | |
| " $( \".ui-button\" ).hover(\n", | |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", | |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
| " );\n", | |
| "\n", | |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
| " // which will in turn request a refresh of the image.\n", | |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", | |
| " properties['type'] = type;\n", | |
| " properties['figure_id'] = this.id;\n", | |
| " this.ws.send(JSON.stringify(properties));\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.send_draw_message = function() {\n", | |
| " if (!this.waiting) {\n", | |
| " this.waiting = true;\n", | |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " var format_dropdown = fig.format_dropdown;\n", | |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
| " fig.ondownload(fig, format);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
| " var size = msg['size'];\n", | |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
| " fig._resize_canvas(size[0], size[1]);\n", | |
| " fig.send_message(\"refresh\", {});\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
| " var x0 = msg['x0'];\n", | |
| " var y0 = fig.canvas.height - msg['y0'];\n", | |
| " var x1 = msg['x1'];\n", | |
| " var y1 = fig.canvas.height - msg['y1'];\n", | |
| " x0 = Math.floor(x0) + 0.5;\n", | |
| " y0 = Math.floor(y0) + 0.5;\n", | |
| " x1 = Math.floor(x1) + 0.5;\n", | |
| " y1 = Math.floor(y1) + 0.5;\n", | |
| " var min_x = Math.min(x0, x1);\n", | |
| " var min_y = Math.min(y0, y1);\n", | |
| " var width = Math.abs(x1 - x0);\n", | |
| " var height = Math.abs(y1 - y0);\n", | |
| "\n", | |
| " fig.rubberband_context.clearRect(\n", | |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
| "\n", | |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
| " // Updates the figure title.\n", | |
| " fig.header.textContent = msg['label'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
| " var cursor = msg['cursor'];\n", | |
| " switch(cursor)\n", | |
| " {\n", | |
| " case 0:\n", | |
| " cursor = 'pointer';\n", | |
| " break;\n", | |
| " case 1:\n", | |
| " cursor = 'default';\n", | |
| " break;\n", | |
| " case 2:\n", | |
| " cursor = 'crosshair';\n", | |
| " break;\n", | |
| " case 3:\n", | |
| " cursor = 'move';\n", | |
| " break;\n", | |
| " }\n", | |
| " fig.rubberband_canvas.style.cursor = cursor;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
| " fig.message.textContent = msg['message'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
| " // Request the server to send over a new figure.\n", | |
| " fig.send_draw_message();\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
| " fig.image_mode = msg['mode'];\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Called whenever the canvas gets updated.\n", | |
| " this.send_message(\"ack\", {});\n", | |
| "}\n", | |
| "\n", | |
| "// A function to construct a web socket function for onmessage handling.\n", | |
| "// Called in the figure constructor.\n", | |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
| " return function socket_on_message(evt) {\n", | |
| " if (evt.data instanceof Blob) {\n", | |
| " /* FIXME: We get \"Resource interpreted as Image but\n", | |
| " * transferred with MIME type text/plain:\" errors on\n", | |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", | |
| " * to be part of the websocket stream */\n", | |
| " evt.data.type = \"image/png\";\n", | |
| "\n", | |
| " /* Free the memory for the previous frames */\n", | |
| " if (fig.imageObj.src) {\n", | |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", | |
| " fig.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
| " evt.data);\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
| " fig.imageObj.src = evt.data;\n", | |
| " fig.updated_canvas_event();\n", | |
| " fig.waiting = false;\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var msg = JSON.parse(evt.data);\n", | |
| " var msg_type = msg['type'];\n", | |
| "\n", | |
| " // Call the \"handle_{type}\" callback, which takes\n", | |
| " // the figure and JSON message as its only arguments.\n", | |
| " try {\n", | |
| " var callback = fig[\"handle_\" + msg_type];\n", | |
| " } catch (e) {\n", | |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " if (callback) {\n", | |
| " try {\n", | |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
| " callback(fig, msg);\n", | |
| " } catch (e) {\n", | |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
| " }\n", | |
| " }\n", | |
| " };\n", | |
| "}\n", | |
| "\n", | |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
| "mpl.findpos = function(e) {\n", | |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
| " var targ;\n", | |
| " if (!e)\n", | |
| " e = window.event;\n", | |
| " if (e.target)\n", | |
| " targ = e.target;\n", | |
| " else if (e.srcElement)\n", | |
| " targ = e.srcElement;\n", | |
| " if (targ.nodeType == 3) // defeat Safari bug\n", | |
| " targ = targ.parentNode;\n", | |
| "\n", | |
| " // jQuery normalizes the pageX and pageY\n", | |
| " // pageX,Y are the mouse positions relative to the document\n", | |
| " // offset() returns the position of the element relative to the document\n", | |
| " var x = e.pageX - $(targ).offset().left;\n", | |
| " var y = e.pageY - $(targ).offset().top;\n", | |
| "\n", | |
| " return {\"x\": x, \"y\": y};\n", | |
| "};\n", | |
| "\n", | |
| "/*\n", | |
| " * return a copy of an object with only non-object keys\n", | |
| " * we need this to avoid circular references\n", | |
| " * http://stackoverflow.com/a/24161582/3208463\n", | |
| " */\n", | |
| "function simpleKeys (original) {\n", | |
| " return Object.keys(original).reduce(function (obj, key) {\n", | |
| " if (typeof original[key] !== 'object')\n", | |
| " obj[key] = original[key]\n", | |
| " return obj;\n", | |
| " }, {});\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
| " var canvas_pos = mpl.findpos(event)\n", | |
| "\n", | |
| " if (name === 'button_press')\n", | |
| " {\n", | |
| " this.canvas.focus();\n", | |
| " this.canvas_div.focus();\n", | |
| " }\n", | |
| "\n", | |
| " var x = canvas_pos.x;\n", | |
| " var y = canvas_pos.y;\n", | |
| "\n", | |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", | |
| " step: event.step,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| "\n", | |
| " /* This prevents the web browser from automatically changing to\n", | |
| " * the text insertion cursor when the button is pressed. We want\n", | |
| " * to control all of the cursor setting manually through the\n", | |
| " * 'cursor' event from matplotlib */\n", | |
| " event.preventDefault();\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " // Handle any extra behaviour associated with a key event\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.key_event = function(event, name) {\n", | |
| "\n", | |
| " // Prevent repeat events\n", | |
| " if (name == 'key_press')\n", | |
| " {\n", | |
| " if (event.which === this._key)\n", | |
| " return;\n", | |
| " else\n", | |
| " this._key = event.which;\n", | |
| " }\n", | |
| " if (name == 'key_release')\n", | |
| " this._key = null;\n", | |
| "\n", | |
| " var value = '';\n", | |
| " if (event.ctrlKey && event.which != 17)\n", | |
| " value += \"ctrl+\";\n", | |
| " if (event.altKey && event.which != 18)\n", | |
| " value += \"alt+\";\n", | |
| " if (event.shiftKey && event.which != 16)\n", | |
| " value += \"shift+\";\n", | |
| "\n", | |
| " value += 'k';\n", | |
| " value += event.which.toString();\n", | |
| "\n", | |
| " this._key_event_extra(event, name);\n", | |
| "\n", | |
| " this.send_message(name, {key: value,\n", | |
| " guiEvent: simpleKeys(event)});\n", | |
| " return false;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
| " if (name == 'download') {\n", | |
| " this.handle_save(this, null);\n", | |
| " } else {\n", | |
| " this.send_message(\"toolbar_button\", {name: name});\n", | |
| " }\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
| " this.message.textContent = tooltip;\n", | |
| "};\n", | |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
| "\n", | |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
| "\n", | |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
| " // object with the appropriate methods. Currently this is a non binary\n", | |
| " // socket, so there is still some room for performance tuning.\n", | |
| " var ws = {};\n", | |
| "\n", | |
| " ws.close = function() {\n", | |
| " comm.close()\n", | |
| " };\n", | |
| " ws.send = function(m) {\n", | |
| " //console.log('sending', m);\n", | |
| " comm.send(m);\n", | |
| " };\n", | |
| " // Register the callback with on_msg.\n", | |
| " comm.on_msg(function(msg) {\n", | |
| " //console.log('receiving', msg['content']['data'], msg);\n", | |
| " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
| " ws.onmessage(msg['content']['data'])\n", | |
| " });\n", | |
| " return ws;\n", | |
| "}\n", | |
| "\n", | |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", | |
| " // This is the function which gets called when the mpl process\n", | |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
| "\n", | |
| " var id = msg.content.data.id;\n", | |
| " // Get hold of the div created by the display call when the Comm\n", | |
| " // socket was opened in Python.\n", | |
| " var element = $(\"#\" + id);\n", | |
| " var ws_proxy = comm_websocket_adapter(comm)\n", | |
| "\n", | |
| " function ondownload(figure, format) {\n", | |
| " window.open(figure.imageObj.src);\n", | |
| " }\n", | |
| "\n", | |
| " var fig = new mpl.figure(id, ws_proxy,\n", | |
| " ondownload,\n", | |
| " element.get(0));\n", | |
| "\n", | |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
| " // web socket which is closed, not our websocket->open comm proxy.\n", | |
| " ws_proxy.onopen();\n", | |
| "\n", | |
| " fig.parent_element = element.get(0);\n", | |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
| " if (!fig.cell_info) {\n", | |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", | |
| " return;\n", | |
| " }\n", | |
| "\n", | |
| " var output_index = fig.cell_info[2]\n", | |
| " var cell = fig.cell_info[0];\n", | |
| "\n", | |
| "};\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
| " fig.root.unbind('remove')\n", | |
| "\n", | |
| " // Update the output cell to use the data from the current canvas.\n", | |
| " fig.push_to_output();\n", | |
| " var dataURL = fig.canvas.toDataURL();\n", | |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
| " // the notebook keyboard shortcuts fail.\n", | |
| " IPython.keyboard_manager.enable()\n", | |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
| " fig.close_ws(fig, msg);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
| " fig.send_message('closing', msg);\n", | |
| " // fig.ws.close()\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
| " // Turn the data on the canvas into data in the output cell.\n", | |
| " var dataURL = this.canvas.toDataURL();\n", | |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", | |
| " // Tell IPython that the notebook contents must change.\n", | |
| " IPython.notebook.set_dirty(true);\n", | |
| " this.send_message(\"ack\", {});\n", | |
| " var fig = this;\n", | |
| " // Wait a second, then push the new image to the DOM so\n", | |
| " // that it is saved nicely (might be nice to debounce this).\n", | |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._init_toolbar = function() {\n", | |
| " var fig = this;\n", | |
| "\n", | |
| " var nav_element = $('<div/>')\n", | |
| " nav_element.attr('style', 'width: 100%');\n", | |
| " this.root.append(nav_element);\n", | |
| "\n", | |
| " // Define a callback function for later on.\n", | |
| " function toolbar_event(event) {\n", | |
| " return fig.toolbar_button_onclick(event['data']);\n", | |
| " }\n", | |
| " function toolbar_mouse_event(event) {\n", | |
| " return fig.toolbar_button_onmouseover(event['data']);\n", | |
| " }\n", | |
| "\n", | |
| " for(var toolbar_ind in mpl.toolbar_items){\n", | |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
| "\n", | |
| " if (!name) { continue; };\n", | |
| "\n", | |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
| " button.click(method_name, toolbar_event);\n", | |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", | |
| " nav_element.append(button);\n", | |
| " }\n", | |
| "\n", | |
| " // Add the status bar.\n", | |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
| " nav_element.append(status_bar);\n", | |
| " this.message = status_bar[0];\n", | |
| "\n", | |
| " // Add the close button to the window.\n", | |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
| " buttongrp.append(button);\n", | |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
| " titlebar.prepend(buttongrp);\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._root_extra_style = function(el){\n", | |
| " var fig = this\n", | |
| " el.on(\"remove\", function(){\n", | |
| "\tfig.close_ws(fig, {});\n", | |
| " });\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
| " // this is important to make the div 'focusable\n", | |
| " el.attr('tabindex', 0)\n", | |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
| " // off when our div gets focus\n", | |
| "\n", | |
| " // location in version 3\n", | |
| " if (IPython.notebook.keyboard_manager) {\n", | |
| " IPython.notebook.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| " else {\n", | |
| " // location in version 2\n", | |
| " IPython.keyboard_manager.register_events(el);\n", | |
| " }\n", | |
| "\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
| " var manager = IPython.notebook.keyboard_manager;\n", | |
| " if (!manager)\n", | |
| " manager = IPython.keyboard_manager;\n", | |
| "\n", | |
| " // Check for shift+enter\n", | |
| " if (event.shiftKey && event.which == 13) {\n", | |
| " this.canvas_div.blur();\n", | |
| " // select the cell after this one\n", | |
| " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
| " IPython.notebook.select(index + 1);\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
| " fig.ondownload(fig, null);\n", | |
| "}\n", | |
| "\n", | |
| "\n", | |
| "mpl.find_output_cell = function(html_output) {\n", | |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
| " // IPython event is triggered only after the cells have been serialised, which for\n", | |
| " // our purposes (turning an active figure into a static one), is too late.\n", | |
| " var cells = IPython.notebook.get_cells();\n", | |
| " var ncells = cells.length;\n", | |
| " for (var i=0; i<ncells; i++) {\n", | |
| " var cell = cells[i];\n", | |
| " if (cell.cell_type === 'code'){\n", | |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
| " var data = cell.output_area.outputs[j];\n", | |
| " if (data.data) {\n", | |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", | |
| " data = data.data;\n", | |
| " }\n", | |
| " if (data['text/html'] == html_output) {\n", | |
| " return [cell, data, j];\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| "}\n", | |
| "\n", | |
| "// Register the function which deals with the matplotlib target/channel.\n", | |
| "// The kernel may be null if the page has been refreshed.\n", | |
| "if (IPython.notebook.kernel != null) {\n", | |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
| "}\n" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.Javascript object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<img src=\"data:image/png;base64,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\">" | |
| ], | |
| "text/plain": [ | |
| "<IPython.core.display.HTML object>" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| }, | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<matplotlib.contour.QuadContourSet at 0x7f69ad28ab38>" | |
| ] | |
| }, | |
| "execution_count": 25, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "# Plot the results\n", | |
| "fig, ax = plt.subplots(1)\n", | |
| "ax.scatter(x,y, c=c)\n", | |
| "xx,yy = np.meshgrid(np.arange(0,120, 0.001), np.arange(0,4,1))\n", | |
| "plot_boundaries_2da(ax, clf, xx, yy, cmap='jet', alpha=0.5)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 26, | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2018-04-18T10:15:15.103576Z", | |
| "start_time": "2018-04-18T10:15:15.098756Z" | |
| } | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Log likelihood is: -18.773908713488765\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "loglikelihood = np.sum(clf.predict_log_proba(phi)[y_one_hot])\n", | |
| "print('Log likelihood is: {}'.format(loglikelihood))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Both the log likelihood has improved and clearly so have our decision boundaries. This question was all about thinking how to project our data to a space where it is linearly separable." | |
| ] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python", | |
| "language": "python", | |
| "name": "python3" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "ipython", | |
| "version": 3 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython3", | |
| "version": "3.5.2" | |
| }, | |
| "toc": { | |
| "nav_menu": { | |
| "height": "30px", | |
| "width": "252px" | |
| }, | |
| "navigate_menu": true, | |
| "number_sections": true, | |
| "sideBar": true, | |
| "threshold": 4, | |
| "toc_cell": false, | |
| "toc_section_display": "block", | |
| "toc_window_display": false | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 2 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment