oerpli
oerpli

Reputation: 340

Layering charts removes marks from one chart

I have the following data:

The measurement dataframe looks approx like this:

                     Time Trend  Value
0     2021-03-24 16:10:20     A   -1.3
1     2021-03-24 16:10:35     A   -5.3
2     2021-03-24 16:10:50     A   -6.3
3     2021-03-24 16:11:05     A   -2.3
4     2021-03-24 22:39:05     A   -5.3
...                   ...   ...    ...
12443 2021-03-24 16:10:20     H    0.0
12444 2021-03-24 22:38:20     H    1.0
12445 2021-03-24 22:38:35     H    2.0
12446 2021-03-24 22:38:50     H    3.0
12447 2021-03-24 22:39:05     H    4.0

For this purpose, I extract the time-ranges where a certain state is "active", as here:

      State               Start                 End
0      Idle 2021-03-24 16:10:20 2021-03-24 16:10:50
1      Pump 2021-03-24 16:10:50 2021-03-24 16:20:05
...
5      Cool 2021-03-24 20:42:20 2021-03-24 22:01:35
6      Pump 2021-03-24 22:01:35 2021-03-24 22:02:50

I then create two charts which I would like to combine with alt.layer

 alt_st = (
        alt.Chart(state_df)
        .mark_rect()
        .encode(
            x=alt.X("Start", title="Time"),
            x2=alt.X2("End", title="Time"),
            y=alt.value(15),
            y2=alt.value(0),
            color=alt.Color("State:O", scale=get_state_scale(state_df.State.unique())),
        )
        # .add_selection(selection)
    )

rectangle state chart

chart = (
    alt.Chart(df)
    .mark_line()
    .encode(
        alt.X("Time"),
        alt.Y("Value"),
        color="Trend",
    )
)

measurement chart

Combining with alt.layer:

alt.layer(chart, alt_st).resolve_legend("independent")

Combined chart

As can be seen, the combined chart has a messed up legend and the marks (lines) from the first chart) are gone.

What I tried so far:

The last thing I found out when trying to create a minimal example here.

without scale

I found a similar question (Altair: Layered Line Chart with Legend and Custom Colors) but couldn't work out how to apply the solution for my case.

My impression so far is that what I'm doing is very unidiomatic for Altair/Vega-Lite - if someone could point out the relevant concepts I'm missing I would be glad.

Upvotes: 3

Views: 100

Answers (1)

joelostblom
joelostblom

Reputation: 48889

What you have at the end looks correct, but you would want to use resolve_scale(color='independent') instead of resolve_legend('independent') to separate the legends correctly:

import altair as alt
from vega_datasets import data

source = data.iowa_electricity()
# Create end year for mark_rect
source['end_year'] = source.groupby('source')['year'].shift(-1)
source = source.dropna()

line = alt.Chart(source).mark_line().encode(
    x="year:T",
    y="net_generation:Q",
    color="source:N")

bar = alt.Chart(source).mark_rect().encode(
    x="year:T",
    x2="end_year",
    color='year(year):O',
    y=alt.value(15),
    y2=alt.value(0)
)

(bar + line).resolve_scale(color='independent')

enter image description here

You can move the bar to the bottom with mark_rect(yOffset=270, y2Offset=300) (offsets are in pixels rather than chart units, the same as when you use alt.value for y and y2):

enter image description here

Upvotes: 2

Related Questions