vega / vega-lite

A concise grammar of interactive graphics, built on Vega.
https://vega.github.io/vega-lite/
BSD 3-Clause "New" or "Revised" License
4.69k stars 612 forks source link

Support using param / expr with `field` keys to control which data column is used for an encoding #7365

Open joelostblom opened 3 years ago

joelostblom commented 3 years ago

I suspect there might be an open issue for this somewhere but I could not find it when searching. The new parameter feature looks very cool, but I can't find and example of param / expr used together with the field key and when I try it in the vega editor, it says it only supports strings as values for this field. Would it be possible to add this functionality so that a field value could be set dynamically, e.g. to change which column is plotted on the x and y axis via a dropdown? Or am I not understanding how parameters are supposed to work?

A workaround for this is mention in https://github.com/vega/vega-lite/issues/7264 by first folding and then filtering on a param / expr, but it would be need if this feature was directly supported. To be clear a use case would be to create something like this (minus the cluster count calculations):

image

domoritz commented 3 years ago

I don't see why it wouldn't work but it may not be easy to add. The way to find out is to change the types for field and then seeing what breaks.

joelostblom commented 3 years ago

Oh cool! Just to clarify, with "change the types for field" does that mean to modify the VegaLite source to accept key-value pairs and not just strings as values for the field keys? Or is it something I can play around with in the online chart editor somehow?

I am thinking the syntax would look something like this Open the Chart in the Vega Editor:

{
  "data": {"url": "data/cars.json"},
  "params": [
    {
      "name": "sel",
      "select": {"type": "point"},
      "bind": {"input": "select", "options": ["Horsepower", "Acceleration"]}
    }
  ],
  "mark": "circle",
  "encoding": {
    "x": {"field": {"param": "sel"}, "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"}
  }
}

Or maybe "x": {"field": {"expr": "sel_key"}, "type": "quantitative"}, I need to read up on the differences.

domoritz commented 3 years ago

I was referring to the typescript sources. If you (or someone else) changes the types to support the param ref type (stricter than just a key value pair), you would see the downstream effects.

kanitw commented 3 years ago

I think this will be super useful, but may require that all transforms and scales properties in Vega that take fields must take signals for the field names too, so this may require massive enhancement to Vega?

ChristopherDavisUCI commented 2 years ago

Hi @kanitw, I was thinking about this feature and interested in trying to contribute. Do I understand your comment correctly that this can't be produced by changes on the Vega-Lite side, but would need to be done on the Vega side?

As an explicit example of what I want: I'd like for example this Repeated Line Chart but where, rather than showing all 3 views side-by-side, instead there is a dropdown menu that allows the user to select from among the 3 options.

Thanks for any suggestions!

Screen Shot 2022-03-03 at 8 39 58 AM
domoritz commented 2 years ago

I think something like that could be. possible if you create a new dataset in Vega that has a field for y that is derived with an expression like data[fieldSignal]. Maybe this works in Vega-Lite already is you use param and calculate transform.

ChristopherDavisUCI commented 2 years ago

Thank you, @domoritz, I will look into this!

jwoLondon commented 2 years ago

I can confirm this works with the existing Vega-Lite implementation, although I am not sure if it is possible to customise the axis title to be data dependent. It would be useful if axis title could accept an expression in addition to a text literal.

Vega-Lite editor

{
  "data": {
    "url": "data/weather.csv"
  },
    "params": [
    {
      "name": "measure",
      "value": "temp_max",
      "bind": {
        "input": "select",
        "options": ["temp_max","precipitation","wind"]
      }
    }
  ],
  "transform": [
    {
      "calculate": "datum[measure]",
      "as": "y"
    }
  ],

    "mark": "line",
    "encoding": {
      "x": {"field": "date", "timeUnit": "month"},
      "y": {
        "field": "y",
        "aggregate": "mean"
      },
      "color": {"field": "location"}
    }
}
domoritz commented 2 years ago

Thank you for putting together the example. I had implemented support for expressions in guide titles before (https://github.com/vega/vega-lite/pull/7265) but have to redo it sometime (I have to redo https://github.com/vega/vega-lite/pull/7438). It's in my queue.

mdejean commented 2 years ago

Thanks for the workaround, @jwoLondon

I can confirm this works with the existing Vega-Lite implementation, although I am not sure if it is possible to customise the axis title to be data dependent. It would be useful if axis title could accept an expression in addition to a text literal.

It seems that it already does:

Vega-Lite editor

...
"y": {
  "field": "y",
  "aggregate": "mean",
  "axis": {"title": {"expr": "measure"}}
},
...
joelostblom commented 2 years ago

@jwoLondon @domoritz Wow it is so cool that this works with the calculate transform and datum[param_name] syntax! :star_struck: I think that largely covers the use case I wanted to have enabled when I opened this issue originally, so maybe the main thing would be to add an example to the documentation/gallery showcasing this? I think this syntax is already quite convenient and not sure I feel the need to have this open since it is already possible to do what I want in a simple way.

@mdejean Thank you for sharing that, it is working for me as well! However, if I fix the schema to version 5.2, I get an error message saying that the axis title only accepts strings (although the chart still shows, this means I can't generate the spec with Altair which checks this before displaying the chart). Was expression referencing in axis titles a recent addition after 5.2?

kanitw commented 2 years ago

@jwoLondon that's a very nice trick!

joelostblom commented 2 years ago

Since datum also accepts expr, maybe once https://github.com/vega/vega-lite/pull/7463 is merged it would be possible to skip the calculate transform in @jwoLondon 's answer and instead use a datum containing an expr as the encoding? Something like this:

{
  "data": {"url": "data/weather.csv"},
  "params": [
    {
      "name": "measure",
      "value": "temp_max",
      "bind": {
        "input": "select",
        "options": ["temp_max", "precipitation", "wind"]
      }
    }
  ],
  "mark": "line",
  "encoding": {
    "x": {"field": "date", "timeUnit": "month"},
    "y": {
      "datum": {
        "expr": "datum[measure]"
      }
    },
    "color": {"field": "location"}
  }
}

This spec is already valid if we replace datum with value but the lines are messed up (Open the Chart in the Vega Editor). I think this is since no domain is generated for y (but a domain would be generated when using datum instead of value if I understand correctly). One problem I see is that I am not sure if it would be possible to specify the type of a datum encoding or if VegaLite could infer it as it does when scalars are past to datum?

yclicc commented 2 years ago

I can't find a way to repro it, but I've got a situation where the data in the data viewer has changed downstream when I use @jwoLondon 's method but a correlation calculation (don't ask) downstream and a kde plot fail to update. The underlying data in the data viewer for these dependent charts has indeed changed but the chart itself doesn't know to refresh. Is there some way (in Vega-Lite or Vega) I can explicitly tell a sub-chart to redraw when it sees a specific parameter get modified?

yclicc commented 2 years ago

Ok, I've got a repro with the following visualisation view here change the x variable to "displacement" and observe that the bottom KDE doesn't update. Change the value again and things get even more broken.


  "config": {"view": {"continuousWidth": 500, "continuousHeight": 500}},
  "data": {"url": "data/cars.json"},
  "usermeta": [
    "Name",
    "Miles_per_Gallon",
    "Cylinders",
    "Displacement",
    "Horsepower",
    "Weight_in_lbs",
    "Acceleration",
    "Year",
    "Origin"
  ],
  "params": [
    {
      "name": "x",
      "value": "Acceleration",
      "bind": {
        "input": "select",
        "options": [
          "Miles_per_Gallon",
          "Displacement",
          "Horsepower",
          "Weight_in_lbs",
          "Acceleration"
        ]
      }
    },
    {
      "name": "y",
      "value": "Miles_per_Gallon",
      "bind": {
        "input": "select",
        "options": [
          "Miles_per_Gallon",
          "Displacement",
          "Horsepower",
          "Weight_in_lbs",
          "Acceleration"
        ]
      }
    }
  ],
  "transform": [
    {"calculate": "datum[x]", "as": "xcalc"},
    {"calculate": "datum[y]", "as": "ycalc"}
  ],
  "hconcat": [
    {
      "mark": {"type": "area", "orient": "horizontal"},
      "width": 100,
      "params": [
        {"name": "ybrush", "select": {"type": "interval", "encodings": ["y"]}}
      ],
      "transform": [
        {"density": "ycalc", "groupby": ["Origin"], "counts": true}
      ],
      "encoding": {
        "x": {"field": "density", "type": "quantitative"},
        "y": {
          "field": "value",
          "type": "quantitative",
          "axis": {"title": {"expr": "y"}}
        },
        "color": {"field": "Origin", "type": "nominal"},
        "tooltip": {"field": "Origin", "type": "nominal"}
      }
    },
    {
      "vconcat": [
        {
          "mark": "point",
          "params": [{"name": "brush", "select": "interval"}],
          "encoding": {
            "color": {
              "condition": {
                "param": "brush",
                "field": "Origin",
                "legend": {"title": "Legend"},
                "type": "nominal"
              },
              "value": "lightgray"
            },
            "tooltip": [
              {"field": "name", "type": "nominal"},
              {"field": "year", "type": "nominal"},
              {"field": "Origin", "type": "nominal"},
              {"field": "xcalc", "type": "quantitative", "title": "x"},
              {"field": "ycalc", "type": "quantitative", "title": "y"}
            ],
            "x": {
              "field": "xcalc",
              "type": "quantitative",
              "axis": {"title": {"expr": "x"}},
              "scale": {"domain": {"param": "xbrush"}}
            },
            "y": {
              "field": "ycalc",
              "type": "quantitative",
              "axis": {"title": {"expr": "y"}},
              "scale": {"domain": {"param": "ybrush"}}
            }
          }
        },
        {
          "mark": "area",
          "height": 100,
          "params": [
            {
              "name": "xbrush",
              "select": {"type": "interval", "encodings": ["x"]}
            }
          ],
          "transform": [
            {"density": "xcalc", "groupby": ["Origin"], "counts": true}
          ],
          "encoding": {
            "x": {
              "field": "value",
              "type": "quantitative",
              "axis": {"title": {"expr": "x"}}
            },
            "y": {"field": "density", "type": "quantitative"},
            "color": {"field": "Origin", "type": "nominal"},
            "tooltip": {"field": "Origin", "type": "nominal"}
          }
        }
      ]
    }
  ]
}```
Sebastian2023 commented 1 year ago

@jwoLondon @joelostblom Is it in Vega lite possible to select the visualized value depending on a nested field? datum[measure] from the example does not seem to be able to handle values such as Motor.Cylinder.

"data": { "values": [ { "Name": "chevrolet chevelle malibu", "Miles_per_Gallon": 18, "Motor": {"Cylinders": 8, "Horsepower": 130}, "Displacement": 307, ... "transform": [{"calculate": "datum[measure]", "as": "y"}],

Vega lite editor

joelostblom commented 1 year ago

@domoritz Just following up on our discussion, when I try using the spec below. I run into Unsupported object: {"expr":"datum[measure]"}. I know you said there are other things in the pipeline before this, so thought I would just note it here for now.

{
  "data": {"url": "data/weather.csv"},
  "params": [
    {
      "name": "measure",
      "value": "temp_max",
      "bind": {
        "input": "select",
        "options": ["temp_max", "precipitation", "wind"]
      }
    }
  ],
  "mark": "line",
  "encoding": {
    "x": {"field": "date", "timeUnit": "month"},
    "y": {
      "datum": {
        "expr": "datum[measure]"
      },
      "type": "quantitative"
    },
    "color": {"field": "location"}
  }
}

Also @Sebastian2023 , sorry for not replying previously. Unfortunately I don't know how to make this work with a nested field.