Notes:
Sources:
Data: data.tsv
Code:
trend-line.ejs
<% demo = {category: "d3js", key: "trend-line", files: files}; %>
<%- partial("../mixins/_demo_title", demo) %>
<form>
<label><input type="radio" name="mode" value="zoom" checked> Zoom</label>
<label><input type="radio" name="mode" value="normal"> Normal</label>
</form>
<div id="chart" class="<%= demo.key %>-chart"></div>
<script type="text/javascript" src="/vendors/bower/d3/d3.min.js"></script>
<script type="text/javascript" src="/js/d3js/<%= demo.key %>.js"></script>
<%- partial("../mixins/_notes", demo) %>
<%- partial("../mixins/_sources_data", demo) %>
<%- partial("../mixins/_code", demo) %>
trend-line.coffee
ready = ->
animationTime = 2000
linearRegression = ((data)->
lr = {}
n = data.length
sum_x = 0; sum_y = 0; sum_xy = 0; sum_xx = 0; sum_yy = 0
data.forEach((d)->
sum_x += d.occurred.getTime()
sum_y += d.value
sum_xx += (d.occurred.getTime() * d.occurred.getTime())
sum_yy += (d.value * d.value)
sum_xy += (d.occurred.getTime() * d.value)
)
lr.slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x)
lr.intercept = (sum_y - lr.slope * sum_x) / n
lr.r2 = Math.pow((n * sum_xy - sum_x * sum_y) / \
Math.sqrt((n * sum_xx - sum_x * sum_x) * (n * sum_yy - sum_y * sum_y)), 2)
lr
)
getInterpolation = ((data, line)->
interpolate = d3.scale.quantile().domain([0,1]).range(d3.range(1, data.length + 1))
return ((t)->
interpolatedLine = data.slice(0, interpolate(t))
line(interpolatedLine)
)
)
d3.tsv('/data/d3js/trend-line/data.tsv', (error, data)->
margin = {top: 50, right: 50, bottom: 50, left: 50}
width = $('#chart').innerWidth() - margin.left - margin.right
height = 500 - margin.top - margin.bottom
time_format = d3.time.format('%Y-%m-%d').parse
data.forEach((d)->
d.occurred = time_format(d.occurred)
d.value = +d.value
)
renderGraph = ( ->
zoomed = d3.select('input[value="zoom"]')[0][0].checked
svg = d3.select('#chart').text('').append('svg').attr({width: width + margin.left + \
margin.right, height: height + margin.top + margin.bottom}).append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.right + ')')
x = d3.time.scale().range([0, width])
y = d3.scale.linear().range([height, 0])
xAxis = d3.svg.axis().scale(x).orient('bottom')
yAxis = d3.svg.axis().scale(y).orient('left')
line = d3.svg.line().x((d)-> x(d.occurred)).y((d)-> y(d.value))
x.domain(d3.extent(data, (d)-> d.occurred))
y.domain([
( -> if zoomed then return d3.min(data, (d)-> d.value) else return 0)()
d3.max(data, (d)-> d.value)
])
svg.append('g').attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')').call(xAxis)
svg.append('g').attr('class', 'y axis').call(yAxis)
svg.append('path').datum(data).transition()
.duration(animationTime).attrTween('d', -> getInterpolation(data, line))
.attr('class', 'line')
lr = linearRegression(data)
regression_line = d3.svg.line().x((d)-> x(d.occurred)).y((d)->
tmp = lr.intercept + (lr.slope * d.occurred)
y(tmp)
)
svg.append('path').datum(data).transition().delay(animationTime).duration(animationTime)
.attrTween('d', -> getInterpolation(data, regression_line)).attr('class', 'rline')
svg.append('text').attr('transform', 'translate(' + width * .7 + ',' + height * .7 + ')')
.style('opacity', 0).transition(1000).delay(animationTime * 2)
.text('Slope: ' + lr.slope.toExponential(3)).style('opacity', 1)
)
renderGraph()
d3.selectAll('input[name="mode"]').on('change', renderGraph)
)
$(document).ready(ready)
_trend-line.styl
.trend-line-chart
.axis
font: 10px sans-serif;
.axis path,line
fill: none;
stroke: #000;
shape-rendering: crispEdges;
.line
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
.rline
fill: none;
stroke: red;
stroke-dasharray: 5,5;
stroke-width: 1.5px;