Graphical user interfaces#

In this notebook we ask bob to create a user interface that allows interacting with a plot.

# load secret API key. You must unpack the contents of api_key.zip 
# into the same folder before going ahead.
from dotenv import load_dotenv
load_dotenv()

import os
from bia_bob import bob
bob.initialize(endpoint='https://llm.scads.ai/v1', model='openai/gpt-oss-120b', api_key=os.environ.get('SCADSAI_API_KEY'))
This notebook may contain text, code and images generated by artificial intelligence. Used model: openai/gpt-oss-120b, vision model: None, endpoint: https://llm.scads.ai/v1, bia-bob version: 0.34.3.. Do not enter sensitive or private information and verify generated contents according to good scientific practice. Read more: https://github.com/haesleinhuepf/bia-bob#disclaimer

In this example we use a videogame sales dataset by Gregory Smith which is licensed CC-BY 4.0.

%%bob
write python code that
* opens the file vgsales.csv using pandas
* if the file doesn't exist, open it from https://zenodo.org/records/5898311/files/vgsales.csv?download=1 
  and save it as vgsales.csv

The script loads vgsales.csv with pandas. If the file is not found locally, it downloads it from the provided Zenodo URL, saves it as vgsales.csv, and then reads it.

import urllib.request
import pandas as pd

# 1. Target filename
filename = "vgsales.csv"

# 2‑3. Download if missing
if not os.path.isfile(filename):
    url = "https://zenodo.org/records/5898311/files/vgsales.csv?download=1"
    urllib.request.urlretrieve(url, filename)
    print(f"Downloaded file and saved as '{filename}'.")

# 4. Load into pandas DataFrame
df = pd.read_csv(filename)

# 5. Quick sanity check
print(df.head())
Downloaded file and saved as 'vgsales.csv'.
   Rank                      Name Platform    Year         Genre Publisher  \
0     1                Wii Sports      Wii  2006.0        Sports  Nintendo   
1     2         Super Mario Bros.      NES  1985.0      Platform  Nintendo   
2     3            Mario Kart Wii      Wii  2008.0        Racing  Nintendo   
3     4         Wii Sports Resort      Wii  2009.0        Sports  Nintendo   
4     5  Pokemon Red/Pokemon Blue       GB  1996.0  Role-Playing  Nintendo   

   NA_Sales  EU_Sales  JP_Sales  Other_Sales  Global_Sales  
0     41.49     29.02      3.77         8.46         82.74  
1     29.08      3.58      6.81         0.77         40.24  
2     15.85     12.88      3.79         3.31         35.82  
3     15.75     11.01      3.28         2.96         33.00  
4     11.27      8.89     10.22         1.00         31.37  
%%bob
Build a graphical user interface using ipywidgets where the user can select the publisher. After the publisher is selected, draw a bar plot showing the NA_Sales, EU_Sales, JP_Sales and Other_Sales.
The bar-plot should be shown right below the pulldown

I will create an interactive UI with ipywidgets that lets the user pick a Publisher from a dropdown. When a publisher is chosen, the code will compute the total NA, EU, JP and Other sales for that publisher and display a bar plot directly under the dropdown.

# --------------------------------------------------------------
# 1. Imports (df is already loaded from the previous step)
# --------------------------------------------------------------
import ipywidgets as widgets
import matplotlib.pyplot as plt
from IPython.display import display

# --------------------------------------------------------------
# 2. Create the publisher dropdown
# --------------------------------------------------------------
publishers = sorted(df['Publisher'].dropna().unique())
publisher_dropdown = widgets.Dropdown(
    options=publishers,
    description='Publisher:',
    value=publishers[0],
    style={'description_width': 'initial'}
)

# --------------------------------------------------------------
# 3. Output widget for the bar plot
# --------------------------------------------------------------
plot_out = widgets.Output()

# --------------------------------------------------------------
# 4. Callback to update the plot when a publisher is selected
# --------------------------------------------------------------
def update_plot(change):
    # Only act on value changes (ignore initial creation)
    if change['type'] != 'change' or change['name'] != 'value':
        return
    
    selected_pub = change['new']
    
    # Filter rows for the selected publisher
    sub = df[df['Publisher'] == selected_pub]
    
    # Sum sales per region
    sales = {
        'NA_Sales'    : sub['NA_Sales'].sum(),
        'EU_Sales'    : sub['EU_Sales'].sum(),
        'JP_Sales'    : sub['JP_Sales'].sum(),
        'Other_Sales' : sub['Other_Sales'].sum()
    }
    
    # Plot
    with plot_out:
        plot_out.clear_output(wait=True)   # clear previous figure
        fig, ax = plt.subplots(figsize=(6, 4))
        ax.bar(sales.keys(), sales.values(),
               color=['steelblue', 'orange', 'green', 'purple'])
        ax.set_ylabel('Total Sales (million units)')
        ax.set_title(f'Total Sales for Publisher: {selected_pub}')
        plt.tight_layout()
        plt.show()

# --------------------------------------------------------------
# 5. Link the callback and display the UI
# --------------------------------------------------------------
publisher_dropdown.observe(update_plot, names='value')

# Show widgets (dropdown on top, plot below)
display(widgets.VBox([publisher_dropdown, plot_out]))

# Trigger initial plot for the default selection
update_plot({'type': 'change', 'name': 'value', 'new': publisher_dropdown.value})

Exercise#

Extend the user interface above by an additional pulldown for selecting which column should serve as hue for the visualization of the data points.