All About DataTables

  • Display tabular data.
  • Built on slickgrid.
  • Sortable, filterable.

In [7]:
from bokeh.io import output_file
output_file('/tmp/bokeh_notebook.html')

Sample Application: World Bank data explorer

Use pandas-datareader to access World Bank data.


In [8]:
from pandas_datareader import wb
indicators_df = wb.get_indicators()
indicators_df['sourceOrganization'] = indicators_df['sourceOrganization'].str.decode("utf-8") 
indicators_df.head()


Out[8]:
id name source sourceNote sourceOrganization topics
0 1.0.HCount.1.25usd Poverty Headcount ($1.25 a day) LAC Equity Lab The poverty headcount index measures the propo... LAC Equity Lab tabulations of SEDLAC (CEDLAS a... Poverty
1 1.0.HCount.10usd Under Middle Class ($10 a day) Headcount LAC Equity Lab The poverty headcount index measures the propo... LAC Equity Lab tabulations of SEDLAC (CEDLAS a... Poverty
2 1.0.HCount.2.5usd Poverty Headcount ($2.50 a day) LAC Equity Lab The poverty headcount index measures the propo... LAC Equity Lab tabulations of SEDLAC (CEDLAS a... Poverty
3 1.0.HCount.Mid10to50 Middle Class ($10-50 a day) Headcount LAC Equity Lab The poverty headcount index measures the propo... LAC Equity Lab tabulations of SEDLAC (CEDLAS a... Poverty
4 1.0.HCount.Ofcl Official Moderate Poverty Rate-National LAC Equity Lab The poverty headcount index measures the propo... LAC Equity Lab tabulations of data from Nation... Poverty

Simple DataTable

  • Sortable
  • Scrollable

In [9]:
from bokeh.io import show, vform
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.layouts import HBox
from bokeh.models.widgets import DataTable, TableColumn, StringFormatter, DateFormatter, Select
import pandas as pd


table_columns = [
    TableColumn(field='id', title='ID'),
    TableColumn(field='name', title='Name'),
    TableColumn(field='source', title='Source'),
    TableColumn(field='sourceNote', title='Source Note'),
    TableColumn(field='sourceOrganization', title='Source Organization'),
    TableColumn(field='topics', title='Topics'),
]
column_names = [tc.field for tc in table_columns]
table_source = ColumnDataSource(indicators_df[column_names])

data_table = DataTable(source=table_source, columns=table_columns, height=400, editable=False)
show(vform(data_table))

Add Filtering

  • Add two filter widgets.
  • Use CustomJS to alter the displayed data.

In [10]:
# two widgets
source_list = ['ALL'] + sorted(indicators_df['source'].unique().tolist())
source_select = Select(title="Source:", value=source_list[0], options=source_list)
topic_list = ['ALL'] + sorted(set([topic.strip() for topics in indicators_df['topics'].unique().tolist() 
                                                 for topic in topics.split(';') if topic.strip()]))
topic_select = Select(title="Topic:", value=topic_list[0], options=topic_list)

# make a copy of the original data - "immutable"
original_table_source = ColumnDataSource(indicators_df[column_names])

arg_dct = dict(
    source=table_source,
    original_source=original_table_source,
    source_select=source_select,
    topic_select=topic_select,
    target_obj=data_table
)

# filtering done in JS
callback_code = """
    var data = source.get('data');
    var original_data = original_source.get('data');
    var source_name = source_select.get('value');
    var topic_name = topic_select.get('value');
    
    // now construct the new data object based on the filtered values
    for (var key in original_data) {
        data[key] = [];
        for (var i = 0; i < original_data["id"].length; ++i) {
            if ((source_name === "ALL" || original_data["source"][i] === source_name) &&
                (topic_name === "ALL" || original_data["topics"][i].indexOf(topic_name) > -1)) {
                data[key].push(original_data[key][i]);
            }
        }
    }
    target_obj.trigger('change');
    source.trigger('change');
"""
generic_callback = CustomJS(args=arg_dct, code=callback_code)
source_select.callback = generic_callback
topic_select.callback = generic_callback

filter_widgets = HBox(children=[source_select, topic_select])
show(vform(filter_widgets, data_table))

In [11]:
from bokeh.models.widgets.tables import HTMLTemplateFormatter
try:
    from urllib import quote
except ImportError as e:
    from urllib.parse import quote


name_template_str = '<a target="_blank" href="<%= url %>"><%= value %></a>'

table_columns = [
    TableColumn(field='id', title='ID'),
    TableColumn(field='name', title='Name', formatter=HTMLTemplateFormatter(template=name_template_str)),
    TableColumn(field='source', title='Source'),
    TableColumn(field='sourceNote', title='Source Note'),
    TableColumn(field='sourceOrganization', title='Source Organization'),
    TableColumn(field='topics', title='Topics'),
]
column_names = [tc.field for tc in table_columns]
df = indicators_df.copy()
df['url'] = df['name'].apply(lambda s: 'http://search.worldbank.org/all?qterm={term}&language=EN'.format(term=quote(s)))
table_source = ColumnDataSource(df)

data_table = DataTable(source=table_source, columns=table_columns, height=400, editable=False)
show(vform(data_table))

In [ ]: