Public
Edited
Mar 21, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//Chart 1: Compare maximum temperature levels to wind speed
vl
.markCircle()
.data(Weather)
.encode(
vl.x().fieldQ("temp_max"),
vl.y().fieldQ("wind"),
vl.color().fieldN("weather")
)
.render()
Insert cell
//Chart 2: Compare precipitation levels to wind speed
vl.markCircle()
.data(Weather)
.encode(
vl.x().fieldQ("precipitation"),
vl.y().fieldQ("wind"),
vl.color().fieldN("weather")
)
.render()
Insert cell
Insert cell
{
//assign both charts to a variable
const tempChart = vl.markCircle().data(Weather).encode(
vl.x().fieldQ("temp_max"),
vl.y().fieldQ("wind"),
vl.color().fieldN("weather") //.scale({ scheme: "accent" })
);

//assign both charts to a variable
const precipChart = vl.markCircle().data(Weather).encode(
vl.x().fieldQ("precipitation"),
vl.y().fieldQ("wind"),
vl.color().fieldN("weather"),
vl.color().fieldN("weather") //.scale({ scheme: "tableau10" })
);

//call hconcat and render
return (
vl.hconcat(tempChart, precipChart) //try using vconcat instead
//.resolve({ scale: { color: "independent" } })
.render()
);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
//Let's add our code here to replicate the image above
//We will be nesting our hconcat inside of the vconcat

vl.vconcat(vl.hconcat(tempChart, precipChart), windTimeline)
.config({ concat: { spacing: 0 } })
.render()
//}
Insert cell
Insert cell
Insert cell
vl.markBar()
.data(Weather)
.encode(
vl.x().fieldQ('wind').bin(true),
vl.y().aggregate('count'),
vl.color().fieldN('weather') //try replacing color with column. Now each column of the chart is encoded with the categories in weather
)
.width(150)
.height(150)
.render()

Insert cell
Insert cell
vl.markBar()
.data(Weather)
.encode(
vl.x().fieldQ("wind").bin(true),
vl.y().aggregate("count"),
vl.column().fieldN("weather") //each column of the chart is encoded with the categories in weather
)
.width(150)
.height(150)
//.resolve({ scale: { color: "independent" } })
.render()
Insert cell
Insert cell
vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT('date').timeUnit('utcmonth'),
vl.y().fieldQ('wind').aggregate('mean'),
vl.column().fieldN('weather') //each column of the chart is encoded with the categories in weather
)
.width(150)
.height(150)
.render()

Insert cell
vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT("date").timeUnit("utcmonth"),
vl.y().fieldQ("wind").aggregate("mean"),
vl.facet().fieldN("weather").columns(3) //we can use the general facet function and then specify that we want two columns, however, we might get a graphical error for an odd # of distinct fields
)
.width(150)
.height(150)
.render()
Insert cell
Insert cell
vl.markArea()
.data(Weather)
.encode(
vl.x().fieldT('date').timeUnit('utcmonth'),
vl.y().fieldQ('wind').aggregate('mean'),
vl.color().fieldN('weather') //each column of the chart is encoded with the categories in weather
)
.width(150)
.height(150)
.render()

Insert cell
Insert cell
vl.markCircle()
.data(Weather)
.encode(
vl.x().fieldQ(vl.repeat('repeat')), //instead of assigning a field, we call repeat row and column
vl.y().count(),
vl.color().fieldN('weather')
)
.repeat(['temp_max', 'wind', 'precipitation']) //note how we call .repeat outside of the encoding definitions inside .markCircle(). We call repeat here to allow us to fully replicate a data set in each view
.columns(3)
.render()
Insert cell
Insert cell
vl.markCircle()
.data(Weather)
.encode(
vl.x().fieldQ(vl.repeat('row')), //instead of assigning a field, we call repeat row and column
vl.y().fieldQ(vl.repeat('column')),
vl.color().fieldN('weather')
)
.repeat({
row: ['temp_max', 'wind', 'precipitation'], //repeat for each row
column: ['precipitation', 'wind', 'temp_max']
})
.render()
Insert cell
Insert cell
Insert cell
Insert cell
vl
.data(Weather)
.encode(vl.x().fieldO("date").timeUnit("utcmonth"), vl.y().fieldQ("temp_max"))
.layer(
//in layer, we didn't define our data again, but rather just added the encoding layers
vl
.markCircle() //layer 1 adds circles
.encode(vl.color().fieldN("weather").scale({ scheme: weatherColors })),

vl.markErrorband({ extent: "stdev" }), //layer 2 adds an Error band

vl
.markLine() //layer 3 shows a line corresponding to temp_max
.encode(vl.y().mean("temp_max"))
)
.render()
Insert cell
Insert cell
Insert cell
{
const precipit = vl
.markLine()
.data(Weather)
.encode(
vl.x().fieldT("date").timeUnit("monthyear"),
vl.y().average("precipitation")
)
.width(700);

const tempMax = vl
.markLine()
.data(Weather)
.encode(
vl.x().fieldT("date").timeUnit("monthyear"),
vl.y().average("temp_max")
)
.width(700);

//call hconcat and render
return vl.vconcat(tempMax, precipit) //try using vconcat instead
.render();
}
Insert cell
Insert cell
{

const minTemp = vl.markCircle()
.data(Weather).encode(
vl.x().fieldQ("temp_min"),
vl.y().fieldQ("wind"),
vl.color().fieldN("weather")
);
const maxTemp = vl.markCircle()
.data(Weather).encode(
vl.x().fieldQ("temp_max"),
vl.y().fieldQ("wind"),
vl.color().fieldN("weather")
);

const barChart = vl.markBar()
.data(Weather)
.encode(
vl.x().fieldQ('wind').bin(true),
vl.y().aggregate('count'),
vl.color().fieldN('weather') //try replacing color with column. Now each column of the chart is encoded with the categories in weather
)
const timeLine = vl.markLine()
.data(Weather).encode(
vl.x().fieldT("date").timeUnit('month'),
vl.y().fieldQ("wind").aggregate('mean'),
vl.color().fieldN("weather"),
)
.width(700)

//call concats and render
return (
vl.vconcat(vl.hconcat(minTemp, maxTemp, barChart), timeLine)
.config({ concat: { spacing: 0 } })
.render()
);
}
Insert cell
Insert cell
Insert cell
Stocks
Insert cell
Insert cell
vl.markLine()
.data(Stocks)
.encode(
vl.x().fieldT('date').timeUnit('monthyear'),
vl.y().fieldQ('price'),
vl.column().fieldN('symbol') //each column of the chart is encoded with the categories in weather
)
.width(150)
.height(150)
.render()

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vl.markCircle()
.data(penguins)
.encode(
vl.x().fieldQ(vl.repeat("row")).scale({ zero: false }),
vl.y().fieldQ(vl.repeat("column")).scale({ zero: false }),
vl.color().fieldN("species").scale({ scheme: "set2" })
)
.width(Math.max(width / 4 - 80, 100))
.height(Math.max(width / 4 - 80, 100))
.repeat({
row: ["culmen_length_mm", "culmen_depth_mm", "flipper_length_mm", "body_mass_g"],
column: ["culmen_length_mm", "culmen_depth_mm", "flipper_length_mm", "body_mass_g"]
})
.render()
Insert cell
Insert cell
Insert cell
Stocks
Insert cell
Insert cell
newDate = [{date: "Jan 1 2005"}]
Insert cell
vl.data(Stocks)
.encode(
vl.x().fieldT('date').timeUnit('year'),
vl.y().fieldQ('price').aggregate('mean')
)
.layer( //in layer, we didn't define our data again, but rather just added the encoding layers
vl.markLine({opacity: .8}) //layer 1 adds circles
.encode(
vl.color().fieldN("symbol").scale({scheme: 'set1'})
),

// vl.markRule({color: 'red', size: 3})
// .data(newDate)
// .encode(
// //vl.x({"datum": 2005})
// vl.y().fieldT('date')

// ),

vl.markBar({opacity: .4, color: 'grey'}) //layer 2
.encode(
vl.y().mean("price")
)
)
.render()
Insert cell
Insert cell
Insert cell
Insert cell
iced_Starbucks = Starbucks_Drink.filter(d => (d.Type == 'Iced Coffee') || (d.Type == 'Iced non-coffee'))
Insert cell
Insert cell
//Chart 1a: A scatterplot matrix comparing the calories, fat, and carbs in iced coffee drinks, versus iced non-coffee drinks.

vl.markCircle()
.data(Starbucks_Drink)
.transform(
vl.filter({"or": [{"field": "Type", "equal": 'Iced Coffee'}, {"field": "Type", "equal": 'Iced non-coffee'}]})
//this filter function takes two predicates: if Type == Iced Coffee, and if Type == Iced non-coffee. The 'or' is used to combine the two predicates together in an array of objects
//this is a complex and messy filter function, a better option would be to map a new array for Iced Coffee and Iced non-coffee by using the filtered dataset called "iced_Starbucks" above
)
.encode(
vl.x().fieldQ(vl.repeat("row")).scale({ zero: false }),
vl.y().fieldQ(vl.repeat("column")).scale({ zero: false }),
vl.color().fieldN("Type").scale({ scheme: "Tableau10" })
)
.width(Math.max(width / 4 - 80, 100))
.height(Math.max(width / 4 - 80, 100))
.repeat({
row: ["Calories", "Fat", "Carb"],
column: ['Calories', 'Fat', 'Carb']
})
.render()
Insert cell
//Chart 1b: A scatterplot matrix comparing the calories, fat, and carbs in iced coffee drinks, versus iced non-coffee drinks.

vl.markCircle()
.data(iced_Starbucks) //this is the same visualization, but the arguments are simpler now that we're not using filtering inside of VL
.encode(
vl.x().fieldQ(vl.repeat("row")).scale({ zero: false }),
vl.y().fieldQ(vl.repeat("column")).scale({ zero: false }),
vl.color().fieldN("Type").scale({ scheme: "Tableau10" })
)
.width(Math.max(width / 4 - 80, 100))
.height(Math.max(width / 4 - 80, 100))
.repeat({
row: ["Calories", "Fat", "Carb"],
column: ['Calories', 'Fat', 'Carb']
})
.render()


Insert cell
//Chart 2: A 2D histogram scatterplot matrix comparing the calories, fat, and carbs in all types of drinks

vl.markSquare()
.data(Starbucks_Drink)
.encode(
vl.x().fieldQ(vl.repeat("row")).bin({maxbins: 10}).scale({ zero: false }),
vl.y().fieldQ(vl.repeat("column")).bin({maxbins: 10}).scale({ zero: false }),
vl.size().aggregate('count')
)
.width(Math.max(width / 4 - 80, 100))
.height(Math.max(width / 4 - 80, 100))
.repeat({
row: ["Calories", "Fat", "Carb"],
column: ['Calories', 'Fat', 'Carb']
})
.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
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