Public
Edited
Feb 19
Paused
Fork of Interaction
2 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const selection = vl.selectSingle(); // setup selection type
return vl.markCircle()
.data(cars)
.select(selection) // apply selection type to circle mark
.encode(
vl.x().fieldQ('Horsepower'),
vl.y().fieldQ('Miles_per_Gallon'),
vl.color().if(selection, vl.fieldO('Cylinders')).value('gray'), // selection indicator
vl.opacity().if(selection, vl.value(0.8)).value(0.1) // selection indicator
)
.render()
}
Insert cell
Insert cell
function plot(selection) { // basic scatterplot setup to respond to selection
return vl.markCircle()
.data(cars)
.select(selection)
.encode(
vl.x().fieldQ('Horsepower'),
vl.y().fieldQ('Miles_per_Gallon'),
vl.color().if(selection, vl.fieldO('Cylinders')).value('grey'),
vl.opacity().if(selection, vl.value(0.8)).value(0.1)
)
.width(240)
.height(180);
}
Insert cell
Insert cell
vl.hconcat(
plot(vl.selectSingle()).title('Single (Click)'),
plot(vl.selectMulti()).title('Multi (Shift-Click)'),
plot(vl.selectInterval()).title('Interval (Drag)')
).render()
Insert cell
Insert cell
vl.hconcat(
plot(vl.selectSingle().on('mouseover')).title('Single (Mouseover)'),
plot(vl.selectMulti().on('mouseover')).title('Multi (Shift-Mouseover)')
).render()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
genres = uniqueValid(movies, d => d.Major_Genre)
Insert cell
Insert cell
mpaa = ['G', 'PG', 'PG-13', 'R', 'NC-17', 'Not Rated']
Insert cell
Insert cell
{
const selectGenre = vl.selectSingle("Select") // name the selection 'Select'
.fields('Major_Genre') // limit selection to the Major_Genre field
.init({Major_Genre: genres[0]}) // use first genre entry as initial value
.bind(vl.menu(genres)); // bind to a menu of unique genre values
// scatter plot, modify opacity based on genre selection
return vl.markCircle()
.data(movies)
.select(selectGenre)
.encode(
vl.x().fieldQ('Rotten_Tomatoes_Rating'),
vl.y().fieldQ('IMDB_Rating'),
vl.tooltip().fieldN('Title'),
vl.opacity().if(selectGenre, vl.value(0.75)).value(0.05)
)
.render();
}
Insert cell
Insert cell
Insert cell
{
// single-value selection over [Major_Genre, MPAA_Rating] pairs
// use specific hard-wired values as the initial selected values
const selection = vl.selectSingle('Select')
.fields('Major_Genre', 'MPAA_Rating')
.init({Major_Genre: 'Drama', MPAA_Rating: 'R'})
.bind({Major_Genre: vl.menu(genres), MPAA_Rating: vl.radio(mpaa)}); // dropdown menu and radio buttons
// scatter plot, modify opacity based on selection
return vl.markCircle()
.data(movies)
.select(selection)
.encode(
vl.x().fieldQ('Rotten_Tomatoes_Rating'),
vl.y().fieldQ('IMDB_Rating'),
vl.tooltip().fieldN('Title'),
vl.opacity().if(selection, vl.value(0.75)).value(0.05)
)
.render();
}
Insert cell
Insert cell
Insert cell
{
const brush = vl.selectInterval()
.encodings('x'); // limit selection to x-axis (year) values
// dynamic query histogram
const years = vl.markBar({width: 4})
.data(movies)
.select(brush)
.encode(
vl.x().year('Release_Date').title('Films by Release Year'),
vl.y().count().title(null)
)
.width(600)
.height(50);
// ratings scatter plot
const ratings = vl.markCircle()
.data(movies)
.encode(
vl.x().fieldQ('Rotten_Tomatoes_Rating'),
vl.y().fieldQ('IMDB_Rating'),
vl.tooltip().fieldN('Title'),
vl.opacity().if(brush, vl.value(0.75)).value(0.05) // no selection on scatter plot, but is conditional
)
.width(600)
.height(400);

return vl.vconcat(years, ratings).spacing(5).render();
}
Insert cell
Insert cell
Insert cell
Insert cell
vl.markCircle()
.data(movies)
.select(
vl.selectInterval().bind('scales') // bind interval selection to scale domains
)
.encode(
vl.x().fieldQ('Rotten_Tomatoes_Rating'),
vl.y().fieldQ('IMDB_Rating')
.axis({minExtent: 30}), // add min extent to stabilize axis title placement
vl.tooltip(['Title', 'Release_Date', 'IMDB_Rating', 'Rotten_Tomatoes_Rating'])
)
.width(600)
.height(450)
.render()
Insert cell
Insert cell
vl.markCircle()
.data(movies)
.select(
vl.selectInterval().bind('scales').encodings('x') // bind to x-axis scale only
)
.encode(
vl.x().fieldQ('Rotten_Tomatoes_Rating'),
vl.y().fieldQ('IMDB_Rating').axis({minExtent: 30}),
vl.tooltip(['Title', 'Release_Date', 'IMDB_Rating', 'Rotten_Tomatoes_Rating'])
)
.width(600)
.height(450)
.render()
Insert cell
Insert cell
Insert cell
Insert cell
{
const brush = vl.selectInterval().encodings('x'); // only x-axis selection
const x = vl.x().fieldT('date').title(null);
const base = vl.markArea() // same base area chart
.encode(x, vl.y().fieldQ('price'))
.width(700);
return vl.data(sp500)
.vconcat(
base.encode(x.scale({domain: brush})), // update x encoding so domain mapped to selection
base.select(brush).height(60) // mapped to brush, short height
)
.render();
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const hover = vl.selectSingle()
.on('mouseover') // select on mouseover
.nearest(true) // select nearest point to mouse cursor
.empty('none'); // empty selection should match nothing
const click = vl.selectMulti()
.empty('none'); // empty selection matches no points
// scatter plot encodings shared by all marks
const plot = vl.markCircle().encode(
vl.x().fieldQ('Rotten_Tomatoes_Rating'),
vl.y().fieldQ('IMDB_Rating')
);
// shared base for new layers
const base = plot.transform(
vl.filter(vl.or(hover, click)) // filter to points in either selection
);
// mark properties for new layers
const halo = {size: 100, stroke: 'firebrick', strokeWidth: 1};
const label = {dx: 4, dy: -8, align: 'right'};
const white = {stroke: 'white', strokeWidth: 2};

// layer scatter plot points, halo annotations, and title labels
return vl.data(movies)
.layer(
plot.select(hover, click),
base.markPoint(halo),
base.markText(label, white).encode(vl.text().fieldN('Title')),
base.markText(label).encode(vl.text().fieldN('Title'))
)
.width(600)
.height(450)
.render();
}
Insert cell
Insert cell
Insert cell
{
const brush = vl.selectInterval()
.resolve('global'); // resolve all selections to a single global instance
return vl.markCircle()
.data(cars)
.select(brush)
.encode(
vl.x().fieldQ(vl.repeat('column')),
vl.y().fieldQ(vl.repeat('row')),
vl.color().if(brush, vl.fieldO('Cylinders')).value('grey'),
vl.opacity().if(brush, vl.value(0.8)).value(0.1)
)
.width(140)
.height(140)
.repeat({
column: ['Acceleration', 'Horsepower', 'Miles_per_Gallon'],
row: ['Acceleration', 'Horsepower', 'Miles_per_Gallon']
})
.render();
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more