Search code examples
pythonfacetaltair

Altair: Limit number of ordered facets


This example code show 6 facets, ordered by their means. How could I display only e.g. 2 top or bottom facets, which are ordered this way? I tried playing around with the transform_window rank option with no luck, it limits the bar contents of all the facets instead.

import altair as alt
from vega_datasets import data

source = data.barley()

alt.Chart(source).mark_bar().encode(
    x='yield:Q',
    y='year:O',
    color='year:N',
    facet=alt.Facet('site',
        sort={
            'field': 'yield',
            'op': 'mean',
            'order': 'descending'
        },
        columns=1
    )
)

Solution

  • You could use a joinaggregate transform to calculate the site means, then rank them using a window transform, and finally filter the ranks using the filter transform (similar to this example in the docs):

    import altair as alt
    from vega_datasets import data
    
    
    source = data.barley()
    
    n_top_sites = 3
    
    alt.Chart(source).mark_bar().encode(
        x='mean_yield_per_site:Q',
        y='year:O',
        color='year:N',
    ).facet(
        alt.Facet('site', sort=['mean_yield_per_site'], title=''),
        columns=1,
    ).transform_joinaggregate(
        mean_yield_per_site='mean(yield)',
        groupby=['site']
    ).transform_window(
        rank='dense_rank()',  # Don't skip any rank numbers, https://vega.github.io/vega-lite/docs/window.html#ops
        sort=[alt.SortField('mean_yield_per_site', order='descending')]
    ).transform_filter(
        f'datum.rank <= {n_top_sites}'
    )
    

    enter image description here

    The same result could be achieved by doing the computation of the top site index labels with pandas and then filter for membership in that list via altair's filter transform:

    n_top_sites = 2
    top_sites = source.groupby('site').mean()['yield'].nlargest(n_top_sites).index.tolist()
    
    alt.Chart(source).mark_bar().encode(
        x='yield:Q',
        y='year:O',
        color='year:N',
        facet=alt.Facet('site',
            sort={
                'field': 'yield',
                'op': 'mean',
                'order': 'descending'
            },
            columns=1
        )
    ).transform_filter(
        f'indexof({top_sites}, datum.site) != -1'
    )