Published
Edited
Oct 7, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {vl} from '@vega/vega-lite-api'
Insert cell
VegaLite = require("vega-embed@5")
Insert cell
import {fromColumns, printTable} from '@uwdata/data-utilities'
Insert cell
d3 = require('d3@7')
Insert cell
Insert cell
Insert cell
Insert cell
data = d3.csv("https://vega.github.io/vega-lite/data/seattle-weather.csv")
Insert cell
Insert cell
${data.length} rows, ${Object.keys(data[0]).length} columns! (This is a live data value by the way, see below)
Insert cell
Insert cell
printTable(data.slice(0, 10))
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
Insert cell
Insert cell
//We'll break down what all of this means shortly below, but for now, look at the difference between the declarative JSON specification, versus the vega-lite API

/*
This is an example of Vega-Lite using the Declarative JSON specification.
Notice that all the parameters to specify our chart are bundled into JSON and passed as an argument into VegaLite
*/
VegaLite({
data: {values: data}, //the array of data to encode in the chart
mark: "bar", //the mark to encode our data with
encoding: { //specifications for our encoding
y:{field: "weather", type: "nominal"},
x:{aggregate: "count", type: "quantitative"},
}
})
Insert cell
//This is an example of a chart made using the Vega-Lite API. Here, we use method CHAINING to assign the parameters of our visualization (more on that below)

//notice that we don't pass our chart specifications as name/value (aka key/value) pairs like above!

vl.markBar() //the mark that we want to encode our data with is now a function
.data(data) //pass our data to vega-lite as an array. you can also pass a URL as a string for vega-lite to parse
.encode( //the encodings of our visualization.
vl.y().fieldN('weather'),
vl.x().count('count')
)
.render()
Insert cell
Insert cell
Insert cell
Insert cell
//Try changing the chart with these parameters above


vl.markBar() //the mark that we want to encode our data with is now a function
.data(data) //pass our data to vega-lite as an array. you can also pass a URL as a string for vega-lite to parse
.encode( //the encodings of our visualization.
vl.y().fieldN('weather'),
vl.x().count('count')
)
.render()
Insert cell
Insert cell
Insert cell
//uncomment this and try it out

// vl.markBar()
// .data(data)
// .toObject()
Insert cell
Insert cell
Insert cell
Insert cell
vl.markBar().data(data).encode(vl.y().fieldN('weather'),vl.x().count('count')).render()
Insert cell
Insert cell
//specify the encoding type to the variable 'graph'
//graph = vl.markBar()
Insert cell
//let's chain data to the variable 'dataEncodedGraph'

Insert cell
//assign the encodings to 'graphEncodings'


Insert cell
//now render graphEncodings
Insert cell
Insert cell
product = ({
name: 'Pencil',
//properties is an object within an object. It is the value to the name (or key) called properties
properties: {
color: 'Red', //there are two key:pairs inside properties
texture: 'Smooth',
},
})
Insert cell
//get properties from product
product.properties
Insert cell
//get the color of the product
product.properties.color
Insert cell
Insert cell
product?.properties?.color


//this is equivalent to a ternary if statement, equal to

//product.prop ? product.prop.color : undefined
//or
//^ check if product.prop == true; if true, return product.prop.color, else return undefined
Insert cell
Insert cell
Insert cell
Insert cell
vl.markTick()
.data(data)
.encode(
//vl.x().fieldN('precipitation') //try using field(), or fieldQ()
//vl.x().fieldQ('precipitation')
vl.x().field('precipitation')
)
.render()
Insert cell
Insert cell
Insert cell
vl.markTick()
.data(data)
.encode(
vl.x().fieldQ('precipitation'),
//vl.y().fieldN('weather') //uncomment this line to see how we can do comparisons betweeen preciptation and weather
)
.render()
Insert cell
Insert cell
Insert cell
vl.markBar()
.data(data)
.encode(
vl.x().fieldN('weather'),
vl.y().count('count'),
vl.color().fieldQ('temp_max').aggregate('mean')
)
.render()
Insert cell
Insert cell
vl.markBar()
.data(data)
.encode(
vl.x().fieldN('weather'),
vl.y().count('count'),
vl.color().fieldQ('temp_max').aggregate('mean').scale({scheme: 'plasma'})
)
.render()
Insert cell
Insert cell
Insert cell
//try it here
vl.markBar()
.data(data)
.encode(
vl.x().fieldN('weather'),
vl.y().fieldQ('wind'),
vl.color().fieldQ('temp_max').aggregate('max'),
vl.color().fieldN('weather')
)
.render()
Insert cell
Insert cell
Insert cell
weatherColors = ["pink", "cornflowerblue", "paleturquoise", 'lightcoral', "gold"]
//Remember that hue is not ordered!
//How else might you color this
Insert cell
vl.markBar()
.data(data)
.encode(
vl.x().fieldQ("temp_max").bin(true), //call bin on this encoding
vl.y().count(),

//Let's uncomment the next line to see the breakdown by types of weather
//vl.color().fieldN('weather').scale({range: weatherColors})
//assign colors to show each of the different weather types. Instead of using a scheme, we define the range of output as the weatherColors array listed
)
.render()
Insert cell
Insert cell
vl.markBar()
.data(data)
.encode(
vl.x().fieldQ("temp_max").bin(true), //pass in {step: 2}. Try adjusting the step size to 2, 3, 4, 5, and 10.
vl.y().count(),

)
.render()
Insert cell
Insert cell
Insert cell
vl.markBar()
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("utcmonth"), //notice that we use fieldT
vl.y().count().stack("normalize").axis({ format: "%" }),
vl.color().fieldN('weather').scale({range: weatherColors})//add color like the same as before
)
.render()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vl.markLine()
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("month"), //notice that we use month instead of utcmonth because we don't want to use universal time
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors })
)
.render()
Insert cell
Insert cell
vl.markLine({opacity: 0.75,strokeWidth: 4}) //we change the strokeWidth and opacity of the lines
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("month"), //notice that we use fieldT, and month instead of utcmonth
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors })
)
.render()

Insert cell
Insert cell
Insert cell
Insert cell
//Uncomment the lines below

vl.markLine()//{point: true}) //add points to our lines
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("month"), //notice that we use fieldT, and month instead of utcmonth
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors }),
//vl.size().field('temp_max').aggregate('mean')
//try changing the size of the points with vl.size!

)
.render()

Insert cell
Insert cell
//To get VegaLite to do show missing values, we need to create them. We could create an empty row for every missing data & weather type, but for the sake of keeping our data simple we are simply creating a day for for every missing month and snow combination in this data.
dataImpute = {
let a = [...data]; // use spread syntax to create clone of our original data b/c our data is immutable
const m = new Date( 2012, 5, 1); // create a date for the first month that we don't see snow
for ( let i = 0; i < 7; i++) { // loop through and add(push) new rows with null values for missing snow months
a.push({
date: new Date(m.setMonth(m.getMonth()+i)),
precipitation: null,
temp_max: null,
temp_min:null,
wind: null,
weather: "snow",})
}
return a;
}
Insert cell
//Let's visualize this line chart again. Now we see that there are missing points in our line chart to reflect this missing null data

vl.markLine({point: true}) //add points to our lines
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("month"),
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors })
)
.render()
Insert cell
Insert cell
//this is an Area chart
vl.markArea() //change markLine to markArea
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("month"),
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors })
)
.render()
Insert cell
Insert cell
//this is an scatterplot chart
vl.markPoint()
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("monthdate"), //switch to monthdate, or play around with values such as year, day, etc.
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors })
)
.render()
Insert cell
Insert cell
vl.markPoint({tooltip: {"content": "data"}, clip: true}) //this is a tooltip that shows data encoded to each point. try hovering over the circles on the scatterplots
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("monthdate"), //switch to monthdate, or play around with values such as year, day, etc.
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors }),
vl.size().fieldQ('precipitation').scale({domain: [1, 50]})
)
.width(800) //width and height specify a fixed size for the chart
.height(400)
.render()
Insert cell
Insert cell
Insert cell
vl.markPoint({tooltip: {"content": "data"}, clip: true}) //this is a tooltip that shows data encoded to each point. try hovering over the circles on the scatterplots
.data(data)
.encode(
vl.x().fieldT("date").timeUnit("monthdate"), //switch to monthdate, or play around with values such as year, day, etc.
vl.y().fieldQ('temp_max').aggregate('mean'),
vl.color().fieldN("weather").scale({ range: weatherColors }),
vl.size().fieldQ('precipitation').scale({domain: [1, 50]})
)
.width(800) //width and height specify a fixed size for the chart
.height(400)
.render()
Insert cell
Insert cell
image4 = FileAttachment("image@4.png").image()

Insert cell
dataFiltered = d3.filter(data,function(d){return d.weather == 'rain' || d.weather == 'snow'})
Insert cell
//Add your code for Challenge 2 here. It should look like the chart above
//{tooltip: {"content": "data"}, clip: true} is a tooltip that shows data encoded to each point. try hovering over the circles on the scatterplots

vl.markPoint({tooltip: {"content": "data"}, clip: true}) //add on code below
.data(dataFiltered)
.encode(
vl.x().fieldT("date").timeUnit("monthdate"), //switch to monthdate, or play around with values such as year, day, etc.
vl.y().fieldQ('wind').aggregate('mean'),
//vl.shape().fieldN("weather"),
vl.color().fieldN("weather").scale({ range: weatherColors }),
vl.size().fieldQ('precipitation').scale({domain: [1, 50]})
)
.width(800) //width and height specify a fixed size for the chart
.height(400)
.render()
Insert cell
Insert cell
Insert cell
vl.markRect() //content of tooltip contains the encoded data
.data(data)
.encode(
vl.y().fieldO("date").timeUnit("month"),
vl.x().fieldO("date").timeUnit("date"),
vl.color().average("temp_max").scale({ scheme: "redyellowblue", reverse: true })
)
.render()
Insert cell
Insert cell
values = [
{"task": "A", "start": 1, "end": 3},
{"task": "B", "start": 3, "end": 8},
{"task": "C", "start": 8, "end": 10}
]
Insert cell
vl.markBar()
.data(values)
.encode(
vl.x().fieldQ('start'),
vl.y().fieldO('task'),
vl.x2().fieldQ('end'), //what if you comment this line out? //what happens if you use y2 instead
)
.render()
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