Creating interactive notebooks in Jupyter

Creating interactive notebooks in JupyterDiego PenillaBlockedUnblockFollowFollowingFeb 25Learn how to interact with your functions by clicking buttons in your notebookPhoto by Owen Beard on UnsplashI really like Jupyter Notebooks.

Whenever I’m using one, I have the feeling of it being basically an interactive spell book: able to display text, images, videos, including math formulas and code, and create an inner environment with your variables and functions.

Compared to carving stones with your notes, this tool makes you feel like you are in the future.

And there is still an interesting aspect of notebooks that I haven’t mentioned: you can also actively interact with your notebook by directly manipulating widgets and allowing specific type of user-computer interactions.

Let’s see how we can do this using ipywidgets, a very cool package with all the tools you need to build a simple GUI.

InstallationLet’s start by installing and enabling the package executing the following code in your terminal.

pip install ipywidgetsjupyter nbextension enable –py widgetsnbextensionIf you are working with Jupyter Lab: you will need to install node.

js and enable it using a different command:jupyter labextension install @jupyter-widgets/jupyterlab-managerBefore we start, it is important to draw a distinction between two types of widgets:Some of them support interaction with the user, for example text windows, buttons and checkboxes.

While others act as containers grouping widgets together, for example boxes, panels and tabs.

Creating widgetsNow that we have the package installed, we can immediately instantiate any widget in our notebook.

You can click here to review the list of currently available widgets along the code to initialize them.

Let’s see some examples defining some widgets:# some handy functions to use along widgetsfrom IPython.

display import display, Markdown, clear_output# widget packagesimport ipywidgets as widgets# defining some widgetstext = widgets.

Text( value='My Text', description='Title', )calendar = widgets.

DatePicker( value = '' , description='Select Date')slider = widgets.

FloatSlider( value=1, min=0, max=10.

0, step=0.

1,)menu = widgets.

Dropdown( options=['red', 'blue', 'green'], value='red', description='Color:')checkbox = widgets.

Checkbox( description='Check to invert',)As shown above (and in most cases), initializing widgets involves calling a simple function and specifying some self explanatory arguments, it is really very straightforward.

Here we defined a dropdown menu, a slider, a checkbox , a text window and a calendar in our notebook.

You can display any of them by calling the object on a cell:menuBut in order to display them altogether we need to use a container widget.

Creating containersThe most common ones are defined using the functions VBox and HBox and serve to display our widgets vertically or horizontally respectively.

They expect a single argument: a list of widgets.

box = widgets.

VBox([text, slider, menu, calendar, checkbox ])boxBecause the VBox and HBox containers are also widgets, we can use them inside one another.

Let’s see a dummy example:widgets.

HBox([box, box])Our VBox container is now displayed two times horizontally using HBoxAlternatively, we can use another type of container on top.

Let’s see the Tab container.

While slightly more complex, it helps us better use space:# defining a list with the contents of our windowschildren = [box, box]# initializing a tabtab = widgets.

Tab()# setting the tab windows tab.

children = children# changing the title of the first and second windowtab.

set_title(0, 'box')tab.

set_title(1, 'copy of box')tabHolding VBox containers in two different windows using a tabWidgets hold attributesNow that we know how to instantiate and layout our widgets, it’s really important to have in mind that each of these widgets is defined as an object and holds its own attributes.

You could say that each widget has its own set of nuances, for example menu has an attribute called options :menu.

optionsA tuple of our defined optionsRedefining this attribute would change the current options of our widget.

menu.

options=('red', 'blue', 'green', 'black')menuWith the exception of the containers, an attribute common among all the widgets that we defined above is value and might well be the most useful one.

It means exactly what it should: calling it it returns the current value of the widget.

# current value of text, checkbox and calendar print('Widget text has value {}, of {}'.

format(text.

value, type(text.

value)))print('Widget checkbox has value {}, of {}'.

format(checkbox.

value, type(checkbox.

value)))print('Widget calendar has value {}, of {}'.

format(calendar.

value, type(calendar.

value)))Notice that different widgets hold different types of values!Being able to retrieve the value of our widget is what really allows you to make them interact with your code.

Let’s see a dummy example using menu.

# We'll use the value of menu to display a plot and its titleimport matplotlib.

pyplot as pltplt.

plot([1,2,3], color = menu.

value)plt.

title('A {} line'.

format(menu.

value))plt.

show()Changing the value of our menuwidget and re-executing the code above will change the title and color of the line.

We can do this because the options of menu correspond to valid values for the argument color of plt.

plot.

This highlights a useful property of widgets: you can use them to control stuff without having to explicitly type it out.

Creating ButtonsYou may have noticed that all we’ve done above involves clicking on the widgets and selecting stuff.

But to be able to actually trigger something we’ve been executing lines of code.

We need a widget able to trigger a response and execute code, we need the power of the button.

Instantiating a button resembles how we’ve been defining widgets:button = widgets.

Button(description='My Button')Nothing happens…But in order to make our button functional we also need to define what happens when we press it.

In simple terms this is achieved by:Defining an Output widget that displays the response.

Defining a function to be executed.

And linking our button, the Output and the function together.

Here is a little snippet I use all the time when creating a new button: this will be the template for our examples.

button = widgets.

Button(description='My Button')out = widgets.

Output()def on_button_clicked(_): # "linking function with output" with out: # what happens when we press the button clear_output() print('Something happens!')# linking button and function together using a button's methodbutton.

on_click(on_button_clicked)# displaying button and its output togetherwidgets.

VBox([button,out])Notice how we are using VBox to vertically display the button and its outputThe function clear_output refreshes our display every time our button is pressed.

If we omit this function, pressing the button several times would print ‘Something happens!’ several times, which seems undesirable.

The Output widget is really an interesting one: we can use it to display print statements (as we did above), plots, videos, images and basicallyanything which displays nicely in a Jupyter notebook will also display well in the Output widget.

Let’s see an example of how we can use it to display Markdown.

from IPython.

display import Markdown# Using output to display Markdownmarkdown_out = widgets.

Output()with markdown_out: display(Markdown('Slider value is ${}$'.

format(slider.

value)))markdown_outUsing Markdown to display the value of the widgetNotice that we are rendering the value of the slider as a mathematical expression by surrounding the placeholder with $ symbols.

Using Markdown programmatically can be something very handy.

ExamplesNow that we had an overview of how to define, organize and use various widgets, (although disjointed) we’ve seen all the tools we need to assemble something cool.

Let’ see a couple of practical examples!Exploring global variablesSome people dislike Jupyter for not being able to easily see what variables are defined in your environment (as you will for example in Spyder).

Let’s make a little help for this.

We will make a menu to select an option from our global variables, hitting the button will check its type `and print it.

Line 4: using globals we get the list of all the global variables defined in our scope.

We exclude variables with underscores and use the rest as options for our menu (line 12).

We create a button, output and a function where we print the type of selected variable (lines 14–20).

Link the button and its function together (line 22), and display a container with our widgets (line 24).

We make use of the button snippetExploring a datasetLet’s have another demonstration of what you can do to explore a dataset using again a menu and a button.

We’ll use the menu users to select a specific value, pressing the button will calculate various insights from the dataset according to it and display the results using Markdown.

At line 8 we define our menu users with the unique values of our column df['Users'] as options.

In lines 16, 17 and 18 we define a button, an output and a function.

The function will make use of users.

value to subset our dataset and compute some variables.

These are passed inside placeholders in string (lines 40–48).

At line 50 we link our button with the function with the button method .

on_click().

At line 52 we finally display our menu, button and output inside the containerVBox.

The widgets don’t do anything by themselves, so you do have to write a bit of code to get somewhere.

This example allows an intuitive way of visualizing and sharing findings in the data: it returns a list of observations.

Bonus: 2D/3D GrapherTurns up you can also display HTML inside a notebook, so let’s see a demonstration assembling widgets to make a grapher for mathematical expressions using Graph3d (a Javascript library) and matplotlib.

We will be calling the value of a text window (equation) inside the function exec.

Each time we press the button, we compute points for our graph by executing python code as a string.

(Calling python as a string looks a bit weird and I understand that there might be a better way to do this, so I left the code at the end).

I wrote some instructions of how to use it:2D GraphTyping either X or Y in the equation text box will make a plot in two dimensions.

If we enter both X and Y it will display a 3D plot.

3D GraphHere we are using the same steps as before: we define a button, an output and a function (which uses the values of our other widgets) and link them together.

This is the key thing you need to remember if you want to use buttons to interact with your code.

Hopefully this is now clear enough.

Here is the code I used to do it:ConclusionsAdding widgets to your notebooks allows you to act as mediator between the code and output by interacting with the variables.

It’s particularly well suited to explore things and dynamically change function calls.

It might also not always be the most productive thing to do.

.

. More details

Leave a Reply