This is related to this SO question. I am trying to recreate @jakevdp 's solution, using the population dataset.
import altair as alt
from altair.expr import datum, if_
from vega_datasets import data
source = data.population.url
bars = alt.Chart(source).mark_bar(stroke='transparent').encode(
alt.X('gender:N', scale=alt.Scale(rangeStep=12), axis=alt.Axis(title='')),
alt.Y('sum(people):Q', axis=alt.Axis(title='population', grid=False)),
color=alt.Color('gender:N', scale=alt.Scale(range=["#EA98D2", "#659CCA"])),
column='age:O'
).transform_filter(
datum.year == 2000
).transform_calculate(
'gender', if_(datum.sex == 2, 'Female', 'Male')
)
text = bars.mark_text(
color='black',
dx = 10,
).encode(
text='sum(people):Q'
)
alt.layer(bars, text).configure_view(
stroke='transparent'
).configure_axis(
domainWidth=0.8
)
Gives me the following error:
SchemaValidationError: Invalid specification
altair.vegalite.v2.api.LayerChart->layer->items, validating 'anyOf'
{'data': {'url': 'https://vega.github.io/vega-datasets/data/population.json'}, 'mark': {'type': 'bar', 'stroke': 'transparent'}, 'encoding': {'color': {'type': 'nominal', 'field': 'gender', 'scale': {'range': ['#EA98D2', '#659CCA']}}, 'column': {'type': 'ordinal', 'field': 'age'}, 'x': {'type': 'nominal', 'axis': {'title': ''}, 'field': 'gender', 'scale': {'rangeStep': 12}}, 'y': {'type': 'quantitative', 'aggregate': 'sum', 'axis': {'grid': False, 'title': 'population'}, 'field': 'people'}}, 'transform': [{'filter': '(datum.year === 2000)'}, {'calculate': "if((datum.sex === 2),'Female','Male')", 'as': 'gender'}]} is not valid under any of the given schemas
It looks like I cannot use alt.layer and column together.
When I commented out the line:
# column='age:O'
It renders.

Any help on overcoming this error and rendering text on every bar in each age bucket? Thanks.
Layering only works with single-panel charts, so you cannot layer a faceted charts. The reason why is that, in general, matching panels between different faceted layers is tricky.
The workaround is to facet a layered chart, which is supported, though it's important that you specify the data in the right place. There's some discussion of this in the documentation.
Modifying your example:
import altair as alt
from altair.expr import datum, if_
from vega_datasets import data
source = data.population.url
bars = alt.Chart().mark_bar(stroke='transparent').encode(
alt.X('gender:N', scale=alt.Scale(rangeStep=12), axis=alt.Axis(title='')),
alt.Y('sum(people):Q', axis=alt.Axis(title='population', grid=False)),
color=alt.Color('gender:N', scale=alt.Scale(range=["#EA98D2", "#659CCA"])),
)
text = bars.mark_text(
color='black',
dx = 10,
).encode(
text='sum(people):Q'
)
alt.layer(bars, text, data=source).facet(
column='age:O'
).transform_filter(
datum.year == 2000
).transform_calculate(
'gender', if_(datum.sex == 2, 'Female', 'Male')
).configure_axis(
domainWidth=0.8
).configure_view(
stroke='transparent'
)
Thank you. Works perfectly. And thank you for the explanation. Layer first, and then facet.
I know this is a year old thread, but it was very helpful to me. One thing...how do you get the mark_text labels to show up in black instead of the gender encoded color?
how do you get the mark_text labels to show up in black instead of the gender encoded color?
Don't include a color encoding in the chart with mark_text() or the base chart that it's derived from.
For example:
base = alt.Chart(data).encode(color='color')
chart = base.mark_text() # text will have the color encoding
base = alt.Chart(data)
chart = base.mark_text() # text will not have the color encoding
Alternatively, you can replace the encoding with a color value:
base = alt.Chart(data).encode(color='color')
chart = base.mark_text().encode(color=alt.value('black')) # text will not have the color encoding
The reason that base.mark_text(color='black') does not change the color in this case is because encodings always supersede mark properties; see https://altair-viz.github.io/user_guide/customization.html#global-config-vs-local-config-vs-encoding for more information.
Thanks! That worked.
Hi, this helped me. But now I'm getting an error when trying to set width and height of faceted chart -
.properties(
# add a title
title="Title",
width=240,
height=180
)
SchemaValidationError: Invalid specification
altair.vegalite.v3.api.Chart, validating 'required'
'data' is a required property
It's difficult to help you without a complete code snippet.
Most helpful comment
Layering only works with single-panel charts, so you cannot layer a faceted charts. The reason why is that, in general, matching panels between different faceted layers is tricky.
The workaround is to facet a layered chart, which is supported, though it's important that you specify the data in the right place. There's some discussion of this in the documentation.
Modifying your example: