I have a NumPy array which contains data from several samples. Some of the samples are outliers and need to be removed via visual inspection. Is there a way to make an interactive line plot in a jupyter notebook where a user can select a line on the plot by clicking it and have that line disappear/be highlighted and the data be marked for removal?
So far the best I have come up with is using Plotly:
import plotly.graph_objects as go
x = np.linspace(0,100)
y = np.random.randint(5, size=(5, 100))
fig = go.Figure()
for line in range(5):
fig.add_trace(go.Line(x=x, y=y[:,line],mode='lines'))
f = go.FigureWidget(fig)
f
Using this code I can get a line graph with lines that are selectable by selecting the corresponding label in the figure legend, but this quickly becomes unfeasible with more samples. Is there a way to do this without plotting a legend and having the lines be selectable directly in the graph?
Thanks
You can use click events which allow you to define a callback
that is bound to each trace. Here is an example of a callback function called update_trace
that will remove a trace when it's clicked on (the @out.capture
decorator isn't necessary, but can be useful for debugging using print statements):
import numpy as np
import plotly.graph_objects as go
from ipywidgets import Output, VBox
np.random.seed(42)
x = np.linspace(0,100)
y = np.random.randint(5, size=(5, 50))
fig = go.Figure()
for line in range(5):
fig.add_trace(go.Scatter(x=x, y=y[line,:],mode='lines',visible=True,name=f'trace_{line+1}'))
f = go.FigureWidget(fig)
out = Output()
@out.capture(clear_output=False)
def update_trace(trace, points, selector):
## determine whether trace was clicked on
if points.point_inds == []:
pass
else:
selected_trace_name = trace.name
for f_trace in f.data:
if (selected_trace_name == f_trace.name) & (f_trace.visible == True):
f_trace.visible = False
print(f"removing {selected_trace_name}")
traces = f.data
for trace in traces:
trace.on_click(update_trace)
VBox([f, out])