The matplotlib is the most frequently used and quite famous python library for plotting charts. It has been extensively developed over the years and provides extensive API for plotting various charts. All the charts generated by matplotlib are static. When doing exploratory data analysis, its quite common to explore data from various perspectives to understand it better. We can merge matplotlib charts with ipywidgets widgets in order to generate interactive charts which updates itself whenever the values of widget changes.
As a part of this tutorial, we'll be explaining how ipywidgets widgets can be linked to matplotlib charts to make them interactive. We have already covered an extensive tutorial on ipywidgets which is available here:
Please feel free to gro through a tutorial on ipywidgets if needed to better understand the working of widgets.
We'll start by importing necessary libraries.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
import warnings
warnings.filterwarnings("ignore")
%matplotlib widget
Please make a note that we have used matplotlib with widget
extension by calling jupyter magic command %matplotlib widget
. The reason behind this is that we want a matplotlib plot as a widget that can be easily linked to other widgets and easily modified.
We'll be using the IRIS dataset and apple OHLC dataset for creating various examples.
We'll be loading both datasets as pandas dataframe.
from sklearn.datasets import load_iris
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df["FlowerType"] = iris.target #[iris.target_names[t] for t in iris.target]
iris_df.head()
apple_df = pd.read_csv("datasets/AAPL.csv", parse_dates=["Date"])
apple_df.head()
The first example consists of drawing scatter plot showing the relationship between two measurements of IRIS flower color-encoded by flower type. We'll be creating two dropdowns for trying various combinations of parameters to understand the relationship between all features combinations of the dataset. There are 4 features of the dataset which are petal width, petal length, sepal width, and sepal length. The dropdowns can be used to check the relationship between all of them.
We have created a method named create_scatter()
which takes two string parameters which are x and y-axis features names of the dataset that will be used to create a scatter plot.
def create_scatter(feature1, feature2):
with plt.style.context("ggplot"):
fig = plt.figure(figsize=(8,4))
plt.scatter(x = iris_df[feature1],
y = iris_df[feature2],
c=iris_df["FlowerType"],
s=20
)
plt.xlabel(feature1.capitalize())
plt.ylabel(feature2.capitalize())
plt.title("%s vs %s"%(feature1.capitalize(), feature2.capitalize()))
The simplest way to convert the above function into an interactive chart with widgets is by using the interact()
function from ipywidgets
. We just need to pass it function name and list of values for parameter and it'll create UI for us with widgets and chart. It'll even link widgets with a chart so that any changes in widget value will result in updating the chart. We can change various dropdown values and the chart will update accordingly.
widgets.interact(create_scatter, feature1=iris.feature_names, feature2=iris.feature_names);
The second example is exactly the same as the previous example with the only change that we can create the same UI as the previous step by keeping the interact()
method as annotation of create_scatter()
method. All other things are the same. We now don't need to pass the function name to interact()
as its already working as a decorator of function.
@widgets.interact(feature1=iris.feature_names, feature2=iris.feature_names)
def create_scatter(feature1, feature2):
with plt.style.context("ggplot"):
fig = plt.figure(figsize=(8,4))
plt.scatter(x = iris_df[feature1],
y = iris_df[feature2],
c=iris_df["FlowerType"],
s=20
)
plt.xlabel(feature1.capitalize())
plt.ylabel(feature2.capitalize())
plt.title("%s vs %s"%(feature1.capitalize(), feature2.capitalize()))
Our third example is the same as our previous example with one minor change which is the position of widgets. The last two examples created widgets by themselves as well as the decided location of widgets also by themselves. Many times we need more control with ourselves and need to change this default UI to layout individual components differently. We might want to layout widgets at the end of the chart or any other combinations.
One way to do that is by using the interactive_output()
function of ipywidgets
. We have declared create_scatter()
same as previous examples.
def create_scatter(feature1, feature2):
with plt.style.context("ggplot"):
fig = plt.figure(figsize=(8,4))
plt.scatter(x = iris_df[feature1],
y = iris_df[feature2],
c=iris_df["FlowerType"],
s=20
)
plt.xlabel(feature1.capitalize())
plt.ylabel(feature2.capitalize())
plt.title("%s vs %s"%(feature1.capitalize(), feature2.capitalize()))
Below we have created two dropdowns using ipywidgets
with options as four features of the IRIS dataset.
feature1_drop = widgets.Dropdown(options=iris.feature_names, value = iris.feature_names[0])
feature1_drop
feature2_drop = widgets.Dropdown(options=iris.feature_names, value = iris.feature_names[1])
feature2_drop
We need to pass the function name and dictionary of function parameter and widgets to interactive_output()
. It'll wrap output of create_scatter()
function into Output
widget. The Output
widget is a special type of widget in ipywidgets which can be used to show any kind of output in a notebook.
out = widgets.interactive_output(create_scatter, {'feature1': feature1_drop, 'feature2':feature2_drop})
type(out)
Below we have created our first UI using the layout facility available in ipywidgets.
We have first laid out two widgets horizontally by using the HBox()
constructor of ipywidgets passing them as a list to it. We have then taken this horizontally laid out dropdowns and output widget from the previous step and passed them to the VBox()
constructor as a list to lay them vertically.
If you are interested in learning about layout facility available in ipywidgets then we suggest that you go through our tutorial on ipywidgets which will give an in-depth ideas about it.
widgets.VBox([widgets.HBox([feature1_drop, feature2_drop]), out])
Our fourth example consists of a line chart depicting the price data of apple. We have created a multi-select widget which lets us select multiple values from Open, High, Low, and Close prices.
multi_select = widgets.SelectMultiple(options=["Open", "High", "Low", "Close"], value=["Open",])
multi_select
Below we are drawing a line chart showing apple open price changes for the period of Apr 2019- Mar 2020. We have wrapped the matplotlib chart into output widgets explained earlier.
output = widgets.Output()
with output:
with plt.style.context("ggplot"):
fig = plt.figure(figsize=(8,4))
plt.plot(apple_df["Date"].values, apple_df["Open"].values)
plt.xlabel("Date")
plt.ylabel("Price ($)")
plt.title("Line Chart for Apple Prices");
Below we have declared a callback function which will be called each time state of multi-select widget changes. Each time we select/de-select values from multi-select, the dictionary will be passed to this function with new and old values. We need to register this function as a callback of multi-select by using the observe()
function in order to pass changes in the value of the widget.
We have passed the function name to the observe()
method of the widget as well as changes to which parameter of the widget to monitor. We are monitoring the value
parameter of multi-select which will have a list of values currently selected.
The update_line_chart()
function will clear figure, loop through all selected values of multi-select, and plot a line for each selected value.
def update_line_chart(selected_vals):
with output:
with plt.style.context("ggplot"):
fig.clear()
for selected in selected_vals["new"]:
plt.plot(apple_df["Date"].values, apple_df[selected].values)
plt.xlabel("Date")
plt.ylabel("Price ($)")
plt.title("Line Chart for Apple Prices");
fig.canvas.draw()
multi_select.observe(update_line_chart, names="value")
Below we have laid output multi-select widget and output widget vertically to create our interactive UI.
widgets.VBox([multi_select, output])
The fifth example that we'll be demonstrating consists of a scatter plot and slider widget. We'll be plotting the same scatter plot as example 1 but this time, we'll be limiting a number of points appearing on the scatter plot with a slider widget. It might happen that we might need to visualizing only a few points in the dataset if it has a lot of points.
We have created a slider widget with a minimum value of 0 and a maximum value of 150 (no of samples in the dataset).
slider = widgets.IntSlider(value=50, min=1, max=iris_df.shape[0], continuous_update=False)
slider.style.handle_color="lawngreen"
slider
Below we have created scatter plot showing the relationship between sepal length and petal length but we have plotted only the first 50 samples from the dataset. We have wrapped plotting code into the Output
widget like the previous example.
output = widgets.Output()
with output:
with plt.style.context("ggplot"):
fig = plt.figure()
scat = plt.scatter(x=iris_df[:50]['sepal length (cm)'],
y=iris_df[:50]['petal length (cm)'],
c=iris_df[:50]["FlowerType"]
)
plt.xlabel("sepal length (cm)".capitalize())
plt.ylabel("petal length (cm)".capitalize())
plt.title("sepal length (cm) vs petal length (cm) Relation")
Below we have created a callback function which will be called each time changes to the slider happens. It'll pass new and old values of the slider to function because we have registered it with the slider widget using observe()
. We have included logic into the callback function which takes new slider value and updates the chart with that many points as selected by the slider.
def update_scatter_chart(new_slider_val):
with output:
with plt.style.context("ggplot"):
fig.clear()
plt.scatter(x=iris_df[:new_slider_val["new"]]['sepal length (cm)'],
y=iris_df[:new_slider_val["new"]]['petal length (cm)'],
c=iris_df[:new_slider_val["new"]]["FlowerType"])
plt.xlabel("Date")
plt.ylabel("Price ($)")
plt.title("Line Chart for Apple Prices");
fig.canvas.draw()
slider.observe(update_scatter_chart, names="value")
Below we have created UI for example. We have created a new widget named label which will be added before the slider.
slider_label = widgets.Label("No. Of Samples to Visualize")
slider_comp = widgets.HBox([slider_label, slider])
widgets.VBox([slider_comp, output])
The sixth example that we'll use for explanation consist of candlestick chart and dropdown. Our apple OHLC dataset has data from Apr-2019 to Mar 2020. We'll be creating a dropdown which will let us select one month at a time from the dataset.
Below we have created a dropdown that will let us select month and year combinations for which OHLC data is available in the dataframe.
year_months_idx = list(zip([2019,]*9 + [2020]*3,[ 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3]))
year_months_names = list(zip([2019,]*9 + [2020]*3,['April', 'May', 'June', 'July', 'August', 'September', 'October',
'November', 'December', 'January', 'February', 'March']))
month_dropdown = widgets.Dropdown(options=list(zip(year_months_names, year_months_idx)), value=year_months_idx[0])
month_dropdown
Below we have created our candlestick chart for Apr-2019 using the mplfinance
library which is based on matplotlib. We have wrapped a chart like previous examples into the Output
widget so that it can be easily updated.
import mplfinance as fplt
output = widgets.Output()
with output:
fplt.plot(
apple_df.set_index("Date")["2019-04"],
type='candle',
style='charles',
title='Apple CandleStick',
ylabel='Price ($)',
volume=True,
ylabel_lower='Shares\nTraded',
show_nontrading=True,
)
Below we have created a callback function which will be called each time someone selects a different values in the dropdown. We are taking new values from the dictionary and updating the candlestick chart with that selection. We have registered a callback function with the widget as explained earlier.
def update_candle_stick(year_month):
dt = "%d-%d"%(year_month["new"][0], year_month["new"][1])
temp_df = apple_df.set_index("Date")[dt]
output.clear_output()
with output:
fplt.plot(
temp_df,
type='candle',
style='charles',
title='Apple CandleStick',
ylabel='Price ($)',
volume=True,
ylabel_lower='Shares\nTraded',
show_nontrading=True,
)
fig.canvas.draw()
month_dropdown.observe(update_candle_stick, names=["value"])
Below we have created a simple UI consisting out dropdown and output widget.
widgets.VBox([month_dropdown, output])
This ends our small tutorials explaining how to link ipywidgets widget and matplotlib charts. Please feel free to let us know your views in the comments section.
If you are more comfortable learning through video tutorials then we would recommend that you subscribe to our YouTube channel.
When going through coding examples, it's quite common to have doubts and errors.
If you have doubts about some code examples or are stuck somewhere when trying our code, send us an email at coderzcolumn07@gmail.com. We'll help you or point you in the direction where you can find a solution to your problem.
You can even send us a mail if you are trying something new and need guidance regarding coding. We'll try to respond as soon as possible.
If you want to