Determining bar width, with a 3d matrix - javascript

I have my data as follows:
var data = [[-5,7,10],[10,15,25],[-18,-14,-6]];
var margin = {top: 0, right: 30, bottom: 80, left: 30},
width = 180 - margin.left - margin.right,
height = 295 - margin.top - margin.bottom;
var x0 = Math.max(-d3.min(data[0]), d3.max(data[2]));
var x = d3.scale.linear()
.domain([-x0, x0])
.range([0, width]);
var y = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, height], .2);
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.selectAll(".bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d, i) { return x(Math.min(0, d[0])); })
.attr("y", function(d, i) { return y(i); })
.attr("width", function(d, i) { return Math.abs(x(d[2]) - x(d[0])); })
.attr("height", y.rangeBand())
.append("rect");
//y axis
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x(0))
.attr("x2", x(0))
.attr("y1", 0)
.attr("y2", height+10);
What I want to do is create a bar chart where data[0] is the starting point, data[1] has a vertical line bisecting the bar and data[2] is the max value of the bar. So far, I think I'm covering the first two use cases correctly, but the third one fails. Any ideas?
http://jsfiddle.net/NPqEm/

Related

d3v4 why are my line elements not appearing?

I am in the process of making a simple set of boxplots on a graph, but for some reason the line elements are not showing up
Code is:
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 40
},
width = 900 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var svgBox = d3.select("#my_dataviz")
.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 yScale = d3.scaleLinear()
.range([height, 0]);
var xScale = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.05)
.align(0.1);
var center = 200
var width = 100
d3.csv("boxPlotData.csv", function(dataset) {
var max = d3.max(dataset, function(d) {
return +d.max;
});
yScale.domain([0, max])
xScale.domain(dataset.map(function(d) {
return d.borough;
}));
svgBox.append("g").call(d3.axisLeft(yScale));
svgBox.append("g").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(xScale));
svgBox.selectAll("line").data(dataset).enter()
.append("line")
.attr("x1", d => xScale(d.borough) + width / 3.5)
.attr("x2", d => xScale(d.borough) + width / 3.5)
.attr("y1", d => yScale(+d.min))
.attr("y2", d => yScale(+d.max))
.attr("stroke", "black");
svgBox.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", d => xScale(d.borough) + width / 3.5)
.attr("y", d => yScale(+d.q3))
.attr("height", d => (yScale(+d.q1) - yScale(+d.q3)))
.attr("width", width)
.attr("stroke", "black")
.style("fill", "#69b3a2");
});
And my data is of the form
Data CSV
The code is entering the "rect" elements as expected, but the line components aren't showing up anywhere in the html?
The issue is you cannot just use svgBox.selectAll("line") because it will select the axis tick marks as well as the axis lines. Instead, I suggest add a class to your line with attr("class", "line") and use svgBox.selectAll(".line") to specifically select lines to append.
So the line append snippet should be:
svgBox.selectAll(".line")
.data(dataset)
.enter()
.append("line")
.attr("class", "line")
.attr("x1", d => xScale(d.borough) + width / 3.5)
.attr("x2", d => xScale(d.borough) + width / 3.5)
.attr("y1", d => yScale(+d.min))
.attr("y2", d => yScale(+d.max))
.attr("stroke", "black");
Here is the working snippet Block: https://bl.ocks.org/akulmehta/4b29fb357ea7f02a1b47b611e03a5468/

How to limit number of ticks to display in D3.js?

I found an example. http://jsfiddle.net/aWJtJ/8/, for limiting number of ticks but it uses D3.js version 3. I converted the code to version 5 but I am not getting the same result. It shows only two bars. Complete Fiddle: http://jsfiddle.net/pmLf095y/
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 10},
body_width = 500,
width = body_width - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Scales.
var x = d3.scaleTime().range([width/data.length/2, width-width/data.length/2]);
var y = d3.scaleLinear().range([height, 0]);
// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");
// X-axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat('%b %Y'));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Set scale domains.
x.domain(data.map(d => new Date(d.datestr)));
y.domain([0, d3.max(data, function(d) { return d.air_used; })]);
// Call x-axis.
d3.select(".x.axis")
.transition().duration(1000)
.call(xAxis);
// Draw bars.
var bars = svg.selectAll(".air_used")
.data(data, function(d) { return d.datestr; });
bars.exit().remove();
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date) - width/data.length/2; })
.attr("width", width / data.length)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", width / data.length)
.attr("x", function(d) { return x(new Date(d.datestr)) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
To set the x domain, you don't need to map the dates but to find the extent of the dates.
x.domain(d3.extent(data, (d => new Date(d.datestr))));
Changed code:
var data = [
{
"air_produced": 0.660985,
"air_used": 0.342706,
"datestr": "2012-12-01 00:00:00",
"energy_used": 0.106402
},
{
"air_produced": 0.824746,
"air_used": 0.400776,
"datestr": "2013-01-01 00:00:00",
"energy_used": 0.250462
},
{
"air_produced": 0.181898,
"air_used": 0.003541,
"datestr": "2013-02-01 00:00:00",
"energy_used": 0.000582
},
{
"air_produced": 1.096685,
"air_used": 0.97719,
"datestr": "2013-03-01 00:00:00",
"energy_used": 0.923212
},
{
"air_produced": 0.283379,
"air_used": 0.241088,
"datestr": "2013-04-01 00:00:00",
"energy_used": 0.23381
}
];
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 10},
body_width = 500,
width = body_width - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Scales.
var x = d3.scaleTime().range([width/data.length/2, width-width/data.length/2]);
var y = d3.scaleLinear().range([height, 0]);
// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");
// X-axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat('%b %Y'));
// Set scale domains.
x.domain(d3.extent(data, (d => new Date(d.datestr))));
y.domain([0, d3.max(data, function(d) { return d.air_used; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
// Call x-axis.
d3.select(".x.axis")
.transition().duration(1000)
.call(xAxis);
// Draw bars.
var bars = svg.selectAll(".air_used")
.data(data, function(d) { return d.datestr; });
bars.exit().remove();
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date) - width/data.length/2; })
.attr("width", width / data.length)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", width / data.length)
.attr("x", function(d) { return x(new Date(d.datestr)) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.air_used {
fill: steelblue;
}
.x.axis path {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="section system-efficiency">
<h4>SYSTEM EFFICIENCY</h4>
</div>
Hope this clears up.
The following line returns only one value [0] but I need to define range [0, ?]:
x.domain(data.map(d => new Date(d.datestr)));
I changed back to original line:
x.domain(d3.extent(data, function(d) { return d.date; }));
Complete code:
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 10},
body_width = 600,
width = body_width - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Scales.
var x = d3.scaleTime().range([width / data.length / 2, width - width / data.length / 2]);
var y = d3.scaleLinear().range([height, 0]);
// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");
// X-axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat('%b %Y'));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");
data.forEach(function(d) {
d.date = parseDate(d.datestr);
});
console.log(data);
// Set scale domains.
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function (d) {
return d.air_used;
})]);
// Call x-axis.
d3.select(".x.axis")
.transition().duration(1000)
.call(xAxis);
// Draw bars.
var bars = svg.selectAll(".air_used")
.data(data, function (d) {
return d.datestr;
});
bars.exit().remove();
bars.transition().duration(1000)
.attr("x", function (d) {
return x(d.date) - width / data.length / 2;
})
.attr("width", width / data.length)
.attr("y", function (d) {
return y(d.air_used);
})
.attr("height", function (d) {
return height - y(d.air_used);
});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", width / data.length)
.attr("x", function (d) {
return x(d.date) - (width / data.length) / 2;
})
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function (d) {
return y(d.air_used);
})
.attr("height", function (d) {
return height - y(d.air_used);
});
Note: I took the v3 example from one of stackoverflow questions, but now I could not find it to reference.

how do i clear my visualization in order to make space for the updated visualization using svg?

here's my d3.js function to visualize a bar graph:
function barGraph(data1)
{
// console(data1.count);
var i = 0;
data1.forEach(function(d){
while(i>0){
d.avspeed= +d.avspeed;
d.duration = +d.duration;
i--;
}
})
//console.log(data1.avspeed);
// console.log(data1.avspeed);
var margin = {top: 40, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//console.log(data1.avspeed);
console.log("hey1");
var formatPercent = d3.format("");
console.log("hey2");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
console.log("hey");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
console.log("hey3");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Avg Speed:</strong> <span style='color:red'>" + d.avspeed + "</span>";
})
var svg = d3.select("#rightside").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.call(tip);
console.log("heyllo ");
x.domain(data1.map(function(d) { return d.tripid; }));
y.domain([0, d3.max(data1, function(d) { return d.avspeed; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Avg Speed");
svg.selectAll(".bar")
.data(data1)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.tripid); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.avspeed); })
.attr("height", function(d) { return height - y(d.avspeed); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
console.log(d.avspeed);
console.log("hello234");
function type(d) {
d.avspeed = +d.avspeed;
return d;
}
}
It displays a graph based on the selected region on the map dynamically. If i select new region, another graph is being created below the old graph. I want the old graph to clear and new graph to be in place of old graph. How do I achieve that.
I am new to d3.js
Thanks in advance.
You can clear away anything inside of the parent container with .html("") while creating your svg:
var svg = d3.select("#rightside").html("").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 + ")");
remove also works but I prefer this slightly terser way of doing it.
Before appending the new avg, just call d3.select('svg').remove(); That will remove the first svg on the page (I'm assuming you only have one). If you don't have an svg, it won't fail, so you can always call it.

Scientific notation in d3.js axis

I am trying to plot some extremely small values with d3.js. Is there a direct way to visualise the tick labels in scientific (exponential) notation?
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<!-- load the d3.js library -->
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var data = [[0.3, 5e-300],[0.1, 3e-300],[0.7, 4e-300],[0.2, 7e-300],[0.6, 2.5e-300],[0.9, 4.2e-300]]
// set the ranges
var x = d3.scaleLinear().range([0, width]).domain([0, d3.max(data, function(d) { return d[0]; })]);
var y = d3.scaleLinear().range([height, 0]).domain([0, d3.max(data, function(d) { return d[1]; })]);
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("cx", function (d,i) { return x(d[0]); } )
.attr("cy", function (d) { return y(d[1]); } )
.attr("r", 8);
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y)
.tickFormat(d3.formatPrefix(".1s", 1e-300)));;
</script>
</body>
Here's an example created with in matplotlib. I would like to achieve the same thing with regard to y-axis notation
A solution with d3.format:
svg.append("g")
.call(d3.axisLeft(y)
.tickFormat(d3.format(".1e")));
Here is a demo:
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var data = [[0.3, 5e-300],[0.1, 3e-300],[0.7, 4e-300],[0.2, 7e-300],[0.6, 2.5e-300],[0.9, 4.2e-300]]
// set the ranges
var x = d3.scaleLinear().range([0, width]).domain([0, d3.max(data, function(d) { return d[0]; })]);
var y = d3.scaleLinear().range([height, 0]).domain([0, d3.max(data, function(d) { return d[1]; })]);
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("cx", function (d,i) { return x(d[0]); } )
.attr("cy", function (d) { return y(d[1]); } )
.attr("r", 8);
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y)
.tickFormat(d3.format(".1e")));
<script src="https://d3js.org/d3.v4.min.js"></script>

D3 assign colors to specific series

I want to assign two colors to two series in my data so that when I sort ascending or descending, the colors remain the same for each group.
The data is pulled programmatically from web map features. I thought I could get away with using an alternating function to assign colors but this doesn't work if I use sorting. I am trying to find a proper way to assign colors specifically to the series.
The data I am using has OL and NOL as two groups. You can see below how it is structured.
jsfiddle
relevant code:
var values = feature.properties;
var data = [
{name:"N11OL",value:values["N11OL"]},
{name:"N11NOL",value:values["N11NOL"]},
{name:"N21OL",value:values["N21OL"]},
{name:"N21NOL",value:values["N21NOL"]},
{name:"N22OL",value:values["N22OL"]},
{name:"N22NOL",value:values["N22NOL"]},
{name:"N23OL",value:values["N23OL"]},
{name:"N23NOL",value:values["N23NOL"]},
{name:"N31_33OL",value:values["N31_33OL"]},
{name:"N31_33NOL",value:values["N31_33NOL"]},
{name:"N41OL",value:values["N41OL"]},
{name:"N41NOL",value:values["N41NOL"]}
];
var Colors = ["#a6cee3", "#1f78b4"]
var margin = {top: 40, right: 2, bottom: 30, left: 180},
width = 400 - margin.left - margin.right,
height = 575 - margin.top - margin.bottom,
barHeight = height / data.length;
// Scale for X axis
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d){return d.value;})])
.range([0, width]);
var y = d3.scale.ordinal()
.domain(["NAICS11", "NAICS21", "NAICS22", "NAICS23", "NAICS31-33", "NAICS41"])
.rangeRoundBands([0, height]);
//y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.outerTickSize(0);
var svg = d3.select(div).select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.classed("chart", true);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = svg.selectAll("g.bar")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", function(d){return x(d.value);})
.attr("fill", function(d, i) {return Colors[i % 2]; }) //Alternate colors
.attr("height", barHeight - 1);
bar.append("text")
.attr("class", "text")
.attr("x", function(d) { return x(d.value) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.value; })
.attr("fill", "white")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("text-anchor", "end");
svg.append("text")
.attr("class", "title")
.attr("x", width/7)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Employment by industry " + "(Total OL: " + feature.properties.IndOLTot + ")");
Starting at Line 241 in your jsFiddle
bar.append("rect")
.attr("width", function(d){return x(d.value);})
.attr("fill", function(d, i) {
if(d.name.indexOf("NOL") > -1) {
//return Colors[0];
return "red";
} else {
//return Colors[1];
return "black";
}
}) //Alternate colors
.attr("height", barHeight - 1);
This checks the name property for the substring "NOL". If the name contains "NOL" it uses the first color, if "NOL" is not found it uses the second color for a fill.
(I'm under the assumption that the series is determined by the name)

Categories

Resources