How to modify the DateTimeTickFormatter class? - bokeh

import pandas as pd
from math import pi
from datetime import datetime as dt
from bokeh.io import show
from bokeh.models import DatetimeTickFormatter
from bokeh.plotting import figure
d = {'col1': [dt(2015, 1, 1), dt(2015, 1, 2), dt(2015, 1, 3)], 'col2': [100, 200, 300]}
df = pd.DataFrame(data=d)
p = figure(plot_width=400, plot_height=400)
p.line(df.col1, df.col2)
p.xaxis.formatter = DatetimeTickFormatter(days=f"%m/%d %H:%M",
months="%m/%d %H:%M",
hours="%m/%d %H:%M",
minutes="%m/%d %H:%M:%S",
minsec="%m/%d %H:%M:%S",
seconds="%m/%d %H:%M:%S")
p.xaxis.major_label_orientation = pi/4
show(p)
I would like to modify the date time ticker class to allow additional data in tick labels. For example, I would like to see the corresponding data in "col2" whenever bokeh places a tick label such as "1/01 00:00 - 100" or "1/01 12:00". There is no added data on second example since it is not a point in the source. For the second example, an interpolation is also acceptable as the data frequency is high in real data set.

Bokeh is actually two separate libraries:
Bokeh the Python Package
A JavaScript library BokehJS
Being a web-plotting tool, the Python part of Bokeh is actually just a very thin wrapper that drives the BokehJS component in the browser. All of the actual work an implementation is in BokehJS (written in TypeScript). Accordingly, it's generally not possible to make pure-Python changes or edits or subclasses to Bokeh that would have much or any effect at all.
It is possible to create custom extensions to Bokeh by supplying your own Javascript implementation of new Bokeh object subclasses. There is in fact specifically an example of a custom tick formatter at the bottom of that docs page. SO is not a code-writing site or service, but if you attempt to create your own custom extension and get stuck, you can provide the complete code for what you try, and ask specific, pointed questions about how to fix that existing code.

Related

Python: Plotly graph not rendering

I'm trying to plot a stock price using Plotly in Python 3.9 (Anaconda/Spyder), but the graph isn't displaying. The following code executes without error, but no plot.
import yfinance
tsla = yfinance.Ticker('TSLA')
hist = tsla.history(period='1y')
import plotly.graph_objects as go
fig = go.Figure(data=go.Scatter(x=hist.index,y=hist['Close'], mode='lines'))
fig.show()
I'm working on local version of Python, no web interaction.
The following code works for plotting in a browser window:
import plotly.express as px
fig = px.bar(x=["a", "b", "c"], y=[1, 3, 2])
fig.write_html('first_figure.html', auto_open=True)
Just not sure why .show() isn't working. Seems to be the most basic function there is to Plotly.
Hoping there's something easy/obvious I'm missing.
Try adding this :
import plotly.io as pio
pio.renderers.default = 'browser'
It will open the plot in your default browser.
More info on renderers

Can I draw an annotation in bokeh with data from a ColumnDataSource?

I'd like to draw a vertical line on my Bokeh plot which gets moved around by javascript in the browser at runtime. (It's a timebar that marks the current time on a time series plot.)
For drawing a static vertical line, I'm using:
from bokeh.models import Span
timebar = Span(location=where_I_want_the_timebar, dimension=height)
my_figure.add_layout(timebar)
In order to enable the interactivity, I think I need to get the location from a ColumnDataSource. However, I can't figure out how to do that, because Span does not accept a source argument.
Alternatively, is there another way for me to move the timebar at runtime?
I found a workaround. In python:
from bokeh.models import Span
timebar = Span(name='timebar' location=where_I_want_the_timebar, dimension=height)
my_figure.add_layout(timebar)
Then in javascript in the browser:
let timebar = Bokeh.documents[0].get_model_by_name('timebar')
timebar.attributes.location = my_new_desired_location
timebar.change.emit()
If someone posts a way to use a ColumnDataSource I will accept that answer.

How to change the extent and position of an existing image in bokeh?

I am displaying 2d data as images of varying shapes in a bokeh server, and therefore need to dynamically update not only the image's data source, but also its dw, dh, x, and y properties. In the dummy example below, these changes are made in a callback function which is connected to a Button widget.
I've figured out that I need to access the glyph attribute of the image's GlyphRenderer object, and I can do so through its update() method (see code). But the changes don't take effect until I click the toolbar's Reset button. I've noticed that the changes also mysteriously take effect the second time I activate the callback() function. What is the proper way to make these changes?
import bokeh.plotting
import bokeh.models
import bokeh.layouts
import numpy as np
# set up the interface
fig1 = bokeh.plotting.figure(x_range=(0, 10), y_range=(0, 10))
im1 = fig1.image([], dw=5, dh=5)
button = bokeh.models.Button(label='scramble')
# add everything to the document
bokeh.plotting.curdoc().add_root(bokeh.layouts.column(button, fig1))
# define a callback and connect it
def callback():
# this always works:
im1.data_source.data = {'image': [np.random.random((100,100))]}
# these changes only take effect after pressing the "Reset"
# button, or after triggering this callback function twice:
im1.glyph.update(x=1, y=1, dw=9, dh=9)
button.on_click(callback)
I don't immediately see why you code isn't work. I can suggest explicitly using a ColumnDataSource and linking all of the Image glyph properties to columns in that source. Then you should be able to update the source.data in a single line and have all of the updates apply.
Here's some incomplete sample code to suggest how to do that:
from bokeh.models import Image, ColumnDataSource
from bokeh.plotting import figure
# the plotting code
plot = figure()
source = ColumnDataSource(data=dict(image=[], x=[], y=[], dw=[], dh=[]))
image = Image(data='image', x='x', y='y', dw='dw', dh=dh)
plot.add_glyph(source, glyph=image)
# the callback
def callback():
source.data = {'image': [np.random.random((100,100))], 'x':[1], 'y':[1], 'dw':[9], 'dh':[9]}
button.on_click(callback)

Unable to set bokeh plotting parameters using BOKEH + HOLOVIEW plotting option via Python

Is there any documentation specifying how to pass Bokeh parameters via holoview?
I am reading the tutorials but I think there is something small I have missed.
There is an example online which describes this in Ipython but I am trying to do it via a python WITHOUT Ipython notebook.
http://holoviews.org/Tutorials/Bokeh_Backend.html?highlight=bokeh
When I run this program I get the curves but the color does not change and I also get this error: WARNING:root:Curve01537: Setting non-parameter attribute style={'line_color': 'green'} using a mechanism intended only for parameters
How can we set the parameter?
Code Example here
from pprint import pprint, pformat
import holoviews as hv
import numpy as np
import pathlib, os
import webbrowser
import lasio, las
from holoviews import Store
from holoviews.plotting.bokeh.element import (line_properties, fill_properties, text_properties)
def plot_bokeh(plot):
#Create renderer instance
myrenderer = hv.Store.renderers['bokeh'].instance(fig='html')
out_file_name = "".join(["./OUTPUT/","gyro", "_graph.html"])
with open (out_file_name, 'w') as f:
#Plot static html
f.write (myrenderer.static_html(plot))
f.close()
webbrowser.open_new_tab(pathlib.Path(os.path.abspath(out_file_name)).as_uri())
def holoview_sandbox():
curve_opts = dict(line_color='green')
xs = np.linspace(0, np.pi*4, 100)
data = (xs, np.sin(xs))
holo_plot = hv.Curve(data, label='MY LABEL' , style=curve_opts)
plot_bokeh(holo_plot)
if __name__ == '__main__':
holoview_sandbox()
In HoloViews the options aren't bound to the objects themselves, which has various benefits including being able to plot with different backends. The pure-Python way of setting style options is this:
curve_opts = dict(line_color='green')
xs = np.linspace(0, np.pi*4, 100)
data = (xs, np.sin(xs))
holo_plot = hv.Curve(data, label='MY LABEL')(style=curve_opts)
The Options Tutorial describes how to set options like this, but please let us know if you found some of that unclear.
This syntax works as well
holo_plot.opts(style={'color': 'green'})
When you change the entry 'line_color' to 'color' in the dict() of Philipp's answer, then this works for the matplotlib backend as well.
Details about setting options can also be found here in addition to Philipp's link.

Animated graphs in ipython notebook

Is there a way of creating animated graphs. For example showing the same graph, with different parameters.
For example is SAGE notebook, one can write:
a = animate([circle((i,i), 1-1/(i+1), hue=i/10) for i in srange(0,2,0.2)],
xmin=0,ymin=0,xmax=2,ymax=2,figsize=[2,2])
a.show()
This has horrible flickering, but at least this creates a plot that animates for me. It is based on Aron's, but Aron's does not work as-is.
import time, sys
from IPython.core.display import clear_output
f, ax = plt.subplots()
n = 30
x = array([i/10.0 for i in range(n)])
y = array([sin(i) for i in x])
for i in range(5,n):
ax.plot(x[:i],y[:i])
time.sleep(0.1)
clear_output()
display(f)
ax.cla() # turn this off if you'd like to "build up" plots
plt.close()
Update: January 2014
Jake Vanderplas has created a Javascript-based package for matplotlib animations available here. Using it is as simple as:
# https://github.com/jakevdp/JSAnimation
from JSAnimation import examples
examples.basic_animation()
See his blog post for a more complete description and examples.
Historical answer (see goger for a correction)
Yes, the Javascript update does not correctly hold the image frame yet, so there is flicker, but you can do something quite simple using this technique:
import time, sys
from IPython.display import clear_output
f, ax = plt.subplots()
for i in range(10):
y = i/10*sin(x)
ax.plot(x,y)
time.sleep(0.5)
clear_output()
display(f)
ax.cla() # turn this off if you'd like to "build up" plots
plt.close()
IPython widgets let you manipulate Python objects in the kernel with GUI objects in the Notebook. You might also like Sage hosted IPython Notebooks. One problem you might have with sharing widgets or interactivity in Notebooks is that if someone else doesn't have IPython, they can't run your work. To solve that, you can use Domino to share Notebooks with widgets that others can run.
Below are three examples of widgets you can build in a Notebook using pandas to filter data, fractals, and a slider for a 3D plot. Learn more and see the code and Notebooks here.
If you want to live-stream data or set up a simulation to run as a loop, you can also stream data into plots in a Notebook. Disclaimer: I work for Plotly.
If you use IPython notebook, v2.0 and above support interactive widgets. You can find a good example notebook here (n.b. you need to download and run from your own machine to see the sliders).
It essentially boils down to importing interact, and then passing it a function, along with ranges for the paramters. e.g., from the second link:
In [8]:
def pltsin(f, a):
plot(x,a*sin(2*pi*x*f))
ylim(-10,10)
In [9]:
interact(pltsin, f=(1,10,0.1), a=(1,10,1));
This will produce a plot with two sliders, for f and a.
If you want 3D scatter plot animations, the Ipyvolume Jupyter widget is very impressive.
http://ipyvolume.readthedocs.io/en/latest/animation.html#
bqplot is a really good option to do this now. its built specifically for animation through python in the notebook
https://github.com/bloomberg/bqplot
On #goger's comment of 'horrible flickering', I found that calling clear_output(wait=True) solved my problem. The flag tells clear_output to wait to render till it has something new to render.
matplotlib has an animation module to do just that. However, examples provided on the site will not run as is in a notebook; you need to make a few tweaks to make it work.
Here is the example of the page below modified to work in a notebook (modifications in bold).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib import rc
from IPython.display import HTML
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro', animated=True)
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
rc('animation', html='html5')
ani
# plt.show() # not needed anymore
Note that the animation in the notebook is made via a movie and that you need to have ffmpeg installed and matplotlib configured to use it.

Resources