Add custom legend (markers color) to plotly python

Hi @qpoToH,

This idea of displaying multiple legends for different characteristics of the traces isn’t something that plotly.js support natively. In most cases, legends entries and traces have a 1 to 1 mapping. With that in mind, here’s an approach/work-around to getting what I think you’re looking for.

from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import plotly.graph_objs as go
import pandas as pd
init_notebook_mode(connected=True)

feed = pd.DataFrame({'AAPL': [100, 120, 130, 120, 150],
                     'MSFT': [80, 70, 90, 100, 99]}, index = pd.date_range('2018-01-01', '2018-01-05'))

trans = pd.DataFrame({'Symbol': ['AAPL', 'AAPL', 'MSFT', 'MSFT'],
                        'Amount': [2, 1, 3, 1],
                        'Action': ['buy', 'sell', 'buy', 'sell'],
                        'Price': [120, 150, 70, 100],
                        'Date': [pd.to_datetime(i) for i in ['2018-01-02', '2018-01-05', '2018-01-02', '2018-01-04']]
                       })

data = []

# Add "All" button
all_but = dict(label = 'All',
               method = 'update',
                  args = [{'visible': [True]},
                          {'title': 'all'}
                           ])

buttonlist = [all_but]
for i, col in enumerate(feed.columns.tolist()):
    stock = go.Scatter(x=feed.index,
                       y=feed.loc[:, col],
                       name=col,
                       legendgroup='stock'
                      )
    # actions
    success = trans.loc[trans.Symbol == col]
    
    # Buy
    success_buy = success[success.Action == 'buy']
    action_buy = go.Scatter(x=success_buy.Date,
                         y=success_buy.Price,
                         name='Buy',
                         mode = 'markers',
                         text = ['Amount ' + str(amount) for amount in success_buy.Amount],
                         hoverinfo = 'text',
                         showlegend=False,
                         marker = dict(
                             size=10,
                             color = 'green'
                         ),
                         legendgroup='Buy',
                        )
    
    # Sell
    success_sell = success[success.Action == 'sell']
    action_sell = go.Scatter(x=success_sell.Date,
                         y=success_sell.Price,
                         name='Sell',
                         mode = 'markers',
                         text = ['Amount ' + str(amount) for amount in success_sell.Amount],
                         hoverinfo = 'text',
                         showlegend=False,
                         marker = dict(
                             size=10,
                             color = 'red'
                         ),
                         legendgroup='Sell',
                        )
    data.append(stock)
    data.append(action_buy)
    data.append(action_sell)
    button = dict(label = col,
                  method = 'update',
                  args = [{'visible': [col == i for i in feed.columns.tolist() for _ in range(3)] + [True, True]},
                          {'title': 'title' + col}
                           ])
    buttonlist.append(button)

# Add single buy and sell traces for legend
data.append(go.Scatter(x=[None], y=[None], mode='markers',
                       marker=dict(size=10, color='green'),
                       legendgroup='Buy', showlegend=True, name='Buy'))

data.append(go.Scatter(x=[None], y=[None], mode='markers',
                       marker=dict(size=10, color='red'),
                       legendgroup='Sell', showlegend=True, name='Sell'))
    
updatemenus = list([
    dict(active=-1,
         buttons=buttonlist,
    )
])
layout = dict(title='Feed', showlegend=True,
              updatemenus=updatemenus)

fig = dict(data=data, layout=layout)
iplot(fig)

A couple of notes:

  1. I split the buy and sell markers into two traces for each stock, that way they can show up separately on the legend.
  2. I placed all of the “Buy” traces in a buy legendgroup and all of the “Sell” traces in a sell legendgroup. This makes sure that when you click on the legend entries to hide/show, all of the “Buy”/“Sell” traces show and hide together.
  3. I added two traces at the very end with x and y of [None], one for buy and one for sell, added them to the corresponding legend group, and gave them showlegend=True. These traces don’t show up on the plot, but they do show up on the legend. And you can think of them as representing all of the other traces in the legend group in the legend.

Hope that helps!
-Jon

7 Likes