gpt4 book ai didi

javascript - 具有 Y 值跟踪的 D3.js 多系列图表

转载 作者:行者123 更新时间:2023-11-29 22:03:51 26 4
gpt4 key购买 nike

工作解决方案:目前,我正在研究样式并解决一些问题,这些问题与我创建由具有值跟踪的多数据系列组成的图表有关。我会尽快尝试为您提供工作代码示例,以便如果有人遇到与我相同或相似的问题,可以作为基础进行处理。目前,我使用的大部分技巧都在下面的评论中。


这将是我在 StackOverflow 上的第一个问题,我期待看到您对我的问题可能有什么答案。

最近我得到一个项目,在这个项目中我必须编写用于生成图表的 Javascript 代码,并且我可以同时从图表的每一行读取 Y 值。我是 D3 框架的新手,现在我能够读取 csv 数据,创建多系列图表并跟踪和读取 Y 值,但只有当我从单个数据系列创建图表时。我试图创建多个类似的函数来跟踪来自不同系列数据的数据,但它不起作用,在控制台中我看到 Y 显示为 null,据我所知。我正在使用 D3 网站上的示例来尝试学习它,现在代码将与这些示例非常相似。

稍后我需要用它做一些其他事情,但我认为在解决该问题后我将能够继续前进。会有这样的:

  • 通过代码减少 csv 中的数据,因为我需要删除标题信息
  • 更改图表的视觉样式并编辑轴比例

现在我有类似的东西。对不起,如果它有点乱,但我仍在学习和尝试很多不同的东西。我还添加了对我来说看起来像的屏幕截图以及我可以获得的一些控制台信息。我希望它能帮助你们看到我做错了什么以及我需要学习什么。此外,这不是我唯一的方法,要展示所有方法会太长。

编辑:我正在尝试一些不同的方法。在页面底部,我将展示我现在所做的事情。

EDIT2: 抱歉,如果我对我的目标不够精确。我想要做的是我希望能够在一个 X 轴值上同时读取绘制线的所有 Y 轴值(将是其中的 4 条)。我添加了第二个代码的屏幕截图,其中我只能读取一个 Y 轴值而无法读取另一个。

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
font: 10px sans-serif;
}

.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}

.x.axis path {
display: none;
}

.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}

.overlay {
fill: none;
pointer-events: all;
}

.focus circle {
fill: none;
stroke: steelblue;
}

</style>
<body>
<script src="d3.min.js"></script>
<script>

var margin = {top: 20, right: 80, bottom: 30, left: 200},
//-margin.left
width = 960 - margin.right,
height = 750 - margin.top - margin.bottom;

var parseDate = d3.time.format("%Y-%M-%d %H:%M").parse,
//dodane do sledzenia myszy i rysowania kuleczek
bisectDate = d3.bisector(function(d) { return d.date; }).left,
formatValue = d3.format(",.2f"),
formatCurrency = function(d) { return "$" + formatValue(d); };

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

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

var color = d3.scale.category10();

var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");

var yAxis = d3.svg.axis()
.scale(y)
.orient("left");

var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.transfers); });

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

d3.csv("data2.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));

data.forEach(function(d) {
d.date = parseDate(d.date);
});

var bitrates = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, transfers: +d[name]};
})
};
});

console.log(bitrates);

//data.sort(function(a, b) {
//return a.date - b.date;
//});

x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(bitrates, function(c) { return d3.min(c.values, function(v) { return v.transfers; }); }),
d3.max(bitrates, function(c) { return d3.max(c.values, function(v) { return v.transfers; }); })
]);

svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);

svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Transfers");

var chart = svg.selectAll(".chart")
.data(bitrates)
.enter().append("g")
.attr("class", "chart");

chart.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
//.attr("d", line);
.style("stroke", function(d) { return color(d.name); });

chart.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.transfers) + ")"; })
.attr("x", 3)
.attr("dy", ".35em");
//.text(function(d) { return d.name; });

//sledzenie myszy i rysowanie kuleczek
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");

focus.append("circle")
.attr("r", 4.5);

focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");

svg.append("g").append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);

function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")");
focus.select("text").text(formatCurrency(d.value));
}
});

</script>

在我看来是这样的: Generated chartCSV 数据文件如下所示:

date,Średni wych.:,Średni wch.:,Maks. wych.:,Maks. wch.:
2014-02-14 15:40,86518717581,101989990772,88304882317,108036052338
2014-02-14 16:00,85739038102,98312113056,87060053514,107154908503

我在试图了解问题所在时检查了一些过度信息:

[Object, Object, Object, Object]
0: Object
name: "Średni wych.:"
values: Array[504]
__proto__: Object
1: Object
2: Object
name: "Maks. wych.:"
values: Array[504]
[0 … 99]
[100 … 199]
100: Object
date: Thu Jan 16 2014 01:00:00 GMT+0100 (Środkowoeuropejski czas stand.)
transfers: 49305177944
__proto__: Object
101: Object
date: Thu Jan 16 2014 01:20:00 GMT+0100 (Środkowoeuropejski czas stand.)
transfers: 42169641572
__proto__: Object
102: Object
date: Thu Jan 16 2014 01:40:00 GMT+0100 (Środkowoeuropejski czas stand.)
transfers: 39400112189
__proto__: Object
103: Object
104: Object
105: Object
106: Object
107: Object
108: Object
109: Object
110: Object

如果您能提供任何帮助,我将不胜感激。我知道一些面向对象编程、HTML、CSS,但现在我并没有真正使用任何框架,学习它很有趣,但另一方面,在试图弄清楚我到底在做什么时可能真的很令人沮丧错了。

编辑

现在我尝试分别画两条线。它工作得很好,它可以让我以后更容易改变线条样式。现在我需要为每一行使用 mousemove 函数。然后将读取的值传递给一些变量并在某个框或其他东西中显示它们会相当容易。

这是我第二次尝试的代码(抱歉帖子变长了):

Screenshot for the second code is called Chart2.jpg

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
font: 10px sans-serif;
}

.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}

.x.axis path {
display: none;
}

.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}

.overlay {
fill: none;
pointer-events: all;
}

.focus circle {
fill: none;
stroke: steelblue;
}

</style>
<body>
<script src="d3.js"></script>
<script>

var margin = {top: 20, right: 50, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

var parseDate = d3.time.format("%d-%b-%y").parse,
bisectDate = d3.bisector(function(d) { return d.date; }).left,
formatValue = d3.format(",.2f"),
formatCurrency = function(d) { return "$" + formatValue(d); };

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");

var yAxis = d3.svg.axis()
.scale(y)
.orient("left");

var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });

var valueline2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.open); });

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

d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
d.open = +d['open data'];
});

data.sort(function(a, b) {
return a.date - b.date;
});

x.domain([data[0].date, data[data.length - 1].date]);
y.domain([0, d3.max(data, function(d) { return Math.max(d.close, d.open); })]);

svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);

svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");

svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);

svg.append("path")
.datum(data)
.attr("class", "line")
.style("stroke", "red")
.attr("d", valueline2);

var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");

focus.append("circle")
.attr("r", 4.5);

focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");

svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove1)
.on("mousemove", mousemove2);

function mousemove1() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus = focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
focus.select("text").text(formatCurrency(d.close));
}

function mousemove2() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus = focus.attr("transform", "translate(" + x(d.date) + "," + y(d.open) + ")");
focus.select("text").text(formatCurrency(d.open));
}
});

</script>

最佳答案

你已经有了所有的基本代码,你只需要让它同时运行。

第一个问题是您在同一元素上设置了两个不同的“mousemove”事件处理程序。 Unless you use namespaces to distinguish them, the second function just replaces the first ,所以你的第一个函数永远不会被调用。与其创建两个具有不同命名空间的事件处理程序,不如将所有事件处理代码放入一个函数中要容易得多。

第二个问题是你只有一个“焦点”元素,所以即使你确实运行了两个函数来设置两个不同的工具提示内容和位置,也只会显示第二个版本,因为它只是替换了第一个.

所以回顾一下,您需要:为每个路径创建一个工具提示/焦点元素,然后有一个事件处理函数,根据您的适当列设置所有值和位置数据文件。

为了保持代码简洁,并允许您快速从两行切换到四行或更多行,我建议您将焦点元素创建为数据连接选择,其中数据是一个数组列名:

var columnNames = d3.keys( data[0] ) //grab the key values from your first data row
//these are the same as your column names
.slice(1); //remove the first column name (`date`);

var focus = svg.selectAll("g")
.data(columnNames)
.enter().append("g") //create one <g> for each columnName
.attr("class", "focus")
.style("display", "none");

focus.append("circle") //add a circle to each
.attr("r", 4.5);

focus.append("text") //add a text field to each
.attr("x", 9)
.attr("dy", ".35em");

现在,当您在 mouseover/mouseout 事件中显示或隐藏焦点时,它会显示或隐藏所有工具提示:

svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);

但是您应该在 mousemove 函数中做什么?第一部分,找出最近的 x 值(日期)是相同的。但是你必须根据正确列中的值设置每个焦点工具提示的文本和位置。您可以这样做,因为每个焦点元素都有一个作为数据对象绑定(bind)到它的列名称,并且 d3 将该数据对象作为第一个参数传递给您传递给 d3 方法的任何函数:

function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
//d is now the data row for the date closest to the mouse position

focus.attr("transform", function(columnName){
return "translate(" + x( d.date ) + "," + y( d[ columnName ] ) + ")";
});
focus.select("text").text(function(columnName){
//because you didn't explictly set any data on the <text>
//elements, each one inherits the data from the focus <g>

return formatCurrency(d[ columnName ]);
});
}

顺便说一下,您可以使用相同的结构——使用列名作为数据,然后在函数中使用该名称来获取正确的数据值——用相同的代码创建所有行,而无需创建单独的数据数组。如果您在实现时遇到困难,请告诉我。

关于javascript - 具有 Y 值跟踪的 D3.js 多系列图表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22064083/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com