Jupyter Notebook & Ipywidgets dropdown cascade not functioning correctly - probably my code

Viewed 533

In Jupyter Notebook I am trying to get 3 cascading dropdown list boxes to function dependant on the values defined in the list boxes.

It works to a fashion but it always takes 2 refreshes of various dropdown list boxes to get the data I want displayed

I started with the example in this article from towardsdatascience and modified it to add the third dropdown box.

I cannot work out where my change or observe is not doing a dynamic update so that no matter what I select I will get a new refresh when ever a dropdown list box is changed.

All help gratefully received Apologies if there is too much code/debug but i see others getting slated for not providing enough.

Here is the code.

# Modified From: - https://towardsdatascience.com/bring-your-jupyter-notebook-to-life-with-

interactive-widgets-bc12e03f0916

import pandas as pd
import numpy as np
import ipywidgets as widgets
from ipywidgets import Layout, AppLayout
from IPython.display import display

# From moriginal article. We use local file
# url = "https://data.london.gov.uk/download/number-international-visitors-london/b1e0f953-4c8a-4b45-95f5-e0d143d5641e/international-visitors-london-raw.csv"
# df_london = pd.read_csv(url)

# Initially, we will get the data and load it into a dataframe:
file = 'C:\\Users\\smelly\\data\\csv\\international-visitors-london-raw.csv'

df_london = pd.read_csv(file, encoding = 'ISO-8859-1')

# Example of when the dropdown year is selected a new text box is
# displayed to show the purpose filter

ALL = 'ALL'
def unique_sorted_values_plus_ALL(array):
    unique = array.unique().tolist()
    unique.sort()
    unique.insert(0, ALL)
    return unique

# variable to store the common output for both dropdowns
output = widgets.Output()


# Here are the three dropdowns:
dropdown_year = widgets.Dropdown(description='Year of Visit',
                                  options = unique_sorted_values_plus_ALL(df_london.year))

dropdown_purpose = widgets.Dropdown(description='Purpose of Visit',
                                     options = unique_sorted_values_plus_ALL(df_london.purpose))
dropdown_market = widgets.Dropdown(description='Country Market',
                                    options = unique_sorted_values_plus_ALL(df_london.market))


def common_filtering(year, purpose, market):
    output.clear_output()
    
    if (year == ALL) & (purpose == ALL) & (market == ALL):
        common_filter = df_london
    elif (year == ALL):
        common_filter = df_london[df_london.market == market]
    elif (year == ALL):
        lcommon_filter = df_london[df_london.purpose == purpose]
    elif (purpose == ALL):
        common_filter = df_london[df_london.year == year]
    else:
        common_filter = df_london[(df_london.year == year) & 
                                  (df_london.purpose == purpose) &
                                  (df_london.market == market)]
    with output:
        display(common_filter)
    
# We amend the event handlers to call the lcommon_filtering function and pass the change.new value as well as the 
# current value of the other dropdown:    
def dropdown_year_eventhandler(change):
    common_filtering(change.new, dropdown_purpose.value, dropdown_market.value)
    
def dropdown_purpose_eventhandler(change):
    common_filtering(dropdown_year.value, dropdown_market.value, change.new)

def dropdown_market_eventhandler(change):
    common_filtering(dropdown_year.value, dropdown_purpose.value, change.new)
    

# We bind the handlers to the dropdowns, and that’s it!    
dropdown_year.observe(dropdown_year_eventhandler, names='value')
dropdown_purpose.observe(dropdown_purpose_eventhandler, names='value')
dropdown_market.observe(dropdown_market_eventhandler, names='value')

display(dropdown_year)

display(dropdown_purpose)

display(dropdown_market)

display(output)

Output If run the above in my Jupyter Notebook here is the output I get:

Set Year of Visit to 2004 - dataframe refreshes showing only entries for 2004

Set Year of Visit to 2004 - dataframe refreshes showing only entries for 2004
        year    quarter             market          dur_stay        mode    purpose         area    Visits (000s)   Spend (£m)  Nights (000s)   sample
6637    2004    January-March       Belgium         1-3 nights      Air     Holiday         LONDON  4.443318        1.824694    7.118778        5
6638    2004    January-March       Belgium         1-3 nights      Air     Business        LONDON  7.285553        2.124391    11.397926       13
6639    2004    January-March       Belgium         1-3 nights      Air     VFR             LONDON  0.502538        0.040518    1.053520        1
6640    2004    January-March       Belgium         1-3 nights      Air     Miscellaneous   LONDON  0.380132        0.118981    0.760264        1
6641    2004    January-March       Belgium         1-3 nights      Sea     Holiday         LONDON  9.906320        1.340740    29.719000       6
...     ...     ...                 ...             ...             ...     ...             ...     ...             ...         ...             ...         
10153   2004    October-December    Other Africa    15+ nights      Air     Holiday         LONDON  0.886168        0.429408    11.692750       2
10154   2004    October-December    Other Africa    15+ nights      Air     Business        LONDON  0.960389        2.196219    29.084010       3
10155   2004    October-December    Other Africa    15+ nights      Air     VFR             LONDON  3.969384        2.211225    221.007949      11
10156   2004    October-December    Other Africa    15+ nights      Air     Study           LONDON  1.405100        4.917840    140.509995      1
10157   2004    October-December    Other Africa    15+ nights      Air     Miscellaneous   LONDON  0.614155        0.315062    48.518200       1

Set purpose of Visit to Business - Nothing changes

Set Year of Visit to 2004 - dataframe refreshes showing only entries for 2004
        year    quarter             market          dur_stay        mode    purpose         area    Visits (000s)   Spend (£m)  Nights (000s)   sample
6637    2004    January-March       Belgium         1-3 nights      Air     Holiday         LONDON  4.443318        1.824694    7.118778        5
6638    2004    January-March       Belgium         1-3 nights      Air     Business        LONDON  7.285553        2.124391    11.397926       13
6639    2004    January-March       Belgium         1-3 nights      Air     VFR             LONDON  0.502538        0.040518    1.053520        1
6640    2004    January-March       Belgium         1-3 nights      Air     Miscellaneous   LONDON  0.380132        0.118981    0.760264        1
6641    2004    January-March       Belgium         1-3 nights      Sea     Holiday         LONDON  9.906320        1.340740    29.719000       6
...     ...     ...                 ...             ...             ...     ...             ...     ...             ...         ...             ...         
10153   2004    October-December    Other Africa    15+ nights      Air     Holiday         LONDON  0.886168        0.429408    11.692750       2
10154   2004    October-December    Other Africa    15+ nights      Air     Business        LONDON  0.960389        2.196219    29.084010       3
10155   2004    October-December    Other Africa    15+ nights      Air     VFR             LONDON  3.969384        2.211225    221.007949      11
10156   2004    October-December    Other Africa    15+ nights      Air     Study           LONDON  1.405100        4.917840    140.509995      1
10157   2004    October-December    Other Africa    15+ nights      Air     Miscellaneous   LONDON  0.614155        0.315062    48.518200

Set country Market to Belgium - Dataframe updates showing year 2004, market = Belgium, purpose = Business Works as expected

Set country Market to Belgium - Dataframe updates showing year 2004, market = Belgium, purpose = Business

        year    quarter             market          dur_stay        mode    purpose         area    Visits (000s)   Spend (£m)  Nights (000s)   sample
6638    2004    January-March       Belgium         1-3 nights      Air     Business        LONDON  7.285553        2.124391    11.397926       13
6642    2004    January-March       Belgium         1-3 nights      Sea     Business        LONDON  3.348380        0.558413    6.682670        2
6645    2004    January-March       Belgium         1-3 nights      Tunnel  Business        LONDON  8.896553        2.482150    14.327898       20
6649    2004    January-March       Belgium         4-7 nights      Air     Business        LONDON  1.873621        1.522606    6.717740        4
6652    2004    January-March       Belgium         4-7 nights      Sea     Business        LONDON  1.674190        0.386508    6.682670        1
6654    2004    January-March       Belgium         4-7 nights      Tunnel  Business        LONDON  1.161537        0.666272    5.417160        3
6657    2004    January-March       Belgium         8-14 nights     Tunnel  Business        LONDON  0.391887        0.589908    5.474880        1
7436    2004    April-June          Belgium         1-3 nights      Air     Business        LONDON  12.233637       3.012382    17.830062       13
7440    2004    April-June          Belgium         1-3 nights      Sea     Business        LONDON  2.438620        0.174643    7.422190        1
7443    2004    April-June          Belgium         1-3 nights      Tunnel  Business        LONDON  9.825394        2.489263    15.252250       21
7446    2004    April-June          Belgium         4-7 nights      Air     Business        LONDON  0.779633        0.299457    3.480100        2
7451    2004    April-June          Belgium         4-7 nights      Tunnel  Business        LONDON  0.427832        0.547138    1.736200        1
7455    2004    April-June          Belgium         8-14 nights     Air     Business        LONDON  0.553725        1.660990    4.364970        1
7458    2004    April-June          Belgium         8-14 nights     Tunnel  Business        LONDON  0.445658        0.000000    4.069230        1
7461    2004    April-June          Belgium         15+ nights      Air     Business        LONDON  1.714030        0.875407    91.202904       1
8313    2004    July-September      Belgium         1-3 nights      Air     Business        LONDON  5.428023        2.023622    9.563622        9
8317    2004    July-September      Belgium         1-3 nights      Sea     Business        LONDON  1.936040        0.038780    5.795310        1
8320    2004    July-September      Belgium         1-3 nights      Tunnel  Business        LONDON  10.915149       3.652393    16.873318       21
8326    2004    July-September      Belgium         4-7 nights      Tunnel  Business        LONDON  2.032828        1.041474    10.141750       3
9291    2004    October-December    Belgium         1-3 nights      Air     Business        LONDON  13.759479       5.657460    19.735172       20
9296    2004    October-December    Belgium         1-3 nights      Sea     Business        LONDON  2.033270        0.026998    2.033270        1
9299    2004    October-December    Belgium         1-3 nights      Tunnel  Business        LONDON  7.496895        2.858201    11.117845       17
9302    2004    October-December    Belgium         4-7 nights      Air     Business        LONDON  1.145586        1.520935    4.933070        3
9305    2004    October-December    Belgium         4-7 nights      Tunnel  Business        LONDON  0.843375        0.503309    3.373500        2
9308    2004    October-December    Belgium         8-14 nights     Air     Business        LONDON  0.321972        0.052481    3.219720        1
9309    2004    October-December    Belgium         8-14 nights     Tunnel  Business        LONDON  0.830616        0.246976    5.399000    1
1 Answers

You had some errors with your filter choosing logic, and also in your event handler code, See below for a different implementation handling your inputs a bit clearer.

import functools
 
def common_filtering(year, purpose, market):
    
    df = df_london.copy()
    filters = []
    if year is not ALL:
        filters.append(df['year']==year)
    if purpose is not ALL:
        filters.append(df['purpose']==purpose)
    if market is not ALL:
        filters.append(df['market']==market)
    output.clear_output()
    with output:
        if filters:
            df_filter = functools.reduce(lambda x,y: x&y, filters)
            display(df.loc[df_filter])
        else:
            display(df)

def dropdown_year_eventhandler(change):
    common_filtering(change.new, dropdown_purpose.value, dropdown_market.value)
    
def dropdown_purpose_eventhandler(change):
    common_filtering(dropdown_year.value, change.new, dropdown_market.value,)

    
def dropdown_market_eventhandler(change):
    common_filtering(dropdown_year.value, dropdown_purpose.value, change.new)
Related