#!/usr/bin/env python # coding: utf-8 # # Data Visualization with Python # ## Part 2: Interactive Visualizations With Bokeh # # Bokeh # # What is Bokeh? # - A python library for browser based visualizations #

# - Cross-platform # - interactive plots # - dashboards # - data applications #

# - High-performance interactivity over very large or streaming datasets #

# - Tight integration with Jupyter and pandas # - shareable, explorable data analysis notebooks #

# - Large community #

# - Active development # # More information # # - Bokeh Homepage: https://bokeh.pydata.org #

# - Bokeh Discussion Group: https://groups.google.com/a/continuum.io/forum/#!forum/bokeh #

# - Userguide: https://bokeh.pydata.org/en/latest/docs/user_guide.html #

# - API Reference: https://bokeh.pydata.org/en/latest/docs/reference.html #

# - Gallery: https://bokeh.pydata.org/en/latest/docs/gallery.html #

# - Tutorial: http://nbviewer.jupyter.org/github/bokeh/bokeh-notebooks/blob/master/index.ipynb#Tutorial # # A Simple Example # # Create a scatterplot using the iris data from the previous lesson. # # Plot petal_width vs. petal_length. # # Colour each data point by species. # In[1]: # Import pandas for handling the data import pandas as pd # import some functions from the bokeh library from bokeh.plotting import figure, output_notebook, show # configure bokeh for notebook mode output_notebook() # In[2]: # load the iris data set iris_data = pd.read_csv('https://git.lumc.nl/courses/programming-course/raw/visualization-2018/visualization/data/iris.csv') # Create a dictionary which maps a color to each species name colormap = { 'setosa': 'orange', 'versicolor': 'green', 'virginica': 'blue' } # add a column to the dataframe which contains the color iris_data['color'] = iris_data['species'].map(colormap) # check that we now have a color column iris_data.head() # In[3]: # Create a figure iris_figure = figure(title = 'Iris Morphology') # Label the x_axis iris_figure.xaxis.axis_label = 'Petal Length' # label the y_axis iris_figure.yaxis.axis_label = 'Petal Width' # Add some circles, using the iris_data dataframe as the data source # plot the 'petal_length' column on the x axis, # the 'petal_width' column on the y axis, # and use the 'color' column for the circle color iris_figure.circle(x='petal_length', y='petal_width', color='color', source=iris_data, fill_alpha=0.5, size=10); # In[4]: # and finally, show it show(iris_figure) # # Add some more interactivity # We can add tooltips to display information about each data point # In[5]: from bokeh.models import HoverTool # Create a HoverTool which displays tooltips # The tooltips are a list of tuples, where each tuple # contains a text label to display, followed by a string # indicating the column to display a value from tooltips = HoverTool( tooltips=[ ("Petal Width", "@petal_width"), ("Petal Length", "@petal_length"), ("Species", "@species") ] ) # add the tooltips to the figure iris_figure.add_tools(tooltips) # In[6]: show(iris_figure) # ```python # # Here is all of the code for the final figure in one cell # # # Import pandas for handling the data # import pandas as pd # # # import some functions from the bokeh library # from bokeh.plotting import figure, output_notebook, show # from bokeh.models import HoverTool # # # configure bokeh for notebook mode # output_notebook() # # # load the iris data set # iris_data = pd.DataFrame.from_csv('https://git.lumc.nl/courses/programming-course/raw/master/visualization/data/iris.csv') # # # Create a dictionary which maps a color to each species name # colormap = { # 'setosa': 'orange', # 'versicolor': 'green', # 'virginica': 'blue' # } # # # add a column to the dataframe which contains the color # iris_data['color'] = iris_data['species'].map(colormap) # # # Create a figure # iris_figure = figure(title = 'Iris Morphology') # # # Label the x_axis # iris_figure.xaxis.axis_label = 'Petal Length' # # # label the y_axis # iris_figure.yaxis.axis_label = 'Petal Width' # # # Add the circles # iris_figure.circle(x='petal_length', y='petal_width', color='color', # source=iris_data, fill_alpha=0.5, size=10) # # # Create a HoverTool which displays tooltips # tooltips = HoverTool( # tooltips=[ # ("Petal Width", "@petal_width"), # ("Petal Length", "@petal_length"), # ("Species", "@species") # ] # ) # # # add the tooltips to the figure # iris_figure.add_tools(tooltips) # # # display the image # show(iris_figure) # ``` # # Let's make a data exploration tool! # # NASA have a data available which provides information about all large meteors which have exploded in the atmosphere since 1988. # # We will use that data set as the basis for an interactive data exploration tool. # In[7]: # load the data fireball_data = pd.read_csv( 'https://git.lumc.nl/courses/programming-course/raw/visualization-2018/visualization/data/fireballs.csv' ) # how does it look? fireball_data.head() # ## Tool Design # # - Basic Requirements # - Display a map of the Earth # - Overlay circles for each meteor event # - Make the size of the circle proportional to the force of the explosion # - Add tool-tips which give more information for each event #

# - Advanced Requirements # - Add a widget which allows us to filter the data for a single year # In[8]: # STEP 1 # Display a map of the earth # the tile provider will display a map of the earth for us import bokeh.tile_providers # the earth is approx 40,000km in circumference # or, -20,000km to +20,000km ==> -20,000,000m to +20,000,000m # lets store this value: map_limit = 20000000 # create a figure fireball_fig = figure(x_range=(-map_limit, map_limit), y_range=(-map_limit, map_limit)) # hide the axes fireball_fig.axis.visible = False # Add a map tile fireball_fig.add_tile(bokeh.tile_providers.STAMEN_TERRAIN); # In[9]: show(fireball_fig) # ## Tool Design # # - Basic Requirements # - Display a map of the Earth # - **Overlay circles for each meteor event** # In[10]: # Now, we can plot a circle for each meteor, # using the X and Y columns for the position fireballs = fireball_fig.circle(x="X", y="Y", source=fireball_data) show(fireball_fig) # In[11]: fireball_fig.renderers.remove(fireballs) # ## Tool Design # # - Basic Requirements # - Display a map of the Earth # - Overlay circles for each meteor event # - **Make the size of the circle proportional to the force of the explosion** # In[12]: # The force column represents the strength of the explosion, let's use it # for the size of the circles. And let's change the color of the circles # to something more 'fiery' fireballs = fireball_fig.circle( x="X", y="Y", size="force", source=fireball_data, fill_color="orange", fill_alpha=0.6, line_color="red", line_alpha=0.6 ) # In[13]: show(fireball_fig) # In[14]: fireball_fig.renderers.remove(fireballs) # In[15]: # Maybe we need to scale the size of the circles a bit! # We can add a column to the dataframe with the scaled force # then use that for the circle size from numpy import sqrt fireball_data["size"] = 5 + sqrt(fireball_data.force) * 5 fireball_data.head() # In[16]: # update the circles to use the 'size' column fireballs = fireball_fig.circle( x="X", y="Y", size="size", source=fireball_data, fill_color="orange", fill_alpha=0.6, line_color="red", line_alpha=0.6 ); # In[17]: show(fireball_fig) # ## Tool Design # # - Basic Requirements # - Display a map of the Earth # - Overlay circles for each meteor event # - Make the size of the circle proportional to the force of the explosion # - **Add tool-tips which give more information for each event** # In[18]: from bokeh.models import HoverTool # use the same syntax as with the iris data tooltips = HoverTool(tooltips=[ ("Date", "@day/@month/@year"), ("Lattitude", "@lattitude"), ("Longitude", "@longitude"), ("Energy (MJ)", "@energy"), ("Force (kT)", "@force") ]) fireball_fig.add_tools(tooltips) # In[19]: show(fireball_fig) # ```python # # The entire code so far # # Import pandas for handling the data # import pandas as pd # # # import some functions from the bokeh library # from bokeh.plotting import figure, output_notebook, show # from bokeh.models import HoverTool # import bokeh.tile_providers # # # numpy sqrt function # from numpy import sqrt # # # configure bokeh for notebook mode # output_notebook() # # # load the data # fireball_data = pd.DataFrame.from_csv( # 'https://git.lumc.nl/courses/programming-course/raw/master/visualization/data/fireballs.csv' # ) # # # map boundary in km # map_limit = 20000000 # # # create a figure # fireball_fig = figure(x_range=(-map_limit, map_limit), y_range=(-map_limit, map_limit)) # # # hide the axes # fireball_fig.axis.visible = False # # # Add a map tile # fireball_fig.add_tile(bokeh.tile_providers.STAMEN_TERRAIN) # # # add a size column which is proportional to the force # fireball_data["size"] = 5 + sqrt(fireball_data.force) * 5 # # # add the circles # fireballs = fireball_fig.circle( # x="X", y="Y", size="size", source=fireball_data, # fill_color="orange", fill_alpha=0.6, # line_color="red", line_alpha=0.6 # ); # # # add the tooltips # tooltips = HoverTool(tooltips=[ # ("Date", "@day/@month/@year"), # ("Lattitude", "@lattitude"), # ("Longitude", "@longitude"), # ("Energy (MJ)", "@energy"), # ("Force (kT)", "@force") # ]) # # fireball_fig.add_tools(tooltips) # # # display it # show(fireball_fig) # ``` # # Final touches # # ## Tool Design # # - Basic Requirements # - Display a map of the Earth # - Overlay circles for each meteor event # - Make the size of the circle proportional to the force of the explosion # - Add tool-tips which give more information for each event #

# - Advanced Requirements # - **Add a widget which allows us to filter the data for a single year** # ## Now it gets complicated. # # ## Don't panic if you don't get it. # # ## I just want to show you how powerful this stuff is. # In[20]: # make a list of all the available years years = list(set(fireball_data.year)) print(years) # In[21]: # we can use the list of years to power a 'slider' widget from ipywidgets import interact, SelectionSlider # The slider will call this function with its value # to begin with we will simply repeat the value given to us def update_year(year): print("Chosen year is {}".format(year)) # make the slider fireball_slider = interact( update_year, year=SelectionSlider(description='year', options=years) ) # Now we have a slider that feeds 'years' to the update_year function. # # We can now change the update_year function so that it modifies the data behind the figure to only display data for the chosen year. # # There are a few other things that need to be changed, I will show the final code and explain. # In[22]: # The final application import pandas as pd # import some functions from the bokeh library from bokeh.plotting import figure, output_notebook, show from bokeh.models import HoverTool, ColumnDataSource import bokeh.tile_providers from bokeh.io import push_notebook # numpy sqrt function from numpy import sqrt # configure bokeh for notebook mode output_notebook() # load the data fireball_data = pd.read_csv( 'https://git.lumc.nl/courses/programming-course/raw/master/visualization/data/fireballs.csv' ) # make a list of the years for use later years = list(set(fireball_data.year)) # map boundary in km map_limit = 20000000 # create a figure fireball_fig = figure(x_range=(-map_limit, map_limit), y_range=(-map_limit, map_limit)) # hide the axes fireball_fig.axis.visible = False # Add a map tile fireball_fig.add_tile(bokeh.tile_providers.STAMEN_TERRAIN) # add a size column which is proportional to the force fireball_data["size"] = 5 + sqrt(fireball_data.force) * 5 # add the circles fireballs = fireball_fig.circle( x="X", y="Y", size="size", source=fireball_data, fill_color="orange", fill_alpha=0.6, line_color="red", line_alpha=0.6 ); # add the tooltips tooltips = HoverTool(tooltips=[ ("Date", "@day/@month/@year"), ("Lattitude", "@lattitude"), ("Longitude", "@longitude"), ("Energy (MJ)", "@energy"), ("Force (kT)", "@force") ]) fireball_fig.add_tools(tooltips) # display it, and keep a handle to the figure fireball_handle = show(fireball_fig, notebook_handle=True) def update_fireballs(year=1988): fireballs.data_source.data = ColumnDataSource(fireball_data[fireball_data["year"] == year]).data fireball_fig.title.text = "Global Bolide Strikes {}".format(year) push_notebook(handle=fireball_handle) # make the slider fireball_slider = interact( update_fireballs, year=SelectionSlider(description='year', options=years) ) # # The End # # Author: Guy Allard # # License: Creative Commons Attribution 3.0 License (CC-by) # In[ ]: