-
Notifications
You must be signed in to change notification settings - Fork 796
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
Getting legend for multilayer chart #984
Comments
Legends are only created if the data within a layer is somehow grouped by a label. You can force this by adding columns of labels; for example: x = np.arange(100)
data = pd.DataFrame({'x': x,
'sin(x)': np.sin(x / 5),
'data': np.sin(x / 5) + 0.3*np.random.rand(100),
'line_label': 100 * ['line'],
'points_label': 100 * ['points']})
line = alt.Chart(data).mark_line(strokeWidth=6, color='orange').encode(
x='x',
y='sin(x)',
opacity='line_label'
)
point = alt.Chart(data).mark_point(color='black').encode(
x='x',
y='data',
shape='points_label'
)
(line + point) though this is admittedly a bit hacky. Also, as far as I know vega is incapable of displaying line marks within a legend as you show in your example above, though @kanitw or @domoritz may be able to correct me on that. |
I think we can use a custom SVG path as a symbol but haven't gotten around to make it the default for Vega-Lite lines yet. |
Thanks! This is indeed a bit hacky:) x = np.arange(100)
data = pd.DataFrame({'x': x,
'sin(x)': np.sin(x / 5),
'data': np.sin(x / 5) + 0.3*np.random.rand(100),
'line_label': 100 * ['line'],
'points_label': 100 * ['points']})
line = alt.Chart(data).mark_line(strokeWidth=6, color='orange').encode(
x='x',
y='sin(x)',
opacity=alt.Opacity('line_label', legend=alt.Legend(title=""))
)
point = alt.Chart(data).mark_point(color='black').encode(
x='x',
y='data',
shape=alt.Shape('points_label', legend=alt.Legend(title=""))
)
(line + point) PS: this is probably a different debate (e.g. #947); shape=alt.Shape('points_label', title="" ) instead of shape=alt.Shape('points_label', legend=alt.Legend(title="") ) Big kudos points! 👍 Is this documented somewhere or is it a more try and see? |
The Any volunteers? 😄 |
Looks great! How can you sort the labels in the legend? |
If you don't need to use different mark types for the layers, you can also use the fold transform documented at https://vega.github.io/vega-lite/docs/fold.html to convert you data to long/tidy form. |
Hi folks, I am new to altair and trying to plot weather/hydrograph data. I am able to plot the data, but I can't seem to specify the colors I want with a legend. My data and plot looks like the below. import numpy as np
import altair as alt
x = np.arange(100)
sin = np.sin(x / 5)
data = pd.DataFrame({'x': x,
'sin(x)': np.sin(x / 5),
'q_95': sin + 100*np.random.rand(100),
'q_75': sin + 75*np.random.rand(100),
'q_50': sin + 50*np.random.rand(100),
'q_25': sin + 25*np.random.rand(100),
'q_05': sin + 5*np.random.rand(100),
})
perc_90 = alt.Chart(data).mark_area(color='#4292c6', opacity = .5,).encode(
x=alt.X('x',axis=alt.Axis(title='Day')),
y=alt.Y('q_05',axis=alt.Axis(title='cfs')),
y2 = 'q_95',
#fill=alt.Color("p90", legend=alt.Legend(title=''))
).properties(
width=800)
perc_50 = alt.Chart(data).mark_area(color='#08519c', opacity = .5).encode(
x=alt.X('x',axis=alt.Axis(title='Day')),
y=alt.Y('q_25',axis=alt.Axis(title='cfs')),
y2 = 'q_75',
#color=alt.Color("p50", legend=alt.Legend(title=''))
)
median = alt.Chart(data).mark_line(color = '#08306b').encode(
x='x',
y='q_50',
#opacity=alt.Color("median", legend=alt.Legend(title=''))
)
perc_90 + perc_50 + median Out of curiosity is there a reason why altair does not allow for custom legends? Thanks, I really love the work so far. |
@jetilton Vega-Lite supports custom legends (and so does Altair). You may need to modify the scale domain and range as in https://vega.github.io/vega-lite/examples/stacked_bar_weather.html. If you have a smaller example, I can give more feedback. |
@domoritz Would you mind taking a look at this small example? I am aiming to use a selector to toggle different layered time-series but can't figure out how to generate a proper legend. This example takes the stock price dataset and I added a dummy 'Price-Earnings' ratio to layer onto the plot, and then use another single axis plot to dashboard-toggle which stock to display. The legend I want to display should identify the 'Price' and 'PE' series instead of the symbols. I understand that the data probably has to be rearranged somehow, and it may not be practical/possible, in which case, is there a way to manually create/label a legend/textbox for this use case? Thanks in advance!
|
For one thing, it's now possible to make native legends interactive: import altair as alt
from vega_datasets import data
stockdata = data.stocks()
stockdata['pe'] = stockdata['price'] / 10
selector = alt.selection_single(
fields=['symbol'],
empty='all',
init={'symbol': 'AAPL'},
bind='legend'
)
price = alt.Chart(stockdata).mark_line(point=True).encode(
x='date:T',
y='price:Q',
color='symbol:N',
opacity=alt.condition(selector, alt.value(1), alt.value(0))
).add_selection(
selector
)
pe = alt.Chart(stockdata).mark_bar().encode(
x='date:T',
y='pe:Q',
color='symbol:N'
).transform_filter(
selector
)
price + pe Beyond that, it's not clear to me how you want your legend to be different than what is shown. Both layers have a shared color encoding that is correctly reflected in the legend. |
Hi Jake - thanks for your reply. I'm am aware of native legend interactivity (which is great). I should have probably mentioned more that I am new to Altair and exploring its possibilities. In this case, I am trying to see where I can take it as a mini-dashboard. The reason I want to try using the selector the way I have it is that, in my use-case:
Also, while your plot is effectively the same as mine, and the native legend does identify the stock correctly by color, it does not clearly show which series is the stock price and which is the stock PE. What I am hoping to do by creating the pseudo-legend is keep that stock identity, but also be able to display a legend which says the This may not be possible, in which case, is it possible to create something like a text box to manually place on chart? I will actually be using a different color for the line and bars (which will remain constant for each stock,eg: price == red; PE == gray), so I could color code the labels in a text box to convey that information.
Here's a very-work-in-progress snapshot of what I am trying to do... |
Unfortunately, I don't have time right now to look at anything but minimal examples that demonstrate a specific issue. |
@domoritz No problem, I will keep exploring. Am really impressed with Altair! |
You could do something like this: import altair as alt
from vega_datasets import data
stockdata = data.stocks()
stockdata['pe'] = stockdata['price'] / 10
selector = alt.selection_single(
fields=['symbol'],
empty='all',
init={'symbol': 'AAPL'},
bind='legend'
)
price = alt.Chart(stockdata).mark_line(point=True).encode(
x='date:T',
y='price:Q',
color='symbol:N',
opacity=alt.condition(selector, alt.value(1), alt.value(0))
).add_selection(
selector
)
pe = alt.Chart(stockdata).transform_calculate(
name='"PE Ratio"'
).mark_bar().encode(
x='date:T',
y='pe:Q',
color=alt.Color('name:N', scale=alt.Scale(scheme='greys'), legend=alt.Legend(title=None))
).transform_filter(
selector
)
(price + pe).resolve_scale(color='independent') The grammar offers a lot of possibilities for customizing legends and scales, depending on exactly what you want to do. |
Thanks, I will give it a try... |
Hello,
|
No, there is still no way to add a legend without specifying an encoding that the legend will represent. |
Are there any plans to have legends not based on a label? While in many cases it is easy to add a column, it not always practical as when you have simulation data involving many parameters and wanting to compare results from different simulations on the same plot. |
What do you mean by “legend not based on a label”? How do you imagine specifying what the legend will contain? |
Before switching to Altair, I was doing plots with Mathematica and you can simply specify your legend withing the plot by using the option PlotLegend ->{"line"}. But even with matplotlib you can specify the legend with the label option as in : plot(x, y, label="line"). Is something like that planned for Altair or even possible with vega-lite? |
Yes, in newer versions of vega-lite you can set encodings to a constant datum value, which will be used to populate the legend. Altair doesn't yet support this, though. In Altair it would probably look something like this (Note that this does not work in the current release): alt.Chart(data).mark_line().encode(
x='x',
y='y',
color=alt.datum("My Line")
) |
when will this feature be available? As for my understanding of a plotting library its crucial. |
What specifically are you asking about? |
I believe @gustavz was asking about the ability to do "color=alt.datum("My Line")". Either way, I'd like to know also! Also, once that functionality is supported, how can the color (like "orange") be specified as well? |
You can currently specify a color like |
You can define the color encoding's scale in the normal way; i.e. |
I would like to add my solution to this issue, as I was struggling a lot to create a "custom legend" for my charts. |
@jakevdp - what still needs to happen for this to work in a released version of Altair? |
Is it possible to make a legend for such a chart
The text was updated successfully, but these errors were encountered: