I am trying to follow this example here for a D3 stacked chart. I've tested it locally and it works fine.
I have adapted the code to match my csv dataset, but unfortunately I get issues with the calculation of y and height attributes:
Error: attribute y: Expected length, "NaN".
Error: attribute height: Expected length, "NaN".
Here is my adapted source code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Enterprise Elements Analysis - In/Out of Scope</title>
<script src="http://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<style type="text/css">
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;
}
</style>
</head>
<body>
<script type="text/javascript">
// Our D3 code will go here
var ratData = [];
d3.csv("./etcounts.csv", function(d) {
return {
type: d.type,
in_scope: +d.in_scope,
out_scope: +d.out_scope
};
}, function(error, rows) {
data = rows;
console.log(data);
createVisualization();
});
function createVisualization() {
// Setup svg using with margins
var margin = {bottom: 75, left: 15, right: 85};
var w = 200 - margin.left - margin.right;
var h = 175 - margin.bottom;
// get length of Array
var arrayLength = data.length; // length of dataset
var x_axisLength = 100; // length of x-axis in our layout
var y_axisLength = 100; // length of y-axis in our layout
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + ",10)");
// set up the properties for stack
var stack = d3.stack()
.keys(["In Scope", "Out Scope"])
.order(d3.stackOrderDescending)
.offset(d3.stackOffsetNone);
// transpose your data using stack
var series = stack(data);
// view the stack
console.log(series);
// setup the Y scale
var yScale = d3.scaleLinear()
.domain([0, d3.max(series, function(d) {
return d3.max(d, function(d) {
return d[1];
});
})])
.range([h, 0]);
// Set some colors into an array
var colors = ["#dfd6d6", "#d85f41"]; // choose colors
// Create groups for each series, rect elements for each segment
var groups = svg.selectAll("g.type")
.data(series)
.enter().append("g")
.attr("class", "type")
.style("fill", function(d, i) {
return colors[i]; // color the rectangles
});
// Create the rectangles
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d,i) {
return i * (x_axisLength/arrayLength) + 30; // Set x coordinate of rectangle to index of data value (i) *25
})
.attr("y", function(d) {
return yScale(d[1]); // set base of rectangle
})
.attr("height", function(d) {
return yScale(d[0]) - yScale(d[1]); // set height of rectangle
})
.attr("width", (x_axisLength/arrayLength) - 1) // set width of rectangle
.on("mouseover", function() {
tooltip.style("display", null); // hide tooltip
})
.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.data.city + ": " + (d[1] - d[0])); // populate tooltip
})
.on("mouseout", function() {
tooltip.style("display", "none");
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + i * 50 + ", 110)"; });
legend.append("rect")
.attr("x", w - 70)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", w - 49)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "In";
case 1: return "Out";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px");
// Create y-axis
svg.append("line")
.attr("x1", 30)
.attr("y1", 0)
.attr("x2", 30)
.attr("y2", 100)
.attr("stroke-width", 2)
.attr("stroke", "black");
// y-axis label
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "middle")
.text("Elements")
.attr("transform", "translate(20, 50) rotate(-90)")
.attr("font-size", "14px")
.attr("font-family", "'Open Sans', sans-serif");
// Create x-axis
svg.append("line")
.attr("x1", 30)
.attr("y1", 100)
.attr("x2", 130)
.attr("y2", 100)
.attr("stroke-width", 2)
.attr("stroke", "black");
}
</script>
</body>
</html>
My Dataset (etcounts.csv) is here:
type,in_scope,out_scope
ERKRS,1,1
KKBER,6,5
KOKRS,1,31
BUKRS,78,143
VKORG,23,13
BWKEY,51,6
EKORG,5,6
WERKS,51,65
LGORT,9,180
SPART,9,3
VTWEG,2,0
PERSA,47,73
Unfortunately my D3/JS skills are not quite up to par, but I would appreciate any help. Thanks - John
Instead of
var stack = d3.stack()
.keys(["In Scope", "Out Scope"]) <-- there is no key as such
.order(d3.stackOrderDescending)
.offset(d3.stackOffsetNone);
it should have been:
var stack = d3.stack()
.keys(["in_scope", "out_scope"])
.order(d3.stackOrderDescending)
.offset(d3.stackOffsetNone);
Reason: there is no keys in your CSV "In Scope", "Out Scope"
It should have been "in_scope", "out_scope"
EDIT
For tool tip :
tooltip.select("text").text(d.data.city + ": " + (d[1] - d[0]));
should have been
tooltip.select("text").text(d.data.type + ": " + (d[1] - d[0]));
Reason: There is no data.city in your CSV.
working code here
Related
I've got a line chart that needs circles on every other point for column A and C (not for column B). I've struggled to figure out how to do it. This is my line chart without the circles:
date,A=count,A=rank,B=count,B=rank,C=count,C=rank
2016-11-01,60588,213,51915,46,41200,10
2016-12-01,73344,216,58536,47,41230,10
2017-01-01,64164,219,53203,50,51220,12
2017-02-01,85295,224,34047,52,61000,15
2017-03-01,86089,226,44636,54,71200,16
2017-04-01,96871,230,55281,55,71000,10
2017-05-01,97622,234,85879,55,67900,10
I've tried dozens of solutions and I'm very stuck! Here is one of the things I've tried:
linesAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) { return xScale(d.date); })
.attr("cy", function(d) { return yScale(d.measurement); });
But that is giving back NaN for the cx and cy values.
My full code looks like this:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
path.line-0 {
fill: none;
stroke: #1F77B4;
}
path.line-1 {
fill: none;
stroke: #FF7F0E;
}
</style>
</head>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="lib/d3.v5.min.js"></script>
<div id="svgdiv"></div>
<script>
//------------------------1. PREPARATION------------------------//
//-----------------------------SVG------------------------------//
var columns=['A=count','B=count'];
var columnsB=['A=rank','B=rank'];
var width = 960;
var height = 500;
var margin = 5;
var padding = 5;
var adj = 75;
// we are appending SVG first
var svg = d3.select("body").append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "-"
+ adj + " -"
+ adj + " "
+ (width + adj *3) + " "
+ (height + adj*3))
.style("padding", padding)
.style("margin", margin)
.classed("svg-content", true);
//-----------------------------DATA-----------------------------//
var timeConv = d3.timeParse("%Y-%m-%d");
var formatTime = d3.timeFormat("%b %y")
var dataset = d3.csv("toShare.csv");
dataset.then(function(data) {
console.log(data.columns.slice(1))
var slices = columns.map(function(id) {
return {
id: id,
values: data.map(function(d){
return {
date: timeConv(d.date),
measurement: +d[id]
};
})
};
})
//----------------------------SCALES----------------------------//
var xScale = d3.scaleTime().range([0,width]);
var yScale = d3.scaleLinear().rangeRound([height, 0]);
xScale.domain(d3.extent(data, function(d){
return timeConv(d.date)}));
yScale.domain([(0), d3.max(slices, function(c) {
return d3.max(c.values, function(d) {
return d.measurement + 4; });
})
]);
//-----------------------------AXES-----------------------------//
var yaxis = d3.axisLeft()
.ticks(9)
.scale(yScale);
var xaxis = d3.axisBottom()
.ticks(7)
.scale(xScale);
//----------------------------LINES-----------------------------//
var line = d3.line()
.x(function(d) { return xScale(d.date); })
.y(function(d) { return yScale(d.measurement); });
let id = 0;
var ids = function () {
return "line-"+id++;
}
//-------------------------2. DRAWING---------------------------//
//-----------------------------AXES-----------------------------//
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xaxis)
.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
50 + ")")
.style("text-anchor", "middle")
.text("Month");
svg.append("g")
.attr("class", "axis")
.call(yaxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - adj)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Running Total");
//----------------------------LINES-----------------------------//
var linesAndDots = svg.selectAll("lines")
.data(slices)
.enter()
.append("g");
linesAndDots.append("path")
.attr("class", ids)
.attr("d", function(d) { return line(d.values); });
linesAndDots.selectAll("line-circle")
.data(data)
.enter().append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) {
console.log("id", id)
return 5; })
.attr("cy", function(d) { return 40; });
//Add the label on the right
linesAndDots.append("text")
.attr("class", ids)
.datum(function(d) {
return {
id: d.id,
value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) {
return "translate(" + (xScale(d.value.date) + 10)
+ "," + (yScale(d.value.measurement) + 5 ) + ")"; })
.attr("x", 5)
.text(function(d) { return d.id.replace("=count", ""); });
});
</script>
</body>
Thanks for the help!
The problem can be inspected by seeing what d really is in the cx and cy function.
cx's problem: you didnt parse the date string into a Date object like you did for the slices data; cy's problem: the data item has no measurement key.
You have used both data and slices. To make the code more consistent, I fix the code of circle drawing using slices too.
linesAndDots
.selectAll(".data-circle")
.data(d=>d.values) // `d` now is the one of the two entries of `slices`,
//and we want to use the `values` array of each entry.
.enter()
.append("circle")
.attr("class", "data-circle")
.attr("r", 5)
.attr("cx", function(d) {
return xScale(d.date);
})
.attr("cy", function(d) {
return yScale(d.measurement)
});
A demo here
I have drawn the following chart with D3 Charting tool v4. I have attached the full code at the bottom of this post.
The red line is the target goal to be achieved. The following code block is drawing this line:
var targetGoalArr = [7];
svg.selectAll(".targetgoal")
.data(targetGoalArr)
.enter().append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#cc0000");
Now I need to label this line with the text Growth Target (7) to the right of it and in two lines. The label has to be broken in two lines as well!
The following screenshot shows the desired output.
How can I achieve the above?
One more thing I am not able to draw is the Y-Axis baseline. In my chart (with red line) I am creating the horizontal lines using a custom tick array. Here is the code:
function draw_yAxis_gridlines() {
return d3.axisLeft(y)
.tickValues(yTicks);
}
svg.append("g")
.attr("class", "grid axis")
.call(draw_yAxis_gridlines()
.tickSize(-width)
);
However, if I do not use custom ticks for Y-Axis, the baseline appears but I am missing the horizontal grid lines. I have to display both at the same time.
Here is my full code:
public function evd_unitary_growth_plan_chart( $data_str ){
ob_start(); ?>
<style> /* set the CSS */
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.5;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
.axis {
font-size: 13px;
font-family: 'Roboto';
color: #808888;
}
</style>
<script type="text/javascript">
var h = 300;
var w = 750;
var barPadding = 2;
function barColor(data_month, current_month) {
if( parseInt(data_month) >= current_month)
return "#008600";
else
return "#c4c4c4";
}
// set the dimensions and margins of the graph
var margin = {top: 30, right: 20, bottom: 30, left: 40},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var data = <?php echo $data_str ?>;
// set the ranges
var x = d3.scaleBand().range([0, width]).padding(0.2);
var y = d3.scaleLinear().range([height, 0]);
var svg = d3.select("#ecbg_unitary").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 + ")");
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.month; }));
var y_domain_upperBound = d3.max(data, function(d) { return d.points; });
y_domain_upperBound = Math.round(y_domain_upperBound / 10) * 10 + 10;
y.domain([0, y_domain_upperBound]);
// Create Y-Axis tick array to draw grid lines
var yTicks = [];
var tickInterval = 5;
for(var i = 0; i <= y_domain_upperBound; i = i + tickInterval) {
yTicks.push(i);
}
console.log(yTicks);
// gridlines in y axis function
function draw_yAxis_gridlines() {
return d3.axisLeft(y)
.tickValues(yTicks);
}
// Reference line - The red line
var targetGoalArr = [7];
svg.selectAll(".targetgoal")
.data(targetGoalArr)
.enter().append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#cc0000");
// append the rectangles for the bar chart
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) {
return x(d.month);
})
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.points); })
.attr("height", function(d) { return height - y(d.points); })
.attr("fill", function(d){return barColor(d.data_month_number, d.current_month_number)});
// column labels
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.points;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return x(d.month) + x.bandwidth() / 2;
})
.attr("y", function(d) {
return y(d.points) - 10;
})
.attr("font-family", "Roboto")
.attr("font-size", "13px")
.attr("font-weight", "bold")
.attr("fill", "#606668");
// add the x Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y gridlines
svg.append("g")
.attr("class", "grid axis")
.call(draw_yAxis_gridlines()
.tickSize(-width)
);
</script>
<?php return ob_get_clean();
}
To add a label to your target line, you are best to create group (g) element, and then append a line and text element to it. The g element can be translated to the correct y position, so that the line and text can be positioned relatively to the g.
var targetGoalArr = [7];
var target = g.selectAll(".targetgoal")
.data(targetGoalArr)
.enter()
.append("g")
.attr("transform", function(d){
return "translate(0, " + y(d) +")"
})
target.append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", 0) //these can be omitted
.attr("y2", 0)
.style("stroke", "#cc0000");
target.append("text")
.text(function(d){ return "Target growth: " + d })
.attr("x", width)
.attr("y", "0.35em")
I am trying to add a regression line to a scatter plot. I am using the below code as an example for scatter plot.
http://bl.ocks.org/majetisiri/57da501b3182bd08d17402261c7187f7
I am appending the path to svg as explained here:
Plot regression line on a scatter plot from regression coefficients
But the regression line is not visible.
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<!-- Example based on http://bl.ocks.org/mbostock/3887118 -->
<!-- Tooltip example from http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html -->
<style>
body {
font: 11px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
.tooltip {
position: absolute;
width: 200px;
height: 28px;
pointer-events: none;
}
h1 {
text-align: center;
}
h2 {
text-align: left;
}
</style>
<body>
<p><span><label for="y-axis">Select y-axis</label></span>
<select id="y-value">
<option value="FLFPR">Female LFPR</option>
<option value="lnGDP">Log GDP per capita</option>
<option value="Fertility">Fertility rate</option>
</select>
<p><span><label for="x-axis">Select x-axis</label></span>
<select id="x-value">
<option value="FLFPR">Female LFPR</option>
<option value="lnGDP">Log GDP per capita</option>
<option value="Fertility">Fertility rate</option>
</select>
<button onclick="setGraph()">submit</button>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="https://unpkg.com/d3-regression#1.2.3/dist/d3-regression.min.js"></script>
<script>
function drawGraph(xText, yText) {
$('svg').remove();
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
/*
* value accessor - returns the value to encode for a given data object.
* scale - maps value to a visual display encoding, such as a pixel position.
* map function - maps from data value to display value
* axis - sets up axis
*/
// setup x
var xValue = function(d) { return d[xText];}, // data -> value
xScale = d3.scale.linear().range([0, width]), // value -> display
xMap = function(d) { return xScale(xValue(d));}, // data -> display
xAxis = d3.svg.axis().scale(xScale).orient("bottom");
// setup y
var yValue = function(d) { return d[yText];}, // data -> value
yScale = d3.scale.linear().range([height, 0]), // value -> display
yMap = function(d) { return yScale(yValue(d));}, // data -> display
yAxis = d3.svg.axis().scale(yScale).orient("left");
// setup fill color
var cValue = function(d) { return d.IG;},
color = d3.scale.category20();
// add the graph canvas to the body of the webpage
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 + ")");
// add the tooltip area to the webpage
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// load data
d3.csv("scatter2.csv", function(error, data) {
// change string (from CSV) into number format
data.forEach(function(d) {
d[yText] = +d[yText];
d[xText] = +d[xText];
//console.log (d.School);
//console.dir (d);
});
// don't want dots overlapping axis, so add in buffer to data domain
xScale.domain([d3.min(data, xValue)-1, d3.max(data, xValue)+1]);
yScale.domain([d3.min(data, yValue)-1, d3.max(data, yValue)+1]);
// x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text(xText);
// y-axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yText);
// draw dots
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 6.6)
.attr("opacity", 0.9)
.style("stroke", function(d) { return color(cValue(d));})
.attr("cx", xMap)
.attr("cy", yMap)
.style("fill", function(d) { return color(cValue(d));})
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d["Player"] + "<br/> " + d.School + "<br/>(" + xValue(d)
+ ", " + yValue(d) + ")")
.style("left", (d3.event.pageX + 10) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// draw legend
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(10," + (i+7) * 20 + ")"; });
// draw legend colored rectangles
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
// draw legend text
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d;})
// get regression coefficients
regData = data.map(item => ({x: item[xText], y: item[yText]}));
res = drawRegressionLine(regData)
console.log("regression results")
console.log(res)
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
// var line = d3.svg.line()
// .x(function(d) { return xScale(d['x']); })
// .y(function(d) { return yScale(d['y']); });
// var lineFunction = d3.svg.line()
// .x(function(d) { return d.x; })
// .y(function(d) { return d.y; })
// .interpolate("linear");
var regLine = svg.append("path")
.datum(res)
.attr("d", line)
.style("stroke", "steelblue")
.style("stroke-width", "6px");
// var lineGraph = svg.append("path")
// .attr("d", line(res))
// .attr("stroke", "blue")
// .attr("stroke-width", 2)
// .attr("fill", "black");
});
}
// draw regression line
function drawRegressionLine(regData) {
console.log("beginning")
console.log("inside draw regression lilne")
linearRegression = d3.regressionLinear()
.x(d => d.x)
.y(d => d.y);
res = linearRegression(regData)
return res;
}
// drawGraph('Passing TD', 'Rushing TD');
function setGraph() {
console.log("inside set graph")
console.log($('#x-value').val())
drawGraph($('#x-value').val(), $('#y-value').val());
}
</script>
</body>
</html>
Please help me find what is wrong with the code.
It looks like you are not passing the your linear regression into your x and y scale.
Try:
var line = d3.svg.line()
.x(function(d) { return xScale(d[0]); })
.y(function(d) { return yScale(d[1]); })
.interpolate("linear");
i've been trying to create an interactive multi line d3 chart.it's very simple right now, and this is what it looks like
enter image description here
the vertical line will show the values at each x point. this mouseover works. but the mouseover/mouseout I want is to select the line I am on, and blur the rest.
I basically want something like this: http://bl.ocks.org/Matthew-Weber/5645518
But nothing I do really works..
Here's the code
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 60, left: 50},
width = 1200 - margin.left - margin.right,
height = 670 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%b %Y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var priceline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.price); })
// Adds the svg canvas
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 + ")");
// Get the data
d3.csv("data.csv", function(error, data) {
// if (error) return console.error(error);
data.forEach(function(d) {
d.date = parseDate(d.date.toString());
d.price = +d.price;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
// Nest the entries by symbol
var dataNest = d3.nest()
.key(function(d) {return d.symbol;}) //change to name
.entries(data)
var color = d3.scale.category10();
// Loop through each symbol / key
dataNest.forEach(function(d) {
console.log("d", d);
svg.append("path")
.attr("class", "line")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key); })
.attr("d", priceline(d.values))
// .on("mouseover", mouseover) this doesn't work! I am thinking it's because I am nesting + using a for loop for each line, but I have to do this since my dataset is quite big and idk how many lines I will be needing
// .on("mouseout", mouseout)
});
function mouseover(d) {
console.log("no");
var me = this;
//d3.select(d.line).classed("line--hover", true);
d3.selectAll(".line").classed("line--hover", function() {
return (this === me);
}).classed("line--fade", function() {
return (this !== me);
});
}
function mouseout(d) {
d3.selectAll(".line")
.classed("line--hover", false)
.classed("line--fade", false);
}
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
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("Price in USD");
var dataL = 0;
var offset = 100;
var legend = d3.select('svg')
.append("g")
.selectAll("g")
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr("transform", function (d, i) {
if (i === 0) {
dataL = d.length + offset
return "translate(0,0)"
} else {
var newdataL = dataL
dataL += d.length + offset
return "translate(" + (newdataL) + ",0)"
}
});
legend.append('text')
.attr("x", 68)
.attr("y", 650)
.text(function (d, i) {
return d
})
.attr("class", "textselected")
.style("text-anchor", "start")
.style("font-size", 15)
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
mouseG.append("path") // this is the black vertical line to follow mouse
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(dataNest)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
// console.log("bye");
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
var xDate = x.invert(mouse[0]),
bisect = d3.bisector(function(d) { return d.date; }).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + 1050 + "," + 620 +")";
});
});
});
The data looks something like this:
symbol,date,price
MSFT,Jan 2000,39.81
MSFT,Feb 2000,36.35
MSFT,Mar 2000,43.22
MSFT,Apr 2000,28.37
MSFT,May 2000,25.45
MSFT,Jun 2000,32.54
MSFT,Jul 2000,28.4
MSFT,Aug 2000,28.4
MSFT,Sep 2000,24.53
MSFT,Oct 2000,28.02
MSFT,Nov 2000,23.34
MSFT,Dec 2000,17.65
MSFT,Jan 2001,24.84
MSFT,Feb 2001,24
MSFT,Mar 2001,22.25
MSFT,Apr 2001,27.56
symbol represents companies, which I might have around 100 of. (this is just sample data)
the css is very simple
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.line--fade {
opacity: 0.3;
}
.line--hover {
stroke-width: 4px;
opacity: 1.0;
}
I also tried using tipsy.js but I'm not sure how to integrate it properly into my code.
Thank you!!
Currently, in case of D3 Hierarchical Bar Chart, Each blue bar represents a folder, whose length encodes the total size of all files in that folder (and all subfolders). I want that the blue bar should encode the length (not size) of the total no. of items within it. For example, in below example if I click on 'vis', it has 7 items within it. Therefore, the the length of the bar for 'vis' should be 7. Similarly for all items in all folders. Here's the link - https://bl.ocks.org/mbostock/1283663
<!DOCTYPE html>
<html>
<head>
<style>
text {
font: 10px sans-serif;
}
rect.background {
fill: white;
}
.axis {
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
</style>
<title>D3</title>
</head>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 30, right: 30, bottom: 40, left: 50},
width = window.innerWidth - margin.left - margin.right-100,
height = window.innerHeight - margin.top - margin.bottom-100;
var y = d3.scale.linear()
.range([height, 0]);
var barHeight = 20;
var color = d3.scale.ordinal()
.range(["steelblue", "#ccc"]);
var duration = 750,
delay = 25;
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
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 + ")");
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", up);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.append("line")
.attr("x1", "100%");
svg.append("g")
.attr("class", "y axis");
d3.json("https://jsonblob.com/api/f577d19c-0f2b-11e7-a0ba-09040711ce47", function(error, root) {
if (error) throw error;
partition.nodes(root);
y.domain([0, root.value]).nice();
down(root, 1000);
});
function down(d,i) {
if (!d.children || this.__transition__) return;
var end = duration + d.children.length * delay;
// Mark any currently-displayed bars as exiting.
var exit = svg.selectAll(".enter")
.attr("class", "exit");
// Entering nodes immediately obscure the clicked-on bar, so hide it.
exit.selectAll("rect").filter(function(p) { return p === d; })
.style("fill-opacity", 1e-6);
// Enter the new bars for the clicked-on data.
// Per above, entering bars are immediately visible.
var enter = bar(d)
.attr("transform", stack(i))
.style("opacity", 1);
// Have the text fade-in, even though the bars are visible.
// Color the bars as parents; they will fade to children if appropriate.
enter.select("text").style("fill-opacity", 1e-6);
enter.select("rect").style("fill", color(true));
// Update the x-scale domain.
y.domain([0, d3.max(d.children, function(d) { return d.value; })]).nice();
// Update the x-axis.
svg.selectAll(".y.axis").transition()
.duration(duration)
.call(yAxis);
// Transition entering bars to their new position.
var enterTransition = enter.transition()
.duration(duration)
.delay(function(d, i) { return i * delay; })
.attr("transform", function(d, i) { return "translate(" + barHeight * i * 2.5 + "," + 0 + ")"; });
// Transition entering text.
enterTransition.select("text")
.style("fill-opacity", 1);
// Transition entering rects to the new y-scale.
enterTransition.select("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(!!d.children); });
// Transition exiting bars to fade out.
var exitTransition = exit.transition()
.duration(duration)
.style("opacity", 1e-6)
.remove();
// Transition exiting bars to the new y-scale.
exitTransition.selectAll("rect")
.attr("height", function(d) { return height - y(d.value); });
// Rebind the current node to the background.
svg.select(".background")
.datum(d)
.transition()
.duration(end);
d.index = i;
}
function up(d) {
if (!d.parent || this.__transition__) return;
var end = duration + d.children.length * delay;
// Mark any currently-displayed bars as exiting.
var exit = svg.selectAll(".enter")
.attr("class", "exit");
// Enter the new bars for the clicked-on data's parent.
var enter = bar(d.parent)
.attr("transform", function(d, i) { return "translate(" + barHeight * i * 2.5 + "," + 0 + ")"; })
.style("opacity", 1e-6);
// Color the bars as appropriate.
// Exiting nodes will obscure the parent bar, so hide it.
enter.select("rect")
.style("fill", function(d) { return color(!!d.children); })
.filter(function(p) { return p === d; })
.style("fill-opacity", 1e-6);
// Update the y-scale domain.
y.domain([0, d3.max(d.parent.children, function(d) { return d.value; })]).nice();
// Update the y-axis.
svg.selectAll(".y.axis").transition()
.duration(duration)
.call(yAxis);
// Transition entering bars to fade in over the full duration.
var enterTransition = enter.transition()
.duration(end)
.style("opacity", 1);
// Transition entering rects to the new y-scale.
// When the entering parent rect is done, make it visible!
enterTransition.select("rect")
.attr("height", function(d) { return height - y(d.value); })
.attr("y", function(d) { return y(d.value); })
.each("end", function(p) { if (p === d) d3.select(this).style("fill-opacity", null); });
// Transition exiting bars to the parent's position.
var exitTransition = exit.selectAll("g").transition()
.duration(duration)
.delay(function(d, i) { return i * delay; })
.attr("transform", stack(d.index));
// Transition exiting text to fade out.
exitTransition.select("text")
.style("fill-opacity", 1e-6);
// Transition exiting rects to the new scale and fade to parent color.
exitTransition.select("rect")
.attr("height", function(d) { return height - y(d.value); })
.attr("y", function(d) { return y(d.value); })
.style("fill", color(true));
// Remove exiting nodes when the last child has finished transitioning.
exit.transition()
.duration(end)
.remove();
// Rebind the current parent to the background.
svg.select(".background")
.datum(d.parent)
.transition()
.duration(end);
}
// Creates a set of bars for the given data node, at the specified index.
function bar(d) {
var bar = svg.insert("g", ".x.axis")
.attr("class", "enter")
.attr("transform", "translate(15,0)")
.selectAll("g")
.data(d.children)
.enter().append("g")
.style("cursor", function(d) { return !d.children ? null :
"pointer"; })
.on("click", down);
bar.append("text")
.attr("x", barHeight / 2)
.attr("y", height + 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) { return d.name; })
.attr("transform", "rotate(45 " + (barHeight / 2) + " " + (height +
10) + ")")
bar.append("rect")
.attr("width", barHeight)
.attr("height", function(d) { return height - y(d.value); })
.attr("y", function(d) { return y(d.value); });
return bar;
}
// A stateful closure for stacking bars horizontally.
function stack(i) {
var y0 = 0;
return function(d) {
var tx = "translate(" + barHeight * i * 1.5 + "," + y0 + ")";
y0 += y(d.value);
return tx;
};
}
bar.append("rect")
.attr("width", barHeight)
.attr("height", function(d) { return height - y(d.value); })
the d.value defines the number that is used to display.
if you need something else, pick the property of the object (d) that is relevant to you.