How to use time staps in ranges using Bokeh server?

Note: This question was also posted here python - How to use time staps in ranges using Bokeh server - Stack Overflow, before I got aware of this mailing list.

How should I use time stamps when I use source.stream in a bokeh app?

Here is a minimal example of what I’m trying to achieve

from bokeh.io import curdoc, output_notebook, show

from bokeh.models import Line, Plot, ColumnDataSource, Range1d
from bokeh.layouts import column
import numpy as np

delta_time = np.timedelta64(1,‘s’)
start_time = np.datetime64(‘2017-01-01T00:00:00’)

tail_length = 30

timerange = Range1d(start_time - tail_length * delta_time, start_time)
source = ColumnDataSource(dict(time=[start_time], y=[0.5]))
plot = Plot(width=200, height=100, x_range=timerange, y_range=Range1d(0,1))
plot.add_glyph(source, Line(x=‘time’,y=‘y’))

def callback():
# update axis
layout.children[0].x_range.start += delta_time
layout.children[0].x_range.end += delta_time

# update data
source.stream(dict(time=[source.data['time'][-1] + delta_time],
                   y=[np.random.rand()]), tail_length)

layout = column(plot)

curdoc().add_root(layout)

move 10 datapoints into the sliding window before the server starts

for _ in range(10):
callback()

then add a new datapoint each second

curdoc().add_periodic_callback(callback, 1000)

``

``

When I start the server, using bokeh serve test.py, the output displays correctly, which indicates that the callback() function works as inteded. But after a second, when the callback is called from curdoc().add_periodic_callback, then it fails to display anything.

To investigate how x_range changes during the callback, I added a print statement print(layout.children[0].x_range.end, type(layout.children[0].x_range.end))
inside the callback() definition. It seems like the type of the x_range coordinates stay the same, but for some bizarre reason, it has a different string representation when the callback is called in curdoc().add_periodic_callback.

bokeh serve test.py
2017-12-01 21:54:45,520 Starting Bokeh server version 0.12.11 (running on Tornado 4.5.2)
2017-12-01 21:54:45,522 Bokeh app running at: http://localhost:5006/test
2017-12-01 21:54:45,522 Starting Bokeh server with process id: 22665
2017-01-01T00:00:01 <class ‘numpy.datetime64’>
2017-01-01T00:00:02 <class ‘numpy.datetime64’>
2017-01-01T00:00:03 <class ‘numpy.datetime64’>
2017-01-01T00:00:04 <class ‘numpy.datetime64’>
2017-01-01T00:00:05 <class ‘numpy.datetime64’>
2017-01-01T00:00:06 <class ‘numpy.datetime64’>
2017-01-01T00:00:07 <class ‘numpy.datetime64’>
2017-01-01T00:00:08 <class ‘numpy.datetime64’>
2017-01-01T00:00:09 <class ‘numpy.datetime64’>
2017-01-01T00:00:10 <class ‘numpy.datetime64’>
2017-12-01 21:54:47,961 200 GET /test (::1) 31.59ms
2017-12-01 21:54:48,381 101 GET /test/ws?bokeh-protocol-version=1.0&bokeh-session-id=jekHIMP5Eo5kjzDDgdMgqba2bNxjbQEpDcBmgET6aHfL (::1) 0.68ms
2017-12-01 21:54:48,381 WebSocket connection opened
2017-12-01 21:54:48,381 ServerConnection created
1483228810001 seconds <class ‘numpy.timedelta64’>
1483228810002 seconds <class ‘numpy.timedelta64’>

What is really going on here?

best regards,

Jon Alm Eriksen

Very interesting behavior! As you can see the 10 callbacks in the

for _ in range(10):
callback()


loop execute when you hit the URL to the bokeh server, those loop through and calculate then results are returned and WebSocket is connected etc.

After that the ColumnDataSource is all the sudden in the float representation of datetime!

The following code shows how converting the float back to datetime will make the graph “work” again.

Notice that I commented out the loop so I only have to convert the first value.

from bokeh.io import curdoc, output_notebook, show
``

from bokeh.models import Line, Plot, ColumnDataSource, Range1d
from bokeh.layouts import column
import numpy as np

delta_time = np.timedelta64(1,'s')
start_time = np.datetime64('2017-01-01T00:00:00')

tail_length = 30

timerange = Range1d(start_time - tail_length * delta_time, start_time)

source = ColumnDataSource(dict(time=[start_time], y=[0.5]))
plot = Plot(width=500, height=500, x_range=timerange, y_range=Range1d(0,1))
plot.add_glyph(source, Line(x='time',y='y'))

def callback():
    # update axis
    # convert long to datetime
    layout.children[0].x_range.start = np.datetime64(layout.children[0].x_range.start, 'ms') + delta_time
    layout.children[0].x_range.end = np.datetime64(layout.children[0].x_range.end, 'ms') + delta_time

    # update data
    source.stream(dict(time=[np.datetime64(source.data['time'][-1],'ms') + delta_time],
                       y=[np.random.rand()]), tail_length)

layout = column(plot)
doc = curdoc()
doc.add_root(layout)

#move 10 datapoints into the sliding window before the server starts
# for _ in range(10):
#     callback()

# then add a new datapoint each second
doc.add_periodic_callback(callback, 1000)

``

···

On Friday, December 1, 2017 at 1:42:07 PM UTC-8, Jon Alm Eriksen wrote:

Note: This question was also posted here https://stackoverflow.com/questions/47601524/how-to-use-time-staps-in-ranges-using-bokeh-server, before I got aware of this mailing list.

How should I use time stamps when I use source.stream in a bokeh app?

Here is a minimal example of what I’m trying to achieve

from bokeh.io import curdoc, output_notebook, show

from bokeh.models import Line, Plot, ColumnDataSource, Range1d
from bokeh.layouts import column
import numpy as np

delta_time = np.timedelta64(1,‘s’)
start_time = np.datetime64(‘2017-01-01T00:00:00’)

tail_length = 30

timerange = Range1d(start_time - tail_length * delta_time, start_time)
source = ColumnDataSource(dict(time=[start_time], y=[0.5]))
plot = Plot(width=200, height=100, x_range=timerange, y_range=Range1d(0,1))
plot.add_glyph(source, Line(x=‘time’,y=‘y’))

def callback():
# update axis
layout.children[0].x_range.start += delta_time
layout.children[0].x_range.end += delta_time

# update data
source.stream(dict(time=[source.data['time'][-1] + delta_time],
                   y=[np.random.rand()]), tail_length)

layout = column(plot)

curdoc().add_root(layout)

move 10 datapoints into the sliding window before the server starts

for _ in range(10):
callback()

then add a new datapoint each second

curdoc().add_periodic_callback(callback, 1000)

``

``

When I start the server, using bokeh serve test.py, the output displays correctly, which indicates that the callback() function works as inteded. But after a second, when the callback is called from curdoc().add_periodic_callback, then it fails to display anything.

bokeh serve test.py
2017-12-01 21:54:45,520 Starting Bokeh server version 0.12.11 (running on Tornado 4.5.2)
2017-12-01 21:54:45,522 Bokeh app running at: http://localhost:5006/test
2017-12-01 21:54:45,522 Starting Bokeh server with process id: 22665
2017-01-01T00:00:01 <class ‘numpy.datetime64’>
2017-01-01T00:00:02 <class ‘numpy.datetime64’>
2017-01-01T00:00:03 <class ‘numpy.datetime64’>
2017-01-01T00:00:04 <class ‘numpy.datetime64’>
2017-01-01T00:00:05 <class ‘numpy.datetime64’>
2017-01-01T00:00:06 <class ‘numpy.datetime64’>
2017-01-01T00:00:07 <class ‘numpy.datetime64’>
2017-01-01T00:00:08 <class ‘numpy.datetime64’>
2017-01-01T00:00:09 <class ‘numpy.datetime64’>
2017-01-01T00:00:10 <class ‘numpy.datetime64’>
2017-12-01 21:54:47,961 200 GET /test (::1) 31.59ms
2017-12-01 21:54:48,381 101 GET /test/ws?bokeh-protocol-version=1.0&bokeh-session-id=jekHIMP5Eo5kjzDDgdMgqba2bNxjbQEpDcBmgET6aHfL (::1) 0.68ms
2017-12-01 21:54:48,381 WebSocket connection opened
2017-12-01 21:54:48,381 ServerConnection created
1483228810001 seconds <class ‘numpy.timedelta64’>
1483228810002 seconds <class ‘numpy.timedelta64’>

What is really going on here?

To investigate how x_range changes during the callback, I added a print statement print(layout.children[0].x_range.end, type(layout.children[0].x_range.end))
inside the callback() definition. It seems like the type of the x_range coordinates stay the same, but for some bizarre reason, it has a different string representation when the callback is called in curdoc().add_periodic_callback.

best regards,

Jon Alm Eriksen

Hi,

Bokeh is actually two different libraries, split across two different language runtimes (Python and JS). So, while we would like to be (and for convenience try to be) as permissive as possible in terms of what is accepted as input on the Python side, ultimately things have to be converted to a standard, common baseline for inter-language exchange. For Bokeh, all date times are ultimately converted to "floating point ms since epoch".

If you do your datetime and timedelta manipulations "outside" of Bokeh using regular Python variables first, then assign to bokeh model properties (e.g. range.start) then things will probably work as you expect. However, once you assign to a property such as range.start the value is converted to the internal format, and this is probably the source of confusion.

Thanks,

Bryan

···

On Dec 1, 2017, at 18:50, [email protected] wrote:

Very interesting behavior! As you can see the 10 callbacks in the

for _ in range(10):
    callback()

loop execute when you hit the URL to the bokeh server, those loop through and calculate then results are returned and WebSocket is connected etc.

After that the ColumnDataSource is all the sudden in the float representation of datetime!

The following code shows how converting the float back to datetime will make the graph "work" again.

Notice that I commented out the loop so I only have to convert the first value.

from bokeh.io import curdoc, output_notebook, show
from bokeh.models import Line, Plot, ColumnDataSource, Range1d
from bokeh.layouts import column
import numpy as np

delta_time = np.timedelta64(1,'s')
start_time = np.datetime64('2017-01-01T00:00:00')

tail_length = 30

timerange = Range1d(start_time - tail_length * delta_time, start_time)

source = ColumnDataSource(dict(time=[start_time], y=[0.5]))
plot = Plot(width=500, height=500, x_range=timerange, y_range=Range1d(0,1))
plot.add_glyph(source, Line(x='time',y='y'))

def callback():
    # update axis
    # convert long to datetime
    layout.children[0].x_range.start = np.datetime64(layout.children[0].x_range.start, 'ms') + delta_time
    layout.children[0].x_range.end = np.datetime64(layout.children[0].x_range.end, 'ms') + delta_time

    # update data
    source.stream(dict(time=[np.datetime64(source.data['time'][-1],'ms') + delta_time],
                       y=[np.random.rand()]), tail_length)

layout = column(plot)
doc = curdoc()
doc.add_root(layout)

#move 10 datapoints into the sliding window before the server starts
# for _ in range(10):
# callback()

# then add a new datapoint each second
doc.add_periodic_callback(callback, 1000)

On Friday, December 1, 2017 at 1:42:07 PM UTC-8, Jon Alm Eriksen wrote:
Note: This question was also posted here python - How to use time staps in ranges using Bokeh server - Stack Overflow, before I got aware of this mailing list.

How should I use time stamps when I use source.stream in a bokeh app?

Here is a minimal example of what I'm trying to achieve

from bokeh.io import curdoc, output_notebook, show

from bokeh.models import Line, Plot, ColumnDataSource, Range1d
from bokeh.layouts import column
import numpy as np

delta_time = np.timedelta64(1,'s')
start_time = np.datetime64('2017-01-01T00:00:00')

tail_length = 30

timerange = Range1d(start_time - tail_length * delta_time, start_time)
source = ColumnDataSource(dict(time=[start_time], y=[0.5]))
plot = Plot(width=200, height=100, x_range=timerange, y_range=Range1d(0,1))
plot.add_glyph(source, Line(x='time',y='y'))

def callback():
    # update axis
    layout.children[0].x_range.start += delta_time
    layout.children[0].x_range.end += delta_time

    # update data
    source.stream(dict(time=[source.data['time'][-1] + delta_time],
                       y=[np.random.rand()]), tail_length)

layout = column(plot)

curdoc().add_root(layout)

# move 10 datapoints into the sliding window before the server starts
for _ in range(10):
    callback()

# then add a new datapoint each second
curdoc().add_periodic_callback(callback, 1000)

When I start the server, using bokeh serve test.py, the output displays correctly, which indicates that the callback() function works as inteded. But after a second, when the callback is called from curdoc().add_periodic_callback, then it fails to display anything.

To investigate how x_range changes during the callback, I added a print statement print(layout.children[0].x_range.end, type(layout.children[0].x_range.end))
inside the callback() definition. It seems like the type of the x_range coordinates stay the same, but for some bizarre reason, it has a different string representation when the callback is called in curdoc().add_periodic_callback.

> bokeh serve test.py
2017-12-01 21:54:45,520 Starting Bokeh server version 0.12.11 (running on Tornado 4.5.2)
2017-12-01 21:54:45,522 Bokeh app running at: http://localhost:5006/test
2017-12-01 21:54:45,522 Starting Bokeh server with process id: 22665
2017-01-01T00:00:01 <class 'numpy.datetime64'>
2017-01-01T00:00:02 <class 'numpy.datetime64'>
2017-01-01T00:00:03 <class 'numpy.datetime64'>
2017-01-01T00:00:04 <class 'numpy.datetime64'>
2017-01-01T00:00:05 <class 'numpy.datetime64'>
2017-01-01T00:00:06 <class 'numpy.datetime64'>
2017-01-01T00:00:07 <class 'numpy.datetime64'>
2017-01-01T00:00:08 <class 'numpy.datetime64'>
2017-01-01T00:00:09 <class 'numpy.datetime64'>
2017-01-01T00:00:10 <class 'numpy.datetime64'>
2017-12-01 21:54:47,961 200 GET /test (::1) 31.59ms
2017-12-01 21:54:48,381 101 GET /test/ws?bokeh-protocol-version=1.0&bokeh-session-id=jekHIMP5Eo5kjzDDgdMgqba2bNxjbQEpDcBmgET6aHfL (::1) 0.68ms
2017-12-01 21:54:48,381 WebSocket connection opened
2017-12-01 21:54:48,381 ServerConnection created
1483228810001 seconds <class 'numpy.timedelta64'>
1483228810002 seconds <class 'numpy.timedelta64'>
....

What is really going on here?

best regards,
Jon Alm Eriksen

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/a69e98ed-b06e-47fe-93cc-13f52663fb8d%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.