score:1

Accepted answer

Missing points:

  1. Important: Ideally you should be using a d3 time scale as you're looking to plot a line based on months. But an ordinal scale would work too. I'm guessing you're looking for d3.scaleBand here.

Band scales are like ordinal scales except the output range is continuous and numeric.

Why not use d3.scaleOrdinal? Here's an example:

let lineData = [
    [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], 
    ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"]
  ]

        let xScale = d3.scaleOrdinal().range(lineData[2])
            .domain(lineData[0]);
            
    console.log('domain is: ' + xScale.domain());
    console.log('range is: ' + xScale.range());
<script src="https://d3js.org/d3.v4.min.js"></script>

You see how the repeated values get mapped onto the months which wouldn't help in your case and so the d3.scaleBand() (you could also play around with using d3.scalePoint. Read more about it in this answer.

  1. Using the above band scale, the domain you'd set would be the lineData[2] the following way:

    xScale = d3.scaleBand().domain(lineData[2])
    
  2. Transforms weren't being applied to the line and the circles. So I appended <g> containers to contain the lines and circles respectively and transformed these group elements. For example:

    var dots = lineChart.append('g')
        .attr("transform", "translate(20, 0)");
    

The solution below just answers this specific question on appending the first line to the chart. As you mentioned in the comments that you'd eventually work on adding the second element as another line, the path selection would change

let lineData = [
    [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], 
    ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"]
  ]
        // set dimensions of the line chart
        let width = 393,
            height = 285;


        // set the scales
        let yScale = d3.scaleLinear()    
            .domain([0, 52])
            .range([height - 40 , 0]);

        let xScale = d3.scaleBand().domain(lineData[2])
            .rangeRound([0, width - 40]);

        // create the line
        let line = d3.line()
            .x(function(d, i) { 
            	return xScale(lineData[2][i]) + xScale.bandwidth()/2; 
            })
            .y(function(d) { return yScale(d); });

        // create svg container
        let lineChart = d3.select('body')
            .append("svg")
            .attr("viewbox", "0 60 960 600")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("width", width)
            .attr("height", height)
            .attr("class", "line-chart")

				var lineG = lineChart.append('g')
        	.attr("transform", "translate(20, 0)");
          
        lineG.append("path")
            .data(lineData)
            .attr("d", line)
            .attr("fill", "none")
            .attr("stroke", "rebeccapurple")
            .attr("stroke-width", 2)

			var dots = lineChart.append('g')
        	.attr("transform", "translate(20, 0)");
        dots.selectAll("dot")
            .data(lineData[0])
            .enter()
            .append("circle").classed('dot', true)
            .attr("r", 3.5)
            .attr("cx", function(d, i) { return xScale(lineData[2][i]) + xScale.bandwidth()/2})
            .attr("cy", function(d) { return yScale(d)})
            .attr("fill", "rebeccapurple")

        lineChart.append("g")
            .attr("transform", "translate(20, 245)")
            .call(d3.axisBottom(xScale))

        lineChart.append("g")
            .attr("transform", "translate(20, 0)")
            .call(d3.axisLeft(yScale))
<script src="https://d3js.org/d3.v4.min.js"></script>

Suggestion: It's not a good practice to set a domain to a static value as you do for yScale [0,52]. Try using d3.extent or d3.min methods.

Hope this helps.

Edit: As you're trying to use d3.scalePoint, you're making a minor mistake in choosing the xScale value in the line function and for the circles. You'd need to choose categories[i] in the line fn as following:

.x(function(d, i) { 
     return xScale(categories[i]); 
 })

Updated snippet using d3.scalePoint

let data = {
    'New': [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    'Terminated': [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0]
  };
  let categories = ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"];
  
            let lineData = [data.New, data.Terminated, categories];

        
            // set dimensions of the line chart
            let width = 393,
                height = 285;

            // set the scales
            let yScale = d3.scaleLinear()    
                .domain([0, 60]).nice()
                .range([height - 40 , 0]);
        
            let xScale = d3.scalePoint()
                .domain(lineData[2].map(function(d, i) { return d; }))
                .range([0, width - 40]);
        
            // create the line
            let line = d3.line()
                .x(function(d, i) { 
                	return xScale(categories[i]); 
                })
                .y(function(d) { return yScale(d); });
        
            // create svg container
            let lineChart = d3.select('body')
                .append("svg")
                .attr("viewbox", "0 60 960 600")
                .attr("preserveAspectRatio", "xMinYMin meet")
                .attr("width", width)
                .attr("height", height)
                .attr("class", "line-chart")

				var lineG = lineChart.append('g')
        	.attr("transform", "translate(20, 0)");
            lineG.append("path")
                .datum(lineData)
                .attr("d", function(d) { return line(d[0]); })
                .attr("fill", "none")
                .attr("stroke", "rebeccapurple")
                .attr("stroke-width", 2)
          
            lineChart.selectAll("dot")
                .data(lineData[0])
                .enter()
                .append("circle")
                .attr("transform", "translate(20, 0)")
                .attr("r", 3.5)
                .attr("cx", function(d, i) { return xScale(categories[i])})
                .attr("cy", function(d) { return yScale(d)})
                .attr("fill", "rebeccapurple")
        
            lineChart.append("g")
                .attr("transform", "translate(20, 245)")
                .call(d3.axisBottom(xScale))

        
            lineChart.append("g")
                .attr("transform", "translate(20, 0)")
                .call(d3.axisLeft(yScale))
<script src="https://d3js.org/d3.v4.min.js"></script>

Hope this helps too.


Related Query