Question: accessing pn.panel.plotly click_data to feed to widgets/other functions

Hello,

I have managed to find how to access the specific datapoint attribute from a pn.panel.plotly graph and can display a text file that shows a desired attribute when a point is clicked on in my plotly sctterplot. I don’t know how to make it interactive such that I can update a pn.widgets.StaticText upon clicking on the points on the plotly graph.

I attempted crating a function in which I tried to add a line as static_text.param.update(value=’#the mapped value of the clicked data) but don’t know how to make this to respond to future clicks on the graph.

1 Like

You can use the click_data for example via something like

@pn.depends(parent_plot_pane.param.click_data)
def child_plot_from_click(event, data=data):
    try:
        row = event["points"][0]["x"]
        return to_child_plot(data, row=row, height=300)
    except:
        return "Click a Country to show its timeseries"

The full code is here

import pandas as pd
import panel as pn
import plotly.express as px

# World Bank API. See https://github.com/tgherzog/wbgapi
import wbgapi as wb

pn.extension("plotly", sizing_mode="stretch_width", template="fast")

ACCENT="#00A170"
SERIES = "EN.ATM.CO2E.PC"
SERIES_PATH = SERIES + ".csv"

def extract():
    try:
        return pd.read_csv(SERIES_PATH, index_col=0)
    except FileNotFoundError:
        data = wb.data.DataFrame(SERIES, labels=True)
        data.to_csv(SERIES_PATH, )
        return data

def transform(source_data):
    data = source_data.dropna(how="all", axis=1)
    data = data.set_index("Country")
    data.columns = [int(column[2:]) for column in data.columns]
    return data

def get_template():
    if pn.state.session_args.get("theme", [b'default'])[0].decode()=="dark":
        return "plotly_dark"
    else:
        return "plotly"

TEMPLATE = get_template()

def to_parent_plot(plot_data: pd.DataFrame, column, rows=20):
    data=plot_data[column].dropna()
    data=data.sort_values(ascending=False)
    data=data.head(rows)
    fig = px.bar(data, y=column, color_discrete_sequence=[ACCENT], template=TEMPLATE)
    fig.layout.autosize = True
    return fig

def to_child_plot(plot_data: pd.DataFrame, row, height=300):
    data=plot_data.loc[row].dropna()
    data=pd.DataFrame(data, columns=[row])
    data.index.name="Year"
    fig = px.line(data, y=row, template=TEMPLATE, height=height)
    fig.update_traces(line=dict(color=ACCENT, width=4))
    return fig

source_data=extract()
data=transform(source_data)

source = pn.pane.Alert(f"""## Source

[World Bank {SERIES}](https://data.worldbank.org/indicator/{SERIES})""", margin=(10,10), alert_type="secondary").servable(area="sidebar")

column = list(data.columns)[-1]
parent_plot = to_parent_plot(data, column=column)
parent_plot_pane = pn.pane.Plotly(parent_plot, config={'responsive': True}, height=400)

@pn.depends(parent_plot_pane.param.click_data)
def child_plot_from_click(event, data=data):
    try:
        row = event["points"][0]["x"]
        return to_child_plot(data, row=row, height=300)
    except:
        return "Click a Country to show its timeseries"

pn.Column(parent_plot_pane, child_plot_from_click, sizing_mode="stretch_both").servable()
pn.state.template.param.update(site="Awesome Panel", title="CO2 emissions (tons per capita)", accent_base_color=ACCENT, header_background=ACCENT)

To serve the app you will need to

panel serve name_of_script.py --show --autoreload
3 Likes

Many thanks indeed. very informative. one follow up question:

I am using a fileupload widget to upload an excel sheet that gets into pandas. when I include it in the body of the file_import function that I am defining, the widget does not show up when I call the function.

how should I include widgets into these functions such that they behave as expected?

regards,
Behrooz