-
Notifications
You must be signed in to change notification settings - Fork 794
/
parameters.rst
484 lines (364 loc) · 16.1 KB
/
parameters.rst
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
.. currentmodule:: altair
.. _parameters:
Parameters, Conditions, & Filters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Parameters are the building blocks of interaction in Altair.
There are two types of parameters: *variables* and *selections*. We introduce these concepts through a series of examples.
.. note::
This material was changed considerably with the release of Altair 5.
.. _basic variable:
Variables: Reusing Values
^^^^^^^^^^^^^^^^^^^^^^^^^
Variable parameters allow for a value to be defined once
and then reused throughout the rest of the chart.
Here is a simple scatter-plot created from the ``cars`` dataset:
.. altair-plot::
import altair as alt
from vega_datasets import data
cars = data.cars.url
alt.Chart(cars).mark_circle().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
)
Variable parameters are created using the :func:`param` function.
Here,
we create a parameter with a default value of 0.1 using the ``value`` property:
.. altair-plot::
:output: none
op_var = alt.param(value=0.1)
In order to use this variable in the chart specification, we explicitly add it to the chart using the :meth:`add_params` method, and we can then reference the variable within the chart specification. Here we set the opacity using our ``op_var`` parameter.
.. altair-plot::
op_var = alt.param(value=0.1)
alt.Chart(cars).mark_circle(opacity=op_var).encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
).add_params(
op_var
)
It's reasonable to ask whether all this effort is necessary. Here is a more natural way to accomplish the same thing that avoids the use of both :func:`param` and ``add_params``.
.. altair-plot::
op_var2 = 0.1
alt.Chart(cars).mark_circle(opacity=op_var2).encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
)
The benefit of using :func:`param` doesn't become apparent until we incorporate an additional component. In the following example we use the ``bind`` property of the parameter, so that the parameter becomes bound to an input element. In this example, that input element is a slider widget.
.. altair-plot::
slider = alt.binding_range(min=0, max=1, step=0.05, name='opacity:')
op_var = alt.param(value=0.1, bind=slider)
alt.Chart(cars).mark_circle(opacity=op_var).encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
).add_params(
op_var
)
Now we can dynamically change the opacity of the points in our chart using the slider. You will learn much more about binding parameters to input elements such as widgets in the section :ref:`binding-parameters`.
.. note::
A noteworthy aspect of Altair's interactivity is that these effects are controlled entirely within the web browser. This means that you can save charts as HTML files and share them with your colleagues who can access the interactivity via their browser without the need to install Python.
Selections: Capturing Chart Interactions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Selection parameters define data queries
that are driven by interactive manipulation of the chart
by the user (e.g., via mouse clicks or drags).
There are two types of selections:
:func:`selection_interval` and :func:`selection_point`.
Here we will create a simple chart and then add an selection interval to it.
We could create a selection interval via ``param(select="interval")``,
but it is more convenient to use the shorter ``selection_interval``.
Here is a simple scatter-plot created from the ``cars`` dataset:
.. altair-plot::
import altair as alt
from vega_datasets import data
cars = data.cars.url
alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
)
First we'll create an interval selection using the :func:`selection_interval`
function (an interval selection is also referred to as a "brush"):
.. altair-plot::
:output: none
brush = alt.selection_interval()
We can now add this selection interval to our chart via ``add_params``:
.. altair-plot::
alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
).add_params(
brush
)
The result above is a chart that allows you to click and drag to create
a selection region, and to move this region once the region is created.
So far this example is very similar to what we did in the :ref:`variable example <basic variable>`:
we created a selection parameter using ``brush = alt.selection_interval()``,
and we attached that parameter to the chart using ``add_params``.
One difference is that here we have not defined how the chart should respond to the selection; you will learn this in the next section.
Conditions & Filters
~~~~~~~~~~~~~~~~~~~~
Conditional Encodings
^^^^^^^^^^^^^^^^^^^^^
The example above is neat, but the selection interval doesn't actually *do* anything yet.
To make the chart respond to this selection, we need to reference the selection in within
the chart specification. Here, we will use the :func:`condition` function to create
a conditional color encoding: we'll tie the color to the ``"Origin"``
column for points in the selection, and set the color to ``"lightgray"``
for points outside the selection:
.. altair-plot::
alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).add_params(
brush
)
As you can see, the color of the points now changes depending on whether they are inside or outside the selection.
Above we are using the selection parameter ``brush`` as a *predicate*
(something that evaluates as `True` or `False`).
This is controlled by the line ``color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))``.
Data points which fall within the selection evaluate as ``True``,
and data points which fall outside the selection evaluate to ``False``.
The ``'Origin:N'`` specifies how to color the points which fall within the selection,
and the ``alt.value('lightgray')`` specifies that the outside points should be given a constant color value;
you can remember this as ``alt.condition(<condition>, <if_true>, <if_false>)``.
This approach becomes even more powerful when the selection behavior is
tied across multiple views of the data within a compound chart.
For example, here we create a ``chart`` object using the same code as
above, and horizontally concatenate two versions of this chart: one
with the x-encoding tied to ``"Horsepower"``, and one with the x-encoding
tied to ``"Acceleration"``
.. altair-plot::
chart = alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).properties(
width=250,
height=250
).add_params(
brush
)
chart | chart.encode(x='Acceleration:Q')
Because both copies of the chart reference the same selection object, the
renderer ties the selections together across panels, leading to a dynamic
display that helps you gain insight into the relationships within the
dataset.
Each selection type has attributes through which its behavior can be
customized; for example we might wish for our brush to be tied only
to the ``"x"`` encoding to emphasize that feature in the data.
We can modify the brush definition, and leave the rest of the code unchanged:
.. altair-plot::
brush = alt.selection_interval(encodings=['x'])
chart = alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).properties(
width=250,
height=250
).add_params(
brush
)
chart | chart.encode(x='Acceleration:Q')
As you might have noticed,
the selected points are sometimes obscured by some of the unselected points.
To bring the selected points to the foreground,
we can change the order in which they are laid out via the following
encoding: ``order=alt.condition(hover, alt.value(1), alt.value(0))``.
You can see an example of this in the :ref:`gallery_selection_zorder` gallery example.
Filtering Data
^^^^^^^^^^^^^^
Using a selection parameter to filter data works in much the same way
as using it within ``condition``.
For example, in ``transform_filter(brush)``,
we are again using the selection parameter ``brush`` as a predicate.
Data points which evaluate to ``True`` (i.e., data points which lie within the selection) are kept,
and data points which evaluate to ``False`` are filtered out.
It is not possible to both select and filter in the same chart,
so typically this functionality will be used when at least two sub-charts are present.
In the following example,
we attach the selection parameter to the upper chart,
and then filter data in the lower chart based on the selection in the upper chart.
You can explore how the counts change in the bar chart
depending on the size and position of the selection in the scatter plot.
.. altair-plot::
brush = alt.selection_interval()
points = alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N'
).add_params(
brush
)
bars = alt.Chart(cars).mark_bar().encode(
x='count()',
y='Origin:N',
color='Origin:N'
).transform_filter(
brush
)
points & bars
Selection Types
~~~~~~~~~~~~~~~
Now that we have seen the basics of how we can use a selection to interact with a chart,
let's take a more systematic look at some of the types of selection parameters available in Altair.
For simplicity, we'll use a common chart in all the following examples; a
simple heat-map based on the ``cars`` dataset.
For convenience, let's write a quick Python function that will take a selection
object and create a chart with the color of the chart elements linked to this
selection:
.. altair-plot::
:output: none
def make_example(selector: alt.Parameter) -> alt.Chart:
cars = data.cars.url
return alt.Chart(cars).mark_rect().encode(
x="Cylinders:O",
y="Origin:N",
color=alt.condition(selector, 'count()', alt.value('lightgray'))
).properties(
width=300,
height=180
).add_params(
selector
)
Next we'll use this function to demonstrate the properties of various selections.
Interval Selections
^^^^^^^^^^^^^^^^^^^
An *interval* selection allows you to select chart elements by clicking and dragging.
You can create such a selection using the :func:`selection_interval` function:
.. altair-plot::
interval = alt.selection_interval()
make_example(interval)
As you click and drag on the plot, you'll find that your mouse creates a box
that can be subsequently moved to change the selection.
The :func:`selection_interval` function takes a few additional arguments; for
example we can bind the interval to only the x-axis, and set it such that the
empty selection contains none of the points:
.. altair-plot::
interval_x = alt.selection_interval(encodings=['x'], empty=False)
make_example(interval_x)
Point Selections
^^^^^^^^^^^^^^^^
A *point* selection allows you to select chart elements one at a time
via mouse actions. By default, points are selected on click:
.. altair-plot::
point = alt.selection_point()
make_example(point)
By changing some arguments, we can select points when hovering over them rather than on
click. We can also set the ``nearest`` flag to ``True`` so that the nearest
point is highlighted:
.. altair-plot::
point_nearest = alt.selection_point(on='pointerover', nearest=True)
make_example(point_nearest)
Point selections also allow for multiple chart objects to be selected.
By default, chart elements can be added to and removed from the selection
by clicking on them while holding the *shift* key, you can try in the two charts above.
Selection Targets
~~~~~~~~~~~~~~~~~
For any but the simplest selections, the user needs to think about exactly
what is targeted by the selection, and this can be controlled with either the
``fields`` or ``encodings`` arguments. These control what data properties are
used to determine which points are part of the selection.
For example, here we create a small chart that acts as an interactive legend,
by targeting the Origin field using ``fields=['Origin']``. Clicking on points
in the upper-right plot (the legend) will propagate a selection for all points
with a matching ``Origin``.
.. altair-plot::
selection = alt.selection_point(fields=['Origin'])
color = alt.condition(
selection,
alt.Color('Origin:N').legend(None),
alt.value('lightgray')
)
scatter = alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=color,
tooltip='Name:N'
)
legend = alt.Chart(cars).mark_point().encode(
alt.Y('Origin:N').axis(orient='right'),
color=color
).add_params(
selection
)
scatter | legend
Alternatively, we could express ``fields=['Origin']`` as ``encodings=['color']``, because our chart maps ``color`` to
``'Origin'``. Also note that there is a shortcut to create interactive legends in Altair
described in the section :ref:`legend-binding`.
Similarly, we can specify multiple fields and/or encodings that must be
matched in order for a datum to be included in a selection.
For example, we could modify the above chart to create a two-dimensional
clickable legend that will select points by both Origin and number of
cylinders:
.. altair-plot::
selection = alt.selection_point(fields=['Origin', 'Cylinders'])
color = alt.condition(
selection,
alt.Color('Origin:N').legend(None),
alt.value('lightgray')
)
scatter = alt.Chart(cars).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=color,
tooltip='Name:N'
)
legend = alt.Chart(cars).mark_rect().encode(
alt.Y('Origin:N').axis(orient='right'),
x='Cylinders:O',
color=color
).add_params(
selection
)
scatter | legend
By fine-tuning the behavior of selections in this way, they can be used to
create a wide variety of linked interactive chart types.
Parameter Composition
~~~~~~~~~~~~~~~~~~~~~
Altair also supports combining multiple parameters using the ``&``, ``|``
and ``~`` for respectively ``AND``, ``OR`` and ``NOT`` logical composition
operands.
Returning to our heatmap examples,
we can construct a scenario where there are two people who can make an interval
selection in the same chart. The person Alex makes a selection box when the
alt-key (macOS: option-key) is selected and Morgan can make a selection
box when the shift-key is selected.
We use :class:`BrushConfig` to give the selection box of Morgan a different
style.
Now, we color the rectangles when they fall within Alex's or Morgan's
selection
(note that you need to create both selections before seeing the effect).
.. altair-plot::
alex = alt.selection_interval(
on="[pointerdown[event.altKey], pointerup] > pointermove",
name='alex'
)
morgan = alt.selection_interval(
on="[pointerdown[event.shiftKey], pointerup] > pointermove",
mark=alt.BrushConfig(fill="#fdbb84", fillOpacity=0.5, stroke="#e34a33"),
name='morgan'
)
alt.Chart(cars).mark_rect().encode(
x='Cylinders:O',
y='Origin:O',
color=alt.condition(alex | morgan, 'count()', alt.ColorValue("grey"))
).add_params(
alex, morgan
).properties(
width=300,
height=180
)
With these operators, selections can be combined in arbitrary ways:
- ``~(alex & morgan)``: to select the rectangles that fall outside
Alex's and Morgan's selections.
- ``alex | ~morgan``: to select the rectangles that fall within Alex's
selection or outside the selection of Morgan
For more information on how to fine-tune selections, including specifying other
mouse and keystroke options, see the `Vega-Lite Selection documentation
<https://vega.github.io/vega-lite/docs/selection.html>`_.