""" Gapminder demo demonstrating how to combine to extend a HoloViews plot with custom bokeh widgets to deploy an app. """ import pandas as pd import numpy as np import holoviews as hv import panel as pn from bokeh.sampledata import gapminder from holoviews import dim, opts renderer = hv.renderer('bokeh') # Declare dataset fertility = gapminder.fertility.reset_index().melt(id_vars='Country', var_name='Year', value_name='Fertility') population = gapminder.population.reset_index().melt(id_vars='Country', var_name='Year', value_name='Population') life_expectancy = gapminder.life_expectancy.reset_index().melt(id_vars='Country', var_name='Year', value_name='Life Expectancy') gapminder_df = pd.merge(pd.merge(pd.merge(fertility, population), life_expectancy), gapminder.regions, on='Country') gapminder_df.Year = gapminder_df.Year.astype('int') ds = hv.Dataset(gapminder_df) # Apply dimension labels and ranges kdims = ['Fertility', 'Life Expectancy'] vdims = ['Country', 'Population', 'Group'] dimensions = { 'Fertility' : dict(label='Children per woman (total fertility)', range=(0, 10)), 'Life Expectancy': dict(label='Life expectancy at birth (years)', range=(15, 100)), 'Population': ('population', 'Population') } # Create Points plotting fertility vs life expectancy indexed by Year gapminder_ds = ds.redim(**dimensions).to(hv.Points, kdims, vdims, 'Year') # Define annotations text = gapminder_ds.clone({yr: hv.Text(1.2, 25, str(int(yr)), fontsize=30) for yr in gapminder_ds.keys()}) # Define options # Combine Points and Text hvgapminder = (gapminder_ds * text).opts( opts.Points(alpha=0.6, color='Group', cmap='Set1', line_color='black', size=np.sqrt(dim('Population'))*0.005, tools=['hover'], title='Gapminder Demo', responsive=True, show_grid=True), opts.Text(text_font_size='52pt', text_color='lightgray') ) # Define custom widgets def animate_update(): year = slider.value + 1 if year > end: year = int(start) slider.value = year # Update the holoviews plot by calling update with the new year. def slider_update(event): hvplot.update((event.new,)) def animate(event): if button.name == '► Play': button.name = '❚❚ Pause' callback.start() else: button.name = '► Play' callback.stop() start, end = ds.range('Year') slider = pn.widgets.IntSlider(start=int(start), end=int(end), value=int(start), name="Year") slider.param.watch(slider_update, 'value') button = pn.widgets.Button(name='► Play', width=60, align='end') button.on_click(animate) callback = button.add_periodic_callback(animate_update, 200, start=False) # Get HoloViews plot and attach document hvplot = renderer.get_plot(hvgapminder) hvplot.update((1964,)) # Create a Panel layout and make it servable pn.Column( hvplot.state, pn.Row(slider, button), sizing_mode='stretch_both' ).servable('Gapminder Demo')