A look at a fundamental building block fo interactive visualization.
Reading, Recording, Notebook, Rmarkdown
robservable("@krisrs1128/week-3-1", include = 3, height = 180)
{const selection = vl.selectSingle();
return vl.markCircle()
.data(cars)
.select(selection)
.encode(
.x().fieldQ('Horsepower'),
vl.y().fieldQ('Miles\_per\_Gallon'),
vl.color().if(selection, vl.fieldO('Cylinders')).value('grey')
vl
).height(180)
.render();
}
vl.selectSingle()
: Allows selection of one observation at a time.vl.selectMulti()
: Allows selection of several observations.vl.selectInterval()
: Allows selection of contiguous intervals of observations.To associate the selection with the points, we bound them to the vl.markCircle()
block using the call .select(selection)
. This is how vega-lite knows that the user interaction should be associated with individual points.
We then apply the idea of conditional encoding to allow the visualization to respond to user selections. Conditional encoding changes the visual encodings of marks depending on whether they are contained within a user selected set. For example, in the plot above, if the observation is within the selected set, it is colored in by the number of cylinders in the car, otherwise it is grey. This is implemented by the .if(selection, vl.fieldO('Cylinders')).value('grey')
modification of the original vl.color()
encoding.
To understand the behavior of the three interaction techniques above, let’s regenerate this plot with the other two types of selections.
function plot(selection) {
return vl.markCircle()
.data(cars)
.select(selection)
.encode(
.x().fieldQ('Horsepower'),
vl.y().fieldQ('Miles\_per\_Gallon'),
vl.color().if(selection, vl.fieldO('Cylinders')).value('grey')
vl
).width(240)
.height(180);
}
We can place the plots side-by-side using the horizontal concatenation we learned in the composition reading.
.hconcat(
vlplot(vl.selectSingle()).title('Single (Click)'),
plot(vl.selectMulti()).title('Multi (Shift-Click)'),
plot(vl.selectInterval()).title('Interval (Drag)'),
plot(vl.selectMulti().on("mouseover").nearest(true)).title('Mouseover Painting')
.render() )
robservable("@krisrs1128/week-3-1", include = 5, height = 220)
Are there ways that we can select multiple observations based on attributes other than their position in a plot (that is, without having to click or drag on them within the plot)? The answer is yes, using dynamic queries, which are discussed in the next recording.
Selection — and interaction more generally — are reversing our usual way of thinking about encoding in visualization. Usually, we are mapping abstract data fields to specific visual marks and their properties. In interaction, we have to start with visual marks and refer to the underlying observations. This then allows changes in the visual marks, and we can iterate.
A corollary of this point is that selections require two specifications: the mechanics of interaction and the way marks and their encodings change in response to an input. It is possible to experiment with variations in one while keeping the other constant.