Color coding Parallel Coordinates - javascript

I have already a code parallel coordinates graph, everything works fine. Now i'm trying to use colors to color-code the parallel coordinates visualization, but something is wrong. In dataset (http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data) i've got different names of wine species (1st column is class identifier (1-3)), but in graph draws only one color. Could anybody help me?
Graph :
enter code here
// CREATE A COLOR SCALE
var color = d3.scale.ordinal()
.domain(['1','2','3'])
.range(['red','blue','green'])
d3.csv("wine.csv", function(error, wine) {
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(wine[0]).filter(function(d) {
return d != "name" && (y[d] = d3.scale.linear()
.domain(d3.extent(wine, function(p) { return +p[d]; }))
.range([h, 0]));
}));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(wine)
.enter().append("path")
.attr("d", path);
// USE THE COLOR SCALE TO SET THE STROKE BASED ON THE DATA
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(wine)
.enter().append("path")
.attr("d", path)
.attr("stroke", function(d) {
var species = d.name.slice(0,d.name.indexOf(' '));
return color(species);
})

Once you already have your ordinal scale for the colors with the domain and range defined, you only need to color your lines according to d.name:
.attr("stroke", function(d) {
return color(d.name);
});

Related

Painting different vertical segments under curve using d3

I am trying to create a simple line graph using d3 which segments the curve and paint each segment with a different colour. Currently I am only able to colour the whole area under the curve.
Current:
Attempting to achieve (Pardon me for the terrible colouring. In a rush for time):
This is bits of relevant code. Please help!
var x = d3.scaleLinear();
var y = d3.scaleLinear();
//Set the range of the data
x.domain([0, Math.max(endGrowth, endPlateau) ,maxX]).range([0, width*0.8, width]);
y.domain([0, maxY]).range([height, 0]);
//Define the area under the Graph
var area1 = d3.area()
.x0(function(d){
return x(d.Rank);
})
.y1(function(d){ return y(d.Elements);})
.y0(height);
//Add the colored regions
svg.append("path")
.data([data])
.attr("class", "areaUnderGraph")
.attr("fill", "blue")
.attr("transform", "translate(" + leftMarginLabel + ",0)")
.attr("d", area1);
Right now, the area under the curve is one path, so it can only be colored one color. The simplest way to color different portions under the curve different colors is to split them up in data. It's not clear where data is coming from, but you'd take sub-sections of the array, like
var segments = [data.slice(0, 2), data.slice(2)];
svg.selectAll("path")
.data(segments)
.enter()
.append("path")
.attr("fill", function(d) { /* use d to choose a color */ })
That's the gist: you'd have multiple slices of the data, and instead of one path, you'd create multiple paths that you can color as you wish.

D3 Filter via selectAll after Transition

I try to build an interactive scatterplot. Therefore I am applying different colors to my data via dropdown like this:
function color_drop_update (select, checkbox) {
if (select == "gender") {
holder.selectAll(".dot").style("fill", colorf_gender);
holder.selectAll(".legend rect").style("fill", color_gender);
}
I can also change the plotted axes via radioboxes and a transition like this:
function transition(dimension) {
if (dimension == "pca") {
var xMap = xMap_pca;
var yMap = yMap_pca;
xScale.domain([d3.min(data, xValue_pca)-1, d3.max(data, xValue_pca)+1]);
yScale.domain([d3.min(data, yValue_pca)-1, d3.max(data, yValue_pca)+1]);
}
else if (dimension == "tsne") {
var xMap = xMap_tsne;
var yMap = yMap_tsne;
xScale.domain([d3.min(data, xValue_tsne)-1, d3.max(data, xValue_tsne)+1]);
yScale.domain([d3.min(data, yValue_tsne)-1, d3.max(data, yValue_tsne)+1]);
}
// Update old
circles.attr("class", "update")
.transition()
.duration(0)
.attr("cx", xMap)
.attr("cy", yMap);
//Update Axis
holder.select(".xaxis")
.transition()
.duration(0)
.call(xAxis);
holder.select(".yaxis")
.transition()
.duration(0)
.call(yAxis);
}
What is working fine:
Different filters and color information before transition
Different filters and color information are kept after the transition: first filter then transition (intended)
What is NOT working:
Choose axis and filter afterwards: first transition then filter!
Would really appreciate any hints and help!
Finally I got it:
it is important, that you mention the markers class even in the transition one again:
circles.attr("class", "update")
.transition()
.duration(0)
.attr("cx", xMap)
.attr("cy", yMap)
.attr("class", "dot");

D3.js category color scale not working properly

So I have one view that creates paths and I'm trying to have it so that each line that I draw has a random color.
I'm currently doing this-
var color = d3.scale.category20();
//other code that does stuff
this.path = svg.append("path")
.attr("d", line(newData))
.style("stroke", function(d,i) {
var colorIndex = Math.random() * (20 - 0) + 0;
return color(colorIndex); })
.attr("fill","none")
.attr("class","line");
This does not draw lines with different colours. Further, when I do this
this.path = svg.append("path")
.attr("d", line(newData))
.style("stroke", function(d,i) {
return color(4); })
.attr("fill","none")
.attr("class","line");
The color is still blue.
Why is that happening?
This is happening because domain was not set -
d3.scale.category10() not behaving as expected
has the answer
I have set up a minimal fiddle to show you the proper way to set up the line function. I also changed the color scheme to category(10) to show more contrasting colors (you can still use category20 and see a difference in colors though). Here is the FIDDLE.
var lineFunction = d3.svg.line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
})
.interpolate("linear");

D3 lines getting rendered as polygons

I am trying to use D3 to render lines, but when I try to do this, the lines get rendered as polygons. I am not sure why. I included a screenshot to show you what it looks like.
Here is the code:
// Creates a time scale using the x_extent
// defined above
var x_scale = d3.time.scale()
.range([margin, width - margin])
.domain(x_extent);
// Creates a similarity scale using the y_extent.
// defined above.
var y_scale = d3.scale.linear()
.range([height - margin, margin])
.domain(y_extent);
// Construct a line.
var line = d3.svg.line()
.x(function(d) {
return x_scale(d.date);
})
.y(function(d) {
return y_scale(d.similarity);
});
// Render a line.
d3.select("svg")
.append("path")
.attr("d", line(data));
Try setting fill and stroke explicitly when appending the path, i.e.
d3.select("svg")
.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("d", line(data));

D3 line graph not filling entire svg width

As the title states, I have created a D3 line/area graph, and I am finding it difficult to get the graph's width to remain constant, depending on the amount of data I have given it to render, it scales the width of the graph accordingly, but I am unsure of how I can get it to remain at a constant width, regardless of the amount of data given, which is what I would like to achieve.
I imagine it has something to do with the scaling of the x and y coordinates, but I am stuck at the moment and can't seem to figure out why it is doing this.
Here is the code I have thus far,
//dimensions and margins
var width = 625,
height = 350,
margin = 5,
// get the svg and set it's width/height
svg = d3.select("#main")
.attr("width", width)
.attr("height", height);
//initialize the graph
init([
[12345,42345,32345,22345,72345,62345,32345,92345,52345,22345],
[1234,4234,3234,2234,7234,6234,3234,9234,5234,2234]
]);
$("button").live('click', function(){
var id = $(this).attr("id");
if(id == "one"){
updateGraph([
[52345,32345,12345,22345,62345,72345,92345,32345,22345,22345,52345,32345,12345,22345,62345,72345,92345,32345,22345,22345,52345,32345,12345,22345,62345,72345,92345,32345,22345,22345],
[4234,12345,2234,32345,6234,7234,9234,3234,2234,2234,4234,1234,2234,3234,6234,7234,9234,3234,2234,2234,4234,1234,2234,3234,6234,7234,9234,3234,2234,2234]
]);
}else if(id == "two"){
updateGraph([
[12345,42345,32345,22345,72345,62345,32345,92345,52345,22345,12345,42345,32345,22345,72345,62345,32345,92345,52345,22345,12345,42345,32345,22345,72345],
[1234,2345,3234,2234,7234,6234,3234,9234,5234,2234,1234,4234,3234,2234,7234,6234,3234,9234,5234,2234,1234,4234,3234,2234,7234]
]);
}
});
function init(data){
var x = d3.scale.linear()
.domain([0,data[0].length])
.range([margin, width-margin]),
y = d3.scale.linear()
.domain([0,d3.max(data[0])])
.range([height-margin, margin]),
/* line path generator */
line = d3.svg.line().interpolate('monotone')
.x(function(d,i) { return x(i); })
.y(function(d) { return y(d); }),
/* area path generator */
area = d3.svg.area().interpolate('monotone')
.x(line.x())
.y1(line.y())
.y0(y(0)),
groups = svg.selectAll("g")
.data(data)
.enter()
.append("g");
svg.select("g")
.selectAll("circle")
.data(data[0])
.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 4);
/* add the areas */
groups.append("path")
.attr("class", "area")
.attr("d",area)
.style("fill", function(d,i) { return (i == 0 ? "steelblue" : "red" ); });
/* add the lines */
groups.append("path")
.attr("class", "line")
.attr("d", line);
}
function updateGraph(data){
var x = d3.scale.linear()
.domain([0,data[0].length])
.range([margin, width-margin]),
y = d3.scale.linear()
.domain([0,d3.max(data[0])])
.range([height-margin, margin]),
/* line path generator */
line = d3.svg.line().interpolate('monotone')
.x(function(d,i) { return x(i); })
.y(function(d) { return y(d); }),
/* area path generator */
area = d3.svg.area().interpolate('monotone')
.x(line.x())
.y1(line.y())
.y0(y(0));
groups = svg.selectAll("g")
.data(data),
circles = svg.select("g")
.selectAll("circle");
circles.data(data[0])
.exit().remove();
circles.data(data[0])
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 4);
/* animate circles */
circles.data(data[0])
.transition()
.duration(1000)
.attr("cx", line.x())
.attr("cy", line.y());
/* animate the lines */
groups.select('.line')
.transition()
.duration(1000)
.attr("d",line);
/* animate the areas */
groups.select('.area')
.transition()
.duration(1000)
.attr("d",area);
}
​
As well as a fiddle http://jsfiddle.net/JL33M/
Thank you!
The width of the graph depends on the range() you give it. range([0,100]) will always "stretch" the domain() values to take up 100 units.
That's what your code is currently doing:
var x = d3.scale.linear()
.domain([0,data[0].length])
.range([margin, width-margin]);// <-- always a fixed width
You want the width to depend on the number of data entries. Say you've decided you want each data point to take up 5 units, then range() needs to depend on the size of the dataset:
var x = d3.scale.linear()
.domain([0,data[0].length])
.range([margin, 5 * data[0].length]);// <-- 5 units per data point
Of course, under these conditions, your graph width grows with the dataset; if you give it a really long data array of, say, 500 points, the graph would be 2500 units wide and likely run off screen. But if your data is such that you know the maximum length of it, then you'll be fine.
On an unrelated note, I think your code could use a refactoring to be less repetitive. You should be able to achieve what you're doing with a single update() function, without the need for the init() function.
This tutorial by mbostock describe the "general update pattern" I'm referring to. Parts II and III then go on to explaining how to work transitions into this pattern.

Categories

Resources