Public
Edited
Oct 28, 2022
1 star
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: "average"},
{field: "temp_max", title: "Average Max Temp", aggregate: "average"}
])
)
.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 scheme 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)'), //Add a title to the chart
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
// selectPoint: name the dropdown menu 'Selection'
// fields: limit selection to the weather field in the dataset
// init: use first weatherCategories entry (drizzle) as initial value
// bind: bind to a menu of unique weatherCategories values

{
//let's chain the functions together and assign it to selectWeather
const selectWeather = vl.selectPoint('Selection')
.fields('weather')
.init({weather: weatherCategories[0]})
.bind(vl.menu(weatherCategories))
// scatter plot, modify opacity based on genre selection
return vl.markCircle()
.data(Weather)
//now we need to register the selection of selectWeather using .params(). Let's add it here
.encode(
vl.x().fieldQ('temp_max'),
vl.y().fieldQ('wind'),
vl.tooltip(['date', 'temp_max', 'wind']), //pass these three fields we want to represent as array
//we will need to adjust the opacity here with for selected and unselected points
//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
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
{
// TO-DO: create an interval selection over an x-axis encoding. Let's assign it to variable brush
//const brush = ;

//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"), //TO-DO: specify our domain here with .scale
vl.y().fieldQ("temp_max"), //TO-DO: let's set the tickCount to 2 here because the view of y does not change
)
.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") //TO-DO: let's set the tickCount to 2 here because the view of y does not change
)
//TO-DO: add our param brush here using .params()
.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
{
//Our brush
const brushy = vl.selectInterval().encodings('x').resolve('intersect') //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), //repeat the chart with the fields below
vl.y().count()
)
.layer(
vl.markBar() //Layer 1: Background color
.params(brushy) //We register our selection that is included in brushy
.encode(
vl.x().fieldQ(vl.repeat('repeat')).bin(true),
vl.y().count()
),
vl.markBar({color: 'goldenrod'}) //Layer 2: Foreground color
.transform( //We use transform here to filter only the data selection in brushy
vl.filter(brushy)
)
.encode(
vl.x().fieldQ(vl.repeat('repeat')).bin(true),
vl.y().count()
)
)
.height(120)
.width(240)
.repeat(['temp_max', 'wind', 'precipitation']) //The repeated columns
.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
Insert cell
{
// Our interval selection (brush). We use resolve('intersect') as we want to resolve all selections to intersect
//TO-DO: DEFINE BRUSH
const brush = vl.selectInterval().encodings('x');

//Our point selection (click). We want to define our encoding of click to the color of the bars that we will click on
//TO-DO: DEFINE CLICK
const click;

//-----------------------
//CHART 1: this is the scatterplot chart that reacts and changes view when we brush over brushChart
const detailChart = vl.markCircle()
.data(Weather)
.transform(
//We're going to use a filter function to get the selection of points in our brush and the clicked point as well
//TO-DO: FILTER BY BRUSH AND CLICK
vl.filter("brush"),
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({color: 'goldenrod'})
.data(Weather)
.transform(
//TO-DO: FILTER BRUSHCHART BASED ON THE SELECTION IN CLICKCHART (horizontal chart)
)
//TO-DO: REGISTER OUR BRUSH SELECTION TO THIS CHART WITH PARAMSHERE
.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 HORIZONTAL BAR CHART WHERE WE CAN CLICK ON BARS TO FILTER
const clickChart = vl.markBar()
.data(Weather)
.transform(
//TO-DO: FILTER BRUSHCHART BASED ON THE SELECTION IN CLICKCHART (horizontal chart)
)
//TO-DO: REGISTER CLICK WITH .params
.encode(
vl.x().count(),
vl.y().fieldN('weather').title('Weather'),
//TO-DO: Define color for the bars that are selected with .if()
vl.color().value('lightgray'),
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
return vl.markPoint()
.data(data)
//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']),
//add opacity here that changes
)
.width(800)
.height(400)
.render()

}
Insert cell
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

{
//add brush code
return vl.markCircle()
.data(Cars)
//register your brush
.encode(
vl.x().fieldQ(vl.repeat('column')),
vl.y().fieldQ(vl.repeat('row')),
vl.color().fieldO('Cylinders'), //add a condition here that changes color based on brush
vl.opacity(), //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 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)
//register ALL YOUR BRUSHES!
.encode(
vl.x().fieldQ(vl.repeat('column')),
vl.y().fieldQ(vl.repeat('row')),
vl.color().fieldO('Cylinders'), //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(), //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