Search code examples
javascriptjsonvisualizationvega-litevega

Vega-Lite - How to Combine Line and Symbol Legend


I am able to implement vega-lite chart which renders as follows:

enter image description here

The code for it looks something like this:

{
  "width": 400,
  "config": {"view": {"continuousWidth": 600, "continuousHeight": 300}},
  "data": {"name": "data-eb6aa7311f370dcc2f64d37c32c9e387"},
  "usermeta": {"embedOptions": {"renderer": "svg"}},
  "layer":[
    {
      "mark": {"type": "bar", "width": {"band": 0.2}},
      "encoding": {
        "x": {"field": "title", 
              "type": "nominal",
              "axis": { "title": "Quizzes"}
             },
        "y": {"field": "my-score", 
              "type": "quantitative",
              "axis": { "title": "Percentage Score"}
             }
      }
    },
    {
      "mark": {"type": "line", "color": "red"},
      "encoding": {
        "x": {"field": "title", "type": "nominal"},
        "y": {"field": "max", "type": "quantitative"}
      }
    },
    {
      "mark": {"type": "circle", "color": "red", "size":100, "opacity": "100"},
      "encoding": {
        "x": {"field": "title", "type": "nominal"},
        "y": {"field": "max", "type": "quantitative"},
      }
    },
    {
      "mark": {"type": "line", "color": "#02c754"},
      "encoding": {
        "x": {"field": "title", "type": "nominal"},
        "y": {"field": "avg", "type": "quantitative"}
      }
    },
    {
      "mark": {"type": "circle", "color": "#02c754", "size":100, "opacity": "100"},
      "encoding": {
        "x": {"field": "title", "type": "nominal"},
        "y": {"field": "avg", "type": "quantitative"}
      }
    },
    {
      "mark": {"type": "line", "color": "#02b6de"},
      "encoding": {
        "x": {"field": "title", "type": "nominal"},
        "y": {"field": "min", "type": "quantitative"}
      }
    },
    {
      "mark": {"type": "circle", "color": "#02b6de", "size":100, "opacity": "100"},
      "encoding": {
        "x": {"field": "title", "type": "nominal"},
        "y": {"field": "min", "type": "quantitative"}
      }
    }
    
  ],
  "title": "Quiz Scores",
  "$schema": "https://vega.github.io/schema/vega-lite/v4.17.0.json",
  "datasets": {
    "data-eb6aa7311f370dcc2f64d37c32c9e387": [ 
      {
        "title": "Quiz-1",
        "my-score": 62,
        "max": 80,
        "avg": 45,
        "min": 15
      },
      {
        "title": "Quiz-2",
        "my-score": 48,
        "max": 48,
        "avg": 30,
        "min": 10
      },
      {
        "title": "Quiz-3",
        "my-score": 54,
        "max": 62,
        "avg": 36,
        "min": 12
      },
      {
        "title": "Quiz-4",
        "my-score": 27,
        "max": 69,
        "avg": 50,
        "min": 9
      },
      {
        "title": "Quiz-5",
        "my-score": 40,
        "max": 48,
        "avg": 30,
        "min": 11
      },
      {
        "title": "Quiz-6",
        "my-score": 50,
        "max": 55,
        "avg": 28,
        "min": 5
      },
    ]
  }
}

I want to render legends for the same something like this:

enter image description here

I am not able to figure out how I can do this. I checked the example given here or precisely this example. But the stock data used by this example has different schema: {symbol, date, price} and it renders different colors for different values in symbol. And vega-lite seem to auto generate legends from this schema. But, in my case, the schema is of the form {title, my-score, max, avg, min}. How do I implement legends as shown in the picture above (also note that legend for bar chart is somewhat wide)? I am fine to have somewhat different legends as long as they are sensible. But do I have to transform data to match the schema to what vega-lite stocks example schema?

PS: You can try out my visualization at this URL.


Solution

  • Here you go.

    enter image description here

    {
      "width": 400,
      "config": {"view": {"continuousWidth": 600, "continuousHeight": 300}},
      "data": {
        "values": [
          {"title": "Quiz-1", "my-score": 62, "max": 80, "avg": 45, "min": 15},
          {"title": "Quiz-2", "my-score": 48, "max": 48, "avg": 30, "min": 10},
          {"title": "Quiz-3", "my-score": 54, "max": 62, "avg": 36, "min": 12},
          {"title": "Quiz-4", "my-score": 27, "max": 69, "avg": 50, "min": 9},
          {"title": "Quiz-5", "my-score": 40, "max": 48, "avg": 30, "min": 11},
          {"title": "Quiz-6", "my-score": 50, "max": 55, "avg": 28, "min": 5}
        ]
      },
      "transform": [{"fold": ["max", "avg", "min", "my-score"]}],
      "usermeta": {"embedOptions": {"renderer": "svg"}},
      "encoding": {
        "x": {"field": "title", "type": "nominal", "axis": {"title": "Quizzes"}}
      },
      "layer": [
        {
          "mark": {"type": "bar", "width": {"band": 0.2}},
          "transform": [{"filter": "datum.key == 'my-score' "}],
          "encoding": {
            "y": {
              "field": "value",
              "type": "quantitative",
              "axis": {"title": "Percentage Score"}
            },
            "color": {"field": "key", "scale": {"range": ["#312eaa"]}}
          }
        },
        {
          "mark": {"type": "line"},
          "transform": [
            {
              "filter": "datum.key == 'max' || datum.key == 'min' ||datum.key == 'avg'  "
            }
          ],
          "encoding": {
            "y": {"field": "value", "type": "quantitative"},
            "stroke": {
              "field": "key",
              "scale": {"range": ["green", "red", "#3aa1ff"]},
              "legend": {"title": ""}
            }
          }
        },
        {
          "mark": {"type": "circle"},
          "transform": [
            {
              "filter": "datum.key == 'max' || datum.key == 'min' ||datum.key == 'avg'  "
            }
          ],
          "encoding": {
            "y": {"field": "value", "type": "quantitative"},
            "fill": {
              "field": "key",
              "scale": {"range": ["green", "red", "#3aa1ff"]},
              "legend": null
            }
          }
        }
      ],
      "title": "Quiz Scores",
      "$schema": "https://vega.github.io/schema/vega-lite/v5.6.json"
    }