如何使用从csv文件中过滤的数据创建多系列折线图? [英] How to create a multiseries line chart using data filtered from a csv file?
问题描述
我有一个data.csv
文件是通过这种方式完成的:
CODE,YEAR,MODALITY,VALUE
AB,2000,first,15
AB,2000,second,
AB,2000,third,33
AB,2001,first,20
AB,2001,second,25
AB,2001,third,87
AB,2002,first,6
AB,2002,second,
AB,2002,third,16
AB,2003,first,50
AB,2003,second,50
AB,2003,third,10
AB,2004,first,20
AB,2004,second,55
AB,2004,third,8
AC,2000,first,
AC,2000,second,97
AC,2000,third,77
AC,2001,first,42
AC,2001,second,5
AC,2001,third,81
AC,2002,first,
AC,2002,second,63
AC,2002,third,14
AC,2003,first,5
AC,2003,second,7
AC,2003,third,0
AC,2004,first,5
AC,2004,second,7
AC,2004,third,0
AD,2000,first,11
AD,2000,second,2
AD,2000,third,36
AD,2001,first,95
AD,2001,second,78
AD,2001,third,88
AD,2002,first,89
AD,2002,second,32
AD,2002,third,79
AD,2003,first,5
AD,2003,second,32
AD,2003,third,9
AD,2004,first,7
AD,2004,second,32
AD,2004,third,91
AE,2000,first,15
AE,2000,second,78
AE,2000,third,1
AE,2001,first,5
AE,2001,second,2
AE,2001,third,64
AE,2002,first,44
AE,2002,second,51
AE,2002,third,
AE,2003,first,40
AE,2003,second,52
AE,2003,third,85
AE,2004,first,45
AE,2004,second,50
AE,2004,third,80
我创建了一个由一些用户可选元素组成的index.html
文件(在本示例中,为简单起见,我选择了4个圆圈,其代码分别为 AB , AC , AD 或 AE )和3个单选按钮(第一,第二和第三). /p>
用户可以选择一个或多个圆圈,还可以选择一个单选按钮.
因此,我有一个数组codes
,其中包含所选圆的代码,一个modalitySelected
变量,其中包含所选的单选按钮.
我想做的是一个折线图,该折线图表示基于用户所做选择的数据.
示例:
每个所选代码都有一行,其值是与所选单选按钮相对应的值.
在此示例中,缺少一些数据,而虚线仅此. (目前,这并不重要).
这是我的代码.最初,管理圆的选择,并创建codes
数组,其中包含所选元素的代码.
然后创建折线图.
index.html :
<body>
<div id="circles">
<svg>
<circle id="AB" cx="10" cy="10" r="10" fill="purple" />
<circle id="AC" cx="60" cy="60" r="5" fill="red" />
<circle id="AD" cx="110" cy="110" r="15" fill="orange" />
<circle id="AE" cx="90" cy="50" r="7" fill="yellow" />
</svg>
</div>
<button type="button" id="finalSelection">Finish</button>
<span style="display:block;margin-top: 10px;">Selected codes: <span class="values"></span></span><br>
<div id="modality-selector-container">
<form id="modality-selector">
<input type="radio" name="modality-selector" id="rb-first" value="first" checked />
<label for="rb-first">First</label>
<input type="radio" name="modality-selector" id="rb-second" value="second" />
<label for="rb-second">Second</label>
<input type="radio" name="modality-selector" id="rb-third" value="third" />
<label for="rb-third">Third</label>
</form>
</div>
<div id="line-chart-container"></div>
<script src="./script.js"></script>
</body>
script.js :
var codes = [];
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
var filtered_data = null;
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50};
var width = 600 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// parse the date/time
var parseTime = d3.timeParse("%Y");
var svg = null;
var valueline = null;
d3.selectAll('#circles svg circle').on('click', function() {
var id = d3.select(this).attr('id');
if(d3.select(this).classed('clicked')) {
d3.select(this).classed('clicked', false).style('stroke', null);
codes.splice(codes.indexOf(id), 1);
}
else {
if(codes.length) {
if(d3.event.ctrlKey) {
d3.select(this).classed('clicked', true).style('stroke', 'blue');
codes.push(id);
}
else {
d3.selectAll(".clicked").classed('clicked', false).style('stroke', null);
codes = [];
d3.select(this).classed('clicked', true).style('stroke', 'blue');
codes.push(id);
}
}
else {
d3.select(this).classed('clicked', true).style('stroke', 'blue');
codes.push(id);
}
}
$('span.values').html(codes.join(', '));
});
$('button#finalSelection').click(function() {
$('span.values').html(codes.join(', '));
console.log("compare: " + codes);
compareCodes();
});
function compareCodes() {
// define the line
valueline = d3.line()
.x(function(d) {
return x(d.YEAR);
})
.y(function(d) {
return y(d.VALUE);
});
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
svg = d3.select("#line-chart-container").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 + ")");
getData();
}
function getData() {
d3.queue()
.defer(d3.csv, './data.csv')
.await(makeLineChart);
}
function makeLineChart(error, data) {
if(error) {
console.log(error);
}
// radio button change
d3.selectAll("input[name='modality-selector']")
.on("change", function(){
console.log(this.value);
modalitySelected = this.value;
// filter data
filtered_data = data.filter(function(d) {
return d.MODALITY == modalitySelected && d.CODE == codes[0];
});
// format the data
filtered_data.forEach(function(d) {
d.YEAR = parseTime(d.YEAR);
d.VALUE = +d.VALUE;
});
updateGraph(filtered_data);
}); // end radio on change
// generate initial line chart - filter data
filtered_data = data.filter(function(d) {
return d.MODALITY == modalitySelected && d.CODE == codes[0];
});
updateGraph(filtered_data);
}
function updateGraph(data) {
var numTickXaxis = data.length;
// scale the range of the data
x.domain(d3.extent(filtered_data, function(d) {
return d.YEAR;
}));
y.domain([0, d3.max(filtered_data, function(d) {
return d.VALUE;
})]);
// add the valueline path
svg.append("path")
.data([filtered_data])
.attr("class", "line")
.attr("d", valueline);
// add the X Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y"))
.ticks(numTickXaxis))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
// add the Y Axis
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y));
var state = svg.selectAll(".line");
state.exit().remove();
}
完整代码为 此处 .
此代码中存在两个问题:
- 更改单选按钮选择时,图形会被覆盖(尽管存在
state.exit().remove();
)
如您所见, - 该代码仅适用于
codes
中的第一个元素.我不知道如何处理codes
由多个元素组成并显示更多行的情况.
我可以考虑修改data.csv
的结构以保持相同内容的想法.
例如,我可以这样编辑文件:
CODE,YEAR,FIRST,SECOND,THIRD
AB,2000,15,50,33
AB,2001,20,25,87
AB,2002,6,,16
AB,2003,50,50,10
AB,2004,20,55,8
AC,2000,,97,77
AC,2001,42,5,81
AC,2002,,63,14
AC,2003,5,7,0
AC,2004,5,7,0
AD,2000,11,2,36
AD,2001,95,78,88
AD,2002,89,32,79
AD,2003,5,32,9
AD,2004,7,32,91
AE,2000,15,78,1
AE,2001,5,2,64
AE,2002,44,51,
AE,2003,40,52,85
AE,2004,45,50,80
有人会知道如何帮助我吗?谢谢!
解决方案
按照此示例,我能够解决问题.
此处 代码.
现在,我只有一个很小的(我希望是)图形问题. 在这张照片中,我们可以清楚地看到问题出在哪里.
线在轴之前开始.为什么?
问题是createAxis()
中的"transform", "translate("
将轴移到了一侧.
所以我添加了
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
转到原始的svg
变量,使其与轴对齐并删除
.attr("transform", "translate(" + 0 + "," + 0 + ")");
从createAxis()
这里是链接: 柱塞
I have a data.csv
file done this way:
CODE,YEAR,MODALITY,VALUE
AB,2000,first,15
AB,2000,second,
AB,2000,third,33
AB,2001,first,20
AB,2001,second,25
AB,2001,third,87
AB,2002,first,6
AB,2002,second,
AB,2002,third,16
AB,2003,first,50
AB,2003,second,50
AB,2003,third,10
AB,2004,first,20
AB,2004,second,55
AB,2004,third,8
AC,2000,first,
AC,2000,second,97
AC,2000,third,77
AC,2001,first,42
AC,2001,second,5
AC,2001,third,81
AC,2002,first,
AC,2002,second,63
AC,2002,third,14
AC,2003,first,5
AC,2003,second,7
AC,2003,third,0
AC,2004,first,5
AC,2004,second,7
AC,2004,third,0
AD,2000,first,11
AD,2000,second,2
AD,2000,third,36
AD,2001,first,95
AD,2001,second,78
AD,2001,third,88
AD,2002,first,89
AD,2002,second,32
AD,2002,third,79
AD,2003,first,5
AD,2003,second,32
AD,2003,third,9
AD,2004,first,7
AD,2004,second,32
AD,2004,third,91
AE,2000,first,15
AE,2000,second,78
AE,2000,third,1
AE,2001,first,5
AE,2001,second,2
AE,2001,third,64
AE,2002,first,44
AE,2002,second,51
AE,2002,third,
AE,2003,first,40
AE,2003,second,52
AE,2003,third,85
AE,2004,first,45
AE,2004,second,50
AE,2004,third,80
I created an index.html
file composed of some user selectable elements (in the example, for simplicity, I chose 4 circles with codes AB, AC, AD or AE) and 3 radio buttons (first, second and third).
The user can select one or more circles and also select a radio button.
So I have an array codes
that contains the codes of the selected circles and a modalitySelected
variable that contains the radio button chosen.
What I would like to do is a line chart that represents the data based on the selection made by the user.
Example:
There is a line for each chosen code and the values are those corresponding to the selected radio button.
In this example there are some missing data, and the dotted line just this. (For now this is not important to represent).
This is my code. Initially the selection of the circles is managed and the codes
array is created which contains the codes of the selected elements.
Then the line chart is created.
index.html:
<body>
<div id="circles">
<svg>
<circle id="AB" cx="10" cy="10" r="10" fill="purple" />
<circle id="AC" cx="60" cy="60" r="5" fill="red" />
<circle id="AD" cx="110" cy="110" r="15" fill="orange" />
<circle id="AE" cx="90" cy="50" r="7" fill="yellow" />
</svg>
</div>
<button type="button" id="finalSelection">Finish</button>
<span style="display:block;margin-top: 10px;">Selected codes: <span class="values"></span></span><br>
<div id="modality-selector-container">
<form id="modality-selector">
<input type="radio" name="modality-selector" id="rb-first" value="first" checked />
<label for="rb-first">First</label>
<input type="radio" name="modality-selector" id="rb-second" value="second" />
<label for="rb-second">Second</label>
<input type="radio" name="modality-selector" id="rb-third" value="third" />
<label for="rb-third">Third</label>
</form>
</div>
<div id="line-chart-container"></div>
<script src="./script.js"></script>
</body>
script.js:
var codes = [];
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value;
var filtered_data = null;
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50};
var width = 600 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// parse the date/time
var parseTime = d3.timeParse("%Y");
var svg = null;
var valueline = null;
d3.selectAll('#circles svg circle').on('click', function() {
var id = d3.select(this).attr('id');
if(d3.select(this).classed('clicked')) {
d3.select(this).classed('clicked', false).style('stroke', null);
codes.splice(codes.indexOf(id), 1);
}
else {
if(codes.length) {
if(d3.event.ctrlKey) {
d3.select(this).classed('clicked', true).style('stroke', 'blue');
codes.push(id);
}
else {
d3.selectAll(".clicked").classed('clicked', false).style('stroke', null);
codes = [];
d3.select(this).classed('clicked', true).style('stroke', 'blue');
codes.push(id);
}
}
else {
d3.select(this).classed('clicked', true).style('stroke', 'blue');
codes.push(id);
}
}
$('span.values').html(codes.join(', '));
});
$('button#finalSelection').click(function() {
$('span.values').html(codes.join(', '));
console.log("compare: " + codes);
compareCodes();
});
function compareCodes() {
// define the line
valueline = d3.line()
.x(function(d) {
return x(d.YEAR);
})
.y(function(d) {
return y(d.VALUE);
});
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
svg = d3.select("#line-chart-container").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 + ")");
getData();
}
function getData() {
d3.queue()
.defer(d3.csv, './data.csv')
.await(makeLineChart);
}
function makeLineChart(error, data) {
if(error) {
console.log(error);
}
// radio button change
d3.selectAll("input[name='modality-selector']")
.on("change", function(){
console.log(this.value);
modalitySelected = this.value;
// filter data
filtered_data = data.filter(function(d) {
return d.MODALITY == modalitySelected && d.CODE == codes[0];
});
// format the data
filtered_data.forEach(function(d) {
d.YEAR = parseTime(d.YEAR);
d.VALUE = +d.VALUE;
});
updateGraph(filtered_data);
}); // end radio on change
// generate initial line chart - filter data
filtered_data = data.filter(function(d) {
return d.MODALITY == modalitySelected && d.CODE == codes[0];
});
updateGraph(filtered_data);
}
function updateGraph(data) {
var numTickXaxis = data.length;
// scale the range of the data
x.domain(d3.extent(filtered_data, function(d) {
return d.YEAR;
}));
y.domain([0, d3.max(filtered_data, function(d) {
return d.VALUE;
})]);
// add the valueline path
svg.append("path")
.data([filtered_data])
.attr("class", "line")
.attr("d", valueline);
// add the X Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y"))
.ticks(numTickXaxis))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
// add the Y Axis
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y));
var state = svg.selectAll(".line");
state.exit().remove();
}
Complete code is HERE.
In this code there are two problems:
- when changing radio button selection, the graph is overwritten (despite the presence of
state.exit().remove();
) - as you can see, the code works only for the first element in
codes
. I don't know how to handle the case wherecodes
is composed of several elements and show more lines.
I can consider the idea of modifying the structure of data.csv
keeping the same content.
For example, I could edit the file like this:
CODE,YEAR,FIRST,SECOND,THIRD
AB,2000,15,50,33
AB,2001,20,25,87
AB,2002,6,,16
AB,2003,50,50,10
AB,2004,20,55,8
AC,2000,,97,77
AC,2001,42,5,81
AC,2002,,63,14
AC,2003,5,7,0
AC,2004,5,7,0
AD,2000,11,2,36
AD,2001,95,78,88
AD,2002,89,32,79
AD,2003,5,32,9
AD,2004,7,32,91
AE,2000,15,78,1
AE,2001,5,2,64
AE,2002,44,51,
AE,2003,40,52,85
AE,2004,45,50,80
Would anyone know how to help me? Thanks!
SOLUTION
Following this example, I was able to solve the problem.
HERE the code.
Now I have only a small (I hope) graphic problem. In this picture we can clearly see what the problem is.
The lines start before the axes. Why?
The problem was that the "transform", "translate("
in your createAxis()
moved your axis to the side.
So I added
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
To your original svg
variable so that it aligns with the axis and removed the
.attr("transform", "translate(" + 0 + "," + 0 + ")");
From the createAxis()
Here's the link: Plunker
这篇关于如何使用从csv文件中过滤的数据创建多系列折线图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!