How to avoid bars in bar graph getting overlapped with legend - d3.js

Below is the code which display 'grouped bars' using D3.
the graph also displays a legend at top-right corner.
Bars are overlapping the legend, so, How I can avoid legend getting overlapped with bars, no matter, whatsoever the height of the bars height are?
when I increase the field values, the bars are not growing too high, but they are still overlapping with legend.
var data = [
{
model_name: "f1",
field1: 19,
field2: 83,
},
{
model_name: "f2",
field1: 67,
field2: 200,
},
{
model_name: "f3",
field1: 200,
field2: 56,
},
];
var margin = { top: 30, right: 10, bottom: 10, left: 50 },
width = 500,
height = 300,
barPadding = 0.2,
axisTicks = { outerSize: 0 };
var svg = d3
.select("body")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
var xScale0 = d3
.scaleBand()
.range([margin.left, width - margin.right])
.padding(barPadding);
var xScale1 = d3.scaleBand();
xScale0.domain(data.map((d) => d.model_name));
xScale1.domain(["field1", "field2"]).range([0, xScale0.bandwidth()]);
var yScale = d3
.scaleLinear()
.range([height - margin.top - margin.bottom, margin.top]);
yScale.domain([
0,
d3.max(data, (d) => (d.field1 > d.field2 ? d.field1 : d.field2)),
]);
var xAxis = d3.axisBottom(xScale0).tickSizeOuter(axisTicks.outerSize);
var yAxis = d3
.axisLeft(yScale)
.tickSize(0)
.tickFormat(function(d) {
return d + "%";
});
svg
.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0,${height - margin.top - margin.bottom})`)
.call(xAxis);
svg
.append("g")
.attr("class", "y axis")
.attr("transform", `translate(${margin.left},0)`)
.call(yAxis)
.call((g) => g.select(".domain").remove());
var model_name = svg
.selectAll(".model_name")
.data(data)
.enter()
.append("g")
.attr("class", "model_name")
.attr("transform", (d) => `translate(${xScale0(d.model_name)},0)`);
model_name
.selectAll(".bar.field1")
.data((d) => [d])
.enter()
.append("rect")
.attr("class", "bar field1")
.style("fill", "skyblue")
.attr("x", () => xScale1("field1"))
.attr("y", (d) => yScale(d.field1))
.attr("width", xScale1.bandwidth())
.attr("height", (d) => {
return height - margin.bottom - margin.top - yScale(d.field1);
});
model_name
.selectAll(".bar.field2")
.data((d) => [d])
.enter()
.append("rect")
.attr("class", "bar field2")
.style("fill", "orange")
.attr("x", () => xScale1("field2"))
.attr("y", (d) => yScale(d.field2))
.attr("width", xScale1.bandwidth())
.attr("height", (d) => {
return height - margin.bottom - margin.top - yScale(d.field2);
});
var legend_symbol_width = 30;
var legend_symbol_height = 5;
var keys = ["title", "title"];
var color = d3
.scaleOrdinal()
.domain(keys)
.range(["skyblue", "orange"]);
var legend = svg
.append("g")
.attr("transform", "translate(" + (width - 20) + ",0)");
keys.forEach(function(key, i) {
var legendRow = legend
.append("g")
// seperating each row of a legend by y axis 20
.attr("transform", "translate(0, " + i * 20 + ")");
legendRow
.append("rect")
.attr("x", -150)
.attr("y", 10)
.attr("width", legend_symbol_width)
.attr("height", legend_symbol_height)
.attr("fill", color(i));
legendRow
.append("text")
.attr("x", -100)
.attr("y", 10 + legend_symbol_height * 1.4)
.style("text-transform", "capitalize")
.text(key);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

You only need to increase the value of the margin.top to, for example, 50. This gives more empty space on top of your bars, you there is enough space for the legend.
var data = [
{
model_name: "f1",
field1: 19,
field2: 83,
},
{
model_name: "f2",
field1: 67,
field2: 200,
},
{
model_name: "f3",
field1: 200,
field2: 56,
},
];
var margin = { top: 50, right: 10, bottom: 10, left: 50 },
width = 500,
height = 300,
barPadding = 0.2,
axisTicks = { outerSize: 0 };
var svg = d3
.select("body")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
var xScale0 = d3
.scaleBand()
.range([margin.left, width - margin.right])
.padding(barPadding);
var xScale1 = d3.scaleBand();
xScale0.domain(data.map((d) => d.model_name));
xScale1.domain(["field1", "field2"]).range([0, xScale0.bandwidth()]);
var yScale = d3
.scaleLinear()
.range([height - margin.top - margin.bottom, margin.top]);
yScale.domain([
0,
d3.max(data, (d) => (d.field1 > d.field2 ? d.field1 : d.field2)),
]);
var xAxis = d3.axisBottom(xScale0).tickSizeOuter(axisTicks.outerSize);
var yAxis = d3
.axisLeft(yScale)
.tickSize(0)
.tickFormat(function(d) {
return d + "%";
});
svg
.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0,${height - margin.top - margin.bottom})`)
.call(xAxis);
svg
.append("g")
.attr("class", "y axis")
.attr("transform", `translate(${margin.left},0)`)
.call(yAxis)
.call((g) => g.select(".domain").remove());
var model_name = svg
.selectAll(".model_name")
.data(data)
.enter()
.append("g")
.attr("class", "model_name")
.attr("transform", (d) => `translate(${xScale0(d.model_name)},0)`);
model_name
.selectAll(".bar.field1")
.data((d) => [d])
.enter()
.append("rect")
.attr("class", "bar field1")
.style("fill", "skyblue")
.attr("x", () => xScale1("field1"))
.attr("y", (d) => yScale(d.field1))
.attr("width", xScale1.bandwidth())
.attr("height", (d) => {
return height - margin.bottom - margin.top - yScale(d.field1);
});
model_name
.selectAll(".bar.field2")
.data((d) => [d])
.enter()
.append("rect")
.attr("class", "bar field2")
.style("fill", "orange")
.attr("x", () => xScale1("field2"))
.attr("y", (d) => yScale(d.field2))
.attr("width", xScale1.bandwidth())
.attr("height", (d) => {
return height - margin.bottom - margin.top - yScale(d.field2);
});
var legend_symbol_width = 30;
var legend_symbol_height = 5;
var keys = ["title", "title"];
var color = d3
.scaleOrdinal()
.domain(keys)
.range(["skyblue", "orange"]);
var legend = svg
.append("g")
.attr("transform", "translate(" + (width - 20) + ",0)");
keys.forEach(function(key, i) {
var legendRow = legend
.append("g")
// seperating each row of a legend by y axis 20
.attr("transform", "translate(0, " + i * 20 + ")");
legendRow
.append("rect")
.attr("x", -150)
.attr("y", 10)
.attr("width", legend_symbol_width)
.attr("height", legend_symbol_height)
.attr("fill", color(i));
legendRow
.append("text")
.attr("x", -100)
.attr("y", 10 + legend_symbol_height * 1.4)
.style("text-transform", "capitalize")
.text(key);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Related

How to fix a display issue in a multiple line graph. The svg.append("path") is not displaying anything

Im trying to create a multiline graph for my angular web app to visualise some data that will add lines according to the items in the data variable. As i'm trying to append the path line in the svg element i can't manage to display it. Right now only the two axes are displayed correctly.
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleTime().domain([new Date(2000, 0, 0), new Date(2017, 0, 0)]).range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var svg = d3.select(this.htmlElement).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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%Y")).ticks(d3.timeYear, 1));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// define the line
var line = d3.line()
.x(function(d) {
return d.name;
})
.y(function(d) {
return d.value;
});
data.forEach(function(d) {
svg.append("path")
.attr("class", "line")
.attr('d', line(d.series))
.attr('stroke', 'blue')
.attr('stroke-width', 2)
.attr('fill', 'none');
});
the structure of the data is
var data = [{
"key": "Germany",
"series": [
{
"a": "Germany",
"name": "2010",
"value": 0.2
},
{
"a": "Germany",
"name": "2011",
"value": 0.5
}
]
}, {
"key": "uk",
"series": [
{
"a":"uk",
"name": "2010",
"value": 0.3
},
{
"a":"uk",
"name": "2011",
"value": 0.6
}
]
}];
i guess that the problem lies on the data.foreach loop but i can't manage to find it.
according to the comments above the working code can be seen below
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleTime().domain([new Date(2000, 0, 0), new Date(2017, 0, 0)]).range([0, width]);
var y = d3.scaleLinear().domain([0,1]).range([height, 0]);
var svg = d3.select(this.htmlElement).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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%Y")).ticks(d3.timeYear, 1));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// define the line
var line = d3.line()
.x(function(d) {
return x(new Date(d.name, 0));
})
.y(function(d) {
return y(d.value);
});
data.forEach(function(d) {
svg.append("path")
.attr("class", "line")
.attr('d', line(d.series))
.attr('stroke', 'blue')
.attr('stroke-width', 2)
.attr('fill', 'none');
});

Creating a reversed D3 stack bar chart

Assuming I have data like this:
const data = [
{
month: 1
apples: ...,
bananas: ...,
cherries: ...,
dates: ...,
},
{
month: 2
apples: ...,
bananas: ...,
cherries: ...,
dates: ...,
},
{
month: 3
apples: ...,
bananas: ...,
cherries: ...,
dates: ...,
}
]
Going for 12 months, using keys of ['apples','bananas','cherries','dates']. The d3.stack() will produce an array for 4 bars with 12 sets of values. This makes sense. However, what if I wanted to create 12 bars with the keys broken up so it's sets of 4 values.
Is it possible to flip things on their heads in this fashion?
You just need to convert the data into the required format and flip the axis and data configurations in the chart as shown below.
Existing :
var margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
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 + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
year: "2006",
redDelicious: "10",
mcintosh: "15",
oranges: "9",
pears: "6"
},
{
year: "2007",
redDelicious: "12",
mcintosh: "18",
oranges: "9",
pears: "4"
},
{
year: "2008",
redDelicious: "05",
mcintosh: "20",
oranges: "8",
pears: "2"
},
{
year: "2009",
redDelicious: "01",
mcintosh: "15",
oranges: "5",
pears: "4"
}
];
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) {
return data.map(function(d) {
return {
x: parse(d.year),
y: +d[fruit]
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.attr("width", x.rangeBand())
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(30," + i * 19 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colors.slice().reverse()[i];
});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0:
return "Anjou pears";
case 1:
return "Naval oranges";
case 2:
return "McIntosh apples";
case 3:
return "Red Delicious apples";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
New :
var margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
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 + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
fruit: "redDelicious",
2006: "10",
2007: "12",
2008: "05",
2009: "01",
2010: "02"
},
{
fruit: "mcintosh",
2006: "15",
2007: "18",
2008: "20",
2009: "15",
2010: "10"
},
{
fruit: "oranges",
2006: "9",
2007: "9",
2008: "8",
2009: "5",
2010: "4"
},
{
fruit: "pears",
2006: "6",
2007: "4",
2008: "2",
2009: "4",
2010: "2"
}
];
var legends = Object.keys(data[0]);
legends.splice(legends.indexOf('fruit'), 1);
// Transpose the data into layers
var dataset = d3.layout.stack()(legends.map(function(year) {
return data.map(function(d) {
return {
x: d.fruit,
y: +d[year]
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574","#6aa8e0"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
//.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.attr("width", x.rangeBand())
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(30," + i * 19 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colors.slice().reverse()[i];
});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return legends.reverse()[i];
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

How to rotate the texts on each bar of a grouped bar chart?

I have a grouped Bar Chart and I am rotating the x axis text. But the x axis text is not properly aligned under the bar. How can i make the x axis text aligned closely to the particular bar?
I think something is going wrong in the x axis text rotation transform attribute.
Here is the code:
the html
<!DOCTYPE html>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js">
</script>
<body>
<div id="chartdiv" style="height: 500px; width: 600px;"> </div>
</body>
and here is the javascript
var data = [
{
"PAG": "FACT",
"Projects": "3",
"High Level Design": "1",
"Business Requirements": "1",
"Change Impact Document": "2",
"MAC Reviews": "3"
}
,
{
"PAG": "Advisory Platforms & Solutions",
"Projects": "1", "MAC Reviews": "1"
},
{
"PAG": "Capital Markets",
"Projects": "2" },
{
"PAG": "Field Management Home Office",
"Projects": "3"
},
{
"PAG": "Institutional Wealth Services",
"Projects": "1",
"Business Requirements": "1",
"Change Impact Document": "1"
},
{
"PAG": "Traditional Invest Products",
"Projects": "2"
},
{
"PAG": "WM Operations",
"Projects": "2",
"High Level Design": "2",
"Low Level Design": "1"
}
];
function keysLen(obj) {
count = 0;
index = 0;
obj.map(function (d, i) {
if (d3.keys(d).length > count) {
count = d3.keys(d).length;
index = i
}
})
return obj[index];
}
var margin = { top: 20, right: 20, bottom: 30, left: 40 },
width = parseInt(d3.select("#chartdiv").style("width")) - margin.left - margin.right,
height = parseInt(d3.select("#chartdiv").style("height")) - margin.top - margin.bottom,
padding = 100;
//var border = 1; var bordercolor = 'black';
var x0 = d3.scale.ordinal().domain(data.map(function (d) { return d.PAG; }))
.rangeRoundBands([0, width - padding], .1);
//var x0 = d3.scale.ordinal()
// .rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear().range([height, 0]);
var ageNames = d3.keys(keysLen(data)).filter(function (d) {
return d != "PAG"
});
var p = d3.scale.category20();
var r = p.range(); // ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
var color = d3.scale.ordinal().range(r);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
//.tickPadding(8)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".0d"));
//add tooltip------------------------------------------------
var tooltip = d3.select("#chartdiv")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
var svg = d3.select("#chartdiv").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 + ")");
data.forEach(function (d) {
d.ages = ageNames.map(function (name) {
return { name: name, value: +d[name] };
});
});
x0.domain(data.map(function (d) { return d.PAG; }));
//x0.domain(data.map(function (d) { return d.PAG; })).rangeRoundBands([0, width - padding], .1);
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function (d) { return d3.max(d.ages, function (d) { return d.value; }); })]).nice().range([height - padding, 0]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(data, function (d) {
return "<strong>Total Count:</strong> <span style='color:#1F497D'>" + d.ages.value + "</span>";
})
//svg.call(tip);
var gXAxis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - padding) + ")")
.call(xAxis);
gXAxis.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-30)");
// xAxis label
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom - 5) + ")")
.style("text-anchor", "middle")
.attr("class", "subtitle")
.text("Process Area Group");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -(height - padding) / 2)
.attr("y", 0 - margin.left)
//.attr("y", -margin.bottom)
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Artifacts")
.attr("class", "subtitle");
var state = svg.selectAll(".PAG")
.data(data)
.enter().append("g")
.attr("class", "PAG")
.attr("transform", function (d) { return "translate(" + x0(d.PAG) + ",0)"; });
state.selectAll("rect")
.data(function (d) { return d.ages; })
.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 - padding - y(d.value); })
.style("fill", function (d) { return color(d.name); })
.on("mouseover", function (d) {
var pos = d3.mouse(this);
tooltip
.transition()
.duration(500)
.style("opacity", 1);
tooltip.html('<strong>' + d.name + ':' + '</strong>' + '<span style=color:#1F497D>' + d.value + '</span>')
.style("left", d3.event.x + "px")
.style("top", d3.event.y + "px")
//.text(d.name + ":" + d.value);
})
.on("mouseout", function () {
tooltip.style("opacity", 0);
});
//Adding Legend for the Grouped Bar Chart
var legend = svg.selectAll(".legend")
.data(ageNames.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", 15)
.attr("height", 15)
.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; });
svg.append("text")
.text("Project Artifacts By PAG")
.attr("x", width / 2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("text-decoration", "underline")
.attr("class", "title");
Please find the below mentioned working jsfiddle for that http://jsfiddle.net/qAHC2/1246/

Insert padding so that points do not overlap with y or x-axis

I have the created this plot in D3: http://bl.ocks.org/cddesja/aee65f660c24cb2144fd
There are two things I would like to change.
1) I would like to make it so that none of my points overlap with the y or x-axis. I thought I could use transform() to create a little bit of a buffer but that creates a gap between the x and y axis lines, something I do not want. How should I do this correctly?
2) I would like to get rid of the tick marks at the intersection of the x and y lines. I am hoping that once I move the tick marks (and subsequently the pts and lines) that these tick marks automagically disappear. I don't know if that's really true or not.
Thanks in advance.
Chris
1.) This is controlled by the domain of your scales. Since your domains are set to the extent of the data, the axis starts there. Easy fix is to increase your domain to be +- N percent of the range. Also, I recommend remove the .nice() as you'll just end up fighting it for control the of the range.
// get extents and range
var xExtent = d3.extent(data, function(d) { return d.grade; }),
xRange = xExtent[1] - xExtent[0],
yExtent = d3.extent(data, function(d) { return d.bin; }),
yRange = yExtent[1] - yExtent[0];
// set domain to be extent +- 5%
x.domain([xExtent[0] - (xRange * .05), xExtent[1] + (xRange * .05)]);
y.domain([yExtent[0] - (yRange * .05), yExtent[1] + (yRange * .05)]);
2.) This is controlled by the outerTickSize option. Just set it to zero.
var yAxisLeft = d3.svg.axis()
.scale(y)
.ticks(yTicks)
.outerTickSize(0) //<-- set to zero
.orient("left");
Full working code sample:
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<title>Proportion of Students Suspended <body></body> Grade</title>
<style type="text/css">
body {
font: 10px Helvetica;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript">
// Set up the margins
var margin = {top: 50, right: 50, bottom: 30, left: 50},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom
yTicks = 5
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxisBot = d3.svg.axis()
.scale(x)
.outerTickSize(0)
.orient("bottom");
var xAxisTop = d3.svg.axis()
.scale(x)
.tickFormat('')
.outerTickSize(0)
.orient("top");
var yAxisLeft = d3.svg.axis()
.scale(y)
.ticks(yTicks)
.outerTickSize(0)
.orient("left");
var yAxisRight = d3.svg.axis()
.scale(y)
.ticks(yTicks)
.tickFormat('')
.outerTickSize(0)
.orient("right");
var line = d3.svg.line()
.x(function(d) { return x(d.grade); })
.y(function(d) { return y(d.bin); });
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 + ")");
var data = [{"grade":1,"bin":0.0398,"number":14943,"total":3.2306},{"grade":2,"bin":0.0605,"number":14128,"total":3.3778},{"grade":3,"bin":0.0777,"number":13590,"total":3.4621},{"grade":4,"bin":0.103,"number":13147,"total":3.7474},{"grade":5,"bin":0.1207,"number":12936,"total":3.7055},{"grade":6,"bin":0.202,"number":12857,"total":5.2757},{"grade":7,"bin":0.2534,"number":12962,"total":5.7908},{"grade":8,"bin":0.2561,"number":13362,"total":5.7759},{"grade":9,"bin":0.1873,"number":16618,"total":5.8429},{"grade":10,"bin":0.168,"number":14996,"total":5.4448},{"grade":11,"bin":0.1178,"number":13119,"total":4.3997},{"grade":12,"bin":0.0605,"number":12061,"total":3.8986}];
var xExtent = d3.extent(data, function(d) { return d.grade; }),
xRange = xExtent[1] - xExtent[0],
yExtent = d3.extent(data, function(d) { return d.bin; }),
yRange = yExtent[1] - yExtent[0];
x.domain([xExtent[0] - (xRange * .05), xExtent[1] + (xRange * .05)]);
y.domain([yExtent[0] - (yRange * .05), yExtent[1] + (yRange * .05)]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxisBot)
.append("text")
.attr("class", "label")
.attr("x", width/2)
.attr("y", 28)
.style("text-anchor", "end")
.text("Grade");
svg.append("g")
.attr("class", "x axis")
.call(xAxisTop);
svg.append("g")
.attr("class", "y axis")
.call(yAxisLeft)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("x", -(height / 5))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Proportion of Students Suspended at Least Once");
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxisRight);
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line)
.style("fill", "none")
.style("stroke", "#3B3A35")
.style("stroke-width", "1.5px");
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", function(d) { return 10; })
.attr("cx", function(d) { return x(d.grade); })
.attr("cy", function(d) { return y(d.bin); })
.attr("transform", "translate(0, 0)")
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("cx")) + 0;
var yPosition = parseFloat(d3.select(this).attr("cy")) + 0;
d3.select(this)
.transition()
.duration(500)
.style("fill-opacity", .35)
.attr("r", 20)
})
.on("mouseout", function() {
//Hide the tooltip
d3.select(this)
.transition()
.duration(500)
.style("fill-opacity", 1)
.attr("r", function(d) { return 10; });
})
.style("fill", "#3B3A35")
.style("stroke", "#3B3A35")
.style("stroke-width", "1.2px");
</script>
</body>
</html>
even though this is an old question, I got the same need but I preferred to modify the range extent of both axis instead of inserting fake values into domain:
var x = d3.scale.linear()
.range([20, width-20]);
var y = d3.scale.linear()
.range([height, 20]);
here the working snippet:
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<title>Proportion of Students Suspended
<body></body> Grade</title>
<style type="text/css">
body {
font: 10px Helvetica;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript">
// Set up the margins
var margin = {
top: 50,
right: 50,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom
yTicks = 5
var x = d3.scale.linear()
.range([20, width - 20]);
var y = d3.scale.linear()
.range([height, 20]);
var xAxisBot = d3.svg.axis()
.scale(x)
.orient("bottom");
var xAxisTop = d3.svg.axis()
.scale(x)
.tickFormat('')
.orient("top");
var yAxisLeft = d3.svg.axis()
.scale(y)
.ticks(yTicks)
.orient("left");
var yAxisRight = d3.svg.axis()
.scale(y)
.ticks(yTicks)
.tickFormat('')
.orient("right");
var line = d3.svg.line()
.x(function(d) {
return x(d.grade);
})
.y(function(d) {
return y(d.bin);
});
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 + ")");
var data = [{
"grade": 1,
"bin": 0.0398,
"number": 14943,
"total": 3.2306
}, {
"grade": 2,
"bin": 0.0605,
"number": 14128,
"total": 3.3778
}, {
"grade": 3,
"bin": 0.0777,
"number": 13590,
"total": 3.4621
}, {
"grade": 4,
"bin": 0.103,
"number": 13147,
"total": 3.7474
}, {
"grade": 5,
"bin": 0.1207,
"number": 12936,
"total": 3.7055
}, {
"grade": 6,
"bin": 0.202,
"number": 12857,
"total": 5.2757
}, {
"grade": 7,
"bin": 0.2534,
"number": 12962,
"total": 5.7908
}, {
"grade": 8,
"bin": 0.2561,
"number": 13362,
"total": 5.7759
}, {
"grade": 9,
"bin": 0.1873,
"number": 16618,
"total": 5.8429
}, {
"grade": 10,
"bin": 0.168,
"number": 14996,
"total": 5.4448
}, {
"grade": 11,
"bin": 0.1178,
"number": 13119,
"total": 4.3997
}, {
"grade": 12,
"bin": 0.0605,
"number": 12061,
"total": 3.8986
}];
x.domain(d3.extent(data, function(d) {
return d.grade;
})).nice();
y.domain(d3.extent(data, function(d) {
return d.bin;
})).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxisBot)
.append("text")
.attr("class", "label")
.attr("x", width / 2)
.attr("y", 28)
.style("text-anchor", "end")
.text("Grade");
svg.append("g")
.attr("class", "x axis")
.call(xAxisTop);
svg.append("g")
.attr("class", "y axis")
.call(yAxisLeft)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("x", -(height / 5))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Proportion of Students Suspended at Least Once");
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxisRight);
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line)
.style("fill", "none")
.style("stroke", "#3B3A35")
.style("stroke-width", "1.5px");
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", function(d) {
return 10;
})
.attr("cx", function(d) {
return x(d.grade);
})
.attr("cy", function(d) {
return y(d.bin);
})
.attr("transform", "translate(0, 0)")
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("cx")) + 0;
var yPosition = parseFloat(d3.select(this).attr("cy")) + 0;
d3.select(this)
.transition()
.duration(500)
.style("fill-opacity", .35)
.attr("r", 20)
})
.on("mouseout", function() {
//Hide the tooltip
d3.select(this)
.transition()
.duration(500)
.style("fill-opacity", 1)
.attr("r", function(d) {
return 10;
});
})
.style("fill", "#3B3A35")
.style("stroke", "#3B3A35")
.style("stroke-width", "1.2px");
</script>
</body>
</html>

D3.js: How to reverse a line graph with respect to x-axis?

I want the graph to be plotted in the opposite order with respect to the x-axis. Currently it plots the values (in order):
{'x': '00:37:23', 'y': 8}, {'x': '00:37:46', 'y': 4}, {'x': '00:38:40', 'y': 2} but I want it to plot in the order of {'x': '00:38:40', 'y': 2}, {'x': '00:37:46', 'y': 4}, {'x': '00:37:23', 'y': 8}
I tried looking up documentation on D3's x-axis but there doesn't seem to be anything about reversing order.
See: http://jsfiddle.net/sc135zs6/
var data = [{'x': '00:38:40', 'y': 2}, {'x': '00:37:46', 'y': 4}, {'x': '00:37:23', 'y': 8}];
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%H:%M:%S").parse;
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")
.ticks(d3.time.minutes, 2)
.tickFormat(d3.time.format("%M:%S"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); });
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 + ")");
data.forEach(function(d) {
d.x = parseDate(d.x);
d.y = d.y;
});
x.domain(d3.extent(data, function(d) { return d.x; }));
y.domain(d3.extent(data, function(d) { return d.y; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 900)
.attr("y", -5)
.attr("dx", ".71em")
.style("text-anchor", "end")
.text("Time Remaining");
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("Score Differential");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
Thanks!
Reverse the scale:
var x = d3.time.scale()
.range([width, 0]);

Resources