[EDIT] Outputted Graph: https://imgur.com/a/D0wtP
I am trying to shade in the area between 2 lines in a graph. The plots shows commits made by software engineers and shows their tt100 lines of raw and productive code, and i am trying to shade the delta region between the 2 lines. Some of the solutions I have found do not seem to match my approach. I appreciate any help. My code is the following:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis--x path {
display: none;
}
.axis--y path {
display: none;
}
.line {
fill: none;
stroke-width: 1.5px;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
</style>
<svg width="1080" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right - 50,
height = svg.attr("height") - margin.top - margin.bottom;
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#f5f5f1")
var parseTime = d3.timeParse("%Y%m");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.time); });
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(5)
}
d3.csv("data.csv", type, function(error, data) {
if (error) throw error;
var employees1 = data.columns.slice(1,3).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, time: d[id]};
})
};
});
var employees2 = data.columns.slice(3).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, time: d[id]};
})
};
});
var employees = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, time: d[id]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
0,
d3.max(employees, function(c) { return d3.max(c.values, function(d) { return d.time; }); })
]);
z.domain(employees.map(function(c) { return c.id; }));
// add the Y gridlines
g.append("g")
.attr("class", "grid")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
)
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -45)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("HOURS TO PRODUCE 100 LINES OF CODE (PRODUCTIVE VS RAW)");
g.append("text")
.attr("x", (width / 4))
.attr("y", 0 - (margin.top / 8))
.attr("text-anchor", "middle")
.style("font-size", "24px")
.text("Churn Over Time");
var employee1 = g.selectAll(".employee1")
.data(employees1)
.enter().append("g")
.attr("class", "employee1");
var employee2 = g.selectAll(".employee2")
.data(employees2)
.enter().append("g")
.attr("class", "employee2");
employee1.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", "A057AE");
employee1.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.time) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
employee2.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", "91BF50");
employee2.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.time) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
});
function type(d, _, columns) {
d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
</script>
And the data.csv file is the following:
date,Paul Productive Code,Paul Raw Code,Michelle Productive Code,Michelle Raw Code
201801,4.1,3.2,2.2,1.9
201802,4.2,3.5,3.4,1.9
201803,4.1,3.1,3.1,1.9
201804,4.5,3.8,3.2,2.3
201805,6.4,4.7,3.7,2.7
201806,8.6,5.5,3.2,2.2
Related
I'm trying to write a generic multiline chart function based on Mike Bostock's example (https://bl.ocks.org/mbostock/3884955).
I'm facing an issue wherein, the last tick label on my Monthly graph x-axis does not show up. The last tick appears fine on the Weekly graph x-axis.
JS Fiddle Link:
http://jsfiddle.net/Q5Jag/11879/
I'm suspecting the issue here could be due to the range specified for the x-axis which for some reason ignores the last value. But I'm not exactly sure what is going on here. Could anyone help me debug ?
Here is my code:
function renderMultiLineChart(datafile, chartDiv, xAxisLabel, yAxisLabel, graphCadence){
var margin = {top: 20, right: 60, bottom: 80, left: 60},
width = 760 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%m/%d/%y");
var x = d3.scaleUtc()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
//var xAxis = d3.axisBottom(x).tickFormat(function(d){ return d.x;});
switch(graphCadence) {
case "Daily":
var xAxis = d3.axisBottom(x).ticks(d3.timeDay.every(1)).tickFormat(d3.timeFormat("%m/%d"))
break;
case "Weekly":
var xAxis = d3.axisBottom(x).ticks(d3.timeSaturday.every(1)).tickFormat(d3.timeFormat("%m/%d"))
break;
case "Monthly":
//var xAxis = d3.axisBottom(x).ticks(d3.timeDay.every(1))
var xAxis = d3.axisBottom(x).ticks(d3.timeMonth.every(1)).tickFormat(d3.utcFormat("%m/%d"))
break;
}
var yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.count); })
var div = d3.select(chartDiv).append("div")
.attr("class", "tooltip")
.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 + ")");
//https://pharos-rest-service-iad.iad.proxy.amazon.com/s3/tool.csv
d3.csv(datafile, function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
//console.log(d);
d.date = parseDate(d.date);
});
var datapoints = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, count: +d[name]};
})
};
});
console.log(data);
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(datapoints, function(c) { return d3.min(c.values, function(v) { return v.count; }); }),
d3.max(datapoints, function(c) { return d3.max(c.values, function(v) { return v.count; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.style("text-anchor", "end")
.attr("transform", "rotate(-45)");
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 30) + ")")
.style("text-anchor", "middle")
.text(xAxisLabel);
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")
.style("fill", "black") // set the line colour
.text(yAxisLabel);
var datapoint = svg.selectAll(".datapoint")
.data(datapoints)
.enter().append("g")
.attr("class", "datapoint");
datapoint.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
j = -1;
datapoint.selectAll("circle")
.data(function(d){return d.values})
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d,i) { return x(d.date); })
.attr("cy", function(d) { return y(d.count); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", 1);
div.html("<b>"+d.count+"</b>")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
.style("fill", function(d,i) { if (i == 0) { j++ }; return color(datapoints[j].name); });
var legendRectSize = 8;
var legendSpacing = 80;
var legendHolder = svg.append('g')
// translate the holder to the right side of the graph
.attr('transform', "translate(" + (100+margin.left+margin.right) + ","+(height+margin.bottom-20)+")")
var legend = legendHolder.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr("transform", function (d, i) {
if (i === 0) {
dataL = legendRectSize + legendSpacing
return "translate(0,0)"
} else {
var newdataL = dataL
dataL += legendRectSize + legendSpacing
return "translate(" + (newdataL) + ",0)"
}
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + 5)
.attr('y', legendRectSize)
.text(function(d) { return d; });
});
}
renderMultiLineChart("https://gist.githubusercontent.com/techyogii/323024b01c1eb4d0c07637e183e1e6d7/raw/422ed207cc2c38426fa726795ecd963f153135dd/app_usage","div#multiChartMonthly","Snapshot Date","Metric Count","Monthly")
renderMultiLineChart("https://gist.githubusercontent.com/techyogii/8ed38bdb3b8e44194ee8570ef9cc5b75/raw/d0c85aaf9eaa7e8819fd6e6e210885b0cfa6f47d/app_usage_weekly","div#multiChartWeekly","Snapshot Date","Metric Count","Weekly")
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
/*display: none;*/
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
div.tooltip {
position: absolute;
text-align: center;
/*width: 60px;
height: 28px;*/
padding: 4px;
font: 14px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 10px;
pointer-events: none;
}
.legend {
font-size: 12px;
}
rect {
stroke-width: 2;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="multiChartWeekly">
<div id="multiChartMonthly">
Thanks,
Yogesh
I believe the problem lies in a misalignment between how d3.timeMonth works and how your data is formatted. You see that d3.timeMonth is labeling the first of every month, whereas the data is grouped on the last day of every month. So when you call x.domain(d3.extent(data, function(d) { return d.date; }));, the last data point's date is less than the next label would have been.
One potential solution is to change your parse function to bump all your month dates forward by one day to make them line up with the first of the month. See below.
To remove any dates that end mid-month, you can filter the data set after parsing dates to clean it up.
function renderMultiLineChart(datafile, chartDiv, xAxisLabel, yAxisLabel, graphCadence){
var margin = {top: 20, right: 60, bottom: 80, left: 60},
width = 760 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = function(dateString){
if(graphCadence == "Monthly"){
var date = new Date(dateString);
date.setDate(date.getDate()+1);
return date;
}
return d3.timeParse("%m/%d/%y")(dateString);
}
var x = d3.scaleUtc()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
//var xAxis = d3.axisBottom(x).tickFormat(function(d){ return d.x;});
switch(graphCadence) {
case "Daily":
var xAxis = d3.axisBottom(x).ticks(d3.timeDay.every(1)).tickFormat(d3.timeFormat("%m/%d"))
break;
case "Weekly":
var xAxis = d3.axisBottom(x).ticks(d3.timeSaturday.every(1)).tickFormat(d3.timeFormat("%m/%d"))
break;
case "Monthly":
//var xAxis = d3.axisBottom(x).ticks(d3.timeDay.every(1))
var xAxis = d3.axisBottom(x).ticks(d3.timeMonth.every(1)).tickFormat(d3.utcFormat("%m/%d"))
break;
}
var yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.count); })
var div = d3.select(chartDiv).append("div")
.attr("class", "tooltip")
.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 + ")");
//https://pharos-rest-service-iad.iad.proxy.amazon.com/s3/tool.csv
d3.csv(datafile, function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
if(graphCadence == "Monthly"){
data = data.filter(function(d){
return d.date.getDate() == 1
});
}
var datapoints = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, count: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(datapoints, function(c) { return d3.min(c.values, function(v) { return v.count; }); }),
d3.max(datapoints, function(c) { return d3.max(c.values, function(v) { return v.count; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.style("text-anchor", "end")
.attr("transform", "rotate(-45)");
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 30) + ")")
.style("text-anchor", "middle")
.text(xAxisLabel);
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")
.style("fill", "black") // set the line colour
.text(yAxisLabel);
var datapoint = svg.selectAll(".datapoint")
.data(datapoints)
.enter().append("g")
.attr("class", "datapoint");
datapoint.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
j = -1;
datapoint.selectAll("circle")
.data(function(d){return d.values})
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d,i) { return x(d.date); })
.attr("cy", function(d) { return y(d.count); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", 1);
div.html("<b>"+d.count+"</b>")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
.style("fill", function(d,i) { if (i == 0) { j++ }; return color(datapoints[j].name); });
var legendRectSize = 8;
var legendSpacing = 80;
var legendHolder = svg.append('g')
// translate the holder to the right side of the graph
.attr('transform', "translate(" + (100+margin.left+margin.right) + ","+(height+margin.bottom-20)+")")
var legend = legendHolder.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr("transform", function (d, i) {
if (i === 0) {
dataL = legendRectSize + legendSpacing
return "translate(0,0)"
} else {
var newdataL = dataL
dataL += legendRectSize + legendSpacing
return "translate(" + (newdataL) + ",0)"
}
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + 5)
.attr('y', legendRectSize)
.text(function(d) { return d; });
});
}
renderMultiLineChart("https://gist.githubusercontent.com/JstnPwll/5a24137a36c9246cf065c58d7f5bb5a5/raw/ff986ee88338e99d10ab93035ffacd3ffe92fd4e/gistfile1.txt","div#multiChartMonthly","Snapshot Date","Metric Count","Monthly")
renderMultiLineChart("https://gist.githubusercontent.com/techyogii/8ed38bdb3b8e44194ee8570ef9cc5b75/raw/d0c85aaf9eaa7e8819fd6e6e210885b0cfa6f47d/app_usage_weekly","div#multiChartWeekly","Snapshot Date","Metric Count","Weekly")
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
/*display: none;*/
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
div.tooltip {
position: absolute;
text-align: center;
/*width: 60px;
height: 28px;*/
padding: 4px;
font: 14px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 10px;
pointer-events: none;
}
.legend {
font-size: 12px;
}
rect {
stroke-width: 2;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="multiChartWeekly">
<div id="multiChartMonthly">
Im trying to append text to a Grouped Bar Chart in d3js v4, more specifically, the values corresponding to each bar. I want the numbers to be displayed inside the bars and I can't get it to work. (Like this: http://bl.ocks.org/ctiml/541d7cc770108ccff79a)
But I want it to work in d3js v4 instead.
Here's my code, I've commented out the part of the code that is supposed to append the text
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<style>
body {
margin:auto;
width:1100px;
}
.axis .domain {
display: none;
}
.bar1 {
opacity:.9;
}
.yaxis {
stroke-dasharray: 1 1;
opacity:.8;
font-family:arial;
font-size:10px;
}
path {
display:none;
}
.baseline {
stroke:#000;
stroke-width:1px;
shape-rendering: crispEdges;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body><br>
<div id="chart1"></div>
<script>
var margin = {top: 20, right: 110, bottom: 30, left: 40},
width = 350 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
formatCount = d3.format("s");
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand();
//.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["steelblue", "lightblue", "darkorange"]);
var g = d3.select("#chart1").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(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();
g.append("g")
.attr("class", "yaxis")
.call(d3.axisLeft(y).ticks(8,"s").tickSize(-width));
g.append("line")
.attr("class", "baseline")
.attr("x1",0)
.attr("x2",width)
.attr("y1",y(0))
.attr("y2",y(0));
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("class", "bar1")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0));
//g.selectAll(".bar-text")
// .data(data)
//.enter().append("text")
// .attr("class",function(d) { return "bar-text " + d.value; })
// .attr("x", function(d) { return x1(d.key)+20; })
// .attr("y", function(d) { return y(d.value)+10; })
//.attr("fill","#000")
// .text(function(d) { return formatCount(d.value)});
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "start")
.selectAll("g")
.data(keys.slice()) //.reverse
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width + 15)
.attr("y", 4)
.attr("width", 12)
.attr("height", 12)
.attr("fill", z);
legend.append("text")
.attr("x", width + 35)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
});
</script>
</body>
</html>
And here's the csv format:
State,Team 1,Team 2,Team 3
2015,2704659,4499890,2159981
2016,2027307,3277946,1420518
Lots of ways to do this; here's how I would do it.
First, keep a reference to the groups g element so that we can append our text with the bars:
var gE = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x0(d.State) + ",0)";
});
gE.selectAll("rect")
.data(function(d) {
...
Then use a sub-selection to add the text:
gE.selectAll("text")
.data(function(d) {
return [d['Team 1'], d['Team 2'], d['Team 3']];
})
.enter()
.append("text")
...
Running code is here.
I am implementing a multi series line chart in d3.js following the example on this block: http://bl.ocks.org/mbostock/3884955. However, the X axis is not displaying. The X axis should display the dates. Here is my code:
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%Y%m%d");
var x = d3.scaleBand().rangeRound([0, width]),
// xAxis = d3.svg.axis().scale(x).orient("bottom"),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.price); });
d3.tsv("data.tsv", type, function(error, data) {
if (error) throw error;
var products = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, price: d[id]};
})
};
});
x.domain(data.map( function(d) { return d.date; }));
y.domain([
d3.min(products, function(c) { return d3.min(c.values, function(d) { return d.price; }); }),
d3.max(products, function(c) { return d3.max(c.values, function(d) { return d.price; }); })
]);
z.domain(products.map(function(c) { return c.id; }));
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Price $");
var product = g.selectAll(".product")
.data(products)
.enter().append("g")
.attr("class", "product");
product.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.id); });
product.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.price) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
});
function type(d, _, columns) {
d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
Here is the resultant chart from my code:- http://bizvizmap.com/timeseries.html
You probably have some problem in your data, because this plunker has exactly your code, with some bogus data I created:
date,foo,bar,baz
20160101,11,23,45
20160102,21,22,35
20160103,44,76,54
20160104,31,23,45
20160105,41,63,15
20160106,12,22,25
Here is the plunker: https://plnkr.co/edit/xZY02zVTEh6o9RwNGJBw?p=preview
The ticks on the x axis are a mess because you're using the full JS date in an ordinal scale. Because of that, it makes more sense using a time scale, as in this other plunker: https://plnkr.co/edit/n5u8N9aK8YELtTCHexgR?p=preview
I am trying convert this chart I made, disregard the styles, using Highcharts, to this D3 multi-line chart.
This is the code for the d3 viz.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis--x path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%Y%m%d");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
d3.tsv("data.tsv", type, function(error, data) {
if (error) throw error;
var cities = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, temperature: d[id]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(d) { return d.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(d) { return d.temperature; }); })
]);
z.domain(cities.map(function(c) { return c.id; }));
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Temperature, ºF");
var city = g.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.id); });
city.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
});
function type(d, _, columns) {
d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
</script>
I would like to keep the data in a tsv and just convert the x-axis from using dates to using the raw numbers seen in the Highchart series. So instead of city, date, and temperature it would be batter, game number, and slugging percentage. I would also like to make it so that the lines end, like in the Highchart example, once there is no corresponding value, and not just have the line dip down to 0. It would also be nice if I could keep the hover effect from the Highchart example.
Unfortunately, I have little idea in how to achieve this. I know that I have to use a different function than the parseTime function currently in the script but that's about as far as I've gotten.
Change your scale to:
var x = d3.scaleLinear().range([0, width])
And don't parse to a date:
function type(d, _, columns) {
d.date = +d.date;
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
I have a stacked bar chart, which has labels in the every stack. Now I wanted to have another label at top of each bar in the graph. I am not able to place a label at the top of the bar, and also I am getting sum of all the values in the chart, where as I want sum for each bar.
Here is my code,
var groups = svg.selectAll("g.cost")
.data(dataset.reverse())
.enter().append("g")
.attr("class", "cost")
.style("fill", function (d, i) { return colors[i]; });
var sum = [0];
var svg = d3.select("svg");
var bar = groups.selectAll("g")
.data(function (d) { return d; })
.enter().append("g")
.attr("transform", function (d, i) { return "translate(0," + i * y(d.y0) - y(d.y0 + d.y) + ")"; });
bar.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())
bar.append("text")
.attr("x", function (d) { return x(d.x); })
.attr("y", function (d) { return y(d.y0 + d.y); })
.attr("dy", ".35em")
.attr('style', 'font-size:13px')
.text(function (d) { if (d.y != 0) { sum += d.y; return "$" + d.y; } })
.style('fill', 'black');
bar.append("text")
.attr("x", function (d) { return x(d.x); })
.attr("y", function (d) { return y(d.y0 + d.y); })
.attr("dy", ".35em")
.attr('style', 'font-size:13px')
.text( sum)
.style('fill', 'black');
jsfiddle: http://fiddle.jshell.net/1fsm8cst/3/
Summarising vertically is not natural with the stack layout since it groups in layers, but d3 has plenty of functionality to help. In this case, d3.nest, d3.sum and d3.values (the really nice thing about d3.sum is that it ignores NaN values). With these you can summarise the data by month and then you can use the same coordinate system and scales to position the summarised elements.
var margin = {top: 20, right: 300, bottom: 35, left: 50};
var width = 760 - margin.left - margin.right,
height = 400 - 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 = [
{ month: "Jan", MobileCoupon: "430000", Bonus: "240000", Promotions: "200000", Merchandise: "150000" },
{ month: "Feb", MobileCoupon: "250000", Bonus: "440000", Promotions: "200000", Merchandise: "150000" },
{ month: "Mar", MobileCoupon: "350000", Bonus: "180000", Promotions: "200000", Merchandise: "150000" },
];
var parse = d3.time.format("%b").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["MobileCoupon", "Bonus", "Promotions", "Merchandise"].map(function(fruit) {
return data.map(function(d) {
return {x: parse(d.month), y: +d[fruit]};
});
}));
var months = d3.nest()
.key(function(d){return parse(d.month)})
.rollup(function(leaves){
return d3.sum(leaves, function(d) {return d3.sum(d3.values(d))});
})
.entries(data);
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) { return d.x; }))
.rangeRoundBands([10, width-10], 0.35);
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 = ["#3D0000", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( d3.format("$,s") );
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%b"));
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
svg.append("g")
.call(xAxis)
.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 svg = d3.select("svg");
var bar = groups.selectAll("g")
.data(function(d) { return d; })
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.x) + ", 0)";
});
var sum=0;
bar.append("rect")
.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())
bar.append("text")
.attr("x", -6)
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("dy", ".35em")
.text(function(d) {sum+=d.y; return d3.format("$,s")(d.y); });
columns = svg.append("g")
.selectAll("text").data(months)
.enter().append("text")
.attr("x", function(d){
return x(d.key) + x.rangeBand()/2
})
.attr("y", function (d) {
return y(d.values);
})
.attr("dy", "1.35em")
.attr('style', 'font-size:13px')
.text( function (d){
return d3.format("$,s")(d.values);
})
.style({fill: 'black', "text-anchor": "middle"});
// svg.call(tip);
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, m) { return "translate(90," + (m+5) * 20 + ")"; });
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 "Mobile Coupon";
case 1: return "Bonus";
case 2: return "Promotions";
case 3: return "Merchandise";
}
});
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;}
text {
font: 10px sans-serif;
text-anchor: end;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
.legend
{
position: relative;
top: -401px;
left: 380px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I am currently a beginner in Javascript and D3.
Hope my solution serves the purpose :
http://jsfiddle.net/sandeepedara/n3ew5sqq/
var margin = {top: 20, right: 300, bottom: 35, left: 50};
var width = 760 - margin.left - margin.right,
height = 400 - 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 = [
{ month: "Jan", MobileCoupon: "430000", Bonus: "240000", Promotions: "200000", Merchandise: "150000" },
{ month: "Feb", MobileCoupon: "250000", Bonus: "440000", Promotions: "200000", Merchandise: "150000" },
{ month: "Mar", MobileCoupon: "350000", Bonus: "180000", Promotions: "200000", Merchandise: "150000" },
];
for (var key in data) {
var sum=0;
if (data.hasOwnProperty(key)) {
var obj = data[key];
for (var prop in obj) {
// important check that this is objects own property
// not from prototype prop inherited
if(obj.hasOwnProperty(prop)){
if(prop=="month"){console.log("month");}
else{
sum = sum + parseInt(obj[prop]);
obj.sum = sum;
}
}
}
}
}
var parse = d3.time.format("%b").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["MobileCoupon", "Bonus", "Promotions", "Merchandise","sum"].map(function(fruit) {
return data.map(function(d) {
return {x: parse(d.month), 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.35);
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 = ["#3D0000", "#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("%b"));
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
svg.append("g")
.call(xAxis)
.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 svg = d3.select("svg");
var bar = groups.selectAll("g")
.data(function(d) { return d; })
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * y(d.y0) - y(d.y0 + d.y) + ")"; });
bar.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())
bar.append("text")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("dy", ".35em")
.text(function(d) { return d.y; });
svg.call(tip);
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, m) { return "translate(90," + (m+5) * 20 + ")"; });
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 "Mobile Coupon";
case 1: return "Bonus";
case 2: return "Promotions";
case 3: return "Merchandise";
}
});