Search code examples
pythonaltairvega-lite

Is there a way to customize the placement of concatenated charts in Altair?


I have the following chart in Altair, and I was wondering if there is a way to change the orientation of the map chart. I have tried using the "center" and "spacing" parameters, but to no avail. I want the map to appear under the legend to make the overall chart more concise.

enter image description here

This is the code used to generate the charts:

background = alt.Chart(states).mark_geoshape(
        fill='white',
        stroke='lightgray'
    ).properties(
        width=200,
        height=200
    ).project('albersUsa')

# Generate the points themselves
climate_points = alt.Chart(climate_with_temps).mark_circle(size=300).encode(
    alt.Longitude('lon:Q'),
    alt.Latitude('lat:Q'),
    alt.OpacityValue(0.25),
    alt.Color('city:N', scale=alt.Scale(scheme='dark2'), legend=None)
).properties(
    title=alt.TitleParams(
        ['Location of Coastal Cities'],
        baseline='bottom',
        orient='bottom',
        fontSize=15   
    )
)

map_chart = background + climate_points

temp_line_plot = alt.Chart(climate_with_temps).mark_line().encode(
    alt.X('month:O', axis=alt.Axis(title="Month"), sort=list(set(climate_with_temps.city))),
    alt.Y('avg_temp:Q', axis=alt.Axis(title="Average Temperature in Fahrenheit"), scale=alt.Scale(domain=[30, 95])),
    alt.Color('city:N'),
    alt.OpacityValue(1)
).properties(
    height=500,
    width=700
)

sun_square_plot = alt.Chart(climate_with_temps).mark_square().encode(
    alt.X('month:O', axis=alt.Axis(title="Month"), sort=list(set(climate_with_temps.city))),
    alt.Y('avg_temp:Q', axis=alt.Axis(title="Average Temperature in Fahrenheit"), scale=alt.Scale(domain=[30, 95])),
    alt.Color('city:N', scale=alt.Scale(scheme='dark2'), legend=alt.Legend(title='City')),
    alt.Size('sun_bin:O', sort=['0 - 50', '50 - 99', '100 - 149', '150 - 199', '200 - 249', '250 - 299', '300 - 350'], legend=alt.Legend(title='Average Hours of Sunshine')),
    alt.OpacityValue(1)
).properties(
    height=500,
    width=700
)

left_chart = sun_square_plot + temp_line_plot
final = alt.hconcat(left_chart, map_chart, center=False, spacing=0)

Solution

  • I don't think there is a straightforward to achieve this, although you could do something like this:

    import altair as alt
    from vega_datasets import data
    
    source = data.cars()
    
    chart1 = alt.Chart(source, height=400).mark_circle(size=60).encode(
        x='Horsepower',
        y='Miles_per_Gallon',
        color='Origin',
        size='Cylinders'
    )
    chart2 = alt.Chart(source, height=200, width=200).mark_circle(size=60).encode(
        x='Horsepower',
        y='Miles_per_Gallon',
        color='Origin',
    )
    
    # Empty chart for spacing
    chart3 = alt.Chart(height=180, width=180).mark_circle(opacity=0)
    
    (chart1 | (chart3 & chart2)).configure_legend(
        orient='right',
        direction='vertical',
        offset=-220,
        symbolDirection='vertical'
    )
    

    enter image description here

    But the simplest might be to create a horizontal legend above both charts instead:

    import altair as alt
    from vega_datasets import data
    
    source = data.cars()
    
    chart1 = alt.Chart(source).mark_circle(size=60).encode(
        x='Horsepower',
        y='Miles_per_Gallon',
        color='Origin',
        size='Cylinders'
    )
    
    chart2 = alt.Chart(source).mark_circle(size=60).encode(
        x='Horsepower',
        y='Miles_per_Gallon',
        color='Origin'
    )
    
    (chart1 | chart2).configure_legend(
        orient='top'
    )
    

    enter image description here