I have two functions, called update_y_with_x and update_x
which are as follows:
update_y_with_x
def update_y_with_x(chosen_tissue):
for i, trace in enumerate(f.data):
if trace.type == "scatter":
# reset all line width
f.data[i].line.width = 1
if chosen_tissue in trace.text:
f.data[i].line.width = 1 + 9
update_x
def update_x(trace, points, selector):
if len(points.point_inds) == 0:
return
# Remove existing bar shades and linked lines' highlights
f.layout.shapes = ()
for i, trace in enumerate(f.data):
if trace.type == "scatter" and f.data[i].line.width != 1:
f.data[i].line.width = 1
# Get tissue identity
chosen_tissue = f.data[points.trace_index].y[points.point_inds[0]]
# Get the index of the chosen tissue
possible_trace_indices = [
i for i, trace in enumerate(f.data) if trace.type == "bar"
]
chosen_tissue_ranks = [
list(f.data[index].y).index(chosen_tissue) for index in possible_trace_indices
]
for i, _ in enumerate(possible_trace_indices):
# Specify the y-range around the clicked bar for shading
y0 = chosen_tissue_ranks[i] + 0.5
y1 = y0 - 1
f.add_hrect(
y0=y0,
y1=y1,
line_width=0,
fillcolor="grey",
opacity=0.3,
row=1,
col=i * 2 + 1,
)
# Update lines
update_y_with_x(chosen_tissue)
as you can see, update_x calls the update_y_with_x function, at the moment, both use a hard-coded plotly go figurewidget, where
f = go.FigureWidget(fig)
and are called like so:
for i, trace in enumerate(f.data):
if trace.type == 'bar': # Check if it's a barplot
f.data[i].on_click(update_x)
I would like to do two things:
-
Move these two functions to an independent function file and import them into the main.ipynb file I’m working with
-
Pass
fas a parameter to each function, rather than having it hard – coded as it currently exists in the function, and make sure that it is passed toupdate_y_with_xprior to it being used inupdate_x
If I’ve missed something key please let me know and would be incredibly grateful for any help 🙂
Edit:
update_x click events on a bar plot. When a bar is clicked, it removes existing shapes, resets line widths for linked scatter plots, adds shaded rectangles around the clicked bars, and updates lines which link these bars together.
What I tried:
I initially exported these functions to a new file called "click_events.py", and added in the argument for each figure_widget, like so:
-
def update_x(
trace, points, selector, figure_widget):(replaced f in file) -
def update_y_with_x(
chosen_tissue, figure_widget):(replaced f in file) -
modify the code in
update_xso thatupdate_y_with_x(chosen_tissue, figure_widget = figure_widget)
However, I believe that when I imported and try to run these functions in the way mentioned above the figure_widget isn’t being passed to ‘update_y_with_x’.
>Solution :
For the two goals you mentioned, moving the functions to an independent file and passing f as a parameter
Steps:
1. Create an Independent Function File:
Create a new Python file (let’s call it plot_utils.py for example) and move the functions update_y_with_x and update_bar into this file. The content of plot_utils.py would look like this:
# plot_utils.py
import plotly.graph_objects as go
def update_y_with_x(fig, chosen_tissue):
for i, trace in enumerate(fig.data):
if trace.type == "scatter":
# reset all line width
fig.data[i].line.width = 1
if chosen_tissue in trace.text:
fig.data[i].line.width = 1 + 9
def update_bar(fig, trace, points, selector):
if len(points.point_inds) == 0:
return
# Remove existing bar shades and linked lines' highlights
fig.layout.shapes = ()
for i, trace in enumerate(fig.data):
if trace.type == "scatter" and fig.data[i].line.width != 1:
fig.data[i].line.width = 1
# Get tissue identity
chosen_tissue = fig.data[points.trace_index].y[points.point_inds[0]]
# Get the index of the chosen tissue
possible_trace_indices = [
i for i, trace in enumerate(fig.data) if trace.type == "bar"
]
chosen_tissue_ranks = [
list(fig.data[index].y).index(chosen_tissue) for index in possible_trace_indices
]
for i, _ in enumerate(possible_trace_indices):
# Specify the y-range around the clicked bar for shading
y0 = chosen_tissue_ranks[i] + 0.5
y1 = y0 - 1
fig.add_hrect(
y0=y0,
y1=y1,
line_width=0,
fillcolor="grey",
opacity=0.3,
row=1,
col=i * 2 + 1,
)
# Update lines
update_y_with_x(fig, chosen_tissue)
2. In your Main Jupyter Notebook (main.ipynb):
In your main Jupyter Notebook, you can import these functions and use them as follows:
# main.ipynb
import plot_utils
import plotly.graph_objects as go
# Create the initial figure
f = go.FigureWidget(fig)
# Register the on_click event
for i, trace in enumerate(f.data):
if trace.type == 'bar': # Check if it's a barplot
f.data[i].on_click(lambda trace, points, selector: plot_utils.update_bar(f, trace, points, selector))
Now, both update_y_with_x and update_bar are in a separate file, and you can import and use them in your main notebook. The f figure is passed as a parameter to both functions, making them more flexible and avoiding hard coding.