Black Lives Matter. Please consider donating to Black Girls Code today.

Avoid legend automatically repositioning

I have a Dash app with plots different data depending on the value selected in a dropdown. In one set of data, the series names are longer so the legend automatically repositions itself into the plot area.

The default behaviour is the legend has it’s own space to the right of the plot area, and as legend name sizes increase the plot area is shrunk accordingly to make room for the legend. This is until some minimum value is reached and plotly decides the legend should be superimposed on top of the plot area.

I never want the legend super imposed and want to keep it on the right. To solve this, ideally would be altering the minimum value rule I mentioned, or less ideally, by setting a fixed (smaller) size for the plot area to allow room to the right of the plot area for a legend with longer names. Is this possible?

I have produced a minimal dash app example below to demonstrate.

# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

app.layout = html.Div(children=[

    # basic example adapted from Dash's getting started docs - demonstrates usable area within Div
    html.Div([
        dcc.Graph(
            id='example-graph1',
            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'line', 'name': 'SF'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'line', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualization',
                }
            }
        )
    ],
    style={'width': 600, 'height': 600, 'border': '5px solid red'}
    ),

    # basic example with long legend name - legend is auto-positioned and is trimmed to size of chart
    html.Div([
        dcc.Graph(
            id='example-graph2',
            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'line', 'name': 'SFABCDEFGHIJKLMNOPQRSTUVWXYZ'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'line', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualization',
                }
            }
        )
    ],
    style={'width': 600, 'height': 600, 'border': '5px solid red'}
    ),

    # attempt to fix legend position
    html.Div([
        dcc.Graph(
            id='example-graph3',
            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'line', 'name': 'SFABCDEFGHIJKLMNOPQRSTUVWXYZ'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'line', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualization',
                    'autosize': False,
                    'legend': dict(orientation='v',yanchor='top',xanchor='right',y=1,x=-1),
                    'margin': dict(r=400)
                }
            }
        )
    ],
    style={'width': 600, 'height': 600, 'border': '5px solid red'}
    ),

])

if __name__ == '__main__':
    app.run_server(debug=True)```

Hi @maxwell8888,

Try setting the layout.legend.x property to 1.02.

            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'scatter', 'name': 'SFABCDEFGHIJKLMNOPQRSTUVWXYZ'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'scatter', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualization',
                    'legend': {'x': 1.02},
                }
            }

This will cause the left edge of the legend to always start just to the right of the end of the xaxis, thus keeping the legend out of the plotting area.

BTW: There is no trace type named line, what you want here is scatter. It just so happens that scatter is what Plotly.js defaults to if it doesn’t understand the trace type.

Hope that helps!
-Jon

1 Like

Hi @jmmease

Thanks for your reply (and correcting line to scatter!). I tried 'legend': {'x': 1.02} but it doesn’t seem to work - I think because it doesn’t over-ride plotly’s not wanting to let the plot area go below a certain size.

See code below which produces the following:

# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

app.layout = html.Div(children=[

    # basic example adapted from Dash's getting started docs - demonstrates usable able within Div
    html.Div([
        dcc.Graph(
            id='example-graph1',
            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'scatter', 'name': 'SF'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'scatter', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualization',
                }
            }
        )
    ],
    style={'width': 600, 'height': 600, 'border': '5px solid red'}
    ),

    # Try setting the layout.legend.x property to 1.02
    html.Div([
        dcc.Graph(
            id='example-graph2',
            figure={
                'data': [
                    {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'scatter', 'name': 'SFABCDEFGHIJKLMNOPQRSTUVWXYZ'},
                    {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'scatter', 'name': u'Montréal'},
                ],
                'layout': {
                    'title': 'Dash Data Visualization',
                    'legend': {'x': 1.02},
                }
            }
        )
    ],
    style={'width': 600, 'height': 600, 'border': '5px solid red'}
    ),

])

if __name__ == '__main__':
    app.run_server(debug=True)

Ok, I see what you mean. Yeah, it doesn’t look like you can keep plotly from overriding the legend location if the string gets too wide.

One idea would be for you to wrap the legend text across multiple lines if it’s longer that a certain width. The textwrap module is really nice for this.

Something like:

import plotly.graph_objs as go

trace_name = 'The quick brows fox jumps over the lazy log'
legend_str = '<br>'.join(textwrap.wrap(trace_name, width=26))
figure={
    'data': [
        {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'scatter', 'name': 'SF'},
        {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'scatter', 'name': legend_str},
    ],
    'layout': {
        'title': 'Dash Data Visualization',
        'width': 600,
        'height': 600
    }
}

@jmmease Thanks, that’s a really nice work-around!

1 Like