Public
Edited
Jan 17, 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("intersect"); // 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("global");

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