mnordstrom
mnordstrom

Reputation: 23

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
    )
)

Upvotes: 1

Views: 170

Answers (1)

joelostblom
joelostblom

Reputation: 48919

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'
)

Upvotes: 1

Related Questions