User set names are overridden by automatic
What happened?
When a user explicitly sets name= on a chart that ends up as an inner spec inside a FacetChart or LayerChart concat panel, Altair silently renames it by appending _0, _1, etc. The user-supplied name is not preserved in the output spec.
This affects two code paths in _combine_subchart_params in altair/vegalite/v6/api.py:
FacetChart— renaming happens inside the param-processing loop when theFacetChartsubchart carries params.LayerChart(introduced in PR #4066) — renaming happens in a new pre-pass that runs unconditionally for allis_concatcases, regardless of whether params are present.
The two cases also have an asymmetry: the FacetChart rename only fires when that subchart has params attached; the LayerChart pre-pass renames even when there are no params at all.
Minimal reproducible examples
Case 1 — FacetChart
import altair as alt
import pandas as pd
df = pd.DataFrame({"x": [1, 2, 3], "y": [1, 4, 9], "category": ["a", "b", "c"]})
hover = alt.selection_point(fields=["x"], on="mouseover", empty=False)
chart = alt.Chart(df, name="my_panel").encode(x="x:Q", y="y:Q").mark_line()
faceted = chart.add_params(hover).facet("category:N")
spec = alt.vconcat(faceted, faceted).to_dict()
print(spec["vconcat"][0]["spec"]["name"]) # "my_panel_0" ← expected "my_panel"
print(spec["vconcat"][1]["spec"]["name"]) # "my_panel_1" ← expected "my_panel"Case 2 — LayerChart
import altair as alt
import pandas as pd
df = pd.DataFrame({"x": [1, 2, 3], "y": [1, 4, 9]})
hover = alt.selection_point(fields=["x"], on="mouseover", empty=False)
base = alt.Chart(df, name="my_panel").encode(x="x:Q", y="y:Q")
lines = base.mark_line()
points = base.encode(size=alt.condition(hover, alt.value(120), alt.value(40))).mark_circle()
layered = lines + points
p1 = layered.transform_filter("datum.x > 1")
p2 = layered.transform_filter("datum.x < 3")
spec = alt.vconcat(p1, p2).add_params(hover).to_dict()
print(spec["vconcat"][0]["layer"][0]["name"]) # "my_panel_0" ← expected "my_panel"
print(spec["vconcat"][1]["layer"][0]["name"]) # "my_panel_1" ← expected "my_panel"The LayerChart case also renames when no params are present at all (the FacetChart case does not):
# No params — LayerChart still renames on PR #4066 branch
base = alt.Chart(df, name="my_panel").encode(x="x:Q", y="y:Q")
layered = base.mark_line() + base.mark_circle()
p1 = layered.transform_filter("datum.x > 1")
p2 = layered.transform_filter("datum.x < 3")
spec = alt.vconcat(p1, p2).to_dict()
print(spec["vconcat"][0]["layer"][0]["name"]) # "my_panel_0" ← expected "my_panel"What would you like to happen instead?
The renaming is necessary — preserving a colliding user-set name would recreate the original rendering bug. But when Altair modifies a user-supplied name, maybe a UserWarning could be emitted so the user knows to update any downstream references. No warning is needed when renaming auto-generated content-hash names (those matching view_<16 hex chars>), since those are an internal implementation detail.
This mirrors the existing pattern in the codebase, where Altair already warns for analogous silent fixups:
UserWarning: Automatically deduplicated selection parameter with identical configuration.
If you want independent parameters, explicitly name them differently ...We also don't want Altair to be too noisy, so I'm not 100% sure on this fix, but I'm recording it here for visibility.
Proposed fix direction
In _combine_subchart_params (altair/vegalite/v6/api.py):
- The
FacetChartbranch (spec.layer[0].name = f"{_view_base_for_chart(spec.layer[0])}_{i}") runs inside the param loop and does not distinguish between user-set and auto-generated names. - The
LayerChartpre-pass added in PR #4066 (layer.name = f"{_view_base_for_chart(layer)}_{i}") has the same issue, and additionally runs even when there are no params.
Add a helper that distinguishes auto-generated names from user-set ones, and emit a warning only for the latter:
import re
_AUTO_NAME_RE = re.compile(r'^view_[0-9a-f]{16}(_\d+)?$')
def _is_auto_name(name: str) -> bool:
return bool(_AUTO_NAME_RE.match(name))Then wrap the rename with a warning for user-set names:
if isinstance(layer, Chart) and layer.name is not Undefined:
new_name = f"{_view_base_for_chart(layer)}_{i}"
if not _is_auto_name(layer.name) and layer.name != new_name:
warnings.warn(
f"Chart name {layer.name!r} was automatically renamed to {new_name!r} "
"to avoid view name collisions in concat. If you need to reference this "
"view by name, use the renamed form.",
UserWarning,
)
layer.name = new_nameThe same guard could be applied to the existing FacetChart branch.
Which version of Altair are you using?
No response
Source: vega/altair