问题
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?
回答1:
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
来源:https://stackoverflow.com/questions/48264567/how-to-create-a-multiseries-line-chart-using-data-filtered-from-a-csv-file