Based on the following two charts:
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
svg {
margin-left: auto; margin-right: auto;
display: block;
}
line,
rect {
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke-width: 1.5px;
}
circle.dot {
fill: #FFF !important;
stroke-width: 1.5px !important;
}
/** Grid lines BEGIN */
/** https://www.d3noob.org/2013/01/adding-grid-lines-to-d3js-graph.html */
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
/** Grid lines END */
</style>
</head>
<body>
<!-- <script src="https://d3js.org/d3.v3.min.js"></script> -->
<script src="d3.min.js?v=3.2.8"></script>
<script src="science.v1.min.js?v=1.9.1"></script>
<script type="text/javascript"charset="utf-8">
// Settings
var width = 440,
height = 400,
padding = 30;
var margin = {
'top': 30,
'right': 125,
'bottom': 30,
'left': 30
};
margin.hor = margin.left + margin.right;
margin.ver = margin.top + margin.bottom;
// Config
var dataset = "https://data.ndarville.com/danish-polls/data.csv", // "data.csv"
parseDate = d3.time.format("%Y/%m/%d").parse,
electionDate = "", // "09/14/2015"
yAxisTitle = "Votes (%)",
dateValue = "Date",
instituteValue = "Polling Firm",
showDots = true,
showAllParties = false,
recalculateYMax = false;
if (showAllParties === false) {
var parties = ["A"];
}
var displayInstitutes = (showAllParties === false && parties.length === 1) ? true : false;
var ignoreFilter = [
"Lead",
"Red (A+B+F+Ø)",
"Blue (V+O+I+C+K)"
]
ignoreFilter.push(dateValue),
ignoreFilter.push(instituteValue);
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var instituteColor = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(7)
.tickSubdivide(2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(function(d) { return d + "%"; });
var svg = d3.select("body").append("svg")
.attr({
"width": width + margin.left + margin.right,
"height": height + margin.top + margin.bottom
})
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv(dataset, function(error, data) {
// Make a domain array of all the headers except those in ignoreFilter
color.domain(
d3.keys(data[0])
.filter(function(key) {
if (ignoreFilter.indexOf(key) === -1) return key;
})
);
// Make a domain array of all polling institutes
instituteColor.domain(
d3.nest()
// Use the instituteValue column as key
.key(function(d) { return d[instituteValue]; })
// Enter data
.entries(data)
.map(function(d) { return d.key; })
);
data.forEach(function(d) {
d.date = parseDate(d[dateValue]);
});
//! Redo as d3.nest
var valueColumns =
color.domain() // Contains array of all parties
.map(function(name) {
return {
// Party name
name: name,
// Value array containing date, value, and institute
values: data.map(function(d) {
return {
date: d.date,
dataValue: parseFloat(d[name]),
institute: d[instituteValue]
};
})
};
});
// Compute x.domain()
if (electionDate === "") {
x.domain(d3.extent(data, function(d) { return d.date; }));
} else {
x.domain([
d3.min(data, function(d) { return d.date; }),
parseDate(electionDate)
]);
}
// Compute y.domain()
if (recalculateYMax === true && showAllParties === false && parties.length === 1) {
y.domain([
0, d3.max(data, function(d) { return d[parties[0]]; })
]);
} else {
y.domain([ //! Reconsider after implementing d3.nest
0, d3.max(valueColumns, function(c) { return d3.max(c.values, function(v) { return v.dataValue; }); })
]);
}
// Append x axis
svg.append("g")
.attr({
"class": "x axis",
"transform": "translate(0," + height + ")"
})
.call(xAxis);
// Append y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr({
"transform": "rotate(-90)",
"y": 6,
"dy": ".71em"
})
.style("text-anchor", "end")
.text(yAxisTitle);
// Gridlines
//
// https://www.d3noob.org/2013/01/adding-grid-lines-to-d3js-graph.html
svg.append("g")
.attr({
"class": "grid",
"transform": "translate(0," + height + ")"
})
.call(xAxis
.tickSize(-height, 0, 0)
.tickFormat("")
.ticks(d3.time.months, 1)
);
// Create graph container
var graph = svg.selectAll(".graph")
.data(valueColumns.filter(function(d) {
return showAllParties === true ? d.name : parties.indexOf(d.name) !== -1;
}))
.enter().append("g")
.attr("class", "graph");
// Connect the dots with line
if (showAllParties === false && parties.length === 1) {
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return d[0]; })
.y(function(d) { return d[1]; });
graph.append("path")
.datum(function() {
var loess = science.stats.loess();
loess.bandwidth(.2);
var xVal = data.map(function(d) { return x(d.date); }).reverse(),
yVal = data.map(function(d) { return y(+d[parties[0]]); }).reverse(),
loessData = loess(xVal, yVal);
var loessData = loess(xVal, yVal);
return d3.zip(xVal, loessData);
})
.attr("class", "line")
.attr("d", line)
.style("stroke", function(d) {
return (showAllParties === false && parties.length === 1) ? "#777" : color(d.name);
});
}
// Add dots
if (showDots === true) {
var dots = svg.selectAll(".dot")
.data(valueColumns.filter(function(d) {
return showAllParties === true ? d.name : parties.indexOf(d.name) !== -1;
}))
.enter()
var n = 0; //! Optimize
while (n < data.length) {
dots.append("circle")
.attr({
"class": "dot",
"r": 2,
"cx": function(d) {
return x(d.values[n].date);
},
"cy": function(d) {
return y(d.values[n].dataValue);
},
"stroke": function(d) {
return displayInstitutes === false ? color(d.name) : instituteColor(d.values[n].institute);
}});
n += 1;
}
}
// Add legend
var legendSize = 18,
legendSpacing = 20,
legendData = displayInstitutes === false ? color.domain() : instituteColor.domain();
var legend = svg.selectAll(".legend")
.data(legendData)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + 0 + "," + i * (legendSize + 2) + ")"; });
legend.append("rect")
.attr("x", width + legendSpacing)
.attr("width", legendSize)
.attr("height", legendSize)
.style("fill", function(d) { return (displayInstitutes === false) ? color(d): instituteColor(d); });
legend.append("text")
.attr("x", width + legendSpacing + legendSize + 6)
.attr("y", legendSize/2)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) { return d; });
});
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js