{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Section 7.1: A First Plotly Streaming Plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Welcome to Plotly's Python API User Guide. \n", "\n", "> Links to the other sections can be found on the User Guide's [homepage](https://plotly.com/python/user-guide#Table-of-Contents:) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Section 7 is divided, into separate notebooks, as follows:\n", "\n", "* [7.0 Streaming API introduction](https://plotly.com/python/intro_streaming)\n", "\n", "* [7.1 A First Plotly Streaming Plot](https://plotly.com/python/streaming_part1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Check which version is installed on your machine and please upgrade if needed. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'1.9.5'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# (*) Import plotly package\n", "import plotly\n", " \n", "# Check plolty version (if not latest, please upgrade)\n", "plotly.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "Import a few modules and sign in to Plotly using our credentials file:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# (*) To communicate with Plotly's server, sign in with credentials file\n", "import plotly.plotly as py \n", " \n", "# (*) Useful Python/Plotly tools\n", "import plotly.tools as tls \n", " \n", "# (*) Graph objects to piece together plots\n", "from plotly.graph_objs import *\n", " \n", "import numpy as np # (*) numpy for math functions and arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, retrieve the stream ids in our credentials file as set up in subsection 7.1:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [], "source": [ "stream_ids = tls.get_credentials_file()['stream_ids']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.1 A first Plotly streaming plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Making Plotly streaming plots sums up to working with two objects:\n", "\n", "* A stream id object (`Stream` in the `plotly.graph_objs` module),\n", "\n", "* A stream link object (`py.Stream`).\n", "\n", "The stream id object is a graph object that embeds a particular stream id to each of your plot's traces. As all graph objects, the stream id object is equipped with extensive `help()` documentation, key validation and a nested update method. In brief, the stream id object initializes the connection between a trace in your Plotly graph and a data stream(for that trace).\n", "\n", "Meanwhile, the stream link object, like all objects in the `plotly.plotly` module, links content in your Python/IPython session to Plotly's servers. More precisely, it is the interface that updates the data in the plotted traces in real-time(as identified with the unique stream id used in the corresponding stream id object).\n", "\n", "If you find the `Stream`/`py.Stream` terminalogy too confusing --- and you do not mind not having access to the methods associated with Plotly graph objects' --- you can forgo the use of the stream id object and substitute it by a python `dict()` in the following examples. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, we start by making an instance of the stream id object:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on class Stream in module plotly.graph_objs.graph_objs:\n", "\n", "class Stream(PlotlyDict)\n", " | Valid attributes for 'stream' at path [] under parents ():\n", " | \n", " | ['token', 'maxpoints']\n", " | \n", " | Run `.help('attribute')` on any of the above.\n", " | '' is the object at []\n", " | \n", " | Method resolution order:\n", " | Stream\n", " | PlotlyDict\n", " | __builtin__.dict\n", " | PlotlyBase\n", " | __builtin__.object\n", " | \n", " | Methods inherited from PlotlyDict:\n", " | \n", " | __copy__(self)\n", " | \n", " | __deepcopy__(self, memodict={})\n", " | \n", " | __dir__(self)\n", " | Dynamically return the existing and possible attributes.\n", " | \n", " | __getattr__(self, key)\n", " | Python only calls this when key is missing!\n", " | \n", " | __getitem__(self, key)\n", " | Calls __missing__ when key is not found. May mutate object.\n", " | \n", " | __init__(self, *args, **kwargs)\n", " | \n", " | __missing__(self, key)\n", " | Mimics defaultdict. This is called from __getitem__ when key DNE.\n", " | \n", " | __setattr__(self, key, value)\n", " | Maps __setattr__ onto __setitem__\n", " | \n", " | __setitem__(self, key, value, _raise=True)\n", " | Validates/Converts values which should be Graph Objects.\n", " | \n", " | force_clean(self, **kwargs)\n", " | Recursively remove empty/None values.\n", " | \n", " | get_data(self, flatten=False)\n", " | Returns the JSON for the plot with non-data elements stripped.\n", " | \n", " | get_ordered(self, **kwargs)\n", " | Return a predictable, OrderedDict version of self.\n", " | \n", " | help(self, attribute=None, return_help=False)\n", " | Print help string for this object or an attribute of this object.\n", " | \n", " | :param (str) attribute: A valid attribute string for this object.\n", " | :param (bool) return_help: Return help_string instead of printing it?\n", " | :return: (None|str)\n", " | \n", " | strip_style(self)\n", " | Recursively strip style from the current representation.\n", " | \n", " | All PlotlyDicts and PlotlyLists are guaranteed to survive the\n", " | stripping process, though they made be left empty. This is allowable.\n", " | \n", " | Keys that will be stripped in this process are tagged with\n", " | `'type': 'style'` in graph_objs_meta.json. Note that a key tagged as\n", " | style, but with an array as a value may still be considered data.\n", " | \n", " | to_string(self, level=0, indent=4, eol='\\n', pretty=True, max_chars=80)\n", " | Returns a formatted string showing graph_obj constructors.\n", " | \n", " | :param (int) level: The number of indentations to start with.\n", " | :param (int) indent: The indentation amount.\n", " | :param (str) eol: The end of line character(s).\n", " | :param (bool) pretty: Curtail long list output with a '..' ?\n", " | :param (int) max_chars: The max characters per line.\n", " | \n", " | Example:\n", " | \n", " | print(obj.to_string())\n", " | \n", " | update(self, dict1=None, **dict2)\n", " | Update current dict with dict1 and then dict2.\n", " | \n", " | This recursively updates the structure of the original dictionary-like\n", " | object with the new entries in the second and third objects. This\n", " | allows users to update with large, nested structures.\n", " | \n", " | Note, because the dict2 packs up all the keyword arguments, you can\n", " | specify the changes as a list of keyword agruments.\n", " | \n", " | Examples:\n", " | # update with dict\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj.update(update_dict)\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | # update with list of keyword arguments\n", " | obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))\n", " | obj.update(title='new title', xaxis=dict(domain=[0,.8]))\n", " | obj\n", " | {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}\n", " | \n", " | This 'fully' supports duck-typing in that the call signature is\n", " | identical, however this differs slightly from the normal update\n", " | method provided by Python's dictionaries.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors inherited from PlotlyDict:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from __builtin__.dict:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes inherited from __builtin__.dict:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", " | \n", " | ----------------------------------------------------------------------\n", " | Methods inherited from PlotlyBase:\n", " | \n", " | to_graph_objs(self, **kwargs)\n", " | Everything is cast into graph_objs. Here for backwards compat.\n", " | \n", " | validate(self)\n", " | Everything is *always* validated now. keep for backwards compat.\n", "\n" ] } ], "source": [ "help(Stream) # call help() to see the specifications of the Stream object!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Get stream id from stream id list \n", "stream_id = stream_ids[0]\n", "\n", "# Make instance of stream id object \n", "stream = Stream(\n", " token=stream_id, # (!) link stream id to 'token' key\n", " maxpoints=80 # (!) keep a max of 80 pts on screen\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `'maxpoints'` key set the maxiumum number of points to keep on the plot from an incoming stream." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Streaming Plotly plots are initialized with a standard (i.e. REST API) call to `py.plot()` or `py.iplot()` that embeds your unique stream ids in each of the plot's traces. \n", "\n", "Each Plotly trace object (e.g. `Scatter`, `Bar`, `Histogram`, etc. More in Section 0) has a `'stream'` key made available to link the trace object in question to a corresponding stream object.\n", "\n", "In our first example, we link a scatter trace object to the stream:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Initialize trace of streaming plot by embedding the unique stream_id\n", "trace1 = Scatter(\n", " x=[],\n", " y=[],\n", " mode='lines+markers',\n", " stream=stream # (!) embed stream id, 1 per trace\n", ")\n", "\n", "data = Data([trace1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, add a title to the layout object and initialize your Plotly streaming plot:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Add title to layout object\n", "layout = Layout(title='Time Series')\n", "\n", "# Make a figure object\n", "fig = Figure(data=data, layout=layout)\n", "\n", "# (@) Send fig to Plotly, initialize streaming plot, open new tab\n", "unique_url = py.plot(fig, filename='s7_first-stream')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Great! Your Plotly streaming plot is intialized. Here's a screenshot:\n", "\n", "\n", "\n", "
\n", "\n", "Now, let's add data on top of it, or more precisely, send a *stream* of data to it. \n", "\n", "So, first" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on class Stream in module plotly.plotly.plotly:\n", "\n", "class Stream\n", " | Interface to Plotly's real-time graphing API.\n", " | \n", " | Initialize a Stream object with a stream_id\n", " | found in https://plotly.com/settings.\n", " | Real-time graphs are initialized with a call to `plot` that embeds\n", " | your unique `stream_id`s in each of the graph's traces. The `Stream`\n", " | interface plots data to these traces, as identified with the unique\n", " | stream_id, in real-time.\n", " | Every viewer of the graph sees the same data at the same time.\n", " | \n", " | View examples and tutorials here:\n", " | https://plotly.com/python/streaming/\n", " | \n", " | Stream example:\n", " | # Initialize a streaming graph\n", " | # by embedding stream_id's in the graph's traces\n", " | import plotly.plotly as py\n", " | from plotly.graph_objs import Data, Scatter, Stream\n", " | stream_id = \"your_stream_id\" # See https://plotly.com/settings\n", " | py.plot(Data([Scatter(x=[], y=[],\n", " | stream=Stream(token=stream_id, maxpoints=100))]))\n", " | # Stream data to the import trace\n", " | stream = Stream(stream_id) # Initialize a stream object\n", " | stream.open() # Open the stream\n", " | stream.write(dict(x=1, y=1)) # Plot (1, 1) in your graph\n", " | \n", " | Methods defined here:\n", " | \n", " | __init__(self, stream_id)\n", " | Initialize a Stream object with your unique stream_id.\n", " | Find your stream_id at https://plotly.com/settings.\n", " | \n", " | For more help, see: `help(plotly.plotly.Stream)`\n", " | or see examples and tutorials here:\n", " | https://plotly.com/python/streaming/\n", " | \n", " | close(self)\n", " | Close the stream connection to plotly's streaming servers.\n", " | \n", " | For more help, see: `help(plotly.plotly.Stream)`\n", " | or see examples and tutorials here:\n", " | https://plotly.com/python/streaming/\n", " | \n", " | heartbeat(self, reconnect_on=(200, '', 408))\n", " | Keep stream alive. Streams will close after ~1 min of inactivity.\n", " | \n", " | If the interval between stream writes is > 30 seconds, you should\n", " | consider adding a heartbeat between your stream.write() calls like so:\n", " | >>> stream.heartbeat()\n", " | \n", " | open(self)\n", " | Open streaming connection to plotly.\n", " | \n", " | For more help, see: `help(plotly.plotly.Stream)`\n", " | or see examples and tutorials here:\n", " | https://plotly.com/python/streaming/\n", " | \n", " | write(self, trace, layout=None, validate=True, reconnect_on=(200, '', 408))\n", " | Write to an open stream.\n", " | \n", " | Once you've instantiated a 'Stream' object with a 'stream_id',\n", " | you can 'write' to it in real time.\n", " | \n", " | positional arguments:\n", " | trace - A valid plotly trace object (e.g., Scatter, Heatmap, etc.).\n", " | Not all keys in these are `stremable` run help(Obj) on the type\n", " | of trace your trying to stream, for each valid key, if the key\n", " | is streamable, it will say 'streamable = True'. Trace objects\n", " | must be dictionary-like.\n", " | \n", " | keyword arguments:\n", " | layout (default=None) - A valid Layout object\n", " | Run help(plotly.graph_objs.Layout)\n", " | validate (default = True) - Validate this stream before sending?\n", " | This will catch local errors if set to True.\n", " | \n", " | Some valid keys for trace dictionaries:\n", " | 'x', 'y', 'text', 'z', 'marker', 'line'\n", " | \n", " | Examples:\n", " | >>> write(dict(x=1, y=2)) # assumes 'scatter' type\n", " | >>> write(Bar(x=[1, 2, 3], y=[10, 20, 30]))\n", " | >>> write(Scatter(x=1, y=2, text='scatter text'))\n", " | >>> write(Scatter(x=1, y=3, marker=Marker(color='blue')))\n", " | >>> write(Heatmap(z=[[1, 2, 3], [4, 5, 6]]))\n", " | \n", " | The connection to plotly's servers is checked before writing\n", " | and reconnected if disconnected and if the response status code\n", " | is in `reconnect_on`.\n", " | \n", " | For more help, see: `help(plotly.plotly.Stream)`\n", " | or see examples and tutorials here:\n", " | http://nbviewer.ipython.org/github/plotly/python-user-guide/blob/master/s7_streaming/s7_streaming.ipynb\n", "\n" ] } ], "source": [ "help(py.Stream) # run help() of the Stream link object" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# (@) Make instance of the `Stream link` object \n", "# with the same stream id as the `Stream Id` object\n", "s = py.Stream(stream_id)\n", "\n", "# (@) Open the stream\n", "s.open()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now use the Stream Link object `s` in order to `stream` data to our plot. \n", "\n", "As an example, we will send a time stream and some random numbers(for 200 iterations):" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# (*) Import module keep track and format current time\n", "import datetime \n", "import time \n", " \n", "i = 0 # a counter\n", "k = 5 # some shape parameter\n", "N = 200 # number of points to be plotted\n", "\n", "# Delay start of stream by 5 sec (time to switch tabs)\n", "time.sleep(5) \n", "\n", "while i\n", " " ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import YouTubeVideo\n", "YouTubeVideo('OVQ2Guypp_M', width='100%', height='350')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It took Plotly around 15 seconds to plot the 200 data points sent in the data stream. After that, the generated plot looks like any other Plotly plot.\n", "\n", "With that said, if you have enough computer resources to let a server run indefinitely, why not have \n", "\n", " >>> while True: \n", "\n", "as the while-loop expression and never close the stream. \n", "\n", "Luckily, it turns out that Plotly has access to such computer resources; a simulation generated using the same code as the above has been running since March 2014.\n", "\n", "This plot is embedded below:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Embed never-ending time series streaming plot\n", "tls.embed('streaming-demos','12')\n", "\n", "# Note that the time point correspond to internal clock of the servers, \n", "# that is UTC time." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anyone can view your streaming graph in real-time. All viewers will see the same data simultaneously (try it! Open up this notebook up in two different browser windows and observer\n", "that the graphs are plotting identical data!).\n", "\n", "Simply put, Plotly's streaming API is awesome!\n", "\n", "In brief: to make a Plotly streaming plot:\n", "\n", "1. Make a `stream id object` (`Stream` in the `plotly.graph_objs` module) containing the `stream id`(which is found in the **settings** of your Plotly account) and the maximum number of points to be keep on screen (which is optional).\n", "\n", "2. Provide the `stream id object` as the key value for the `stream` attribute in your trace object. \n", "\n", "3. Make a `stream link object` (`py.Stream`) containing the same stream id as the `stream id object` and open the stream with the `.open()` method.\n", "\n", "4. Write data to the `stream link object` with the `.write()` method. When done, close the stream with the `.close()` method.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here are the links to the subsections' notebooks:\n", "\n", "* [7.0 Streaming API introduction](https://plotly.com/python/intro_streaming)\n", "* [7.1 A first Plotly streaming plot](https://plotly.com/python/streaming_part1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \n", "
\n", "\n", "

Got Questions or Feedback?

\n", "\n", "Reach us here at: Plotly Community\n", "\n", "

What's going on at Plotly?

\n", "Check out our twitter: \n", "@plotlygraphs\n", "\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Requirement already up-to-date: publisher in /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages\r\n" ] } ], "source": [ "from IPython.display import display, HTML\n", "\n", "display(HTML(''))\n", "display(HTML(''))\n", "\n", "! pip install publisher --upgrade\n", "import publisher\n", "publisher.publish(\n", " 's7_streaming_p1-first-stream', 'python/streaming_part1//', 'Getting Started with Plotly Streaming',\n", " 'Getting Started with Plotly Streaming',\n", " title = 'Getting Started with Plotly Streaming',\n", " thumbnail='', language='python',\n", " layout='user-guide', has_thumbnail='false') " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.9" } }, "nbformat": 4, "nbformat_minor": 0 }