Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per-group (e.g. legendgroup) trace styling #2744

Closed
mwouts opened this issue Jun 19, 2018 · 18 comments
Closed

Per-group (e.g. legendgroup) trace styling #2744

mwouts opened this issue Jun 19, 2018 · 18 comments
Labels
feature something new

Comments

@mwouts
Copy link

mwouts commented Jun 19, 2018

If I want two traces on two sub-plots to share the same legend, I (think I) have to explicitly specify the trace color, like

import plotly.graph_objs as go
from plotly.offline import iplot, init_notebook_mode
init_notebook_mode()

style1 = dict(name='trace1', legendgroup='trace1', line=dict(color='red'))
style2 = dict(name='trace2', legendgroup='trace2', line=dict(color='blue'))

fig = go.Figure(
    data=[dict(y=[1, 2, 3], **style1),
          dict(y=[2, 1, 3], **style2),
          dict(y=[1, 2, 3], showlegend=False, xaxis='x2', **style1),
          dict(y=[1, 3, 2.5], showlegend=False, xaxis='x2', **style2)],
    layout=dict(xaxis=dict(domain=[0, .45]),
                xaxis2=dict(domain=[.55, 1]))
)
iplot(fig)

newplot-12

One issue with the above is that I lose the default plotly colors (see plotly/plotly.py#1026 for getting them back).

Is there a more generic way to tell plotly that two traces should share the same legend/style, or even count as one? Thanks

@alexcjohnson
Copy link
Collaborator

You're correct, that ability doesn't exist today. I like the idea, and in v2 we could consider making this the default behavior, but for now it would need to be opt-in. Something like sharegroupstyle: true (though we have other kinds of groups too, it's getting a bit long but we may need sharelegendgroupstyle), would make this trace inherit style attributes from the first trace in the legend group before applying its own values. I'm a little uneasy with the order-dependence of that, could have some unexpected consequences upon reordering or adding/removing traces (and we'll have to be careful how it behaves on hiding/showing traces) but I think we can sort that out.

@alexcjohnson alexcjohnson added the feature something new label Jun 19, 2018
@mwouts
Copy link
Author

mwouts commented Jun 20, 2018

Yes, that would be very convenient. Do I understand well that the first trace in the sharedlegendgroupstyle would then define a legendstyle? Isn't this a concept you want to have outside of traces, then?
I've never faced the issue of reordering/removing traces, but your remark makes me think of the existing legendgroup and showlegend pair. Usually I have a single trace with showlegend=True within a legendgroup, does that resist well to trace updating?

@jacobq
Copy link

jacobq commented Jun 25, 2018

This issue also probably belongs in https://github.com/plotly/plotly.py rather than plotly.js, right?

@mwouts
Copy link
Author

mwouts commented Jun 26, 2018

Well, you probably know better than I - still, the rationale for reporting here is that it is a question on the API, not on the python wrappers.

@jonmmease
Copy link
Contributor

@jacobq The example is in Python, but any change to the trace properties would need to be implemented in Plotly.js first. Then plotly.py will get it automatically.

@suraj021
Copy link

any update as of October 2018.

@Gertoolz
Copy link

This would be great for subplots showing different data from the same elements. I'm having problems trying to workarround this.

@peacej
Copy link

peacej commented Oct 31, 2018

I would love this feature too.

@bodacious-bill
Copy link

Created a github account just to pile on.

The way plotly currently works for this issue goes against the expected behavior, compared to most other common statistical / graphing programs I've used. Grouped legends is a crutch, but then manually setting colors and setting showlegend to false for an arbitrary number of traces becomes programmatically cumbersome. This is a major hassle when programming a dashboard for even simple data.

@etpinard etpinard changed the title Do I really need to specify color for two traces that share a legend? Per-group (e.g. legendgroup) trace styling Apr 4, 2019
@NSLnick
Copy link

NSLnick commented Apr 8, 2019

For anyone still encountering this issue and looking for a work around I'd suggest generating a color value based on a hash of your group name as per this stack exchange as per this stack exchange.

If you have similar group names (i.e group01, group 02, ...) you may want to consider shuffling or reversing your string to get clearly different values. This method has the advantage that using the hash function you can easily use the same color outside of the ploty ecosystem.

@gialmisi
Copy link

gialmisi commented Sep 9, 2019

Piling up on this as well. Would love to have the option to share the same style for the same legend groups. This is very useful, for example, when showing the values and limits for the same datum.

@mwills
Copy link

mwills commented Apr 15, 2020

Any update on enabling this feature?

@mol1337
Copy link

mol1337 commented Jul 1, 2020

I think i have found a workaround for this problem (if I understood the problem correctly). Forgive me for my formatting, I just started using GitHub today.

I have two subplots in which the legend is shared and the line colors should be the same for the same legend entry:
Capture

I had a lot of trouble achieving this but i ended up using Legendgroups based on the legend entry and controlling the colors of the lines using based on the legend entries:

import numpy as np
import pandas as pd
import plotly
from plotly.offline import plot
import plotly.graph_objects as go
import plotly.express as px
import plotly.io as pio
pio.renderers.default = "browser"
from plotly.subplots import make_subplots


#Dataframe:
df = pd.DataFrame(np.array([[1.4, 1.1, 1.3, "Name1"],[1, 2.3, 2.0, "Name2"],[2.3,1,7, "Name2"],[2.75, 2.8, 2.4, "Name1"]]),
                   columns=["Time",'Measurement_result1','Measurement_result2', 'Machine_ID'])

#Array of the machine IDs
machine_names = df.Machine_ID.unique()

#Make the figure
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.02)

#Colors
cols = plotly.colors.DEFAULT_PLOTLY_COLORS

#Loop over the machine names (legend entry) and put each machine name in a legend group with a specific color
for k,i in enumerate(machine_names):
    fig.add_trace(go.Scatter(x=df.loc[df['Machine_ID'] == i].Time, y=df.loc[df['Machine_ID'] == i].Measurement_result1, name = i,legendgroup = i,line=dict(width=2, color=cols[k])), row=1, col=1)
    fig.add_trace(go.Scatter(x=df.loc[df['Machine_ID'] == i].Time, y=df.loc[df['Machine_ID'] == i].Measurement_result2, name = i,legendgroup = i,showlegend=False,line=dict(width=2, color=cols[k])), row=2, col=1)

fig.update_layout(xaxis2_rangeslider_visible=True, xaxis2_rangeslider_thickness=0.1 )
fig.update_layout(legend_title_text='Machine ID',legend_itemclick = "toggleothers")#,legend_traceorder="grouped"
fig.show()

I hope this can help.

@erajabi
Copy link

erajabi commented Jul 3, 2020

The following approach worked for me:

colors = {'group1': 'green',
          'group2': 'orange'}

    for index, name in enumerate(names):
       bars.append(go.Bar(name=name, x=x_array[index], y=y_array[index], marker={'color': colors[name]}))

@jackparmer
Copy link
Contributor

This issue has been tagged with NEEDS SPON$OR

A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.

Sponsorship range: $10k-$15k

What Sponsorship includes:

  • Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
  • Tests for this feature
  • Long-term support (continued support of this feature in the latest version of Plotly.js)
  • Documentation at plotly.com/javascript
  • Possibility of integrating this feature with Plotly Graphing Libraries (Python, R, F#, Julia, MATLAB, etc)
  • Possibility of integrating this feature with Dash
  • Feature announcement on community.plotly.com with shout out to Sponsor (or can remain anonymous)
  • Gratification of advancing the world's most downloaded, interactive scientific graphing libraries (>50M downloads across supported languages)

Please include the link to this issue when contacting us to discuss.

@nlooije
Copy link

nlooije commented Sep 24, 2020

The following approach worked for me:

colors = {'group1': 'green',
          'group2': 'orange'}

    for index, name in enumerate(names):
       bars.append(go.Bar(name=name, x=x_array[index], y=y_array[index], marker={'color': colors[name]}))

I needed this for an arbitrary number of groups but in that case it is not practical to define a color list.
I solved the problem by using HSL colors, the first group get hue = 0 and each new group the hue is increased by 360/length(groups).
This in principle gives 360 relatively distinctly colored traces which is probably fine because to be honest plotting more traces in the same plot gives a chaotic plot anyway.

@xxiro
Copy link

xxiro commented Oct 14, 2020

For anyone still encountering this issue and looking for a work around I'd suggest generating a color value based on a hash of your group name as per this stack exchange as per this stack exchange.

If you have similar group names (i.e group01, group 02, ...) you may want to consider shuffling or reversing your string to get clearly different values. This method has the advantage that using the hash function you can easily use the same color outside of the ploty ecosystem.

Thanks! This is by far the fastest work-around I've tried. For python, here's a module that hash strings into hex color code directly:

https://pypi.org/project/colorhash/

@gvwilson
Copy link
Contributor

Hi - this issue has been sitting for a while, so as part of our effort to tidy up our public repositories I'm going to close it. If it's still a concern, we'd be grateful if you could open a new issue (with a short reproducible example if appropriate) so that we can add it to our stack. Cheers - @gvwilson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new
Projects
None yet
Development

No branches or pull requests