在D3图表中查找阈值交点

时间:2014-08-24 07:21:05

标签: javascript math d3.js

我使用(优秀的)D3创建了一个简单的折线图。它具有表示为水平红线的阈值。我想要做的是计算值线与阈值线相交的位置,如下所示:

line chart with threshold

我可以非常接近,因为我可以访问数据数组,但我需要非常准确,并且能够在各种规模上工作。如果有人可以提供帮助,我将非常感激。非常感谢。

Fiddle is here

<div id="chart"></div>


path.line { stroke-width: 2; fill: none; }

var data = [{
    "time": "2014-02-25T19:00:00Z",
    "temp": "39.08"
}, {
    "time": "2014-02-25T21:00:00Z",
    "temp": "39.51"
}, {
    "time": "2014-02-25T23:00:00Z",
    "temp": "39.14"
}, {
    "time": "2014-02-26T01:00:00Z",
    "temp": "40.79"
}, {
    "time": "2014-02-26T03:00:00Z",
    "temp": "41.17"
}, {
    "time": "2014-02-26T05:00:00Z",
    "temp": "40.39"
}, {
    "time": "2014-02-26T07:00:00Z",
    "temp": "40.49"
}, {
    "time": "2014-02-26T09:00:00Z",
    "temp": "40.29"
}, {
    "time": "2014-02-26T11:00:00Z",
    "temp": "39.90"
}, {
    "time": "2014-02-26T13:00:00Z",
    "temp": "39.66"
}, {
    "time": "2014-02-26T15:00:00Z",
    "temp": "39.01"
}, {
    "time": "2014-02-26T17:00:00Z",
    "temp": "37.91"
}, {
    "time": "2014-02-26T19:00:00Z",
    "temp": "39.64"
}, {
    "time": "2014-02-26T21:00:00Z",
    "temp": "38.21"
}, {
    "time": "2014-02-26T23:00:00Z",
    "temp": "39.60"
}, {
    "time": "2014-02-27T01:00:00Z",
    "temp": "40.13"
}, {
    "time": "2014-02-27T03:00:00Z",
    "temp": "40.51"
}, {
    "time": "2014-02-27T05:00:00Z",
    "temp": "40.78"
}, {
    "time": "2014-02-27T07:00:00Z",
    "temp": "40.54"
}, {
    "time": "2014-02-27T09:00:00Z",
    "temp": "40.29"
}, {
    "time": "2014-02-27T11:00:00Z",
    "temp": "40.24"
}, {
    "time": "2014-02-27T13:00:00Z",
    "temp": "40.00"
}, {
    "time": "2014-02-27T15:00:00Z",
    "temp": "40.01"
}, {
    "time": "2014-02-27T17:00:00Z",
    "temp": "38.96"
}, {
    "time": "2014-02-27T19:00:00Z",
    "temp": "39.20"
}, {
    "time": "2014-02-27T21:00:00Z",
    "temp": "40.14"
}, {
    "time": "2014-02-27T23:00:00Z",
    "temp": "38.71"
}, {
    "time": "2014-02-28T01:00:00Z",
    "temp": "40.88"
}, {
    "time": "2014-02-28T03:00:00Z",
    "temp": "40.98"
}, {
    "time": "2014-02-28T05:00:00Z",
    "temp": "41.05"
}, {
    "time": "2014-02-28T07:00:00Z",
    "temp": "40.60"
}, {
    "time": "2014-02-28T09:00:00Z",
    "temp": "40.47"
}, {
    "time": "2014-02-28T11:00:00Z",
    "temp": "40.39"
}, {
    "time": "2014-02-28T13:00:00Z",
    "temp": "40.44"
}, {
    "time": "2014-02-28T15:00:00Z",
    "temp": "37.58"
}, {
    "time": "2014-02-28T17:00:00Z",
    "temp": "38.26"
}, {
    "time": "2014-02-28T19:00:00Z",
    "temp": "37.83"
}, {
    "time": "2014-02-28T21:00:00Z",
    "temp": "39.26"
}, {
    "time": "2014-02-28T23:00:00Z",
    "temp": "38.25"
}, {
    "time": "2014-03-01T01:00:00Z",
    "temp": "39.60"
}, {
    "time": "2014-03-01T03:00:00Z",
    "temp": "40.09"
}, {
    "time": "2014-03-01T05:00:00Z",
    "temp": "39.92"
}, {
    "time": "2014-03-01T07:00:00Z",
    "temp": "39.67"
}, {
    "time": "2014-03-01T09:00:00Z",
    "temp": "39.73"
}, {
    "time": "2014-03-01T11:00:00Z",
    "temp": "39.93"
}, {
    "time": "2014-03-01T13:00:00Z",
    "temp": "39.90"
}, {
    "time": "2014-03-01T15:00:00Z",
    "temp": "39.82"
}, {
    "time": "2014-03-01T17:00:00Z",
    "temp": "37.47"
}, {
    "time": "2014-03-01T19:00:00Z",
    "temp": "38.30"
}, {
    "time": "2014-03-01T21:00:00Z",
    "temp": "39.17"
}, {
    "time": "2014-03-01T23:00:00Z",
    "temp": "38.87"
}, {
    "time": "2014-03-02T01:00:00Z",
    "temp": "40.01"
}, {
    "time": "2014-03-02T03:00:00Z",
    "temp": "40.31"
}, {
    "time": "2014-03-02T05:00:00Z",
    "temp": "39.31"
}, {
    "time": "2014-03-02T07:00:00Z",
    "temp": "39.78"
}, {
    "time": "2014-03-02T09:00:00Z",
    "temp": "40.32"
}, {
    "time": "2014-03-02T11:00:00Z",
    "temp": "40.48"
}, {
    "time": "2014-03-02T13:00:00Z",
    "temp": "39.94"
}, {
    "time": "2014-03-02T15:00:00Z",
    "temp": "38.42"
}, {
    "time": "2014-03-02T17:00:00Z",
    "temp": "39.41"
}, {
    "time": "2014-03-02T19:00:00Z",
    "temp": "39.56"
}, {
    "time": "2014-03-02T21:00:00Z",
    "temp": "36.89"
}, {
    "time": "2014-03-02T23:00:00Z",
    "temp": "39.53"
}, {
    "time": "2014-03-03T01:00:00Z",
    "temp": "40.97"
}, {
    "time": "2014-03-03T03:00:00Z",
    "temp": "40.58"
}, {
    "time": "2014-03-03T05:00:00Z",
    "temp": "38.17"
}, {
    "time": "2014-03-03T07:00:00Z",
    "temp": "39.50"
}, {
    "time": "2014-03-03T09:00:00Z",
    "temp": "40.47"
}, {
    "time": "2014-03-03T11:00:00Z",
    "temp": "40.28"
}, {
    "time": "2014-03-03T13:00:00Z",
    "temp": "37.48"
}, {
    "time": "2014-03-03T15:00:00Z",
    "temp": "38.13"
}, {
    "time": "2014-03-03T17:00:00Z",
    "temp": "39.17"
}, {
    "time": "2014-03-03T19:00:00Z",
    "temp": "39.27"
}, {
    "time": "2014-03-03T21:00:00Z",
    "temp": "38.65"
}, {
    "time": "2014-03-03T23:00:00Z",
    "temp": "39.78"
}, {
    "time": "2014-03-04T01:00:00Z",
    "temp": "39.62"
}, {
    "time": "2014-03-04T03:00:00Z",
    "temp": "39.49"
}, {
    "time": "2014-03-04T05:00:00Z",
    "temp": "39.65"
}, {
    "time": "2014-03-04T07:00:00Z",
    "temp": "40.07"
}, {
    "time": "2014-03-04T09:00:00Z",
    "temp": "40.72"
}, {
    "time": "2014-03-04T11:00:00Z",
    "temp": "40.46"
}, {
    "time": "2014-03-04T13:00:00Z",
    "temp": "38.86"
}, {
    "time": "2014-03-04T15:00:00Z",
    "temp": "39.40"
}, {
    "time": "2014-03-04T17:00:00Z",
    "temp": "40.39"
}, {
    "time": "2014-03-04T19:00:00Z",
    "temp": "39.61"
}, {
    "time": "2014-03-04T21:00:00Z",
    "temp": "38.94"
}, {
    "time": "2014-03-04T23:00:00Z",
    "temp": "40.43"
}, {
    "time": "2014-03-05T01:00:00Z",
    "temp": "40.17"
}, {
    "time": "2014-03-05T03:00:00Z",
    "temp": "39.81"
}, {
    "time": "2014-03-05T05:00:00Z",
    "temp": "40.68"
}, {
    "time": "2014-03-05T07:00:00Z",
    "temp": "39.80"
}, {
    "time": "2014-03-05T09:00:00Z",
    "temp": "40.38"
}, {
    "time": "2014-03-05T11:00:00Z",
    "temp": "39.05"
}, {
    "time": "2014-03-05T13:00:00Z",
    "temp": "37.91"
}, {
    "time": "2014-03-05T15:00:00Z",
    "temp": "39.28"
}, {
    "time": "2014-03-05T17:00:00Z",
    "temp": "39.72"
}, {
    "time": "2014-03-05T19:00:00Z",
    "temp": "38.84"
}, {
    "time": "2014-03-05T21:00:00Z",
    "temp": "39.74"
}, {
    "time": "2014-03-05T23:00:00Z",
    "temp": "40.63"
}, {
    "time": "2014-03-06T01:00:00Z",
    "temp": "39.66"
}, {
    "time": "2014-03-06T03:00:00Z",
    "temp": "40.71"
}, {
    "time": "2014-03-06T05:00:00Z",
    "temp": "40.67"
}, {
    "time": "2014-03-06T07:00:00Z",
    "temp": "40.93"
}, {
    "time": "2014-03-06T09:00:00Z",
    "temp": "40.48"
}, {
    "time": "2014-03-06T11:00:00Z",
    "temp": "39.54"
}, {
    "time": "2014-03-06T13:00:00Z",
    "temp": "40.54"
}, {
    "time": "2014-03-06T15:00:00Z",
    "temp": "39.90"
}, {
    "time": "2014-03-06T17:00:00Z",
    "temp": "39.85"
}, {
    "time": "2014-03-06T19:00:00Z",
    "temp": "39.37"
}, {
    "time": "2014-03-06T21:00:00Z",
    "temp": "40.58"
}, {
    "time": "2014-03-06T23:00:00Z",
    "temp": "39.72"
}, {
    "time": "2014-03-07T01:00:00Z",
    "temp": "40.40"
}, {
    "time": "2014-03-07T03:00:00Z",
    "temp": "40.68"
}, {
    "time": "2014-03-07T05:00:00Z",
    "temp": "40.72"
}, {
    "time": "2014-03-07T07:00:00Z",
    "temp": "41.08"
}, {
    "time": "2014-03-07T09:00:00Z",
    "temp": "38.06"
}, {
    "time": "2014-03-07T11:00:00Z",
    "temp": "39.39"
}, {
    "time": "2014-03-07T13:00:00Z",
    "temp": "39.83"
}, {
    "time": "2014-03-07T15:00:00Z",
    "temp": "40.51"
}, {
    "time": "2014-03-07T17:00:00Z",
    "temp": "40.21"
}, {
    "time": "2014-03-07T19:00:00Z",
    "temp": "38.25"
}, {
    "time": "2014-03-07T21:00:00Z",
    "temp": "40.39"
}, {
    "time": "2014-03-07T23:00:00Z",
    "temp": "40.54"
}, {
    "time": "2014-03-08T01:00:00Z",
    "temp": "39.31"
}, {
    "time": "2014-03-08T03:00:00Z",
    "temp": "40.29"
}, {
    "time": "2014-03-08T05:00:00Z",
    "temp": "40.94"
}, {
    "time": "2014-03-08T07:00:00Z",
    "temp": "41.05"
}, {
    "time": "2014-03-08T09:00:00Z",
    "temp": "39.82"
}, {
    "time": "2014-03-08T11:00:00Z",
    "temp": "39.14"
}, {
    "time": "2014-03-08T13:00:00Z",
    "temp": "36.90"
}, {
    "time": "2014-03-08T15:00:00Z",
    "temp": "39.69"
}, {
    "time": "2014-03-08T17:00:00Z",
    "temp": "39.61"
}, {
    "time": "2014-03-08T19:00:00Z",
    "temp": "38.97"
}, {
    "time": "2014-03-08T21:00:00Z",
    "temp": "39.58"
}, {
    "time": "2014-03-08T23:00:00Z",
    "temp": "40.39"
}, {
    "time": "2014-03-09T01:00:00Z",
    "temp": "40.85"
}, {
    "time": "2014-03-09T03:00:00Z",
    "temp": "40.66"
}, {
    "time": "2014-03-09T05:00:00Z",
    "temp": "40.91"
}, {
    "time": "2014-03-09T07:00:00Z",
    "temp": "40.83"
}, {
    "time": "2014-03-09T09:00:00Z",
    "temp": "37.44"
}, {
    "time": "2014-03-09T11:00:00Z",
    "temp": "39.01"
}, {
    "time": "2014-03-09T13:00:00Z",
    "temp": "37.28"
}, {
    "time": "2014-03-09T15:00:00Z",
    "temp": "38.47"
}, {
    "time": "2014-03-09T17:00:00Z",
    "temp": "39.60"
}, {
    "time": "2014-03-09T19:00:00Z",
    "temp": "39.15"
}, {
    "time": "2014-03-09T21:00:00Z",
    "temp": "40.64"
}, {
    "time": "2014-03-09T23:00:00Z",
    "temp": "37.76"
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%SZ").parse;
data.forEach(function (d) { d.time = parseDate(d.time); });

var chart = d3.select("#chart");

var padding = 40,
    width = 950,
    height = 300,
    xTicks = 10,
    yTicks = 8;

var svg = chart.append("svg")
    .attr("width", width + padding * 2)
    .attr("height", height + padding * 2)
    .append("g")
    .attr("transform", "translate(" + padding + "," + padding + ")");

var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(xTicks);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(yTicks);

var valueLine = d3.svg.line()
    .interpolate("basis")
    .x(function (d) {
    return x(d.time);
})
    .y(function (d) {
    return y(d.temp);
});

x.domain(d3.extent(data, function (d) {
    return d.time;
}));
y.domain([33, 43]);

var mainLine = svg.append("path").attr({
    "class": "line",
    stroke: "steelblue",
    d: valueLine(data)
});

var threshold = 40.5;
svg.append("line").attr("stroke", "#F00").attr("x1", 0).attr("y1", y(threshold)).attr("x2", width).attr("y2", y(threshold)).attr("class", "line");

2 个答案:

答案 0 :(得分:0)

这个修正后的Fiddle在JS中进行了线性插值,并说明了为什么基础插值可能没有达到预期效果。我已经将一些样式移动到CSS中,并且还添加了圈子以便使用鼠标悬停文本进行观察,以便我们可以看到每个观察的详细信息。

使用第475行运行此操作,两者都已注释掉而不是:

// .interpolate("basis")

在数据集的末尾,截至05年3月5日和23:00的观察结果均大幅上升然后急剧下降。基础插值线不超过阈值,但观察结果如此。如果求解插值线,则无法识别这些交点。这可能是你想要的,但你的问题并不清楚。

末尾的交叉数组是我们得到交叉时间的地方。它遍历每个数据对,检查它们是否位于阈值的不同侧。如果它们是,那么就会有一个简单的线性插值计算来确定它穿过的时间,假设温度在观测值之间线性上升。

答案 1 :(得分:0)

安格斯的答案比我的好,但我发布这个以防有人发现它有用。另外,感谢Pablo的有用建议。

将此代码添加到最后:

var yLine = y(threshold);
console.log(yLine);
var node = mainLine.node();
var pathLength = node.getTotalLength();
for (var i = 0; i < pathLength; i++) {
    var point = node.getPointAtLength(i);
    console.log(point);
    if(Math.floor(point.y) == yLine) {
        svg.append("circle").attr({cx:point.x, cy:point.y, r:2});
    }
}

这就是CSS:

circle {
    fill: green;
}

或者打开此修订后的Fiddle