Draw Beautiful and Interactive Line Charts Using Bokeh in Python | by Christopher Tao | Towards Data Science

The line chart is probably the most basic but useful chart that demonstrates the relationships between two dimensions. It is usually used to show the variance of a variable y over the time-axis x continuously, where the x-axis must be sorted.
It is quite easy to get insights from line charts such as whether the variable is increasing, decreasing or fluctuating, as well as obtain its periodic patterns, peaks and spikes. Also, we could plot multiple lines on the line chart to compare their different trends over the same time window, and even sometimes get insights that how they influence each other.
In this article, I am going to demonstrate how to use Bokeh, one of the most popular visualisation libraries in Python, to draw a beautiful and interactive line chart.
The official website has defined Bokeh very well as follows.
Bokeh is an interactive visualization library for modern web browsers. It provides elegant, concise construction of versatile graphics, and affords high-performance interactivity over large or streaming datasets. Bokeh can help anyone who would like to quickly and easily make interactive plots, dashboards, and data applications.
Basically, Bokeh can help us to generate visualisations in web pages that can be viewed with any popular browser. However, it is important to emphasize that it also supports Jupyter Notebooks. So, you don’t have to generate the charts to a separate tab on your browser every time. It is just like Matplotlib that can plot the data directly rendered in your notebook.
It is very easy to install Bokeh. Simply run pip in the command line to obtain it.
pip install bokeh
Most of the time, I’ll import Bokeh as follows, which are only the essential packages that will be utilised. Of course, we may need to import more features when there are some customised requirements.
from bokeh.plotting import figure, output_notebook, show
The figure package is the main one that we are going to use for plotting the data. The show package will render the chart in either separated web pages or directly on the notebook. The output_notebook is only necessary when we want to render the charts in a notebook, so we need it in this tutorial.
Once we imported output_notebook, we just need to run this function to enable the “notebook mode”.
Now we should begin.
Let’s start with some basics. Although Matplotlib will also do the job well, Bokeh made things more intuitive and much easier to get a beautiful one.
Firstly, we need to generate some random data.
import numpy as np# Generate random datax = np.arange(1, 11)y = np.random.rand(10)
Then, initialise the “canvas” of the figure.
# Generate canvasfig = figure(title='Line Chart Example',             x_axis_label='x',             y_axis_label='y',             width=800,             height=400)
Here, the width and height are actual pixels, which is different from Matplotlib. In fact, I like to use these pixels, which is more friendly to anyone who has some web frond-end experiences.
After that, let’s draw the line.
# Draw the linefig.line(x, y,          line_alpha=0.8,         legend_label='example value',          line_width=2)
As shown in the code, we can set the transparency of the line using line_alpha, set the legend label, as well as line width easily.
Finally, we need to call show(fig) function to render this line chart. If you have run output_notebook() correctly, the chart should be plotted as follows.
What if we have multiple lines? Bokeh provides a very convenient function multi_line() to plot multiple lines in one go.
We can keep using the x series, but generate two different y series.
# Generate two data seriesy1 = np.random.rand(10)y2 = np.random.rand(10) + 1
Here I just added 1 to the second y2 data to make it higher than y1, nothing magic.
Then, let’s create the figure and draw the two lines. Don’t forget to “show()” it.
# Multiple Linesfig = figure(title='Multi-Line Chart Example', width=800, height=400)# Draw two linesfig.multi_line([x, x], [y1, y2],                color=['blue', 'red'],                alpha=[1, 0.3],                line_width=2)# Show the figureshow(fig)
So far, not too much different compare to Matplotlib except Bokeh is more beautiful. But wait, have you noticed that there is a toolbar on the right of the chart. The default tools including
pan gesture
box zoom in
mouse wheel zoom in/out
save the graph in PNG image
reset the position and zoom-level
help
There are more interactive features supported that can be enabled when the chart is generated. You may refer to the official documentation to get more details, but I believe these default tools are quite enough for most of the scenarios.
Sometimes we may need to customize our line charts for some reason. In this section, I will provide some examples.
Let’s say, we want to compare some mathematical functions to get ideas about their trend when x is getting larger. Here are the functions:
y=x
y=x2
y=logx
y=10^x
y=e^x
Let’s generate the data first.
# Generate functions and datax = np.arange(0, 3, 0.1)y1 = np.power(x, 2)y2 = np.log(x)y3 = np.power(10, x)y4 = np.exp(x)
There will be some warnings because log0 is undefined, but don’t worry, it will be a -inf in the Numpy array. Also, Bokeh will be smart enough to just ignore that value when the data is rendered.
Now, we could use the multi_line() function again to plot all the functions in one go. However, I want to do this one by one because I think it is clearer to separate the lines and their configurations. Of course, it is up to you to do either way.
fig = figure(title='Compare the Trends of Different Functions', width=800, height=400)fig.line(x, x, legend_label='y=x', color='blue')fig.line(x, y1, legend_label='y=x^2', color='red')fig.line(x, y2, legend_label='y=logx', color='green')fig.line(x, y3, legend_label='y=10^x', color='purple')fig.line(x, y4, legend_label='y=e^x', color='brown')show(fig)
Well, it has been plotted correctly, but it’s not useable. Except for the function y=10^x, the others are all squeezed together. So, with this line chart, we can not actually compare the trends.
The solution is to modify the scale of the y-axis. If we could make the y-axis not linear, it should be able to distinguish the functions clearer. Bokeh can do this very easily by setting the y_axis_type to log.
# Generate figure objectfig = figure(title='Compare the Trends of Different Functions', width=800, height=600, y_axis_type='log')
There is another problem. Even though we have used different colours for these functions, it is still not quite easy to distinguish them. Let’s also add different shapes for the data points of different functions, too.
# Use blue colour and circle for y=xfig.line(x, x, legend_label='y=x', color='blue', line_width=2)fig.circle(x, x, legend_label='y=x', color='blue', fill_color='white', size=5)# Use red colour and triangle for y=x^2fig.line(x, y1, legend_label='y1=x^2', color='red', line_width=2)fig.triangle(x, y1, legend_label='y1=x^2', color='red', fill_color='white', size=5)# Use green colour and square for y=logxfig.line(x, y2, legend_label='y2=logx', color='green', line_width=2)fig.square(x, y2, legend_label='y2=logx', color='green', fill_color='white', size=5)# Use purple colour and diamond for y=10^xfig.line(x, y3, legend_label='y3=10^x', color='purple', line_width=2)fig.diamond(x, y3, legend_label='y3=10^x', color='purple', fill_color='white', size=5)# Use brown colour and hexgon for y=e^xfig.line(x, y4, legend_label='y4=e^x', color='brown', line_width=2)fig.hex(x, y4, legend_label='y4=e^x', color='brown', fill_color='white', size=5)
Cool! Now we can easily identify the functions and their trends.
The figure looks good now, but it would be great if we can relocate the legend to the top left corner because there is nothing over there. Also, there are still too many lines on this figure, we may want to hide some of them so that we can compare only a few of them at one time.
Yes, Bokeh can do that easily. Actually, we don’t even need to generate that figure again. Since we have the figure object fig, we can directly modify its attributes to implement these.
# Relocate Legendfig.legend.location = 'top_left'# Click to hide/show linesfig.legend.click_policy = 'hide'
Now, let’s say we want to have the mouse hover on the lines to get the value of the data points. We can also achieve that easily in Bokeh. Also, we don’t need to re-generate the figure because we can add the “tooltips” to the figure object.
from bokeh.models.tools import HoverToolfig.add_tools(HoverTool(    tooltips=[        ('(x, y)', '(@x, @y)')    ]))
We need to import HoverTool class in order to add it as a “tool” in the figure object. Please note that Bokeh has made it quite flexible to customise the tooltips. Specifically, we can define each line of text as a tuple. For the second value of the tuple, we can use @ sign to refer to the data field. In this example, I have defined only one tuple. If you define multiple tuples with different formats and/or data fields, the tooltips will display them all.
What if we have more specific requirements? Bokeh has made the tooltips flexible, we can even use HTML Template to customise the tooltips.
fig.add_tools(HoverTool(    tooltips='<font face="Arial" size="3">When x is @x{0.0}, y is @y{0.00}</font>',    mode='vline'))
Please note that I have also added mode='vline' this time. This will display the tooltips for all the lines at the same vertical x-axis position. So, it will show the values at the same x for all the different y, which is better for comparing purposes.
Let’s use a fancy line chart from the official gallery to finish this article. Bokeh is indeed very beautiful.
from scipy.integrate import odeintsigma = 10rho = 28beta = 8.0/3theta = 3 * np.pi / 4def lorenz(xyz, t):    x, y, z = xyz    x_dot = sigma * (y - x)    y_dot = x * rho - x * z - y    z_dot = x * y - beta* z    return [x_dot, y_dot, z_dot]initial = (-10, -7, 35)t = np.arange(0, 100, 0.006)solution = odeint(lorenz, initial, t)x = solution[:, 0]y = solution[:, 1]z = solution[:, 2]xprime = np.cos(theta) * x - np.sin(theta) * ycolors = ["#C6DBEF", "#9ECAE1", "#6BAED6", "#4292C6", "#2171B5", "#08519C", "#08306B",]fig = figure(title="Lorenz attractor example", background_fill_color="#fafafa")fig.multi_line(np.array_split(xprime, 7), np.array_split(z, 7),             line_color=colors, line_alpha=0.8, line_width=1.5)show(fig)
In this article, I have introduced how to use Bokeh to draw beautiful line charts. As one of the most popular visualisation libraries in Python, Bokeh provides a lot of out-of-box visualising features such as interactive toolsets. When it comes to the lower level, it also provides very flexible features to let us customising our figures.
medium.com
If you feel my articles are helpful, please consider joining Medium Membership to support me and thousands of other writers! (Click the link above)
[1] Bokeh Latest Documentation, https://docs.bokeh.org/en/latest/index.html
[2] Bokeh Gallery, Lorenz Attractor, https://docs.bokeh.org/en/latest/docs/gallery/lorenz.html