Published
Edited
Aug 22, 2019
1 fork
5 stars
Insert cell
Insert cell
Insert cell
bananas = d3.csv("https://gist.githubusercontent.com/kimnewzealand/368cd9661b740058b4309ee6a323ad5a/raw/89f9dd55a7a152dfc9ee11ecaf63118b1fab3a01/PBANSOPUSDM.csv")
Insert cell
md`We will reuse the Vega-lite library but this time setting the DATE field type as temporal to create a first plot.`
Insert cell
vegalite({
"title": "Global price of Bananas",
"data": {
"values": bananas
},
"mark": "line",
"encoding": {
"x": {"field": "DATE", "type": "temporal"},
"y": {"field": "PBANSOPUSDM",
"title": " USD per Metric Ton",
"type": "quantitative"}
}
})
Insert cell
md`Now let's create an annotated chart following this [example](https://observablehq.com/@vega/vega-lite-annotated-time-series) and this [Vega-lite tutorial](https://observablehq.com/@uwdata/interaction). This time we will import the library'@vega/vega-lite-api' as v1 so we don't wrap in vegalite().

> Alongside visual encodings and data transformations, Vega-Lite provides a selection abstraction for authoring interactions. `
Insert cell
annotatedplot = {
// select a point for which to provide details-on-demand
const hover = vl.selectSingle()
.encodings('x') // limit selection to x-axis value
.on('mouseover') // select on mouseover events
.nearest(true) // select data point nearest the cursor
.empty('none'); // empty selection includes no data points
// Encode the base line chart of banana prices.
// Vega-Lite uses encodings to bind data fields (with a given data type) to available encoding channels of a chosen mark type.
const line = vl.markLine().encode(
// Add the DATE field as a temporal (T) variable to the y axis
vl.x().fieldT('DATE'),
// Add the PBANSOPUSDM field as a quantitative (Q) variable to the y axis
vl.y().fieldQ('PBANSOPUSDM'),
);
// shared base for new layers, filtered to hover selection
const base = line.transform(vl.filter(hover));
// mark properties for text label layers
const label = {align: 'right', dx: 5, dy: -5};
const white = {stroke: 'white', strokeWidth: 2.5};
return vl.data(bananas)
.layer(
line,
// add a rule mark to serve as a guide line
vl.markRule({color: 'yellow'})
.transform(vl.filter(hover))
.encode(vl.x().fieldT('DATE')),
// add circle marks for selected time points, hide unselected points
line.markCircle()
.select(hover) // use as anchor points for selection
.encode(vl.opacity().if(hover, vl.value(1)).value(0)),
// add white stroked text to provide a legible background for labels
base.markText(label, white).encode(vl.text().fieldQ('PBANSOPUSDM')),
// add text labels for stock prices
base.markText(label).encode(vl.text().fieldQ('PBANSOPUSDM'))
)
.width(700)
.height(400)
.render();
}
Insert cell
md`**Day 61**

Now we will try out a bananas pplot using [plotly.js](https://plot.ly/javascript/) based on this [time series example](https://observablehq.com/@jashkenas/plotly-js).`


Insert cell

chart = {
const div = DOM.element('div');
const trace = {
type: "scatter",
mode: "lines",
name: 'PBANSOPUSDM',
x: unpack(bananas, 'DATE'),
y: unpack(bananas, 'PBANSOPUSDM'),
line: {color: '#17BECF'}
}
const data = [trace];
const layout = {
title : 'Global Banana Prices',
yaxis : {text:'PBANSOPUSDM'},
width: width
};
plotly.newPlot(div, data, layout);
return div;
}


Insert cell
function unpack(rows, key) {
return rows.map(function(row) {
return row[key];
});
}
Insert cell
Insert cell
index = d3.csv('https://gist.githubusercontent.com/kimnewzealand/c30029b53fafc39a0475f167605bf688/raw/c9bd8e15601efb81a19dbe2a8f097a6c0d1a7662/Prices_Weighted_average_prices_for_selected_food_items_Monthly_Jun_2006Mar_2019.csv')
Insert cell

indexchart = {
const div = DOM.element('div');
const Avos = {
type: "scatter",
mode: "lines",
name: "Avocados",
x: unpack(index, 'Month'),
// https://encycolorpedia.com/568203 Avocado / #568203 Hex Color Code
y: unpack(index, 'Avocados'),
line: {color: '#568203'}
}
const Kiwis = {
type: "scatter",
mode: "lines",
name: "Kiwifruit",
x: unpack(index, 'Month'),
y: unpack(index, 'Kiwifruit'),
// https://encycolorpedia.com/cebc97 Nerolac Kiwifruit - 4467 / #cebc97 Hex Color Code
line: {color: '#cebc97'}
}
const Kumaras = {
type: "scatter",
mode: "lines",
name: "Kumara",
x: unpack(index, 'Month'),
//https://encycolorpedia.com/e4622d Earthpaint 1-6-7 Sweet Potato / #e4622d Hex Color Code
y: unpack(index, 'Kumara'),
line: {color: '#e4622d'}
}
const Bananas = {
type: "scatter",
mode: "lines",
name: "Bananas",
x: unpack(index, 'Month'),
y: unpack(index, 'Bananas'),
// https://encycolorpedia.com/ffe135 Banana yellow / #ffe135 Hex Color Code
line: {color: '#ffe135'}
}
const data = [Avos,Kumaras,Bananas,Kiwis];
const layout = {
title : 'Comparing NZ Food Prices',
width: width
};
plotly.newPlot(div, data, layout);
return div;
}


Insert cell
Insert cell
yearsdata = d3.nest()
.key(d => d.Month.slice(0, 4))// instead of .getUTCFullYear() as Month is not in date format
.entries(index)
.reverse()
.filter(d =>d.key >2015 );

Insert cell
Insert cell
chartsample = {
const svg = d3.select(DOM.svg(width, height * yearsdata.length)) //Sett he height to 4 rows for 2016 to 2019
.style("font", "10px sans-serif")
.style("width", "100%")
.style("height", "auto");

const year = svg.selectAll("g")
.data(yearsdata)
.join("g")
// The SVG translation transform, which pushes the whole g group over and down by some amount. so the year groupings will all start at x=40 but the y coordinate will scale depending on the i from 0,1,2,3 for each object in the yearsdata array
.attr("transform", (d, i) => `translate(40,${height * i + cellSize * 1.5})`);
year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
// Add the key value ie the year as text atthe translation transform position above
.text(d => d.key);
return svg.node();
}
Insert cell
Insert cell
cellSize = 15
Insert cell
width = 900
Insert cell
height = (5/7)*cellSize*10
Insert cell
weekday = "weekday" // set one option for weekday instead of dropdown in example
Insert cell
Insert cell
chartsample2 = {

const svg = d3.select(DOM.svg(width, height * yearsdata.length)) //Sett he height to 4 rows for 2016 to 2019
.style("font", "10px sans-serif")
.style("width", "100%")
.style("height", "auto");

const year = svg.selectAll("g")
// Join yearsdata to g
.data(yearsdata)
.join("g")
// The SVG translation transform, which pushes the whole g group over and down by some amount. so the year groupings will all start at x=40 but the y coordinate will scale depending on the i from 0,1,2,3 for each object in the yearsdata array
.attr("transform", (d, i) => `translate(40,${height * i + cellSize * 1.5})`);
year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
// Add the key value ie the year as text at the translation transform position above
.text(d => d.key);
// Append the day text values at the translation transform position above
year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
// Create date data for weekdays
.data(( d3.range(3, 8) ).map(i => new Date(1995, 0, i)))
.join("text")
.attr("x", -5)
.attr("y", d => (countDay(d) + 0.5) * cellSize)
// the dy attribute i ndicates a shift along the y-axis on the position of an element or its content. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dy
.attr("dy", "0.31em")
.text(formatDay);

return svg.node();
}
Insert cell
formatDay = d => "SMTWTFS"[d.getUTCDay()] //The getUTCDay() method returns the day of the week in the specified date according to universal time, where 0 represents Sunday
Insert cell
countDay = d => (d.getUTCDay() + 6) % 7
Insert cell
Insert cell
chartsample3 = {

const svg = d3.select(DOM.svg(width, height2 * yearsdata.length)) //Sett he height to 4 rows for 2016 to 2019
.style("font", "10px sans-serif")
.style("width", "100%")
.style("height", "auto");

const year = svg.selectAll("g")
// Join yearsdata to g
.data(yearsdata)
.join("g")
// The SVG translation transform, which pushes the whole g group over and down by some amount. so the year groupings will all start at x=40 but the y coordinate will change depending on the i from 0,1,2,3 for each object in the yearsdata array
.attr("transform", (d, i) => `translate(40,${height2 * i + cellSize * 1.5})`);
year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
// Add the key value ie the year as text at the translation transform position above
.text(d => d.key);
// Append the month text values at the translation transform position above
year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
// Create date data for months, which will be used to extract the Month
.data(( d3.range(1, 13) ).map(i => new Date(1995, i, 0)))
.join("text")
.attr("x", d => (countMonth(d) + 1.5) * cellSize)
.attr("y", -5)
// the dy attribute indicates a shift along the y-axis on the position of an element or its content. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dy
.attr("dx", "0.31em")
.text(formatMonth);


return svg.node();
}
Insert cell
height2 = (5/7)*cellSize*5
Insert cell
formatMonth = d => "JFMAMJJASOND"[d.getUTCMonth()] //The getUTCMonth() returns the month of the specified date according to universal time, as a zero-based value (where zero indicates the first month of the year).
Insert cell
countMonth = d => (d.getUTCMonth() ) *2
Insert cell
Insert cell
Insert cell
Insert cell
yearsdata
Insert cell
chartsample5 = {

const svg = d3.select(DOM.svg(width, height2 * yearsdata.length+40)) //Sett he height to 4 rows for 2016 to 2019
.style("font", "10px sans-serif")
.style("width", "100%")
.style("height", "auto")
.attr("text-anchor", "middle");

const year = svg.selectAll("g")
// Join yearsdata to g
.data(yearsdata)
.join("g")
// The SVG translation transform, which pushes the whole g group over and down by some amount. so the year groupings will all start at x=40 but the y coordinate will change depending on the group index i from 0,1,2,3 for each object in the yearsdata array
.attr("transform", (d, i) => `translate(40,${height2 * i + cellSize * 1.5})`);
year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
// Add the key value ie the year as text at the translation transform position above
.text(d => d.key);
// Append the month text values at the translation transform position above
year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
// Create date data for months, which will be used to extract the Month
.data(( d3.range(1, 13) ).map(i => new Date(1995, i, 0)))
.join("text")
.attr("x", d => (countMonth(d) + 1.5) * cellSize)
.attr("y", -5)
// the dy attribute indicates a shift along the y-axis on the position of an element or its content. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dy
.attr("dx", "0.31em")
.text(formatMonth);

year.append("g")
.attr("class", "boxes")
.selectAll("rect")
.data(d => d.values)
.join("rect")
.attr("width", cellSize - 0.5)
.attr("height", cellSize - 0.5)
.attr("x", (d,i) => (i*2+ 1.5) * cellSize-5) // Use the index i for each month in each year grouping
.attr("y", 10)
.attr("fill", d => color(d.Avocados))
.attr("stroke","grey")
.append("title")
.text(d => `${"Date: "+d.Month+"\nPrice: $"+d.Avocados}`); // Add an SVG tooltip to the title to confirm the Month and Avo price
//Add Title
svg.append("text")
.attr("transform", `translate(${height2 * yearsdata.length+20},${height2 * yearsdata.length+20})`)
.style("text-anchor", "middle")
.style("font-weight", 700)
.text("New Zealand Monthly Weighted Average Retail Price of 1 kg Avocados (NZD$)");
return svg.node();
}
Insert cell
height2 * yearsdata.length+40
Insert cell
color = d3.scaleSequential(d3.interpolatePRGn).domain([30,0])
Insert cell
countMonthIndex = d => (d.length() ) *2
Insert cell
Insert cell
avos_preds=d3.csv('https://gist.githubusercontent.com/kimnewzealand/a8254de001aa27bafcc90d61aec9c28d/raw/715b7d7ca9d7b0ddddb5568a480c2923f6f328e5/avocados_pred_ts.csv')
Insert cell
avos_preds
Insert cell

avoschart = {
const div = DOM.element('div');
const Avos = {
type: "scatter",
mode: "lines",
name: "Avocados",
x: unpack(index, 'Month'),
// https://encycolorpedia.com/568203 Avocado / #568203 Hex Color Code
y: unpack(index, 'Avocados'),
line: {color: '#568203'}
}
const Avos_preds = {
type: "scatter",
mode: "lines",
name: "Forecast",
x: unpack(avos_preds, 'Month'),
// https://encycolorpedia.com/568203 Avocado / #568203 Hex Color Code
y: unpack(avos_preds, 'PredictedPrice'),
line: {color: 'darkblue'}
}
const data = [Avos,Avos_preds];
const layout = {
title : 'NZ Avocado Prices',
width: width
};
plotly.newPlot(div, data, layout);
return div;
}


Insert cell
Insert cell
index_w_preds =d3.csv('https://gist.githubusercontent.com/kimnewzealand/62926b8a236954eda2b0905794c4ceb9/raw/c38ed6c3faa6fb1bbe6f9273b39e524f2f38cfa0/avocados_w_pred_ts.csv')
Insert cell
yearsdatapreds = d3.nest()
.key(d => d.Month.slice(0, 4))// instead of .getUTCFullYear() as Month is not in date format
.entries(index_w_preds )
.reverse()
.filter(d =>d.key >2015 );
Insert cell
calendarview = {

const svg = d3.select(DOM.svg(width, height2 * yearsdatapreds.length+40)) //Sett he height to 4 rows for 2016 to 2019
.style("font", "10px sans-serif")
.style("width", "100%")
.style("height", "auto")
.attr("text-anchor", "middle");

const year = svg.selectAll("g")
// Join yearsdata to g
.data(yearsdatapreds)
.join("g")
// The SVG translation transform, which pushes the whole g group over and down by some amount. so the year groupings will all start at x=40 but the y coordinate will change depending on the group index i from 0,1,2,3 for each object in the yearsdata array
.attr("transform", (d, i) => `translate(40,${height2 * i + cellSize * 1.5})`);
year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
// Add the key value ie the year as text at the translation transform position above
.text(d => d.key);
// Append the month text values at the translation transform position above
year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
// Create date data for months, which will be used to extract the Month
.data(( d3.range(1, 13) ).map(i => new Date(1995, i, 0)))
.join("text")
.attr("x", d => (countMonth(d) + 1.5) * cellSize)
.attr("y", -5)
// the dy attribute indicates a shift along the y-axis on the position of an element or its content. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dy
.attr("dx", "0.31em")
.text(formatMonth);

year.append("g")
.attr("class", "boxes")
.selectAll("rect")
.data(d => d.values)
.join("rect")
.attr("width", cellSize - 0.5)
.attr("height", cellSize - 0.5)
.attr("x", (d,i) => (i*2+ 1.5) * cellSize-5) // Use the index i for each month in each year grouping
.attr("y", 10)
.attr("fill", d => color(d.Price))
.attr("stroke",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return "lightblue"}`;
} else {
return "darkgrey"}`;
}))
.attr("stroke-width",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return 5}`;
} else {
return 3}`;
}))
.append("title")
.text(function(d) {
if(d.Month > "2019-03") {
return `${"Date: "+d.Month+"\nForecast Price: $"+Math.round(d.Price)}`; // Add condition to add Forecast to label and round Price to whole number
} else {
return `${"Date: "+d.Month+"\nPrice: $"+ Math.round(d.Price)}`;
}
});
//Add Title
svg.append("text")
.attr("transform", `translate(${height2 * yearsdata.length+10},${height2 * yearsdata.length+70})`)
.style("text-anchor", "middle")
.style("font-weight", 700)
.text("NZ Monthly Weighted Average Retail and Forecast Price of 1 kg Avocados (NZD$)");
return svg.node();
}
Insert cell
calendarview1 = {

const svg = d3.select(DOM.svg(width, height2 * yearsdatapreds.length+40)) //Sett he height to 4 rows for 2016 to 2019
.style("font", "10px sans-serif")
.style("width", "100%")
.style("height", "auto")
.attr("text-anchor", "middle");


const year = svg.selectAll("g")
// Join yearsdata to g
.data(yearsdatapreds)
.join("g")
// The SVG translation transform, which pushes the whole g group over and down by some amount. so the year groupings will all start at x=40 but the y coordinate will change depending on the group index i from 0,1,2,3 for each object in the yearsdata array
.attr("transform", (d, i) => `translate(40,${height2 * i + cellSize * 1.5})`);
year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
// Add the key value ie the year as text at the translation transform position above
.text(d => d.key);
// Append the month text values at the translation transform position above
year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
// Create date data for months, which will be used to extract the Month
.data(( d3.range(1, 13) ).map(i => new Date(1995, i, 0)))
.join("text")
.attr("x", d => (countMonth(d) + 1.5) * cellSize)
.attr("y", -5)
// the dy attribute indicates a shift along the y-axis on the position of an element or its content. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dy
.attr("dx", "0.31em")
.text(formatMonth);

year.append("g")
.selectAll("rect")
.data(d => d.values)
.join("rect")
.attr("width", cellSize - 0.5)
.attr("height", cellSize - 0.5)
.attr("x", (d,i) => (i*2+ 1.5) * cellSize-5) // Use the index i for each month in each year grouping
.attr("y", 10)
.attr("fill", d => color(d.Price))
.attr("stroke",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return "lightblue"}`;
} else {
return "darkgrey"}`;
}))
.attr("stroke-width",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return 5}`;
} else {
return 3}`;
}))
.append("title")
.text(function(d) {
if(d.Month > "2019-03") {
return `${"Date: "+d.Month+"\nForecast Price: $"+Math.round(d.Price)}`; // Add condition to add Forecast to label and round Price to whole number
} else {
return `${"Date: "+d.Month+"\nPrice: $"+ Math.round(d.Price)}`
}});

//Add Title
svg.append("text")
.attr("transform", `translate(${height2 * yearsdata.length+10},${height2 * yearsdata.length+70})`)
.style("text-anchor", "middle")
.style("font-weight", 700)
.text("NZ Monthly Weighted Average Retail and Forecast Price of 1 kg Avocados (NZD$)");

const boxes = year.append("g")
.selectAll("rect")
.data(d => d.values)
.join("rect")
.attr("width", cellSize - 0.5)
.attr("height", cellSize - 0.5)
.attr("x", (d,i) => (i*2+ 1.5) * cellSize-5) // Use the index i for each month in each year grouping
.attr("y", 10)
.attr("fill", d => color(d.Price))
.attr("stroke",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return "lightblue"}`;
} else {
return "white"}`;
}))
.attr("stroke-width",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return 6}`;
} else {
return 3}`;
}))
;

yield svg.node();

await boxes.transition()
.duration(1000)
.attr("stroke",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return "lightblue"}`;
} else {
return "white"}`;
}))
.end();

while (true) {
yield svg.node();

await boxes.transition()
.duration(200)
.attr("stroke",(function(d) {
if(d.Month > "2019-03") { // Add condition to
return "yellow"}`;
} else {
return "white"}`;
}))
.end();
}


return svg.node();
}
Insert cell
md`## Imports`
Insert cell
d3 = require("d3@^5.9","d3-time")
Insert cell
vegalite = require("@observablehq/vega-lite@0.1")
Insert cell
import {vl} from '@vega/vega-lite-api'
Insert cell
plotly = require('https://cdn.plot.ly/plotly-latest.min.js')
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