Search code examples
vega-litevega

How to access the underlying data from a viz


I am using vega-lite to generate a viz.

The viz includes the following transform:

        {
            calculate: "year(datum['Order Date'])",
            as: "Year"
        },

I would like to allow the user to filter the viz based on the values of this field. For example, I'd like to give my users a menu with 2018, 2019, 2020, and 2021, and allow them to select one.

Does vega (or vega-lite) expose functions that'll allow me to programmatically obtain the valid values for this filter based on the data? (eg. 2018, 2019, 2020, and 2021).

My alternative is to parse the data myself, and re-implement the calculate logic that vega-lite is already doing under the covers. I stumbled across the loader and inferTypes functions exposed in the vega library, which allows me to inspect the data a bit, but these don't help me with generated fields (eg. via calculate transformations).

Another alternative is to create vizzes with distinct aggregates for the above calculate transformation and scrape the resulting viz, but this seems a bit too hacky.


Solution

  • You can use params to bind it with a select input which can have a dropdown options. This input can be attached to any element using the element config. Then, use these values in filter transform to filter out your data through year.

    Below is an example config or refer in editor:

    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "description": "",
      "data": {
        "url": "https://raw.githubusercontent.com//jamesjeffery77/jamesjeffery77.github.io/main/share-electricity-low-carbon_fullDataset.csv"
      },
      "height": 300,
      "width": 350,
      "mark": {"type": "bar", "color": "skyblue"},
      "transform": [
        {"filter": "datum.year==year"},
        {
          "filter": {
            "field": "country",
            "oneOf": [
              "United Kingdom",
              "Spain",
              "France",
              "Netherlands",
              "Portugal",
              "Italy",
              "Poland",
              "Albania",
              "Germany",
              "Belgium",
              "Austria",
              "Denmark"
            ]
          }
        }
      ],
      "params": [
        {
          "name": "year",
          "value": 2019,
          "bind": {
            "input": "select",
            "options": ["2015", "2016", "2018", "2019", "2020", "2021"],
            "name": "Select the year:"
          }
        }
      ],
      "encoding": {
        "y": {
          "field": "percentage",
          "type": "quantitative",
          "title": "Percentage of low carbon energy",
          "axis": {"grid": false}
        },
        "x": {
          "field": "country",
          "type": "nominal",
          "title": "",
          "axis": {"grid": false, "labelAngle": 20},
          "sort": "-y"
        },
        "tooltip": [
          {"field": "country", "title": "Country"},
          {"field": "percentage", "title": "percentage of low carbon energy"}
        ]
      }
    }
    

    Edit

    To get the data, you just need to use the vega api's as view.data('source_0') where source_0 is the name of data in your dataviewer. To set or add data use the same api but with a 2nd parameter as view.data('source_0', data). Refer documentation to know more on this.

    My suggestion would be to use the params approach as done in the above example in which you don't need to create a dropdown in vega. You can have your own dropdown externally and using the API's you can update the view params.

    Refer the below example of param's changing using API's and also I have logged the data on console in case you prefer another approach or refer fiddle:

    var yourVlSpec = {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "description": "",
      "data": {
        "url": "https://raw.githubusercontent.com//jamesjeffery77/jamesjeffery77.github.io/main/share-electricity-low-carbon_fullDataset.csv"
      },
      "height": 300,
      "width": 350,
      "mark": {"type": "bar", "color": "skyblue"},
      "transform": [
        {"filter": "datum.year==year"},
        {
          "filter": {
            "field": "country",
            "oneOf": [
              "United Kingdom",
              "Spain",
              "France",
              "Netherlands",
              "Portugal",
              "Italy",
              "Poland",
              "Albania",
              "Germany",
              "Belgium",
              "Austria",
              "Denmark"
            ]
          }
        }
      ],
      "params": [
        {
          "name": "year",
          "value": 2018
        }
      ],
      "encoding": {
        "y": {
          "field": "percentage",
          "type": "quantitative",
          "title": "Percentage of low carbon energy",
          "axis": {"grid": false}
        },
        "x": {
          "field": "country",
          "type": "nominal",
          "title": "",
          "axis": {"grid": false, "labelAngle": 20},
          "sort": "-y"
        },
        "tooltip": [
          {"field": "country", "title": "Country"},
          {"field": "percentage", "title": "percentage of low carbon energy"}
        ]
      }
    }
    var view;
    vegaEmbed("#vis", yourVlSpec).then(res => {
        view = res.view;
      //To get the data
      console.log(view.data('source_0'));
      
    });
    
    /* To set the Data
    view.data('source_0',[new data to be set]);
    */
    
    function handleChange(a,b){
    var selectValue = document.getElementById("myselect").value;
    //Set the param name value dynamically.
    view.signal('year',selectValue)
    view.runAsync();
    }
     <script src="https://cdn.jsdelivr.net/npm/vega@5.20.2/build/vega.min.js"></script>
     <script src="https://cdn.jsdelivr.net/npm/vega-lite@5.0.0/build/vega-lite.min.js"></script>
     <script src="https://cdn.jsdelivr.net/npm/vega-embed@6.17.0/build/vega-embed.min.js"></script>
    
    <select id="myselect" style="width:100px;" onchange="handleChange()">
      <option>2018</option>
      <option>2019</option>
      <option>2020</option>
    </select>
    <br>
    <div id="vis"></div>