为 D3 可视化创建平均 Y 线

Creating an average Y line for D3 visualization?

本文关键字:创建 D3 可视化      更新时间:2023-09-26

我正在尝试创建一个可视化,以帮助与我一起工作的学生了解拟合度量,例如 r 平方。 对于 R 平方,我希望在我的图上同时拥有回归线和 Y 均值线。 (我的最终目标是在点和/或线之间留出线条来表示学生可以单击以查看或看不到的 ESS、TSS 和 SSR)。

我的回归线工作正常,但是当我尝试添加我的平均线时,它最终会得到一个奇怪的起点和终点,并且在平均值 (4.4) 处明显不是一条平线。 我还在控制台中收到以下错误:

Error: Invalid value for <path> attribute d="M112,235.71428571428572L194,119.14285714285712L276,NaNL358,NaNL440,NaN"

对应于代码行:

.attr({

在我的avg.append("路径")中,我的avgline(至少,我认为这就是我在那里指定的原因):

svg.append("path")
                .datum(avgdataset)
                .attr({
                d: avgline,
                stroke: "green",
                "stroke-width": 1,
                fill: "none",
                "stroke-dasharray": "5,5",
                });

我试过如何无休止地指定 avgline(这种玩法通常最终不会产生任何行)。 我也尝试使用数据而不是数据,但无济于事。 我可能犯了一个非常基本的错误,因为我是javascript和D3的新手。

这是到目前为止我的所有代码,把它放在上下文中:

//Width and height
var w = 500;
var h = 300;
var padding = 30;
var dataset = [
  [1, 1],
  [2, 5],
  [3, 4],
  [4, 7],
  [5, 5]
];
//Create scale functions
var xScale = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) {
    return d[0];
  })])
  .range([padding, w - padding * 2]);
var yScale = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) {
    return d[1];
  })])
  .range([h - padding, padding]);
var rScale = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) {
    return d[1];
  })])
  .range([2, 5]);
//Define X axis
var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient("bottom")
  .ticks(5);
//Define Y axis
var yAxis = d3.svg.axis()
  .scale(yScale)
  .orient("left")
  .ticks(5);
//Create SVG element
var svg = d3.select("body")
  .append("svg")
  .attr("width", w)
  .attr("height", h);
//Create circles
svg.selectAll("circle")
  .data(dataset)
  .enter()
  .append("circle")
  .attr("cx", function(d) {
    return xScale(d[0]);
  })
  .attr("cy", function(d) {
    return yScale(d[1]);
  })
  .attr("r", 4)
  .append("svg:title")
  .text(function(d) {
    return d[0] + "," + d[1];
  });;
//average stuff
var sum = 0,
  average;
for (var i = 0; i < dataset.length; i++) {
  sum += dataset[i][1];
}
average = sum / dataset.length;
console.log(average);
var avgdataset = [
  [1, average],
  [2, average],
  [3, average],
  [4, average],
  [5, average]
];
console.log(avgdataset);
document.write(avgdataset);
//Create labels
/*svg.selectAll("text")
		   .data(dataset)
		   .enter()
		   .append("text")
		   .text(function(d) {
		   		return d[0] + "," + d[1];
		   })
		   .attr("x", function(d) {
		   		return xScale(d[0]);
		   })
		   .attr("y", function(d) {
		   		return yScale(d[1]);
		   })
		   .attr("font-family", "sans-serif")
		   .attr("font-size", "11px")
		   .attr("fill", "red");
		*/
//Create X axis
svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + (h - padding) + ")")
  .call(xAxis);
//Create Y axis
svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(" + padding + ",0)")
  .call(yAxis);
var lr = ss.linear_regression().data(dataset).line();
var forecast_x = 20
console.log(lr)
var lrline = d3.svg.line()
  .x(function(d, i) {
    return xScale(i);
  })
  .y(function(d, i) {
    return yScale(lr(i));
  });
svg.append("path")
  .datum(Array(dataset.length * forecast_x))
  .attr({
    d: lrline,
    stroke: "black",
    "stroke-width": 1,
    fill: "none",
    "stroke-dasharray": "5,5",
  });
var avgline = d3.svg.line()
  //.x(function(d, i) { return xScale(i); })
  //.y(function(d, i) { return yScale(avgdataset(i)); });
  .x(function(d, i) {
    return xScale(d[0]);
  })
  .y(function(d, i) {
    return yScale(d[i]);
  });
svg.append("path")
  .datum(avgdataset)
  .attr({
    d: avgline,
    stroke: "green",
    "stroke-width": 1,
    fill: "none",
    "stroke-dasharray": "5,5",
  });
//to get the m and b for the equation line
var mvalue = ss.linear_regression().data(dataset).m();
console.log(mvalue);
var bvalue = ss.linear_regression().data(dataset).b();
console.log(bvalue);
//equation written out
svg.append("text")
  .text("Y= " + mvalue + "x + " + bvalue)
  .attr("class", "text-label")
  .attr("x", 60)
  .attr("y", 30);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://raw.github.com/tmcw/simple-statistics/master/src/simple_statistics.js"></script>

与Kaiido类似,在我看来,avgline函数是问题所在。 您正在传入数组数组,xy没有访问数组的正确部分。 我使用过的大多数示例都传递了一个对象数组,如下所示:

var data = [ {x: 1, y: 4.4}, {x:2, y:4.4}, etc];

如果你构造一个这样的对象,你可以简单地将其传递给avgline然后然后可以使用如下内容优雅地访问数据的正确部分:

var avgline = d3.svg.line() //changed x and y function to reflect changed data
    .x(function(d, i) {
        return xScale(d.x);
    })
    .y(function(d, i) {
        return yScale(d.y);
    });

这样做有很多优点。 例如,您可以确保所有数据都对应于此结构,然后您只需要一个行构造函数而不是两个。

我想你几乎明白了,除了avgdataset不是一个函数而是一个数组。

只需更换

var avgline = d3.svg.line()
  //.x(function(d, i) { return xScale(i); })
  //.y(function(d, i) { return yScale(avgdataset(i)); });
  .x(function(d, i) {
    return xScale(d[0]);
  })
  .y(function(d, i) {
    return yScale(d[i]);
  });

var avgline = d3.svg.line()
    .x(function(d, i) { return xScale(i); })
    .y(function(d, i) { return yScale(avgdataset[i][1]); });

    //Width and height
    var w = 500;
    var h = 300;
    var padding = 30;
    var dataset = [[1, 1], [2, 5], [3, 4], [4, 7], [5, 5]];
    //Create scale functions
    var xScale = d3.scale.linear()
                         .domain([0, d3.max(dataset, function(d) { return d[0]; })])
                         .range([padding, w - padding * 2]);
    var yScale = d3.scale.linear()
                         .domain([0, d3.max(dataset, function(d) { return d[1]; })])
                         .range([h - padding, padding]);
    var rScale = d3.scale.linear()
                         .domain([0, d3.max(dataset, function(d) { return d[1]; })])
                         .range([2, 5]);
    //Define X axis
    var xAxis = d3.svg.axis()
                      .scale(xScale)
                      .orient("bottom")
                      .ticks(5);
    //Define Y axis
    var yAxis = d3.svg.axis()
                      .scale(yScale)
                      .orient("left")
                      .ticks(5);
    //Create SVG element
    var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
    //Create circles
    svg.selectAll("circle")
       .data(dataset)
       .enter()
       .append("circle")
       .attr("cx", function(d) {
            return xScale(d[0]);
       })
       .attr("cy", function(d) {
            return yScale(d[1]);
       })
       .attr("r", 4
       )
       .append("svg:title")
       .text(function(d){return d[0] + "," + d[1];});;
    //average stuff
    var sum = 0, average;
    for (var i = 0; i < dataset.length; i++) {
        sum += dataset[i][1];
    }
    average = sum / dataset.length;
    console.log(average);       
    var avgdataset = [[1, average], [2, average], [3, average], [4, average], [5, average]];
    console.log(avgdataset);
    document.write(avgdataset);
    //Create labels
    /*svg.selectAll("text")
       .data(dataset)
       .enter()
       .append("text")
       .text(function(d) {
            return d[0] + "," + d[1];
       })
       .attr("x", function(d) {
            return xScale(d[0]);
       })
       .attr("y", function(d) {
            return yScale(d[1]);
       })
       .attr("font-family", "sans-serif")
       .attr("font-size", "11px")
       .attr("fill", "red");
    */
    //Create X axis
    svg.append("g")
        .attr("class", "axis")
        .attr("transform", "translate(0," + (h - padding) + ")")
        .call(xAxis);
    //Create Y axis
    svg.append("g")
        .attr("class", "axis")
        .attr("transform", "translate(" + padding + ",0)")
        .call(yAxis);
    var lr = ss.linear_regression().data(dataset).line();
    var forecast_x = 20
    console.log(lr)
    var lrline = d3.svg.line()
        .x(function(d, i) { return xScale(i); })
        .y(function(d, i) { return yScale(lr(i)); });
    svg.append("path")
        .datum(Array(dataset.length*forecast_x))
        .attr({
        d: lrline,
        stroke: "black",
        "stroke-width": 1,
        fill: "none",
        "stroke-dasharray": "5,5",
        });
    var avgline = d3.svg.line()
        .x(function(d, i) { return xScale(i); })
        .y(function(d, i) { return yScale(avgdataset[i][1]); });
    svg.append("path")
        .datum(avgdataset)
        .attr({
        d: avgline,
        stroke: "green",
        "stroke-width": 1,
        fill: "none",
        "stroke-dasharray": "5,5",
        });
    //to get the m and b for the equation line
    var mvalue = ss.linear_regression().data(dataset).m();
    console.log(mvalue);
    var bvalue = ss.linear_regression().data(dataset).b();
    console.log(bvalue);
    //equation written out
    svg.append("text")
        .text("Y= " + mvalue + "x + " + bvalue)
        .attr("class", "text-label")
        .attr("x", 60)
        .attr("y", 30);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://raw.github.com/tmcw/simple-statistics/master/src/simple_statistics.js"></script>