How to plot recs and circles with two legends with Altair?

Viewed 155

I would like to create two charts that are superimposed, but with two legends. One chart uses rects with one color palette, and the second chart displays circles with a second color palette. This should be very straightforward, but something is wrong. I only get a single legend. I also want the legends to be selectable. Here is a self-contained MWE, representative of a more complex use case. Below the code, I show an image of what the code produces: single legend, single color palette. Is this expected behavior or some kind of bug? Any insight is appreciated. Thanks!

streamimport pandas as pd
import altair as alt
import streamlit as st

# Demonstrate two categorical legends with selection_multi.
# There appears to be a bug when using shift-click on one menu, then the other.

def drawPlot():

    x1 = [1, 2, 3]
    y1 = [1, 2, 3]
    x2 = [4, 5, 6]
    y2 = [4, 5, 6]
    df = pd.DataFrame({'x1':x1, 'y1':y1, 'x2':x2, 'y2':y2})

    palette1 = alt.Color('x1:N',
        scale=alt.Scale(
          domain=[1, 2, 3],
          range=['lightgreen', 'darkgreen', 'yellow'],
        )
    )
    palette2 = alt.Color('x2:N',
        scale=alt.Scale(
          domain=[4, 5, 6],
          range=['lightblue', 'darkblue', 'purple'],
        )
    )

    select1 = alt.selection_multi(fields=['x1'], bind='legend')
    select2 = alt.selection_multi(fields=['x2'], bind='legend')

    nodes1 = alt.Chart(df).mark_rect(
        width=20, height=20,
    ).encode(
        x = 'x1:N',
        y = 'y1:N',
        color = palette1,
    ).add_selection(
        select1
    )

    nodes2 = alt.Chart(df).mark_circle(
        width=20, height=20, size=1200,
    ).encode(
        x = 'x2:N',
        y = 'y2:N',
        color = palette2,
    ).add_selection(
        select2
    )

    full_chart = (nodes1 + nodes2).properties(
        height=500,
        width=1000,
    )

    return full_chart

#----------------------------------------------------------------
if __name__ == "__main__":
    chart = drawPlot()
    st.altair_chart(chart, use_container_width=True)

Output when the code above is executed.

1 Answers

Altair/Vega-Lite combine existing scales among charts into a single legend by default when possible for a more compact layout. When scales are independent of each other and should be represented in separate legends, you would need to resolve them manually, in your case it would look like this

chart.resolve_scale(color='independent')

enter image description here

You can read more on this page in the docs.

Related