Search code examples
pythonvegaaltairvega-lite

Control canvas size with altair / vega-lite / vega-embed


I'm building graphs using vega-lite, and rendering them within a DOM using vega-embed. I love the tools and the abstractions, but I'm still figuring out how some of them work.

I'm working on an application where controlling the exact size of the canvas is important. Unfortunately, when I specify (for example)

bars = alt.Chart(df).mark_bar().encode(
  x='bins:O',
  y="weights:Q"
).properties(width=240, height=200)

I get a chart object that looks like this:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json",
  "config": {
    "view": {
      "height": 300,
      "width": 400
    }
  },
  "data": {
    "name": "data-a681d02fb484e64eadd9721b37015d5b"
  },
  "datasets": {
    "data-a681d02fb484e64eadd9721b37015d5b": [
      {
        "bins": 3.7,
        "weights": 5.555555555555555
      },
      {
        "bins": 10.8,
        "weights": 3.439153439153439
      },
      {
        "bins": 17.9,
        "weights": 17.857142857142858
      },
      {
        "bins": 25.0,
        "weights": 24.206349206349206
      },
      {
        "bins": 32.0,
        "weights": 16.137566137566136
      },
      {
        "bins": 39.1,
        "weights": 12.3015873015873
      },
      {
        "bins": 46.2,
        "weights": 9.788359788359788
      },
      {
        "bins": 53.3,
        "weights": 5.423280423280423
      },
      {
        "bins": 60.4,
        "weights": 3.439153439153439
      },
      {
        "bins": 67.5,
        "weights": 1.8518518518518516
      }
    ]
  },
  "encoding": {
    "x": {
      "field": "bins",
      "type": "ordinal"
    },
    "y": {
      "field": "weights",
      "type": "quantitative"
    }
  },
  "height": 200,
  "mark": "bar",
  "width": 240
}

(Note the different height and width in config.view.)

I'm rendering to HTML like this:

<div id="my_id"></div>
<script>
const vlSpec = my_chart_obj;
vegaEmbed('#my_id', vlSpec, {
    actions: false
}).then(result=>console.log(result)).catch(console.warn);
</script>

This produces:

<div id="my-id" class="vega-embed">
  <canvas width="284" height="252" class="marks"></canvas>
  <div class="vega-bindings"></div>
</div>

Note: width="284" height="252"

It looks like width and height are being padded, but I can't figure out why or how.

The graph itself looks lovely, except that it's the wrong size and messes up my layout as a result.

enter image description here

Help!


Solution

  • The width and height in config.view are set by Altair's default theme, and have no effect on the chart you created above, because they are overridden by the chart-level width and height properties.

    If you would feel better removing this config from your chart, you can run

    alt.themes.enable('none')
    

    to clear the default chart theme (alt.themes.enable('default') will restore the default).


    Regarding the size of the canvas: the chart width and heightproperties by default control the size of the chart panel itself, not including additional padding for axis labels and titles. If you want the entire canvas to fit in the specified bounds, including label padding, you can specify this via the autosize property; for example:

    alt.Chart(df).mark_bar().encode(
      x='bins:O',
      y="weights:Q"
    ).properties(
        width=240,
        height=200,
        autosize=alt.AutoSizeParams(
            type='fit',
            contains='padding'
        )
    )
    

    enter image description here

    But be aware that this will not work for every chart type; see Autosize Limitations for more information.