banderlog013
banderlog013

Reputation: 2503

Display only single line on hover, hide all other lines

Is there is a way to hide all other lines on a figure, when I am hovering a single one?

Example:

import numpy as np
import plotly.express as px

np.random.seed(42)
data = np.random.randint(0, 10, (5, 10))
fig = px.line(data_frame=pd.DataFrame(data))

fig.show()

will produce:

enter image description here

and I want this, when I hover particular line (but without manually switching off all other lines and keeping X, Y axes dims):

enter image description here

UPD: inside jupyter notebook

Upvotes: 2

Views: 1683

Answers (2)

vestland
vestland

Reputation: 61214

The following suggestion draws from the posts Plotly Dash: How to reset the "n_clicks" attribute of a dash-html.button? and Plotly-Dash: How to code interactive callbacks for hover functions in plotly dash and will let you display a single trace by hovering on any point or part of the line. The rest of the traces aren't completely hidden though, but are made grey and transparent in the background so that you can more easily make another selection.

To reset the figure and make all traces fully visible at the same time, just click Clear Selection. I understand that you would prefer a "plain" Jupyter approach to obtain this functionality, but you'd be missing out on the true power of plotly which reveals itself to the full extent only through Dash and JupyterDash. With this suggestion, you won't see any difference between Dash and "plain" Jupyter since the figure, or app, is displayed inline with app.run_server(mode='inline')

Plot 1 - Upon launch. Or after clicking Clear selection

enter image description here

Plot 2 - Selection = trace a

enter image description here

Plot 3 - Selection = trace b

enter image description here

Complete code

import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash

# pandas and plotly settings
pd.options.plotting.backend = "plotly"

# app info
app = JupyterDash(__name__)

# sample data and figure setup
df = pd.DataFrame(np.random.randint(-1,2,size=(200, 12)), columns=list('abcdefghijkl'))
df = df.cumsum()#.reset_index()
fig = df.plot(title = 'Selected traces = all', template='plotly_dark')#.update_traces(line_color = 'rgba(50,50,50,0.5)')
set_fig = go.Figure(fig)
colors = {d.name:d.line.color for i, d in enumerate(set_fig.data)}

# jupyterdash app
app.layout = html.Div([html.Button('Clear selection', id='clearit', n_clicks=0),
                       dcc.Graph(id='hoverfig',figure=fig,#clear_on_unhover = True
                                                ),])
colors = {d.name:d.line.color for i, d in enumerate(set_fig.data)}

# callbacks
@app.callback(
    Output('hoverfig', 'figure'),
    [Input('hoverfig', 'hoverData'), Input('clearit', 'n_clicks')])
def display_hover_data(hoverData, resetHover):
    changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
    if 'clearit' in changed_id:
        return set_fig
    else:
        try:
            fig2 = fig.update_layout(title = 'Selected trace = ' + fig.data[hoverData['points'][0]['curveNumber']].name)
            fig2.for_each_trace(lambda t: t.update(line_color = 'rgba(50,50,50,0.5)',line_width = 1) if t.name != fig.data[hoverData['points'][0]['curveNumber']].name else t.update(line_color = colors[t.name], line_width = 2))
            return fig2
        except:
            return fig

app.run_server(mode='inline', port = 8070, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True)

Upvotes: 3

It_is_Chris
It_is_Chris

Reputation: 14113

import plotly.graph_objects as go
import numpy as np
import pandas as pd

# callback function for on_hover
def hide_traces_on_hover(trace, points, selector):
    if len(points.point_inds)==1: # identify hover
        i = points.trace_index # get the index of the hovered trace
        f.data[i].visible = True # keep the hovered trace visible
        # create a list of traces you want to hide
        hide_traces = [l_trace for idx, l_trace in enumerate(f.data) if idx != i] 
        for l_trace in hide_traces: # iterate over hide_traces
            l_trace.visible = 'legendonly' # hide all remaining traces
    
# callback function to unhide traces on click
def unhide_traces_on_click(trace, points, selector):
    for l_trace in f.data:
        l_trace.visible = True
        
# your sample data frame
np.random.seed(42)
data = np.random.randint(0, 10, (5, 10))
df = pd.DataFrame(data)

f = go.FigureWidget() # create figure widget

f.update_yaxes(autorange=False) # set auto range to false
# define the range of the y-axis using min and max functions
f.update_layout(yaxis_range=[df.values.min()-1, df.values.max()+1])

# create your traces from the data frame
for col in df.columns:
    trace = go.Scatter(x=df.index, y=df[col], mode='lines+markers')
    f.add_trace(trace)

# assign your functions to each trace
for trace in f.data:
    trace.on_hover(hide_traces_on_hover)
    trace.on_click(unhide_traces_on_click)

f

If you are running into issues, here is the jupyter notebook support documentation for using FigureWidget and here is the jupyter lab support documentation. Make sure you have the ipywidgets package installed. Also, just as a FYI, here is the FigureWidget documentation.

When you hover over a marker in the graph.

enter image description here

When you click on any marker of the visible trace all the hidden traces will become visible again.

enter image description here

Upvotes: 2

Related Questions