Consider the following:
page.html:
<!DOCTYPE html>
<!-- https://bl.ocks.org/mbostock/3886208 -->
<style>
.grid line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
pointer-events: none;
}
.monthNumbersLabel {
position:relative;top:15px;
}
/*Line Graph*/
.line {
fill: none;
stroke-width: 2px;
}
/*Points on line graph*/
.point {
r: 3.5;
}
svg:first-of-type {
margin-top: 30px;
}
</style>
<script src="https://d3js.org/d3.v4.js"></script>
<body>
<script>
var margin = {top: 50, right: 20, bottom: 50, left: 80},
width = 1400 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%m/%d/%Y");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleTime()
.range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal()
.range(['#4472C4','#ED7D31','#FFC000','#C00000',
'#E495A5']);
var xMonthAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat("%b")); // label every month
var xYearAxis = d3.axisBottom(x)
.ticks(d3.timeYear.every(1))
.tickFormat(d3.timeFormat("%Y")); // label every year
var yAxis = d3.axisLeft(y).tickFormat(d3.format(".0%")).tickSize(-width+margin.left+margin.right);
// load .csv file
d3.csv("test_data.csv" + '?' + Math.floor(Math.random() * 1000), // for avoiding caching: see https://stackoverflow.com/questions/13053096/avoid-data-caching-when-using-d3-text
function(error, data){
if (error) throw error;
data.forEach(function(d) {
d.Week_Ending = parseDate(d.Week_Ending);
d.Percent = +d.Percent;
});
data.sort(function(a, b) { return b.Week_Ending - a.Week_Ending; });
x.domain(d3.extent( data, function(d){ return d.Week_Ending; }) );
var max = x.domain()[1];
var min = x.domain()[0];
x.domain([min,max]);
y.domain([0, d3.max(data, function(d) { return d.Percent; })]).nice();
// x-axis
var monthAxis = g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xMonthAxis);
const firstDataYear = x.domain()[0];
if (firstDataYear.getMonth() == 11){ // When .getmonth() == 11, this shows two years overlapping with each other, making the label look ugly
const firstDataYearOffset = d3.timeDay.offset(firstDataYear, 1);
var tickValues = x.ticks().filter(function(d) { return !d.getMonth()});
if(tickValues.length && firstDataYearOffset.getFullYear() !== tickValues[1].getFullYear()) {
tickValues = [firstDataYearOffset].concat(tickValues);
} else {
tickValues = [firstDataYearOffset];
}
} else {
var tickValues = x.ticks().filter(function(d) { return !d.getMonth()});
if(tickValues.length && firstDataYear.getFullYear() !== tickValues[0].getFullYear()) {
tickValues = [firstDataYear].concat(tickValues);
} else {
tickValues = [firstDataYear];
}
}
xYearAxis.tickValues(tickValues);
var yearAxis = g.append("g")
.attr("class", "yearaxis axis")
.attr("transform", "translate(0," + (height + 25) + ")")
.call(xYearAxis);
var valueAxis = g.append("g")
.attr("class", "y axis grid")
.call(yAxis);
monthAxis.selectAll("g").select("text");
var dataByCategory = d3.nest()
.key(function(d) { return d.Site; })
.entries(data);
// https://stackoverflow.com/a/15030117/3625022
function flatten(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
var dataFrame = flatten(dataByCategory.map(function(a) {return a.values;}));
x.domain(d3.extent(data, function(d) { return d.Week_Ending; }));
y.domain([0, d3.max(data, function(d) { return d.Percent; })]);
var valueline = d3.line()
.x(function(d) { return x(d.Week_Ending); })
.y(function(d) { return y(d.Percent); });
g.selectAll('path')
.data(dataByCategory)
.enter()
.append("path")
.attr("class", function(d){
return "line " + d.key;
})
.attr("stroke", function(d){
return z(d.key);
})
.attr("d", function(d) {
return valueline(d.values);
});
g.selectAll('dot')
.data(dataFrame) // have to use a "flat" version to get the points
.enter()
.append("circle")
.attr("class", function(d){
return "point " + d.Site;
})
.attr("fill", function(d){
return z(d.Site);
})
.attr("cx", function (d, i){
return x(d.Week_Ending);
})
.attr("cy", function (d, i){
return y(d.Percent);
});
}
);
</script>
</body>
test_data.csv:
Site,Percent,Week_Ending
1,0.01999,8/27/2016
1,0.02564,9/24/2016
1,0.0287,10/22/2016
1,0.02751,11/19/2016
1,0.02624,12/17/2016
1,0.02826,1/14/2017
1,0.02698,2/11/2017
1,0.026,3/11/2017
1,0.031479367,4/8/2017
1,0.030840815,5/6/2017
1,0.03475,6/3/2017
1,0.03592,7/1/2017
1,0.03729,7/29/2017
1,0.041,8/26/2017
1,0.03856,9/23/2017
1,0.03886,10/21/2017
1,0.03723,11/18/2017
1,0.03513,12/16/2017
1,0.03172,1/13/2018
2,0.03819,8/27/2016
2,0.021,9/24/2016
2,0.02641,10/22/2016
2,0.01769,11/19/2016
2,0.01597,12/17/2016
2,0.01894,1/14/2017
2,0.01881,2/11/2017
2,0.01572,3/11/2017
2,0.014007761,4/8/2017
2,0.012521765,5/6/2017
2,0.01319,6/3/2017
2,0.0188,7/1/2017
2,0.01517,7/29/2017
2,0.02421,8/26/2017
2,0.01511,9/23/2017
2,0.01333,10/21/2017
2,0.0127,11/18/2017
2,0.01023,12/16/2017
2,0.01205,1/13/2018
3,0.01157,8/27/2016
3,0.01265,9/24/2016
3,0.00864,10/22/2016
3,0.01176,11/19/2016
3,0.01069,12/17/2016
3,0.01086,1/14/2017
3,0.01228,2/11/2017
3,0.01108,3/11/2017
3,0.00915436,4/8/2017
3,0.005970321,5/6/2017
3,0.01039,6/3/2017
3,0.01026,7/1/2017
3,0.01368,7/29/2017
3,0.01323,8/26/2017
3,0.00651,9/23/2017
3,0.00753,10/21/2017
3,0.00776,11/18/2017
3,0.00827,12/16/2017
3,0.0097,1/13/2018
4,0.0322,8/27/2016
4,0.02678,9/24/2016
4,0.03054,10/22/2016
4,0.04087,11/19/2016
4,0.02543,12/17/2016
4,0.04023,1/14/2017
4,0.02782,2/11/2017
4,0.03351,3/11/2017
4,0.036742785,4/8/2017
4,0.032985858,5/6/2017
4,0.03487,6/3/2017
4,0.03926,7/1/2017
4,0.03093,7/29/2017
4,0.03565,8/26/2017
4,0.03464,9/23/2017
4,0.03243,10/21/2017
4,0.0351,11/18/2017
4,0.03647,12/16/2017
4,0.03273,1/13/2018
5,0.02887,8/27/2016
5,0.04095,9/24/2016
5,0.04198,10/22/2016
5,0.05206,11/19/2016
5,0.0473,12/17/2016
5,0.04028,1/14/2017
5,0.05827,2/11/2017
5,0.05404,3/11/2017
5,0.055141464,4/8/2017
5,0.044402436,5/6/2017
5,0.04225,6/3/2017
5,0.03228,7/1/2017
5,0.03833,7/29/2017
5,0.03042,8/26/2017
5,0.03674,9/23/2017
5,0.03439,10/21/2017
5,0.0348,11/18/2017
5,0.03562,12/16/2017
5,0.03496,1/13/2018
Why is it that some paths are not being drawn, while others are?
I don't think it's because of missing data, at a quick glance. If this does happen to be because of missing data, I would at least prefer that points in adjacent months be connected. I've looked at http://bl.ocks.org/d3noob/38744a17f9c0141bcd04 as well as https://bocoup.com/blog/showing-missing-data-in-line-charts#solution2.