Public
Edited
Mar 12, 2023
4 forks
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vl.markBar({tooltip: {"content": "encoding"}, clip: true}) //content of tooltip with
.data(data)
.encode(
vl.y().aggregate('count'),
vl.x().fieldN("weather")
)
.render()
Insert cell
Insert cell
vl.markBar()
.data(data)
.encode(
vl.y().aggregate('count'),
vl.x().fieldN("weather"),
//vl.tooltip().average('temp_max').format({content: 'encoding'}) //this is also another way of specifying tooltips through chained functions that is more in line with the Vega-Lite API.
)
.render()
Insert cell
Insert cell
vl.markBar()
.data(data)
.encode(
vl.y().aggregate('count'),
vl.x().fieldN("weather"),
vl.tooltip([{field: "weather"}, //this is another way of adding in more custom info into your tooltip that uses JSON spec
{field: "temp_min", title: "Average Min Temp", aggregate: "mean"},
{field: "temp_max", title: "Average Max Temp", aggregate: "mean"}
])
)
.render()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const selection = vl.selectPoint(); //We create a selection instance which indicates that we want a selection defined over a point value
return vl.markCircle()
.data(Weather)
.params(selection) //we register the selection in the chart where we will interact with our marks for selection
.encode(
vl.x().fieldQ('temp_max'),
vl.y().fieldQ('wind'),
vl.color().if(selection, vl.color().fieldN('weather').scale({scheme: 'tableau10'})).value('grey'), //color to grey if it is not selected
vl.opacity().if(selection, vl.value(0.9)).value(0.1) //change opacity as well
)
.render()
}
Insert cell
Insert cell
function plot(selection) {

//See how our plot function returns a chart VERY SIMILAR to the one above, except that we don't call render
return vl.markCircle()
.data(Weather)
.params(selection) //we register a selection with a visualized mark like above
.encode(
vl.x().fieldQ('temp_max'),
vl.y().fieldQ('wind'),
vl.color().if(selection, vl.fieldN('weather')).value('grey'), //color to grey if it is not selected. we didn't specify color() specifically inside our if statement as we're just using the default color in this case
vl.opacity().if(selection, vl.value(0.8)).value(0.1) //change opacity as well
)
.width(300) //we've specified a height and width here
.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
//We use the uniqueValid function imported from UW's misc data utilities to return a sorted list of unique values in the data array
weatherCategories = uniqueValid(Weather, d => d.weather)
Insert cell
Insert cell
Weather
Insert cell
Insert cell
{
const selectWeather = vl
.selectPoint("SelectionMenu") // name the dropdown menu 'Selection'
.fields("weather") // limit selection to the weather field in the dataset
.init(weatherCategories[0]) // use first weatherCategories entry (drizzle) as initial value
.bind(vl.menu(weatherCategories).name("Weather: ")); // bind to a menu of unique weatherCategories values

// scatter plot, modify opacity based on genre selection
return vl
.markCircle()
.data(Weather)
.params(selectWeather)
.encode(
vl.x().fieldQ("temp_max"),
vl.y().fieldQ("wind"),
vl.color().if(selectWeather, vl.fieldN("weather")).value("grey"),
vl.tooltip(["date", "temp_max", "wind"]), //pass these three fields we want to represent as array

vl.opacity().if(selectWeather, vl.value(0.75)).value(0.05) //if point is part of the selection, keep opacity higher than nonselected points
)
.render();
}
Insert cell
Insert cell
Insert cell
{
const selectWeather = vl
.selectPoint("SelectionMenu") // name the dropdown menu 'Selection'
.fields("weather") // limit selection to the weather field in the dataset
.init(weatherCategories[0]) // use first weatherCategories entry (drizzle) as initial value
.bind(vl.menu(weatherCategories).name("Weather: ")); // bind to a menu of unique weatherCategories values

const filterPrecipitation = vl
.param("PrecipitationSlider")
.value(0)
.bind(vl.slider(0, 45, 0.1).name("Filter precipitation: ")); // bind to slider

//
const show = vl.and(selectWeather, "datum.precipitation >= PrecipitationSlider");
// scatter plot, modify opacity based on genre selection
return vl
.markCircle()
.data(Weather)
.params(selectWeather, filterPrecipitation)
.encode(
vl.x().fieldQ("temp_max"),
vl.y().fieldQ("wind"),
vl.color().if(show, vl.fieldN("weather")).value("grey"),
vl.tooltip(["date", "temp_max", "wind"]), //pass these three fields we want to represent as array
vl.opacity().if(show, vl.value(0.75)).value(0.05) //if point is part of the selection, keep opacity higher than nonselected points
)
.render();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//We're going to create two charts

{
//bigChart is our detail chart, because we can see details of the data points more clearly (and will see even more with brushing)
const bigChart = vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT("date"),
vl.y().fieldQ("temp_max").axis({tickCount: 2}) //set the tickCount to 2 on y-axis
)
.width(480) // set chart as 480px wide
//we don't need to specify height here as the system makes some assumptions about sizing to simplify things

//smallChart is our overview chart -- it doesn't show details well, but gives a good overview of what the average max temp is over the years
const smallChart = vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT("date"), //treat date as a time Encoding
vl.y().fieldQ("temp_max").axis({tickCount: 2}) //set the tickCount to 2 on y-axis
)
.width(480)
.height(60) //set height to be smaller, this will be our

return vl.vconcat(bigChart, smallChart)
.render()

}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// create an interval selection over an x-axis encoding
const brush = vl.selectInterval().encodings('x');

//bigChart is our detail chart, because we can see details of the data points more clearly (and will see even more with brushing)
//this is the chart that reacts and changes view when we brush over smallChart
const bigChart = vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT("date").scale({domain: brush}), //.scale({domain: brush}) tells Vega-Lite that the domain of dates we see in bigChart (aka what is shown on the x-axis encoding) is limited to the domain of brush (aka our selection)
vl.y().fieldQ("temp_max").axis({tickCount: 2}), //set the tickCount to 2 on y-axis. we don't set scale because the view of y is not changing
)
.width(480)

//smallChart is our overview chart -- it doesn't show details well, but gives a good overview of what the average max temp is over the years
//we add the brush in smallChart because that is where we will control our brush
const smallChart = vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT("date"),
vl.y().fieldQ("temp_max").axis({tickCount: 2})
)
.params(brush) //add interval brush selection to the chart
.width(480)
.height(60) //same width as above, but we keep the height small to make it more compact

//Concatenate the two charts together
return vl.vconcat(bigChart, smallChart)
.title('Our Overview + Detail Chart')
.render()

}
Insert cell
Insert cell
Insert cell
Insert cell
{
const brushy = vl.selectInterval().encodings('x').resolve('intersection') //resolve all selections to consist of the intersection of all brushes (only points that reside within all brushes will be selected)
const bar = vl
.data(Weather)
.encode(
vl.x().fieldQ(vl.repeat('repeat')).bin(true),
vl.y().count()
)
.layer(
vl.markBar()
.params(brushy)
.encode(
vl.x().fieldQ(vl.repeat('repeat')).bin(true),
vl.y().count()
),
vl.markBar({color: 'goldenrod'})
.transform(
vl.filter(brushy)
)
.encode(
vl.x().fieldQ(vl.repeat('repeat')).bin(true),
vl.y().count()
)
)
.height(120)
.width(240)
.repeat(['temp_max', 'wind', 'precipitation'])
.columns(3)

return bar.render()
}
Insert cell
Insert cell
Insert cell
Insert cell
{
// create an interval selection over an x-axis encoding
const brush = vl.selectInterval().encodings('x'); //we didn't specify resolve here because we only have 1 brush so farrushes will be selected)

//this is the chart that reacts and changes view when we brush over overviewChart
const detailChart = vl.markCircle()
.data(Weather)
.transform(
vl.filter(brush) //***We use filter to get the interval selection of data points that are in our brush. we want to filter out other data
)
.encode(
vl.x().fieldQ('temp_max').scale({domain: [-10, 40]}),
vl.y().fieldQ('wind').scale({domain: [0, 10]}),
vl.color().fieldN('weather'),//.scale({domain: weatherCategories}),
vl.tooltip(['precipitation', 'wind']) //show both precipitation and wind
)
//brushChart is where we will control our brush
const brushChart = vl.markBar()
.data(Weather)
.encode(
vl.x().fieldT("date").timeUnit('utcmonthyear'), //set utcmonthyear so we are using utctime
vl.y().average("temp_max").axis({tickCount: 2})
)
.params(brush) //***ADD interval brush selection to the chart as parameter
.width(480)
.height(60) //same width as above, but we keep the height small to make it more compact

//Concatenate the two charts together
return vl.vconcat(detailChart, brushChart)
.render()

}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
WeatherDatesSorted = Weather.map(d=> d.date).sort() //get weather dates sorted by earliest to latest --- if your date format is not like such, you might have to do a conversion between date objects
Insert cell
Insert cell
Insert cell
{
// Our interval selection (brush)
//I added resolve('intersect') as we want to resolve all selections to intersect
const brush = vl.selectInterval().encodings('x').resolve('intersect');

//Click is going to give us the ability to select multiple selectiona multi selection
const click = vl.selectPoint().encodings('color');
//CHART 1: this is the chart that reacts and changes view when we brush over brushChart
const detailChart = vl.markCircle()
.data(Weather)
.transform(
vl.filter(brush), //we use filter to get the interval selection of data points that are in our brush. we want to filter out other data
vl.filter(click)
)
.encode(
vl.x().fieldQ('temp_max').scale({domain: [-10, 40]}),
vl.y().fieldQ('wind').scale({domain: [0, 10]}),
vl.color().fieldN('weather').scale({domain: weatherCategories})
)
.height(200)
.width(200)

//CHART 2: brushChart is where we will control our brush
const brushChart = vl.markBar()
.data(Weather)
.transform(vl.filter(click))
.params(brush) //add interval brush selection to the chart
.encode(
vl.x().fieldT("date").timeUnit('utcmonthyear').scale({domain: [earliestDate, latestDate]}),
vl.y().average("temp_max").axis({tickCount: 2}),
vl.tooltip().format({content: 'encoding'})
)
.width(480)
.height(120) //same width as above, but we keep the height small to make it more compact


//CHART 3: **NEW** THIS IS OUR BAR CHART WHERE WE CAN CLICK ON BARS TO FILTER
const clickChart = vl.markBar()
.data(Weather)
.transform(vl.filter(brush))
.params(click)
.encode(
vl.x().count(),
vl.y().fieldN('weather').title('Weather'),
vl.color().value('lightgray').if(click, vl.color().fieldN('weather').title('Weather')), //GRAY IF NOT SELECTED
vl.tooltip().format({content: 'encoding'})
)
.width(480)
//we need to specify that we want our clicked selection
//Concatenate the three charts together
return vl.hconcat(detailChart, vl.vconcat(brushChart, clickChart))
.render()

}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//ADD CODE TO THIS CHART
//MAKE YOUR BRUSH FADE POINTS THAT FALL OUTSIDE OF THE BRUSH
{

//add a brush

const brush = vl.selectInterval().resolve('global');
return vl.markPoint()
.data(data)
.params(brush) //define your brush with a parameter
.encode(
vl.x().fieldT("date").timeUnit("utcmonthdate"),
vl.y().fieldQ('temp_max'),
vl.color().fieldN("weather").scale({ range: weatherColors }),
vl.tooltip(['date', 'temp_max', 'precipitation']),
vl.opacity().if(brush, vl.value(0.9)).value(0.1)
)
.width(800)
.height(400)
.render()

}
Insert cell
Insert cell
mutable brush = vl.selectInterval().resolve('global');
Insert cell
brush
Insert cell
//EDIT THE CODE BELOW TO ADD A BRUSH FOR THE SCATTERPLOT MATRIX
//WHEN POINTS FALL IN THE BRUSH, THEY SHOULD BE COLORED, OR ELSE TURNED GRAY AND HAVE LOWERED OPACITY

{
//resolve all selections to a single global instance
return vl.markCircle()
.data(Cars)
.params(brush) //register your brush
.encode(
vl.x().fieldQ(vl.repeat('column')),
vl.y().fieldQ(vl.repeat('row')),
vl.color().if(brush, vl.color().fieldO('Cylinders')).value('grey'), //add a condition here that changes color based on brush
vl.opacity().if(brush, vl.value(0.8)).value(0.1), //add a condition here to change opacity based on brush
)
.width(140)
.height(140)
.repeat({
column: ['Acceleration', 'Horsepower', 'Miles_per_Gallon'],
row: ['Miles_per_Gallon', 'Horsepower', 'Acceleration']
})
.render();
}
Insert cell
Insert cell
//BUIILD ON CHART C1, AND EDIT THE CODE BELOW TO ADD A SELECTION INTERACTION FOR THE LEGEND
//WHEN A CYLINDER CATEGORY IS CLICKED, POINTS THAT FALL WITHIN THAT CYLINDER

{
//copy your brush code
const brush = vl.selectInterval().resolve('union'); //resolve all selections to a single global instance

const legend = vl.selectPoint()
.fields('Cylinders')
.bind('legend'); // bind to interactions with the color legend

//uncomment this
const brushAndLegend = vl.and(brush, legend);
return vl.markCircle()
.data(Cars)
.params(brush, legend)
//register ALL YOUR BRUSHES!
.encode(
vl.x().fieldQ(vl.repeat('column')),
vl.y().fieldQ(vl.repeat('row')),
vl.color().if(brushAndLegend, vl.color().fieldO('Cylinders').scale({scheme: 'tableau10'})).value('grey'), //add a condition here that changes color based on brushAndLegend - REMEMBER, YOU WANT TO get the selection predicate from both your interval (brush) and discrete (point) selections
vl.opacity().if(brushAndLegend, vl.value(0.8)).value(0.1), //add a condition here to change opacity based on brush. see above comment
)
.width(140)
.height(140)
.repeat({
column: ['Acceleration', 'Horsepower', 'Miles_per_Gallon'],
row: ['Miles_per_Gallon', 'Horsepower', 'Acceleration']
})
.render();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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