-
-
Notifications
You must be signed in to change notification settings - Fork 826
Open
Labels
Description
What is your suggestion?
I'm wondering whether layering simple faceted charts should be possible. Specifically, I'm talking about situations like this:
import altair as alt
import pandas as pd
df = pd.DataFrame(
{
"time": [1, 2, 3, 4],
"label1": ["A", "A", "B", "B"],
"label2": ["C", "C", "D", "D"],
"value1": [10, 20, 30, 40],
"value2": [15, 25, 35, 45],
}
)
# Option 1 works
base = alt.Chart(df).encode(x="time")
chart = alt.layer(
base.mark_point().encode(y="value1"),
base.mark_line().encode(y="value2"),
).facet(column="label1", row="label2")
# Option 2 does not work
# base = alt.Chart(df).mark_point().encode(x="time", column="label1", row="label2")
# alt.layer(
# base.encode(y="value1"),
# base.encode(y="value2"),
# )In situations where we want to layer multiple charts that have defined the same facets, I would think that both options above are equivalent.
I tried this out inside the api.py part but can't finish it.
class LayerChart(TopLevelMixin, _EncodingMixin, core.TopLevelLayerSpec):
"""A Chart with layers within a single panel."""
@utils.use_signature(core.TopLevelLayerSpec)
def __init__(
self,
data: Optional[ChartDataType] = Undefined,
layer: Sequence[LayerType] = (),
**kwargs: Any,
) -> None:
# Faceted charts can't be layered,
# but if all layers have the same facets,
# we can remove the facets from the spec and facet the layered chart later on.
facet_encoding_dicts = [
{
"row": spec._get("encoding")._get("row"),
"facet": spec._get("encoding")._get("facet"),
"column": spec._get("encoding")._get("column"),
}
for spec in layer
]
facet_encodings_are_identical_across_layers = False
# Check if all facets are Undefined in the first dict
if not all(_ is Undefined for _ in facet_encoding_dicts[0].values()):
# Check if all dicts are the same
for _ in facet_encoding_dicts[1:]:
if _ != facet_encoding_dicts[0]:
break
else:
facet_encodings_are_identical_across_layers = True
if facet_encodings_are_identical_across_layers:
# Let us remove the facet encodings from each layer
for spec in layer:
encoding = spec._get("encoding")
if encoding is not Undefined:
for channel in ["row", "facet", "column"]:
if encoding._get(channel) is not Undefined:
encoding.__setattr__(channel, Undefined)
# TODO: check for conflicting interaction
for spec in layer:
_check_if_valid_subspec(spec, "LayerChart")
_check_if_can_be_layered(spec)
super().__init__(data=data, layer=list(layer), **kwargs)
self.layer: list[ChartType]
self.params: Optional[Sequence[_Parameter]]
self.data: Optional[ChartDataType]
self.data, self.layer = _combine_subchart_data(self.data, self.layer)
# Currently (Vega-Lite 5.5) the same param can't occur on two layers
self.layer = _remove_duplicate_params(self.layer)
self.params, self.layer = _combine_subchart_params(self.params, self.layer)
# Some properties are not allowed within layer; we'll move to parent.
layer_props = ("height", "width", "view")
combined_dict, self.layer = _remove_layer_props(self, self.layer, layer_props)
for prop in combined_dict:
self[prop] = combined_dict[prop]
# I tried this, but that is wrong:
# if facet_encodings_are_identical_across_layers:
# self = self.facet(
# facet=facet_encoding_dicts[0]["facet"],
# row=facet_encoding_dicts[0]["row"],
# column=facet_encoding_dicts[0]["column"],
# )My idea is to loop over the layer and check if the encodings for row, facet, and column are identical for all layers. If so, I'd like to pull them out of the encoding and apply the faceting after layering the chart.
If this is something useful, I'd appreciate some help. If this is just fundamentally not possible and I have missed something, I'm also eager to hear that :)
Cheers!
Have you considered any alternative solutions?
No response