我正在尝试创建一个可视化,以帮助与我一起工作的学生了解拟合度量,例如 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
函数是问题所在。 您正在传入数组数组,x
和y
没有访问数组的正确部分。 我使用过的大多数示例都传递了一个对象数组,如下所示:
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>