D3.js具有Y值跟踪的多系列图表 [英] D3.js Multi-Series Chart with Y value tracking

查看:280
本文介绍了D3.js具有Y值跟踪的多系列图表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

工作解决方案:现在,我正在开发样式,并解决一些关于我的问题,创建包含带有值跟踪的多数据系列的图表。我会尽快,我可以给你一个工作代码soo的样本,如果任何人会遇到相同或类似的问题,我会做,作为一个基地。现在我使用的大部分提示都在下面的注释中。

Working solution: Right now I'm working on styling and on solving some of the issues regarding my problem with creating chart consisting of multiple-data series with values tracking. I will try as soon as I can to give you a sample of working code soo if anybody will came across the same or similar problem as I did, could work on it as a base. For now most of the tips which I used are in the comments below.

这将是我对StackOverflow的第一个问题,我期待看到你可能对我的问题的答案。

This will be my first question on StackOverflow and I'm looking forward to seeing what answers you might have to my problem.

最近我有项目,我必须写Javascript代码生成图表,其中我将能够同时从图表的每一行读取Y值。我非常新的D3框架,现在我能够读取csv数据,创建多系列图表和跟踪和读取Y值,但只有当我从单个数据系列创建图表。我试图创建多个类似的功能,将跟踪不同数据系列的数据,但它不会工作,在控制台,我看到Y显示为null从我可以理解。我使用D3网站的例子来尝试学习它,现在代码将非常类似于这些例子。

Recently I got project in which I have to write Javascript code for generating charts and in which I would be able to read Y values from every line of the chart at the same time. I very new to D3 framework and by now I'm able to read csv data, create multi-series chart and track and read Y value but only when I'm creating chart from a single data series. I was trying to create multiple similar functions that would track data from diferent series of data but it won't work and in console i see that the Y is showing as null from what I can understand. I was using examples from D3 website to try to learn it and for now code will be very similar to those examples.

后来我需要做一些其他的事情但我认为,在解决这个问题后,我将能够继续前进。将有如下:

Later on I would need to do some other things with it but i think that after solving that problem i will be able to keep going. There will be like:


  • 通过代码减少csv中的数据,因为我需要删除标题信息

  • 更改图表的视觉样式和编辑轴缩放

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

For now I have something like that. Sorry if it is a little bit messy but I'm still learning and trying a lot of different things. I have added also screenshot from what it looks like for me and some console information that i could get. I hope it will help you guys see what I'm doing wrong and what I would need to learn. Also this is not my only approach and it would be too long to show them all.

编辑:方法。在页面底部,我会显示我现在做了什么。

I'm trying a little bit different approach. On the bottom of the page i will show what I have done by now.

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

Sorry if i was't precise enough about my goal. What I'm trying to do with this is I want to be able to read all Y-axis values of drawn lines (it will be 4 of them) at the same time on one X-axis value. I have added screenshot of the second code in which I'm able to read only one Y-axis value and can't read the over one.

<!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>

看起来像这样:
生成的图表
CSV数据文件如下所示:

It looks like for me like this: Generated chart CSV data file looks like this:

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

我试图理解错误时所检查的一些信息:

Some over information that I inspected while trying to understand what is wrong:

[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,但现在我不是真的工作与任何框架,它是有趣的学习,但在过去的手可能真的令人沮丧,试图找出我在做什么错误。

I would really appreciate any help from you. I know some Object Oriented Programming, HTML, CSS, but for now I wasn't really working with any framework and it is fun to learn but on the over hand could be really frustrating while trying to figure out what the heck I'm doing wrong.

EDIT

现在我试图分开绘制两行。它工作的伟大,它可以使我更容易,以后更改线条样式。现在我需要使用mousemove函数的每一行。

Now I'm trying drawing two lines separately. It is working great and it could make it easier for me to change lines style later on. Now i need to use mousemove function for each of those lines. Then it would be fairly easy to just pass readed values to some variables and show them in some box or something.

这是我第二次尝试的代码(对不起,对不起, post getting long):

This is the code for the my second try(sorry for post getting long):

第二个代码的屏幕截图叫做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事件处理程序。 除非使用命名空间来区分它们,否则第二个函数只会替换第一个 ,所以你的第一个函数永远不会被调用。

The first problem is that you're setting two different "mousemove" event handlers on the same elements. Unless you use namespaces to distinguish them, the second function just replaces the first, so your first function is never getting called. Rather than creating two event handlers with different namespaces, it's much easier to just put all your event-handling code into one function.

第二个问题是你只有一个事件处理代码,而不是创建两个具有不同命名空间的事件处理程序。 focus元素,因此即使您运行了两个函数来设置两个不同的工具提示内容和位置,也只显示第二个版本,因为它只替换第一个版本。

The second problem is that you only have one "focus" element, and so even if you did run both functions to set the two different tooltip contents and position, only the second version would be displayed, because it just replaces the first.

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

So to recap, you need to: create a tooltip/focus element for each path, and then have one event-handling function that sets all the values and positions according to the appropriate column of your data file.

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

To keep the code concise, and to allow you to quickly switch from two lines to four or more, I'm going to suggest that you create the focus elements as a data-joined selection, where the data is an array of column names:

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事件中显示或隐藏焦点时,会显示或隐藏全部工具提示:

Now, when you show or hide focus in the mouseover/mouseout events, it will show or hide all the tooltips:

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值(日期)是相同的。但是,然后您必须根据正确的列中的值设置每个焦点工具提示的文本和位置。您可以这样做,因为每个焦点元素都有一个列名称作为数据对象绑定到它,并且d3会将该数据对象作为第一个参数传递给任何传递给d3方法的函数:

But what should you do in your mousemove function? The first part, figuring out the nearest x-value (date) is the same. But then you have to set the text and position of each focus tooltip according to the values in the correct column. You can do this because each focus element has a column name bound to it as a data object, and d3 will pass that data object as the first parameter to any function you pass in to a d3 method:

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 ]);
   });
}

顺便说一句,你可以使用同样的结构 - 作为数据,然后在函数中使用该名称来获取正确的数据值 - 使用相同的代码创建所有行,而无需创建单独的数据数组。如果您在执行时遇到问题,请告诉我们。

By the way, you can use this same structure -- use the column names as data, and then use that name in a function to grab the correct data value -- to create all your lines with the same code, without having to create separate data arrays. Let me know if you have difficulty implementing that.

这篇关于D3.js具有Y值跟踪的多系列图表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆