Public
Edited
Jan 24, 2023
Fork of Interaction
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.selectPoint();
return vl.markCircle()
.data(cars)
.params(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)
)
.render()
}
Insert cell
Insert cell
function plot(selection) {
return vl.markCircle()
.data(cars)
.params(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(300)
.height(225);
}
Insert cell
Insert cell
vl.hconcat(
plot(vl.selectPoint()).title('Point (Click)'),
plot(vl.selectInterval()).title('Interval (Drag)')
).render()
Insert cell
Insert cell
plot(
vl.selectPoint().on('mouseover')).title('Point (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.selectPoint('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)
.params(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
{
// point-value selection over [Major_Genre, MPAA_Rating] pairs
// use specific hard-wired values as the initial selected values
const selection = vl.selectPoint('Select')
.fields('Major_Genre', 'MPAA_Rating')
.init({Major_Genre: 'Drama', MPAA_Rating: 'R'})
.bind({Major_Genre: vl.menu(genres), MPAA_Rating: vl.radio(mpaa)});
// scatter plot, modify opacity based on selection
return vl.markCircle()
.data(movies)
.params(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)
.params(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)
)
.width(600)
.height(400);

return vl.vconcat(years, ratings).spacing(5).render();
}
Insert cell
Insert cell
Insert cell
Insert cell
vl.markCircle()
.data(movies)
.params(
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)
.params(
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');
const x = vl.x().fieldT('date').title(null);
const base = vl.markArea()
.encode(x, vl.y().fieldQ('price'))
.width(700);
return vl.data(sp500)
.vconcat(
base.encode(x.scale({domain: brush})),
base.params(brush).height(60)
)
.render();
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const hover = vl.selectPoint()
.on('mouseover') // select on mouseover
.toggle(false) // do not toggle on shift-hover
.nearest(true); // select nearest point to mouse cursor
const click = vl.selectPoint();
// combine to select points in either selection
// empty selections should match nothing
const hoverOrClick = vl.or(click.empty(false), hover.empty(false));
// 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(hoverOrClick)
);
// 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.params(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
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const brush = vl.selectInterval()
.resolve('global'); // resolve all selections to a single global instance
const legend = vl.selectPoint()
.fields('Cylinders')
.bind('legend'); // bind to interactions with the color legend
const brushAndLegend = vl.and(brush, legend);
return vl.markCircle()
.data(cars)
.params(brush, legend)
.encode(
vl.x().fieldQ(vl.repeat('column')),
vl.y().fieldQ(vl.repeat('row')),
vl.color().if(brushAndLegend, vl.fieldO('Cylinders')).value('grey'),
vl.opacity().if(brushAndLegend, vl.value(0.8)).value(0.1)
)
.width(140)
.height(140)
.repeat({
column: ['Acceleration', 'Horsepower', 'Miles_per_Gallon'],
row: ['Miles_per_Gallon', 'Horsepower', 'Acceleration']
})
.render();
}
Insert cell
Insert cell
Insert cell
{
const brush = vl.selectInterval().encodings('x').resolve('intersect');
const hist = vl.markBar().encode(
vl.x().fieldQ(vl.repeat('row'))
.bin({maxbins: 100, minstep: 1}) // up to 100 bins, but no smaller than 1 unit
.axis({format: 'd', titleAnchor: 'start'}), // integer format, left-aligned title
vl.y().count().title(null) // no y-axis title
);
return vl.layer(
hist.params(brush).encode(vl.color().value('lightgrey')),
hist.transform(vl.filter(brush))
)
.width(900).height(100)
.repeat({row: ['delay', 'distance', 'time']})
.data(flights)
.transform(
vl.calculate('datum.delay < 180 ? datum.delay : 180').as('delay'), // clamp delays > 3 hours
vl.calculate('hours(datum.date) + minutes(datum.date) / 60').as('time') // fractional hours
)
.config({view: {stroke: null}}) // no outline
.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