Reputation: 2063
I need a resizable faceted chart, but width="container"
refers to the individual facets. The best I've been able to come up with is some custom HTML, but it doesn't really work properly.
rand = np.random.RandomState(0)
data = pd.DataFrame\
( rand.randint(100,500,(15,2))
, index=[*["a"]*5,*["b"]*5,*["c"]*5]
, columns=["x","y"]
).rename_axis("k").reset_index()
chart1 =\
( alt.Chart(data)
. mark_point()
. encode(x="x",y="y",color="k",shape="k",size="k")
)
chart2 =\
( alt.Chart(data)
. transform_regression
( on="x"
, regression="y"
, groupby=["k"]
, method="poly"
)
. mark_line()
. encode(x="x",y="y",color="k")
)
chart =\
( (chart1 + chart2)
. properties(height=300, width="container")
. facet(column="k:N")
. interactive()
)
stats.chart_embed\
( chart
, filename="TEMP.html"
, container=container
, style=style
)
container="""<div class="test"><div class="vega-vis"></div></div>"""
style = """
body {
white-space: nowrap;
}
.vega-vis {
width: 100%;
}
.test {
width: 32%;
}
...
"""
Upvotes: 0
Views: 1083
Reputation: 2063
Should've done a bit more research. There are already a few stackoverflow questions. The best describes using javascript to set the width. I'm not sure if you can hook into Vega's resize mechanism via addResizeListener
without triggering an another resize; care must be taken to avoid an infinite loop. Or you can disable automatic resizing, hook into the container's resize event via ResizeObserver and trigger a chart resize via view.width().run()
.
An open Vega issue with many workarounds, such as editing the Vega JSON. I'd imagine a JSON-PATCH to vegaEmbed
via the opt.patch
. However the patch may be overwritten when the width
signal is updated.
And a draft Vega PR:
And finally, you can modify the HTML. A version that words perfectly:
chart =\
( (chart1 + chart2)
. properties
( height=300
, width="container"
)
. facet(column="k:N")
. properties
( autosize=alt.AutoSizeParams
( type="fit"
# The resize parameter is the only required parameter, but I
# included the others in case you have a more complicated chart.
, resize=True
, contains="padding"
)
)
. interactive()
)
<div class="vega-vis"></div>
.vega-vis {
width: 100%;
}
.chart-wrapper {
/* Normally 'chart-wrapper' is the size of its 'vega-vis' container minus
* the padding for the button. The size of 'chart-wrapper' determines the
* size of the canvas/plot. So you want 'chart-wrapper' to be 1/3 of the
* container minus the padding.
*/
width: calc(33% - var(--vega-action-padding,38px));
}
There are some downsides to this method. With very data-intensive charts enabling resize via the autosize
parameter greatly slows down the page. And it's not possible to resize vertically concatenated charts so each chart is the same width when one is faceted. Furthermore after a chart is resized to the width of the container its axes are vertically aligned to the adjacent charts. Since faceted charts don't include the legend and padding in the container calculation this forces adjacent charts to exceed the container width, and so on for all charts. It would be necessary to set the width of each concatenated chart via JavaScript by mucking around the chart's internals - not an easy task.
Upvotes: 2