2017-05-03 2 views
1

Ich habe ein Streudiagramm, das eine Trendlinie/durchschnittliche Linie basierend auf den bereitgestellten Daten zeigt. Das einzige Problem ist, dass die Trendlinie in die y-Achse blutet (siehe Bild).Wie kann ich eine Trendlinie auf einem d3 Streudiagramm abschneiden?

Hier ist mein d3-Code, wie kann ich die Trendlinie "trimmen", um nur die geplottete Region des Diagramms zu passen?

scatterPlot.js

jQuery.sap.require("sap/ui/thirdparty/d3"); 
jQuery.sap.declare("pricingTool.ScatterPlot"); 

sap.ui.core.Element.extend("pricingTool.ScatterPlotItem", { metadata : { 
    properties : { 
     "quarter" : {type : "string", group : "Misc", defaultValue : null}, 
     "values" : {type : "object", group : "Misc", defaultValue : null} 
    } 
}});  

sap.ui.core.Control.extend("pricingTool.ScatterPlot", { 
    metadata : { 
     properties: { 
      "title": {type : "string", group : "Misc", defaultValue : "ScatterPlot Title"} 
     }, 
     aggregations : { 
      "items" : { type: "pricingTool.ScatterPlotItem", multiple : true, singularName : "item"} 
     }, 
     defaultAggregation : "items", 
     events: { 
      "onPress" : {}, 
      "onChange":{}  
     }   
    }, 

    init: function() { 
     //console.log("vizConcept.ScatterPlot.init()"); 
     this.sParentId = ""; 
    }, 

    createScatterPlot : function() { 
     //console.log("vizConcept.ScatterPlot.createScatterPlot()"); 
     var oScatterPlotLayout = new sap.m.VBox({alignItems:sap.m.FlexAlignItems.Center,justifyContent:sap.m.FlexJustifyContent.Center}); 
     var oScatterPlotFlexBox = new sap.m.FlexBox({height:"auto",alignItems:sap.m.FlexAlignItems.Center}); 
     /* ATTENTION: Important 
     * This is where the magic happens: we need a handle for our SVG to attach to. We can get this using .getIdForLabel() 
     * Check this in the 'Elements' section of the Chrome Devtools: 
     * By creating the layout and the Flexbox, we create elements specific for this control, and SAPUI5 takes care of 
     * ID naming. With this ID, we can append an SVG tag inside the FlexBox 
     */ 

     this.sParentId=oScatterPlotFlexBox.getIdForLabel(); 
     oScatterPlotLayout.addItem(oScatterPlotFlexBox); 

     return oScatterPlotLayout; 
    }, 


    /** 
    * The renderer render calls all the functions which are necessary to create the control, 
    * then it call the renderer of the vertical layout 
    * @param oRm {RenderManager} 
    * @param oControl {Control} 
    */ 
    renderer: function(oRm, oControl) { 
     var layout = oControl.createScatterPlot(); 

     oRm.write("<div"); 
     oRm.writeControlData(layout); // writes the Control ID and enables event handling - important! 
     oRm.writeClasses(); // there is no class to write, but this enables 
     // support for ColorBoxContainer.addStyleClass(...) 

     oRm.write(">"); 
     oRm.renderControl(layout); 
     oRm.addClass('verticalAlignment'); 
     oRm.write("</div>"); 
    }, 

    onAfterRendering: function(){ 
     //console.log("vizConcept.ScatterPlot.onAfterRendering()"); 
     //console.log(this.sParentId); 
     var cItems = this.getItems(); 
     var data = []; 
     for (var i=0;i<cItems.length;i++){ 
      var oEntry = {}; 
      for (var j in cItems[i].mProperties) { 
       oEntry[j]=cItems[i].mProperties[j]; 
      } 
      data.push(oEntry); 
     } 
     $("svg").last().remove(); 

     /* 
     * ATTENTION: See .createScatterPlot() 
     * Here we're picking up a handle to the "parent" FlexBox with the ID we got in .createScatterPlot() 
     * Now simply .append SVG elements as desired 
     * EVERYTHING BELOW THIS IS PURE D3.js 
     */ 

     var margin = { 
       top: 25, 
       right: 30, 
       bottom: 80, 
       left: 90 
      }, 
      width = 600 - margin.left - margin.right, 
       height = 300 - margin.top - margin.bottom; 

       var tableData = data[0].values; 
       var dates = []; 


       for(var i = 0; i<tableData.length; i++){ 
        dates[i] = new Date(tableData[i].date); 
        dates.sort(function(a,b) { 
         return a -b; 
        }) 
       } 

       var minDate = dates[0], 
        maxDate = dates[dates.length-1]; 

      //test// 
      var year = new Date(dates[0]); 
      minDate.setMonth(year.getMonth(), -2); 

      var year = new Date(dates[dates.length-1]); 
      console.log(year); 
      //maxDate.setMonth(year.getMonth(), 6); 
      console.log(maxDate); 

      //end test// 


      // Our X scale 
      //var x = d3.scale.linear() 
      var x = d3.time.scale() 
       .domain([minDate, maxDate]) 
       .range([0, width]); 

      // Our Y scale 
      var y = d3.scale.linear() 
       .range([height, 0]); 

      // Our color bands 
      var color = d3.scale.ordinal() 
       .range(["#000", "#004460", "#0070A0", "#008BC6", "#009DE0", "#45B5E5", "8CCDE9", "#DAEBF2"]); //"#00A6ED", 

      // Use our X scale to set a bottom axis 
      var xAxis = d3.svg.axis() 
       .scale(x) 
       .orient("bottom") 
       .ticks(8) 
       .tickFormat(d3.time.format("%b-%Y")); 

      // Same for our left axis 
      var yAxis = d3.svg.axis() 
       .scale(y) 
       .orient("left"); 

      var tip = d3.select("body").append("div") 
       .attr("class", "sctooltip") 
       .style("position", "absolute") 
       .style("text-align", "center") 
       .style("width", "80px") 
       .style("height", "42px") 
       .style("padding", "2px") 
       .style("font", "11px sans-serif") 
       .style("background", "#F0F0FF") 
       .style("border", "0px") 
       .style("border-radius", "8px") 
       .style("pointer-events", "none") 
       .style("opacity", 0); 

     var vis = d3.select("#" + this.sParentId); 
     var svg = vis.append("svg") 
     .attr("width", width + margin.left + margin.right) 
     .attr("height", height + margin.top + margin.bottom) 
     .style("background-color","white") 
     .style("font", "12px sans-serif") 
     .append("g") 
     .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

     x.domain([minDate, maxDate]); 

     // Our Y domain is from zero to our highest total 
     y.domain([0, d3.max(data, function (d) { 
      var max = d3.max(d.values, function (dd){ 
       return(+dd.price); 
      }) 
      return max; 
     })]); 
     var totalval = 0; 
     var totalval2 = 0; 
     var values = 0; 

     data.forEach(function (d) { 
      d.values.forEach(function (dd){ 
       values +=1; 
       totalval += +dd.date; 
       totalval2 += +dd.price; 
      }); 
     }); 

     var priceAverage = totalval2/values; 

     var average = totalval/totalval2; 

     var value = data[0].values[0].price; 

     var line_data = [ 
         {"x": 0, "y": y.domain()[0]}, 
         {"x": y.domain()[1]*average, "y": y.domain()[1]} 
         ]; 


     var avgline = d3.svg.line() 
      .x(function(d){ return x(d.x); }) 
      .y(function(d){ return y(d.y); }) 
      .interpolate("linear"); 

     svg.append("g") 
      .attr("class", "x axis") 
      .style("fill", "none") 
      .style("stroke", "grey") 
      .style("shape-rendering", "crispEdges") 
      .attr("transform", "translate(0," + height + ")") 
      .call(xAxis) 
      .selectAll("text") 
      .style("text-anchor", "end") 
      .attr("dx", "-.8em") 
      .attr("dy", ".15em") 
      .attr("transform", "rotate(-25)"); 

     svg.append("g") 
      .attr("class", "y-axis") 
      .style("fill", "none") 
      .style("stroke", "grey") 
      .style("shape-rendering", "crispEdges") 
      .call(yAxis); 

     //average line 
     svg.append("path") 
      .attr("class", "avgline") 
      .style("stroke", "#000") 
      .style("stroke-width", "1px") 
      .style("stroke-dasharray", ("4, 4")) 
      .attr("d", avgline(line_data)); 

     var plot = svg.selectAll(".values") //changed this from quarter 
      .data(data) 
      .enter().append("g"); 

     plot.selectAll("dot") 
      .data(function (d) { 
      return d.values; 
     }) 
      .enter().append("circle") 
      .attr("class", "dot") 
      .attr("r", 5) 
      .attr("cx", function (d){ 
       return x(d.date); 
      }) 
      .attr("cy", function (d) { 
      return y(d.price); 
     }) 
      .style("stroke", "#004460") 
      .style("fill", function (d) { 
      return color(d.name); 
     }) 
      .style("opacity", .9) 
      .style("visibility", function(d){ 
       if(+d.date != 0){ 
        return "visible"; 
       }else{ 
        return "hidden"; 
       } 
      }) 
      .style("pointer-events", "visible") 
      .on("mouseover", function(d){ 
        tip.transition() 
         .duration(200) 
         .style("opacity", .8); 
        tip.html(d.name + "<br/>" + d.quarter + "<br />" + "Avg. " +(+d.date/+d.price).toFixed(2)) 
         .style("left", (d3.event.pageX-40) + "px") 
         .style("top", (d3.event.pageY-50) + "px"); 

       }) 
       .on("mouseout", function(d){ 
        tip.transition() 
         .duration(500) 
         .style("opacity", 0); 
       });; 

     // var legend = svg.selectAll(".legend") 
     // .data(color.domain()) 
     // .enter().append("g") 
     // .attr("class", "legend") 
     // .attr("transform", function (d, i) { 
     // return "translate(0," + i * 16 + ")"; 
     // }); 

     // legend.append("rect") 
     // .attr("x", width - 12) 
     // .attr("width", 12) 
     // .attr("height", 12) 
     // .style("fill", color); 

     // legend.append("text") 
     // .attr("x", width - 24) 
     // .attr("y", 6) 
     // .attr("dy", ".35em") 
     // .style("text-anchor", "end") 
     // .style("font", "11px sans-serif") 
     // .text(function (d) { 
     // return d; 
     // }); 


     //y-axis label 
     svg.append("text") 
     .attr("transform", "rotate(-90)") 
     .attr("x", - (height/2)) 
     .attr("y", 10 - margin.left) 
     .attr("dy", "1em") 
     .style("text-anchor", "middle") 
     .style("font", "16px sans-serif") 
     .text("PPI Cost($)"); 

     //x-axis label 
     svg.append("text") 
      .attr("transform", "translate("+(width/2) +","+ (height +margin.top +50)+")") 
      .style("text-anchor", "middle") 
      .style("font", "16px sans-serif") 
      .text("Purchase Date"); 


     var avglabel = svg.append("g") 
      .attr("transform", "translate(" + (width-40) + ",140)"); 
     avglabel.append("text") 
      .style("text-anchor", "middle") 
      .text("Average: $" + priceAverage.toFixed(2)); 
    } 
}); 

how can I trim the trendline/avgline?

+0

Warum setzen Sie 'x: 0' in das erste Objekt von' line_data'? –

+0

Nur eine Schätzung !!! –

+0

Haben Sie nichts dagegen, ich sehe gerade, dass Sie tatsächlich die Skalierung 'x' in Ihrem Zeilengenerator haben. Das habe ich vermisst. Deshalb gebe ich der Waage gerne größere Namen, wie 'xScale'. –

Antwort

0

Versuchen Sie, die x-Werte zu Beginn und am Ende des Graphen Einstellung:

var line_data = [ 
    {x: x.domain()[0], y: average}, 
    {x: x.domain()[1], y: average} 
]; 

Alternativ können Sie den Pfad String direkt eingestellt ohne Verwendung eines Pfadgenerators:

svg.append("path") 
    .attr("class", "avgline") 
    .style("stroke", "#000") 
    .style("stroke-width", "1px") 
    .style("stroke-dasharray", ("4, 4")) 
    .attr("d", [ 
     "M", x.range()[0], y(average), 
     "H", x.range()[1] 
    ].join(' ')) 
Verwandte Themen