d3.js lines not showing for part of filtered then nested data (added plot images)

187 Views Asked by At

I have a data that is filtered by year then nested by group. The first few years there is one group, while the remaining years have seven to eight groups for which the lines are plotted. Along with lines I also plot points of actual data. here is the plot for the year 2007 that has only one group:
2007 line plot Notice the y-axis is missing and only the points are showing but no lines.

Here is the plot for the year 2010 that has eight groups:
2010 lines plot Notice the y-axis, all points and lines are showing.

Here is the associated code:

Defining line

  var line = d3.svg.line()
                    .x(function(d) { return LoanAmount_scale(d['amount_mean']); })
                    .y(function(d) { return Investor_scale(d['Investors_mean']); });

       var svg =  d3.select("body")
                     .append("svg")
                     .attr("width", width + margin.left + margin.right )
                     .attr("height", height + margin.top + margin.bottom);

        var plot =  svg.append("g")
                       .attr("class", "chart")
                        .attr("width" , width- margin.left)
                        .attr("height", height)
                        .attr("transform","translate ("+ margin.left + "," + margin.top + ")");

menu selection

     // setting stage for dropdown menu 
          // years
        var years = ['Select Year']; 
        var i= 2006;    
        for( i = 2006; i<= 2014; i++){
            years.push(i);
        }

         // selecting an option
        var select = d3.select('body')
                       .append('select')
                       .attr('class','select')
                       .attr('id','select_id')
                       .on('change',change);

          // available options on dropdown menu
        var option = select.selectAll('option')
                            .data(years)
                            .enter()
                            .append('option')
                            .text(function(d){ return d});

        // menu selection of year  
        function change(){
            var select_year = d3.select('select')
                             .property('value')
            // reset chart before updating year
            update(data,'Select Year');
            // update year 
            update(data,select_year);
        }

Update function filters year and nests by group, then plots lines and points

            function update(data, year){
             var Filtered = data.filter(function(d){ return d["LoanOrig_Yr"] == year});

             var nested_by_rating = d3.nest()
                              .key(function(d) { 
                                   return d['ProsperRating']
                              }).sortKeys(d3.ascending)
                               .entries(Filtered);

             // add lines for each rating    
            var lines =  plot.selectAll("path")
                              .data(nested_by_rating)

             // remove old lines
            lines.exit().remove();  


           plot.selectAll("circle")
                    .data(nested_by_rating)
                    .exit().remove();

            nested_by_rating.forEach( function(d,i){

               var prosperR = d.key
              // entering data
                 lines.enter()
                      .append("path")
                      .attr("class", "line")
                      .attr("d", line(d.values))
                      .attr("stroke", RatingColor(prosperR))
                       .attr("stroke-width", 3)
                       .attr("stroke-opacity",0.3)
                       .attr("fill","none");
               debugger;
                plot.selectAll("path.dot")
                    .data(d.values)
                    .enter().append("circle")
                    .attr("class", "dot")
                    .attr("cx", line.x())
                    .attr("cy", line.y())
                    .attr("r", 3.5)
                    .attr("fill","Black");
                debugger;
                // dynamic legend
                    svg.append("text")
                        .attr("x", 1300) 
                        .attr("y", 100 + i*20)
                        .attr("class", "legend")    // style the legend
                        .style("fill", function() { // Add the colours dynamically
                            return d.color = RatingColor(prosperR); })
                        .text(prosperR); 

Reset chart between year selection

            // reseting chart between years 
            if (year == 'Select Year'){ 
                gX.call(xAxis); 
                gY.call(yAxis);

                svg.append("text")
                        .attr("x", 1300) 
                        .attr("y", 100)
                        .attr("class", "legend")    // style the legend
                        .style("fill", "white")
                        .text(["NR"]);

                svg.selectAll("legend_text")
                            .data(["1.AA","2.A","3.B","4.C","5.D","6.E","7.HR","NR"])
                            .enter()
                            .append("text")
                            .attr("class","legend")
                            .attr("x", 1300) 
                            .attr("y", function(d,i){return 100 + i*20;
                                                    })
                            .attr("class", "legend")    // style the legend
                            .style("fill", "white")
                            .style("stroke","white")
                            .text(function(d,i){return d;});

            } \\ end if statement
       } \\end .forEach statement

When tracking data entry and exit using the browser inspector, I find the data in both cases move in and out as expected. Observe the points data are passed using line.x() and line.y(), so it is very strange that the line does not show in the year 2007 and other similar years with one group.

I would appreciate any help fixing this bug. Thank you.

1

There are 1 best solutions below

0
rioV8 On

You delete the lines by selecting all paths, and the axis line is a path.

Also the update of the legend was adding each time a lot of entries.

Black legend text is hidden/shown by setting fill to white or black.

The axis do not need update drawing for the Select Year, they stay the same for all graphs.

var data = d3.csv.parse(d3.select("#dataset").text());

data.forEach(function(d){
     d.LoanOrig_Yr = parseInt(+d.LoanOrig_Yr);
     d.amount_median = parseFloat(+d.amount_median);
     d.Investors_median = parseInt(+d.Investors_median);
});

function draw(data) {

    var margin = {top: 5, right: 100, bottom: 150, left: 80},
            width = 1460 - margin.left - margin.right,
            height = 600 - margin.top - margin.bottom;

    var margin2 = {top: 430, right: 200, bottom: 110, left:80},
          height2 = 600 - margin2.top - margin2.bottom;

    var margin3 = {top: 40, right:20, bottom: 80, left:180},
        width2 = 1300 - margin3.left - margin3.right;

    // display title
    d3.select("body")
               .append("h2")
               .text("Funding low risk loans is shared by more investors than high risk loans");

    // guiding  text
    d3.select("body")
      .append("p")
      .text("An investor may fund the whole loan or fractions of several loans with minimum"+
            " investment amount of $25."+
            " Prosper rating started in July 2009." +
           " Prosper Rating (ordered): AA (lowest risk), A, B, C, D, E, HR (high risk), NR (not rated)." +
            " Points on the line reflect loan amount average computed by "+
            " agregating loans over brackets of size $1500, starting at $1000." +
            " Data for 2014 is only for the first quarter."
            );

    var Investor_extent = d3.extent(data, function(d){
      return d.Investors_median;
      });

    var Investor_scale = d3.scale.linear()
      .domain(Investor_extent)
      .range([height,margin.top]);

    var LoanAmount_extent = d3.extent(data, function(d){
         return d.amount_median;
       });

    var LoanAmount_scale = d3.scale.linear()
      .range([margin.left, width])
      .domain(LoanAmount_extent);

    var ProsperRating = ["1.AA","2.A","3.B","4.C","5.D","6.E","7.HR","NR"];
    var colors = ['Brown','Red','Orange','Yellow','Green','Blue','Purple','Gray'];

    var RatingColor = d3.scale.ordinal()
          .domain(ProsperRating)
          .range(colors);

    var xAxis = d3.svg.axis()
                  .scale(LoanAmount_scale)
                  .orient("bottom")
                  .ticks(12);

    var yAxis = d3.svg.axis()
                  .scale(Investor_scale)
                  .orient("left")
                  .ticks(10);
    var line = d3.svg.line()
                .x(function(d) { return LoanAmount_scale(d.amount_median); })
                .y(function(d) { return Investor_scale(d.Investors_median); });
    var svg =  d3.select("body")
                 .append("svg")
                 .attr("width", width + margin.left + margin.right )
                 .attr("height", height + margin.top + margin.bottom);
    var plot =  svg.append("g")
                   .attr("class", "chart")
                    .attr("width" , width- margin.left)
                    .attr("height", height)
                    .attr("transform","translate ("+ margin.left + "," + margin.top + ")");

    var div = d3.select("body")
                .append("div")
                .attr("class","tooltip")
                .style("opacity",0);

    // setting stage for dropdown menu
    // years
    var years = ['Select Year', 2007, 2010];


    // selecting an option
    var select = d3.select('body')
                   .append('select')
                   .attr('class','select')
                   .attr('id','select_id')
                   .on('change',change);

    // available options on dropdown menu
    var option = select.selectAll('option')
                        .data(years)
                        .enter()
                        .append('option')
                        .text(function(d){ return d});

    // menu selection of year
    function change(){
        var select_year = d3.select('select')
                         .property('value')
        // reset chart before updating year
        update(data,'Select Year');
        // update year
        update(data,select_year);
    }

    // add x axis
    var gX = plot.append("g")
                .attr("class","x axis")
                .attr("transform","translate (0 " + height + ")")
                .call(xAxis);

    // add y axis
    var gY = plot.append("g")
                .attr("class", "y axis")
                .attr("transform" , "translate( " + margin.left + " ,0)")
                .call(yAxis);

    //add x axis label
    plot.append("text")
        .attr("class", "x label")
        .attr("text-anchor", "middle")
        .attr("x", width/2 )
        .attr("y", height + 40)
        .text("Loan Amount median (US dollars)");

    //add y axis label
    plot.append("text")
        .attr("class", "y label")
        .attr("text-anchor", "middle")
        .attr("x",0-(height/2))
        .attr("y", 20)
        .attr("dy", "1em")
        .attr("transform", "rotate(-90)")
        .text("Median number of investors who share funding one loan");

    // legend title
    svg.append("text")
       .attr("x",1300)
       .attr("y",70)
       .attr("class","legend_title")
       .style("fill","white")
       .text("Legend: Prosper Rating");

    svg.append("text")
       .attr("x",1300)
       .attr("y",30)
       .attr("class","legend_title")
       .style("fill","white")
       .text("point mouse at line to determine rating");

    function update(data, year){
        var Filtered = data.filter(function(d){ return d.LoanOrig_Yr == year});

        var nested_by_rating = d3.nest()
                          .key(function(d) { return d.ProsperRating })
                          .sortKeys(d3.ascending)
                          .entries(Filtered);

         // add lines for each rating
        var lines =  plot.selectAll(".line")
                         .data(nested_by_rating)

         // remove old lines
        lines.exit().remove();

        plot.selectAll("circle")
            .data(nested_by_rating)
            .exit().remove();

        nested_by_rating.forEach( function(d,i){

            var prosperR = d.key;
            // entering data
            lines.enter()
                .append("path")
                .attr("class", "line")
                .attr("d", line(d.values))
                .attr("stroke", RatingColor(prosperR))
                .attr("stroke-width", 3)
                .attr("stroke-opacity",0.3)
                .attr("fill","none");

            plot.selectAll("path.dot")
                .data(d.values)
                .enter().append("circle")
                .attr("class", "dot")
                .attr("cx", line.x())
                .attr("cy", line.y())
                .attr("r", 3.5)
                .attr("fill","Black");

            // dynamic legend
            svg.append("text")
                .attr("x", 1300)
                .attr("y", 100 + i*20)
                .attr("class", "legend")    // style the legend
                .style("fill", function() { return RatingColor(prosperR); }) // Add the colours dynamically
                .text(prosperR);
            // mouse hover tip tool
            lines.on("mouseover", function(d){
                    div.transition()
                       .duration(200)
                       .style("opacity", 0.9);
                    div.html("Prosper Rating : " + prosperR)
                       .style("left", (d3.event.pageX) + "px")
                       style("top", (d3.event.pageY - 2) + "px");
                    })
                .on("mouseout", function(d) {
                    div.transition()
                       .duration(500)
                       .style("opacity", 0);
                    })
        });

        // reseting chart between years
        if (year == 'Select Year'){
            svg.selectAll(".legend_title").style("fill","white");
            svg.selectAll(".legend").remove();
        } else {
            svg.selectAll(".legend_title").style("fill","black");
        }
    }
}

draw(data);