We are plotting a multilinear graph using d3.v2.js .
We are using ordinal scale for x-axis and linear scale for y-axis as we have labels(string) to be shown in x-axis
and numbers to be shown in y-axis.
In some cases graph appears to be fine , but in some cases it plots x-axis and y-axis independently and y-axis values are not in sync with x-axis values.
Also plotting starts from 0 instead of first x-axis value.
Any pointers to this issue would be of great help.
Thanks in advance.
Please find the code below.
function plotMOAGraph(data , sigPathways){
var margin = {top: 20, right: 80, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scale.ordinal(
(d3.range(0,sigPathways.length))).rangeBands([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.pathway); })
.y(function(d) { return y(d.score); });
var svg = d3.select("#graphDiv").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 450 + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "pathway"; }));
var entityNames = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {pathway: d.pathway, score: +d[name]};
})
};
});
x.domain(sigPathways);
y.domain([
d3.min(entityNames, function(c) { return d3.min(c.values, function(v) { return v.score; }); }),
d3.max(entityNames, function(c) { return d3.max(c.values, function(v) { return v.score; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("dy", "9em")
.attr("dx","40em")
.style("text-anchor", "end")
.text("Pathways");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "-4em")
.style("text-anchor", "end")
.text("Pathway Scores");
var tooltip = d3.select("#graphDiv")
.append("div")
.style("visibility", "hidden")
;
var rotateXAxis = function(d) {
var name = d.substr(0, 15);
if(name !== d) {
name = name + " ... ";
}
var el = d3.select(this);
el.text('').attr("transform", "rotate(-45)").attr("text-anchor", "end").on("mouseover", showTooltip).on("mouseout",hideTooltip);
var tspan = el.append('tspan').text(name);
tspan.attr('x', 0).attr('dy', '0');
};
svg.selectAll('g.x.axis g text').each(rotateXAxis);
function showTooltip(d) {
tooltip.text(d).style("position","absolute")
.style("top", (d3.event.pageY)-10+"px")
.style("left", (d3.event.pageX)-300+"px")
.style("visibility", "visible")
.style("font-size", "12px");
}
function hideTooltip() {
tooltip.style("visibility", "hidden");
}
var entityName = svg.selectAll(".entityName")
.data(entityNames)
.enter().append("g")
.attr("class", "entityName");
entityName.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values); })
.style("stroke", function(d) {return color(d.name); });
entityName.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.pathway) + "," + y(d.value.score) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
}
Related
I start with d3 using version 3.
I created a grouped bar chart that looks like this:
I added a drop-down list that contains all the genders (Woman (femme), Couple...).
I'm blocked because I can't highlight the bars that correspond to a certain gender when I choose from the drop-down list.
I found on the internet that I could use this code:
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
the "Section" part contains my selected gender
I would like that when I select, for example torque, that the bars corresponding to the torque remain in color and the others are in gray.
This is the all code :
source : https://bl.ocks.org/hydrosquall/7966e9c8e8414ffcd8b5
Highlight : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600,
height = 250 ;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#54E868", "#54CCE8", "#6395FF", "#50FFC5"]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
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("../CSV/GenreOrigine.csv", function(error, data) {
if (error) throw error;
//Couples, Famille...
var genreNames = d3.keys(data[0]).filter(function(key) { return key !== "Origine"; });
data.forEach(function(d) {
//Valeur pour chacune des origines par rapport au genre
d.genres = genreNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Origine; }));
x1.domain(genreNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.genres, function(d) { return d.value; }); })]);
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", 0.3)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Nombre de personnes");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var Origine = svg.selectAll(".Origine")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Origine) + ",0)"; });
Origine.selectAll("rect")
.data(function(d) { return d.genres; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.style("fill", function(d) { return color(d.name) })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.name)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.name));
});
Origine.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
var legend = svg.selectAll(".legend")
.data(genreNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
});
You can select all the rect in the SVG and filter them in the function, so the code would look like:
var sect = document.getElementById('inds');
var section = sect.options[sect.selectedIndex].value;
if(section !== '') {
d3.selectAll("rect")
.attr('opacity', function(d) {
if(d.name !== section) {
return 0.2;
} else {
return 1;
}
})
} else {
d3.selectAll('rect')
.attr('opacity', 1)
}
In the example above, I changed the opacity attribute, but you can change fill with the same approach.
I want to display negative and positive values with d3js... with dis code, it made only the positive values.
i've worked with this : http://jsfiddle.net/chrisJamesC/tNdJj/4/
can anyone help me?
her it is the code:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 1200 - margin.left - margin.right,
height = 390 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(d3.csv)), Math.abs(d3.max(d3.csv)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height,0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(d3.csv.length))
.rangeRoundBands([0, width], .1);
var color = d3.scale.ordinal()
.range(["#FFFFFF", "#FF0000", "#FE642E", "#00FF00", "#04B404", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var chart2 = d3.select("#graph2")
.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 + ")");
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
d3.csv("dataanzeigeauto3.asp", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
chart2.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart2.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("dy", ".71em")
.attr("y", 6)
.style("text-anchor", "end")
.text("Anzahl");
var state2 = chart2.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state2.selectAll("rect")
.data(function(d) { return d.ages; }) .enter().append("rect")
.attr("class", function(d) { return d < 0 ? "bar negative" : "bar positive"; })
.attr("y", function(d) { return y(Math.max(0, (d.y1))); })
.attr("height", function(d) { return Math.abs(y(d.y0) - y(d.y1)); })
.attr("width", x.rangeBand())
.style("fill", function(d) { return color(d.name); })
.call(d3.helper.tooltip()
.attr({class: 'tooltip2'})
.text(function(d, i){ return d.name + ': '+ (-((d.y0) - (d.y1))) ; })
);
var legend2 = chart2.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 100 + ")"; });
legend2.append("rect")
.attr("x", width - 30)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend2.append("text")
.attr("x", width - 35)
.attr("y", 14)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
I've adapted this code from the multi-line line chart example here. The biggest issue I'm now having after researching what changes I needed to make is that the data lines disappear when I use .rangePoints on the x-axis ordinal scale. With just .range, the x-axis displays nothing and the data lines are bunched up along the left side of the y-axis. This has something to do with the fact I altered the original code from a time scale to ordinal, but I'm stumped as to what further changes I need to make.
Code below:
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 280 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([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("linear")
.x(function(d) { return x(d.episodes); })
.y(function(d) { return y(d.season); });
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) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "episodes"; }));
var seasons = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.episodes, season: +d[name]};
})
};
});
x.domain(data.map(function(d) { return d.episodes; }));
y.domain([
d3.min(seasons, function(c) { return d3.min(c.values, function(v) { return v.season; }); }),
d3.max(seasons, function(c) { return d3.max(c.values, function(v) { return v.season; }); })
]);
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("Viewership (in mlns)");
var s = svg.selectAll(".city")
.data(seasons)
.enter().append("g")
.attr("class", "city");
s.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
s.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.season) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
I'm a d3 novice trying to create a simple, two-series bar chart that transitions when different buttons are clicked. The original chart is constructed:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#d4d4d4", "#58bd5b",]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("div.d3space").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("/assets/data/data3.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Client; }));
x1.domain(hourBuckets).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.hours, function(d) { return d.value; }); })]);
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("Hours");
var client = svg.selectAll(".client")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Client) + ",0)"; });
client.selectAll("rect")
.data(function(d) { return d.hours; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(hourBuckets.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
The csv being accessed is in the following format:
Client,Planned,Actual
ICC,25,50
RNR,50,47.5
MB,10,2.5
This chart renders as desired. The piece I am struggling with is getting this graph to transition to reflect different data when a link is clicked (link has id="fourweeks"). I have tried this onclick function:
window.onload = function() {
var a = document.getElementById("fourweeks");
var b = document.getElementById("eightweeks");
var c = document.getElementById("twelveweeks");
a.onclick = function() {
d3.csv("/assets/data/data1.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
var client = svg.selectAll(".client")
client.selectAll("rect")
.data(function(d) { return d.hours; })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
});
}
}
...no dice. I can get this to work when creating / transitioning simple one-series bar charts that use list inputs, but not the multi-series csv ones. data2.csv is the exact same file as data1.csv, with the values adjusted slightly.
Thanks for your time reading - any advice?
First svg.selectAll(".client") returns an empty selection, because you gave these elements the class 'g' instead of 'client'.
Secondly you need to update the data of the .client-elements:
var client = svg.selectAll(".client")
.data(data);
btw. you should use selection.classed() instead of selection.attr('class')
I am a newbie with D3 library and I am stuck with zooming on a graph.
I display correctly my data over several graphs. But when I zoom, everything goes wrong. I don't know if I miss something with Domains or Ranges or anything... so I ask.
You can find a demo of my code here: http://pastehtml.com/view/cos13vodt.html
And here is the jsFiddle example: http://jsfiddle.net/84mSQ/
And my JS code is there:
var margin = {top: 30, right: 150, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//To parse dates as they are into the CSV
var parseDate = d3.time.format("%Y/%m/%d-%H:%M").parse;
var format = d3.time.format("%d/%m/%y-%H:%M");
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")/*.ticks(30)*/;
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 2])
.on("zoom", function(scale, translate){
console.log("fonction zoom");
console.log(scale); console.log(translate);
zoomed(scale, translate);
});
// A line generator.
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("svg:g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
svg.append("rect")
.attr("class", "pane")
.attr("width", width)
.attr("height", height);
// Get the data
d3.csv("./enregistrement-subset2.csv", function(data) {
color.domain(d3.keys(data[0])
.filter(function(key) {
return key !== "date" && key !== "ECS - Button A" ;
}));
data.forEach(function(d) {
//Parse the date
d.date = parseDate(d.date);
});
var dataSet = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
//parses the number by using the '+' operator
if(name == "CO2 chambre"){
return { date: d.date, value: (+d[name])/10};
}
else{
return { date: d.date, value: +d[name]};
}
})
};
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(dataSet, function(c) { var mini = d3.min(c.values, function(v) { return v.value; }); return mini; }),
d3.max(dataSet, function(c) { var maxi = d3.max(c.values, function(v) { return v.value; }); return maxi; })
]);
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("Value of");
var valueSet = svg.selectAll(".valueSet")
.data(dataSet)
.enter().append("g")
.attr("class", "valueSet");
valueSet.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); })
.call(line);
valueSet.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.value) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
//zoomed();
});
function zoomed() {
console.log("here", d3.event);
svg.select("g.x.axis").call(xAxis);
svg.select("g.y.axis").call(yAxis);
//svg.selectAll("path.line").call(line);
svg.selectAll("path.line").attr("d", line);
//d3.select("#footer span").text("PĂ©riode de temps: " + x.domain().map(format).join("-"));
}
Can anybody tell me what I did wrong with this code ?
Should I re-design it ?
Are there performances issue to preview if I use a huge amount of data and what should I do then ?
var zoom = d3.behavior.zoom()
.x(x)
**.scaleExtent([1, 2])** <---
.on("zoom", function(scale, translate){
console.log("fonction zoom");
console.log(scale); console.log(translate);
zoomed(scale, translate);
});
take out the .scaleExtent([1, 2]) and check if it is working
should be
var zoom = d3.behavior.zoom()
.x(x)
.on("zoom", function(scale, translate){
console.log("fonction zoom");
console.log(scale); console.log(translate);
zoomed(scale, translate);
});
it is because your x axis is time.