Plotly Streaming in Python/v3

Plotly Streaming


Note: this page is part of the documentation for version 3 of Plotly.py, which is not the most recent version.
See our Version 4 Migration Guide for information about how to upgrade.

Streaming Support

Streaming is no longer supported in Chart Studio Cloud.
Streaming is still available as part of Chart Studio Enterprise. Additionally, Dash supports streaming, as demonstrated by the Dash Wind Streaming example.

Getting Started with Streaming

In [2]:
import numpy as np
import plotly.plotly as py
import plotly.tools as tls
import plotly.graph_objs as go

Before you start streaming, you're going to need some stream tokens. You will need one unique stream token for every trace object you wish to stream to. Thus if you have two traces that you want to plot and stream, you're going to require two unique stream tokens. Notice that more tokens can be added via the settings section of your Plotly profile: https://plotly.com/settings/api

Now in the same way that you set your credentials, as shown in Getting Started, you can add stream tokens to your credentials file.

In [3]:
stream_ids = tls.get_credentials_file()['stream_ids']
print stream_ids
[u'a3yx8ev3pg', u'2w50b45d0z', u'cew1sbgo4s', u'nhlxf6ig3d', u'n9s75hv9ic', u'96kd717ava', u'v6f5oeb8ut', u'4lm5a0gsr8', u'0xhh453c6m']

You'll see that stream_ids will contain a list of the stream tokens we added to the credentials file.

An Example to Get You Started

Now that you have some stream tokens to play with, we're going to go over how we're going to put these into action. There are two main objects that will be created and used for streaming:

  • Stream Id Object
  • Stream link Object

We're going to look at these objects sequentially as we work through our first streaming example. For our first example, we're going to be streaming random data to a single scatter trace, and get something that behaves like the following:

Stream Id Object

The Stream Id Object comes bundled in the graph_objs package. We can then call help to see the description of this object:

In [4]:
help(go.Stream)
Help on class Stream in module plotly.graph_objs.graph_objs:

class Stream(PlotlyDict)
 |  Valid attributes for 'stream' at path [] under parents ():
 |
 |      ['token', 'maxpoints']
 |
 |  Run `<stream-object>.help('attribute')` on any of the above.
 |  '<stream-object>' is the object at []
 |
 |  Method resolution order:
 |      Stream
 |      PlotlyDict
 |      __builtin__.dict
 |      PlotlyBase
 |      __builtin__.object
 |
 |  Methods inherited from PlotlyDict:
 |
 |  __copy__(self)
 |
 |  __deepcopy__(self, memodict={})
 |
 |  __dir__(self)
 |      Dynamically return the existing and possible attributes.
 |
 |  __getattr__(self, key)
 |      Python only calls this when key is missing!
 |
 |  __getitem__(self, key)
 |      Calls __missing__ when key is not found. May mutate object.
 |
 |  __init__(self, *args, **kwargs)
 |
 |  __missing__(self, key)
 |      Mimics defaultdict. This is called from __getitem__ when key DNE.
 |
 |  __setattr__(self, key, value)
 |      Maps __setattr__ onto __setitem__
 |
 |  __setitem__(self, key, value, _raise=True)
 |      Validates/Converts values which should be Graph Objects.
 |
 |  force_clean(self, **kwargs)
 |      Recursively remove empty/None values.
 |
 |  get_data(self, flatten=False)
 |      Returns the JSON for the plot with non-data elements stripped.
 |
 |  get_ordered(self, **kwargs)
 |      Return a predictable, OrderedDict version of self.
 |
 |  help(self, attribute=None, return_help=False)
 |      Print help string for this object or an attribute of this object.
 |
 |      :param (str) attribute: A valid attribute string for this object.
 |      :param (bool) return_help: Return help_string instead of printing it?
 |      :return: (None|str)
 |
 |  strip_style(self)
 |      Recursively strip style from the current representation.
 |
 |      All PlotlyDicts and PlotlyLists are guaranteed to survive the
 |      stripping process, though they made be left empty. This is allowable.
 |
 |      Keys that will be stripped in this process are tagged with
 |      `'type': 'style'` in graph_objs_meta.json. Note that a key tagged as
 |      style, but with an array as a value may still be considered data.
 |
 |  to_string(self, level=0, indent=4, eol='\n', pretty=True, max_chars=80)
 |      Returns a formatted string showing graph_obj constructors.
 |
 |      :param (int) level: The number of indentations to start with.
 |      :param (int) indent: The indentation amount.
 |      :param (str) eol: The end of line character(s).
 |      :param (bool) pretty: Curtail long list output with a '..' ?
 |      :param (int) max_chars: The max characters per line.
 |
 |      Example:
 |
 |          print(obj.to_string())
 |
 |  update(self, dict1=None, **dict2)
 |      Update current dict with dict1 and then dict2.
 |
 |      This recursively updates the structure of the original dictionary-like
 |      object with the new entries in the second and third objects. This
 |      allows users to update with large, nested structures.
 |
 |      Note, because the dict2 packs up all the keyword arguments, you can
 |      specify the changes as a list of keyword agruments.
 |
 |      Examples:
 |      # update with dict
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj.update(update_dict)
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |
 |      # update with list of keyword arguments
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      obj.update(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |
 |      This 'fully' supports duck-typing in that the call signature is
 |      identical, however this differs slightly from the normal update
 |      method provided by Python's dictionaries.
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from PlotlyDict:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
 |
 |  ----------------------------------------------------------------------
 |  Methods inherited from __builtin__.dict:
 |
 |  __cmp__(...)
 |      x.__cmp__(y) <==> cmp(x,y)
 |
 |  __contains__(...)
 |      D.__contains__(k) -> True if D has a key k, else False
 |
 |  __delitem__(...)
 |      x.__delitem__(y) <==> del x[y]
 |
 |  __eq__(...)
 |      x.__eq__(y) <==> x==y
 |
 |  __ge__(...)
 |      x.__ge__(y) <==> x>=y
 |
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |
 |  __gt__(...)
 |      x.__gt__(y) <==> x>y
 |
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |
 |  __le__(...)
 |      x.__le__(y) <==> x<=y
 |
 |  __len__(...)
 |      x.__len__() <==> len(x)
 |
 |  __lt__(...)
 |      x.__lt__(y) <==> x<y
 |
 |  __ne__(...)
 |      x.__ne__(y) <==> x!=y
 |
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |
 |  fromkeys(...)
 |      dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
 |      v defaults to None.
 |
 |  get(...)
 |      D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
 |
 |  has_key(...)
 |      D.has_key(k) -> True if D has a key k, else False
 |
 |  items(...)
 |      D.items() -> list of D's (key, value) pairs, as 2-tuples
 |
 |  iteritems(...)
 |      D.iteritems() -> an iterator over the (key, value) items of D
 |
 |  iterkeys(...)
 |      D.iterkeys() -> an iterator over the keys of D
 |
 |  itervalues(...)
 |      D.itervalues() -> an iterator over the values of D
 |
 |  keys(...)
 |      D.keys() -> list of D's keys
 |
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |
 |  setdefault(...)
 |      D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
 |
 |  values(...)
 |      D.values() -> list of D's values
 |
 |  viewitems(...)
 |      D.viewitems() -> a set-like object providing a view on D's items
 |
 |  viewkeys(...)
 |      D.viewkeys() -> a set-like object providing a view on D's keys
 |
 |  viewvalues(...)
 |      D.viewvalues() -> an object providing a view on D's values
 |
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from __builtin__.dict:
 |
 |  __hash__ = None
 |
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T
 |
 |  ----------------------------------------------------------------------
 |  Methods inherited from PlotlyBase:
 |
 |  to_graph_objs(self, **kwargs)
 |      Everything is cast into graph_objs. Here for backwards compat.
 |
 |  validate(self)
 |      Everything is *always* validated now. keep for backwards compat.

As we can see, the Stream Id Object is a dictionary-like object that takes two parameters, and has all the methods that are assoicated with dictionaries. We will need one of these objects for each of trace that we wish to stream data to. We'll now create a single stream token for our streaming example, which will include one scatter trace.

In [5]:
# Get stream id from stream id list 
stream_id = stream_ids[0]

# Make instance of stream id object 
stream_1 = go.Stream(
    token=stream_id,  # link stream id to 'token' key
    maxpoints=80      # keep a max of 80 pts on screen
)

The 'maxpoints' key sets the maxiumum number of points to keep on the plotting surface at any given time. More over, if you want to avoid the use of these Stream Id Objects, you can just create a dictionary with at least the token parameter defined, for example:

In [6]:
stream_1 = dict(token=stream_id, maxpoints=60)

Now that we have our Stream Id Object ready to go, we can set up our plot. We do this in the same way that we would any other plot, the only thing is that we now have to set the stream parameter in our trace object.

In [7]:
# Initialize trace of streaming plot by embedding the unique stream_id
trace1 = go.Scatter(
    x=[],
    y=[],
    mode='lines+markers',
    stream=stream_1         # (!) embed stream id, 1 per trace
)

data = go.Data([trace1])

# Add title to layout object
layout = go.Layout(title='Time Series')

# Make a figure object
fig = go.Figure(data=data, layout=layout)

The Stream Link Object is what will be used to communicate with the Plotly server in order to update the data contained in your trace objects. This object is in the plotly.plotly object, an can be reference with py.Stream

In [10]:
help(py.Stream)  # run help() of the Stream link object
Help on class Stream in module plotly.plotly.plotly:

class Stream
 |  Interface to Plotly's real-time graphing API.
 |
 |  Initialize a Stream object with a stream_id
 |  found in https://plotly.com/settings.
 |  Real-time graphs are initialized with a call to `plot` that embeds
 |  your unique `stream_id`s in each of the graph's traces. The `Stream`
 |  interface plots data to these traces, as identified with the unique
 |  stream_id, in real-time.
 |  Every viewer of the graph sees the same data at the same time.
 |
 |  View examples and tutorials here:
 |  https://plotly.com/python/streaming/
 |
 |  Stream example:
 |  # Initialize a streaming graph
 |  # by embedding stream_id's in the graph's traces
 |  import plotly.plotly as py
 |  from plotly.graph_objs import Data, Scatter, Stream
 |  stream_id = "your_stream_id" # See https://plotly.com/settings
 |  py.plot(Data([Scatter(x=[], y=[],
 |                        stream=Stream(token=stream_id, maxpoints=100))]))
 |  # Stream data to the import trace
 |  stream = Stream(stream_id) # Initialize a stream object
 |  stream.open() # Open the stream
 |  stream.write(dict(x=1, y=1)) # Plot (1, 1) in your graph
 |
 |  Methods defined here:
 |
 |  __init__(self, stream_id)
 |      Initialize a Stream object with your unique stream_id.
 |      Find your stream_id at https://plotly.com/settings.
 |
 |      For more help, see: `help(plotly.plotly.Stream)`
 |      or see examples and tutorials here:
 |      https://plotly.com/python/streaming/
 |
 |  close(self)
 |      Close the stream connection to plotly's streaming servers.
 |
 |      For more help, see: `help(plotly.plotly.Stream)`
 |      or see examples and tutorials here:
 |      https://plotly.com/python/streaming/
 |
 |  heartbeat(self, reconnect_on=(200, '', 408))
 |      Keep stream alive. Streams will close after ~1 min of inactivity.
 |
 |      If the interval between stream writes is > 30 seconds, you should
 |      consider adding a heartbeat between your stream.write() calls like so:
 |      >>> stream.heartbeat()
 |
 |  open(self)
 |      Open streaming connection to plotly.
 |
 |      For more help, see: `help(plotly.plotly.Stream)`
 |      or see examples and tutorials here:
 |      https://plotly.com/python/streaming/
 |
 |  write(self, trace, layout=None, validate=True, reconnect_on=(200, '', 408))
 |      Write to an open stream.
 |
 |      Once you've instantiated a 'Stream' object with a 'stream_id',
 |      you can 'write' to it in real time.
 |
 |      positional arguments:
 |      trace - A valid plotly trace object (e.g., Scatter, Heatmap, etc.).
 |              Not all keys in these are `stremable` run help(Obj) on the type
 |              of trace your trying to stream, for each valid key, if the key
 |              is streamable, it will say 'streamable = True'. Trace objects
 |              must be dictionary-like.
 |
 |      keyword arguments:
 |      layout (default=None) - A valid Layout object
 |                              Run help(plotly.graph_objs.Layout)
 |      validate (default = True) - Validate this stream before sending?
 |                                  This will catch local errors if set to
 |                                  True.
 |
 |      Some valid keys for trace dictionaries:
 |          'x', 'y', 'text', 'z', 'marker', 'line'
 |
 |      Examples:
 |      >>> write(dict(x=1, y=2))  # assumes 'scatter' type
 |      >>> write(Bar(x=[1, 2, 3], y=[10, 20, 30]))
 |      >>> write(Scatter(x=1, y=2, text='scatter text'))
 |      >>> write(Scatter(x=1, y=3, marker=Marker(color='blue')))
 |      >>> write(Heatmap(z=[[1, 2, 3], [4, 5, 6]]))
 |
 |      The connection to plotly's servers is checked before writing
 |      and reconnected if disconnected and if the response status code
 |      is in `reconnect_on`.
 |
 |      For more help, see: `help(plotly.plotly.Stream)`
 |      or see examples and tutorials here:
 |      http://nbviewer.ipython.org/github/plotly/python-user-guide/blob/master/s7_streaming/s7_streaming.ipynb

You're going to need to set up one of these stream link objects for each trace you wish to stream data to.
Below we'll set one up for the scatter trace we have in our plot.

In [11]:
# We will provide the stream link object the same token that's associated with the trace we wish to stream to
s = py.Stream(stream_id)

# We then open a connection
s.open()

We can now use the Stream Link object s in order to stream data to our plot.
As an example, we will send a time stream and some random numbers:

In [49]:
# (*) Import module keep track and format current time
import datetime
import time

i = 0    # a counter
k = 5    # some shape parameter

# Delay start of stream by 5 sec (time to switch tabs)
time.sleep(5)

while True:

    # Current time on x-axis, random numbers on y-axis
    x = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
    y = (np.cos(k*i/50.)*np.cos(i/50.)+np.random.randn(1))[0]

    # Send data to your plot
    s.write(dict(x=x, y=y))

    #     Write numbers to stream to append current data on plot,
    #     write lists to overwrite existing data on plot

    time.sleep(1)  # plot a point every second    
# Close the stream when done plotting
s.close()