Published
Edited
Apr 14, 2020
1 fork
Insert cell
md`
# Animated Line Graph
Animation https://bl.ocks.org/mbostock/5649592
Google stock price. Data: Yahoo Finance
`
Insert cell
md`## Rationale.
\nFor this interactive design we implemented a line graph to represent stock data from a Google stock price data set. It seemed to us that the approach to this situation was visually relevant to locate the rises and falls of the stocks, depending on economic factors and the growth involved in supply and demand. Brushing was involved in the visualization for highlighting the most important key points in the graph, such as the growth of the Google stock at one point before April of this year and the falls that happened to be located twice, closing onto the 1000 mark.
\nTo further showcase the details of the rises and falls of the stock, a date vs number of trades relationship had to be invoked for measures to be accounted for. We considered utilizing an area graph from the Vega-Lite libraries to further locate more relationships and trends within the rises and falls, but we ended up making the graph as simple as it can get. We conducted some tools and research from the Vega-Lite libraries and D3 functionalities provided and discussed what was more feasible for our peers to interpret and we ultimately gathered our thought processes towards the mentality of a stock, which is a line graph with hovering capabilities.\n`
Insert cell
md`## Overview of Development Process.
\nAs mentioned in part of the rationale, we gathered some research and possible techniques to attempt this visualization in a holistic fashion. Ruth conducted the visualization after, and Hermont took notes on what was the best features of the visualization and what could be improved for further development and interaction down the road. A few other people we know from our classes lended their insights and suggestions to maximizing the goals of our visualization along the way. Ruth and Hermont conducted the report for the visualization after the whole process was finished to its full potential. \n
To give a rough estimate, it took Ruth and Hermont 8 hours to give the visualization its full capacity of information, including 3-4 hours of research behind the whole interaction implementations. The interactive parts of the visualization took the most time of the process out of all the steps involved.`
Insert cell
viewof dataSelector = radio( {
title: 'Select the data to be plotted',
options: [
{ label: 'Initial', value: 'data' },
{ label: 'Alternate', value: 'data1' },
],
value: 'data'
} );
Insert cell
dataSelector
Insert cell
bool_func= { var v
if(dataSelector == 'data')
{return data}
else{} return data1 }
Insert cell
chart = {
const svg = d3.select(DOM.svg(width, height))
.style("-webkit-tap-highlight-color", "transparent")
.style("overflow", "visible");

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line).call(transition);

const tooltip = svg.append("g");

svg.on("touchmove mousemove", function() {
const {date, value} = bisect(d3.mouse(this)[0]);

tooltip
.attr("transform", `translate(${x(date)},${y(value)})`)
.call(callout, `${value.toLocaleString(undefined, {style: "currency", currency: "USD"})}
${date.toLocaleString(undefined, {month: "short", day: "numeric", year: "numeric"})}`);
});

svg.on("touchend mouseleave", () => tooltip.call(callout, null));

return svg.node();
}
Insert cell
height = 500
Insert cell
margin = ({top:20, right:30, bottom:30, left:40})
Insert cell
x = d3.scaleTime()
.domain(d3.extent(bool_func, d => d.date))
.range([margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(bool_func, d=> d.value)]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 200).tickSizeOuter(0))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.y))
Insert cell
function transition(path) {
path.transition()
.duration(7500)
.attrTween("stroke-dasharray", tweenDash)
.on("end", () => { d3.select(this).call(transition); });
}
Insert cell
function tweenDash() {
const l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) { return i(t) };
}
Insert cell
callout = (g, value) => {
if (!value) return g.style("display", "none");

g
.style("display", null)
.style("pointer-events", "none")
.style("font", "10px sans-serif");

const path = g.selectAll("path")
.data([null])
.join("path")
.attr("fill", "white")
.attr("stroke", "black");

const text = g.selectAll("text")
.data([null])
.join("text")
.call(text => text
.selectAll("tspan")
.data((value + "").split(/\n/))
.join("tspan")
.attr("x", 0)
.attr("y", (d, i) => `${i * 1.1}em`)
.style("font-weight", (_, i) => i ? null : "bold")
.text(d => d));

const {x, y, width: w, height: h} = text.node().getBBox();

text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}
Insert cell
bisect = {
const bisect = d3.bisector(d => d.date).left;
return mx => {
const date = x.invert(mx);
const index = bisect(data, date, 1);
const a = data[index - 1];
const b = data[index];
return date - a.date > b.date - date ? b : a;
};
}
Insert cell
line = d3.line()
.defined(d => !isNaN(d.value))
.x(d => x(d.date))
.y(d => y(d.value))
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
function updateData () {
var data = dataSelector === 'data1' ? data: data1;
y.domain( d3.extent( dataSelector, datapoint => datapoint.value ) )
x.domain( d3.extent( dataSelector, datapoint => datapoint.date ) )
var path = d3.select( '.chart__line' )
.transition()
.duration( 500 )
.attr( 'd', line( data ) );
}
Insert cell
data = Object.assign((d3.csvParse(await FileAttachment("GOOG - GOOG.csv").text(), d3.autoType)).map(({date, close}) => ({date, value: close})), {y: "$ Close"})
Insert cell
data1=Object.assign((d3.csvParse(await FileAttachment("NQ=F - NQ=F (1).csv").text(), d3.autoType)).map(({date, close}) => ({date, value: close})), {y: "$ Close"})
Insert cell
d3 = require('d3@5')
Insert cell
import { radio } from '@jashkenas/inputs';
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more