An implementation in D3 of Figure 4-25 "Scatterplot created in R and designed in Illustrator" in "Visualize This" by Nathan Yau.
xxxxxxxxxx
<body>
<style>
body {
font-family: Georgia;
font-size: 14px;
color: #333;
}
#container {
width: 500px;
height: 500px;
margin: 0 auto;
position: relative;
border: 1px solid #eee;
}
#inner-container {
position: absolute;
top: 20px;
left: 20px;
bottom: 20px;
right: 20px;
}
svg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
}
#header {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
}
#header-text {
width: 420px;
}
#max-subscribers {
position: absolute;
left: 26px;
top: 109px;
}
#subscribers-1st {
position: absolute;
font-weight: bold;
font-size: 12px;
left: 50px;
top: 195px;
}
#subscribers-31st {
position: absolute;
font-weight: bold;
font-size: 12px;
left: 415px;
top: 195px;
}
#gain-31st {
position: absolute;
font-size: 12px;
color: #999;
left: 415px;
top: 210px;
}
#reporting-error {
position: absolute;
font-size: 12px;
left: 230px;
top: 290px;
font-weight: bold;
}
#reporting-error-text {
position: absolute;
font-size: 11px;
width: 210px;
left: 230px;
top: 305px;
}
#footer-month {
position: absolute;
bottom: 10px;
left: 60px;
font-size: 12px;
}
#footer {
position: absolute;
bottom: 0;
right: 0;
font-size: 12px;
}
.axis.x path {
stroke-width: 1;
}
.axis.x line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.axis.x path {
display: none;
}
.axis.y path {
display: none;
}
.axis-rule.y {
stroke-width: 1;
stroke: #eee;
shape-rendering: crispEdges;
}
.axis-rule-0.y {
stroke-width: 2;
stroke: #333;
shape-rendering: crispEdges;
}
.subscribers-line {
stroke-width: 2;
stroke: #999;
shape-rendering: crispEdges;
}
</style>
<div id="container">
<div id="inner-container">
<div id="header">INCREASE IN SUBSCRIBERS</div>
<div id="header-text">In January 2010, the number of subscribers via RSS and email increase to 27,611, making it the tenth month in a row with at least a ten percent increase.</div>
<div id="max-subscribers">thousand subscribers</div>
<div id="subscribers-1st">25,047</div>
<div id="subscribers-31st">27,611</div>
<div id="gain-31st">(+10%)</div>
<div id="reporting-error">Reporting Error</div>
<div id="reporting-error-text">A source reported incorrect subscriber counts for January 12 and 13. Counts are more than 17,000 to low.</div>
<div id="footer-month">JANUARY 2010</div>
<div id="footer">Source: FeedBurner | Nathan Yau</div>
</div>
</div>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var width = 500,
height = 500,
margin = { top: 150, right: 20, bottom: 75, left: 20},
padding = { top: 0, right: 15, bottom: 0, left: 60 };
// Radius and color of the data points
var radius = 4,
color = '#8B001B';
// Width/height of the chart without margin and padding
var chartWidth = width - margin.left - margin.right - padding.left - padding.right;
var chartHeight = height - margin.top - margin.bottom - padding.top - padding.bottom;
var xValue = function(d) { return d.date; },
xScale = d3.time.scale().range([0, chartWidth]),
xMap = function(d) { return xScale(xValue(d)); },
xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickFormat(function(d) { return d.getDate(); })
.tickSize(8)
.ticks(function() { // To create a specific set of tickmarks
return [1,5,10,15,20,25,30]
.map(function(d) { return new Date(2010, 0, d); })
}),
xAxisMinor = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(31) // A minor tick for each of the Days
.tickSize(4)
.tickFormat(function(d) { return; })
var yValue = function(d) { return d.subscribers; },
yScale = d3.scale.linear().range([chartHeight, 0]),
yMap = function(d) { return yScale(yValue(d)); },
yAxis = d3.svg.axis()
.scale(yScale)
.orient('right')
.tickFormat(function(d) { return d / 1000; });
var svg = d3.select('#container').append('svg')
.attr('width', width)
.attr('height', height);
var chart = svg.append('g')
.attr('class', 'chart')
.attr('transform', 'translate(' + (margin.left + padding.left) + ', ' + (margin.top + padding.top) + ')');
var csvfile = 'flowingdata_subscribers.csv';
d3.csv(csvfile, function(d) {
return {
date: d3.time.format('%m-%d-%Y').parse(d.Date),
subscribers: +d['Subscribers']
};
}, function(error, data) {
// Set the domain based on the data
xScale.domain(d3.extent(data, xValue));
yScale.domain([0, d3.max(data, yValue)]).nice(); // nice to extent the domain to a round value
// Render the X axis (major and minor ticks as two axis)
chart.append('g')
.attr('class', 'axis x')
.attr('transform', 'translate(0, ' + chartHeight + ')')
.call(xAxis)
chart.append('g')
.attr('class', 'axis x')
.attr('transform', 'translate(0, ' + chartHeight + ')')
.call(xAxisMinor)
// Render the Y axis
chart.append('g')
.attr('transform', 'translate(' + (-padding.left - 5) + ', -12)')
.attr('class', 'axis y')
.call(yAxis);
// Render the Y axis rules
chart.selectAll('.axis-rule.y')
.data(yScale.ticks(7))
.enter()
.append('line')
.attr('class', function(d) {
if (d == 0) {
return 'axis-rule-0 y';
} else {
return 'axis-rule y'
}
})
.attr('x1', -padding.left)
.attr('x2', chartWidth + padding.right)
.attr('y1', function(d) { return yScale(d); })
.attr('y2', function(d) { return yScale(d); })
// Add a vertical line for the 1st and 31st data point
chart.selectAll('.description-line')
.data([data[0], data[data.length - 1]])
.enter()
.append('line')
.attr('class', 'subscribers-line')
.attr('x1', xMap)
.attr('x2', xMap)
.attr('y1', yMap)
.attr('y2', 64)
// Render the data
chart.selectAll('.point')
.data(data)
.enter()
.append('circle')
.attr('class', 'point')
.attr('cx', xMap)
.attr('cy', yMap)
.attr('r', radius)
.attr('fill', color);
});
</script>
</body>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js