Resizing the boxplot - javascript

I am displaying the following boxplot and would like to give it style features like: resize it to 400x800px , display it in the middle of the html file... Now I cant see the x axis labels for example because of the size... Any help?
Please check the complete code on this plunker.
The script is starting like this:
var labels = true; // show the text labels beside individual boxplots?
var margin = {top: 25, right: 25, bottom: 25, left: 25};
var width = 800 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var min = 0,
max = 10;
// parse in the data
d3.csv("boxplot_year.csv", function(error, csv) {
var data = [];
data[0] = [];
data[1] = [];
data[2] = [];
data[3] = [];
data[4] = [];
data[5] = [];
data[0][0] = "Y2010";
data[1][0] = "Y2011";
data[2][0] = "Y2012";
data[3][0] = "Y2013";
data[4][0] = "Y2014";
data[5][0] = "Y2015";
data[0][1] = [];
data[1][1] = [];
data[2][1] = [];
data[3][1] = [];
data[4][1] = [];
data[5][1] = [];
csv.forEach(function(x) {
var v1 = Math.floor(x.Y2010),
v2 = Math.floor(x.Y2011),
v3 = Math.floor(x.Y2012),
v4 = Math.floor(x.Y2013),
v5 = Math.floor(x.Y2014),
v6 = Math.floor(x.Y2015);
var rowMax = Math.max(Math.max(v1,v2), Math.max(Math.max(v3,v4), Math.max(v5,v6)));
var rowMin = Math.min(Math.min(v1,v2), Math.min(Math.min(v3,v4), Math.min(v5,v6)));
data[0][1].push(v1);
data[1][1].push(v2);
data[2][1].push(v3);
data[3][1].push(v4);
data[4][1].push(v5);
data[5][1].push(v6);
if (rowMax > max) max = rowMax;
if (rowMin < min) min = rowMin;
});
var boxplot = d3.box()
.whiskers(iqr(1.5))
.height(height)
.domain([min, max])
.showLabels(labels);
var svg = d3.select("#boxplot").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "box")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// the x-axis
var x = d3.scale.ordinal()
.domain( data.map(function(d) { console.log(d); return d[0]; } ) )
.rangeRoundBands([0 , width], 0.7, 0.3);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
// the y-axis
var y = d3.scale.linear()
.domain([min, max])
.range([height + margin.top, 0 + margin.top]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// draw the boxplots
svg.selectAll(".box")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x(d[0]) + "," + margin.top + ")"; } )
.call(boxplot.width(x.rangeBand()));
// draw y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text") // and text1
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "middle")
.style("font-size", "10px")
.text("Grade");
// draw x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height + margin.top + 10) + ")")
.call(xAxis)
.append("text") // text label for the x axis
.attr("x", (width / 2) )
.attr("y", 10 )
.attr("dy", ".71em")
.style("text-anchor", "middle")
.style("font-size", "10px")
.text("Year");
});
// Returns a function to compute the interquartile range.
function iqr(k) {
return function(d, i) {
var q1 = d.quartiles[0],
q3 = d.quartiles[2],
iqr = (q3 - q1) * k,
i = -1,
j = d.length;
while (d[++i] < q1 - iqr);
while (d[--j] > q3 + iqr);
return [i, j];
};
}

You can adjust the bottom margin to 100. This is sufficient to show the bottom y-axis.
var margin = {top: 25, right: 25, bottom: 100, left: 25};
Based on your comment, to make both axis unite at 0 add a transform to your y-axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate (0,10)")
.call(yAxis)
To get rid of the Y from Y2010 add a slice to your labels like below. However be aware that it is changing your data so in your console log you will see years as 2010, 2011 etc.
// the x-axis
var x = d3.scale.ordinal()
.domain( data.map(function(d) { d[0] = d[0].slice(1); console.log(d); return d[0]; } ) )
.rangeRoundBands([0 , width], 0.7, 0.3);
Hope this helps.

Remove the width and height attributes on your svg and make it expand to full size of its outer container using the viewBox attribute, replace the var svg part as follows, I hard coded the viewBox, change it as you see fit:
var svg = d3.select("#boxplot").append("svg")
.attr("viewBox","0 0 800 800")
.attr("preserveAspectRatio","none")
.style("display","block")
.style("width","100%")
.style("height","100%")
.attr("class", "box")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Since you now make your svg expand as much as possible, only modify the outer div as you see fit, either on "resize", periodically etc. To better understand what viewBox does: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox

Related

D3.js Histogram - Position labels on x axis and quantities of each bin

Adapting a histogram with D3 (v3) I find two problems to solve (original code here):
<script type="text/javascript">
var faithfulData = [20,21,26,18,24,24,25,25,21,20,20,18,28,23,17,26,27,27,20,28,23,26,];
var datos_unicos = Array.from(new Set(faithfulData))
var margin = {top: 4, right: 10, bottom: 40, left: 40},
width = 360 - margin.left - margin.right,
height = 180 - margin.top - margin.bottom;
var cant_ticks = datos_unicos.length;
var edad_min = Math.min.apply(Math, datos_unicos) - 3;
var edad_max = Math.max.apply(Math, datos_unicos) + 3;
var vartickValues = []
var tope = (edad_max)+1;
for (var i =edad_min; i< tope; i++) {
vartickValues.push(i);
}
var x = d3.scale.linear()
.domain([edad_min, edad_max])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, .1])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickValues(vartickValues)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format("%"));
var line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
var histogram = d3.layout.histogram()
.frequency(false)
.bins(cant_ticks);
var svg = d3.select("#plantel_distribucion").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x plantel_axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", 34)
.style("text-anchor", "end")
.text("Edad de las jugadoras");
svg.append("g")
.attr("class", "y plantel_axis")
.call(yAxis);
var data = histogram(faithfulData),
kde = kernelDensityEstimator(epanechnikovKernel(7), x.ticks(100));
svg.selectAll(".plantel_bar")
.data(data)
.enter().insert("rect", ".axis")
.attr("class", "plantel_bar")
.attr("x", function(d) { return x(d.x) + 1; })
.attr("y", function(d) { return y(d.y); })
.attr("width", x(data[0].dx + data[0].x) - x(data[0].x) - 1)
.attr("height", function(d) { return height - y(d.y); });
svg.append("path")
.datum(kde(faithfulData))
.attr("class", "plantel_line")
.attr("d", line);
//});
function kernelDensityEstimator(kernel, x) {
return function(sample) {
return x.map(function(x) {
return [x, d3.mean(sample, function(v) { return kernel(x - v); })];
});
};
}
function epanechnikovKernel(scale) {
return function(u) {
return Math.abs(u /= scale) <= 1 ? .75 * (1 - u * u) / scale : 0;
};
}
</script>
1) How to place the labels on the x axis in the center of the bin? In other words, the tick mark and its label on the center of the bar.
2) How do I place the quantity (frequency) of each bin above its bar?
I appreciate your comments and leave an image with the current development:
Thanks
For question1, if you just want to add some ticks at the center of each bin, there are x and dx attributes in histogram that indicate the position and step of each bin. You can compute the x tick by xtick = x + dx / 2.
For question2, I think you can draw a line chart above the histogram, and set the z-index to be 2.
I hope the above helps. :)

d3.js : changing axis text label with glyphicon

I'm new to d3js and i'm trying to manipulate a simple graph with 2 axis and some rect to show some data.
I've set the range of data to my y axis with some object name. This object has also a type "technical" or "canonical".
I'm trying to replace this "technical" or "canonical" with a bootstrap's glyphicon.
I've tried to replace the datas from the range with a internal text containing the proper glyphicon but without success
//datas is the data structure containing my chart datas.
//objects will be the array use for the domain
var objects = datas.map(function (d) {
return d.object + getExchangeObjectType(d.type);
});
var margin = {top: 40, right: 20, bottom: 200, left: 400},
width = 1200 - margin.left - margin.right,
height = (objects.length*30) - margin.top - margin.bottom;
var canonical = "<span class='glyphicon glyphicon-copyright-mark'></span>";
var technical = "<span class='glyphicon glyphicon-wrench'></span>";
function getExchangeObjectType(type){
if (type == 'Technical')
return technical;
else
return canonical;
}
//datas is the data structure containing my chart datas.
//objects will be the array use for the domain
var objects = datas.map(function (d) {
return d.object + getExchangeObjectType(d.type);
});
var x = d3.scale.ordinal().rangePoints([0, width]);
var y = d3.scale.ordinal().rangeBands([height, 0],.1,.1);
// define x & y axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("top")
.ticks(percents.length)
;
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(objects.length)
;
// define the domain of datas
y.domain(objects);
x.domain(percents);
Here is the svg part:
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 + ")");
// draw x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.selectAll("text")
.attr("x",20)
.style("text-anchor", "end")
;
// draw y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("text-anchor", "end")
;
svg.selectAll("bar")
.data(datas)
.enter().append("rect")
.style("fill", function (d) { return getColor(d.value);})
.attr("y", function(d){return d.object + getExchangeObjectType(d.type);})
.attr("height", y.rangeBand())
.attr("x", 0 )
.attr("width", function(d) { return ( d.value * width) / 100 ; })
;
I've found out my solution use the unicode !
I've added my type in the label text and then replace it with an unicode :
.text(function(d){
var oldText=d.split(":");
if (oldText[1]=="Canonical")
return oldText[0]+" \ue194";
else
return oldText[0]+" \ue136"
})

How to move d3 ticks on y-axis

I have a bar chart see plunker the problem is that I would like to move the y-axis ticks to be at the middle left side of the rects but they appear on the top and end. and I cannot seem to move them without destroying the chart.
my code
var info = [{
name: "Walnuts",
value: 546546
}, {
name: "Almonds",
value: 456455
}
];
/* Set chart dimensions */
var width = 960,
height = 500,
margin = {
top: 10,
right: 10,
bottom: 20,
left: 60
};
//subtract margins
width = width - margin.left - margin.right;
height = height - margin.top - margin.bottom;
//sort data from highest to lowest
info = info.sort(function(a, b) {
return b.value - a.value;
});
//Sets the y scale from 0 to the maximum data element
var max_n = 0;
var category = []
for (var d in info) {
max_n = Math.max(info[d].value, max_n);
category.push(info[d].name)
}
var dx = width / max_n;
var dy = height / info.length;
var y = d3.scale.ordinal()
.domain(category)
.range([0, height]);
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
var svg = d3.select("#chart")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr('preserveAspectRatio', 'xMidYMin')
.attr("viewBox", '0 0 ' + parseInt(width + margin.left + margin.right) + ' ' + parseInt(height + margin.top + margin.bottom))
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.selectAll(".bar")
.data(info)
.enter()
.append("rect")
.attr("class", function(d, i) {
return "bar" + d.name;
})
.attr("x", function(d, i) {
return 0;
})
.attr("y", function(d, i) {
return dy * i;
})
.attr("width", function(d, i) {
return dx * d.value
})
.attr("height", dy)
.attr("fill", function(d, i) {
if (d.name == 'Walnuts') {
return 'red'
} else {
return 'green'
}
});
var y_xis = svg.append('g')
.attr('id', 'yaxis')
.call(yAxis);
You are using range in y axis like this:
var y = d3.scale.ordinal()
.domain(category)
.range([0, height]);
You should be using 'rangeRoundBands' since the y scale is ordinal
var y = d3.scale.ordinal()
.domain(category)
.rangeRoundBands([0, height], .1);
working code here
For d3 versions like v4/v5.
Defining height as the graph/plot height, and max as the maximum value of y.
import { parseSvg } from 'd3-interpolate/src/transform/parse'
const yScale = d3
.scaleLinear()
.domain([0, max])
.rangeRound([height, 0])
const yAxis = d3.axisLeft(yScale)
svg
.append('g')
.call(yAxis)
.selectAll('.tick')
.each(function(data) {
const tick = d3.select(this)
const { translateX, translateY } = parseSvg(tick.attr('transform'))
tick.attr(
'transform',
translate(translateX, translateY + height / (2 * max))
)
})
Recently I needed something very very similar and I solved this with a call with selecting all text elements in the selection and moving their dy upwards. I will give an example with OP's code:
var y_xis = svg.append('g')
.attr('id','yaxis')
.call(yAxis)
.call(selection => selection
.selectAll('text')
.attr('dy', '-110') // this moves the text labels upwards
.attr('x', '110')); // this does the same job but horizontally

D3: How to show only top 3 of 6 items in bar chart?

I'm an extreme newbie to D3. I am working with a web map built in LeafletJS and have put a D3 bar chart into a popup that shows the various ethnic groups for a district in Afghanistan. In my real project, I have over 26 ethnic groups in the area and I only want to show the top 3 largest ethnic groups in the bar chart.
I've set up a demo in jsfiddle with only 6 ethnic groups for now.
How do I only show the top 3 largest ethnic groups in my bar chart and hide the rest of the ethnic groups?
Example of my JSON data:
var myData = [{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Name":"Gulran","Province":"Hirat","Ethnic1":0.19,"Ethnic2":0.32,"Ethnic3":"0.10","Ethnic4":"0.00","Ethnic5":"0.10","Ethnic6":"0.00"},"geometry":{"type":"Polygon","coordinates":[[[60.941162109375,29.897805610155874],[61.92993164062499,31.034108344903512],[63.34716796874999,31.3348710339506],[64.05029296875,30.401306519203583],[64.412841796875,29.735762444449076],[64.09423828125,29.36302703778376],[62.29248046875,29.36302703778376],[60.941162109375,29.897805610155874]]]}},{"type":"Feature","properties":{"Name":"Chahar Burjak","Province":"Nimroz","Ethnic1":0.25,"Ethnic2":0.12,"Ethnic3":0.03,"Ethnic4":0.01,"Ethnic5":"0.00","Ethnic6":"0.00"},"geometry":{"type":"Polygon","coordinates":[[[63.38012695312499,31.3348710339506],[65.06103515625,31.80289258670676],[65.6982421875,31.156408414557],[66.016845703125,30.467614102257855],[65.291748046875,30.164126343161097],[64.22607421875,30.0405664305846],[63.38012695312499,31.3348710339506]]]}}]}];
My D3 code:
var div = $('<div id="chart" style="width: 600px; height: 400px;"><svg></div>')[0];
var popup = L.popup({minWidth: 600}).setContent(div);
layer.bindPopup(popup);
var values = feature.properties;
var data = [
{name:"Ethnic1",value:values["Ethnic1"]},
{name:"Ethnic2",value:values["Ethnic2"]},
{name:"Ethnic3",value:values["Ethnic3"]},
{name:"Ethnic4",value:values["Ethnic4"]},
{name:"Ethnic5",value:values["Ethnic5"]},
{name:"Ethnic6",value:values["Ethnic6"]}
];
var margin = {top: 20, right: 30, bottom: 40, left: 40},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom,
barHeight = height / data.length;
var formatPercent = d3.format(".0%");
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d){return d.value;})])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatPercent);
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", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
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("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d.value) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
The way you do it is to first sort the data. Once you have sorted, you can filter the data to use only the top 3 values for creating the bar chart.
I have forked your fiddle. Here is the link https://jsfiddle.net/ankit89/x8u31jdo/
The important piece of code that I added/modifed are
var sortedData = data.sort(function(a,b){
return b.value - a.value;
});
//if you want to just keep top three
sortedData = sortedData.filter(function(d,i){
return i < 3;
});
var bar = svg.selectAll("g.bar")
.data(sortedData)
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
Here is the Working Fiddle for you.
And the code used is as below
function GetTopThreeEthnic(arrayData){ //sorting to top 3 function
arrayData.sort(function(a, b) {
return parseFloat(b.value) - parseFloat(a.value);
});
return arrayData.slice(0, 3);
}
And just make a call to this function after you initialize your data variable.
var data = [
{name:"Ethnic1",value:values["Ethnic1"]},
{name:"Ethnic2",value:values["Ethnic2"]},
{name:"Ethnic3",value:values["Ethnic3"]},
{name:"Ethnic4",value:values["Ethnic4"]},
{name:"Ethnic5",value:values["Ethnic5"]},
{name:"Ethnic6",value:values["Ethnic6"]}
];
data = GetTopThreeEthnic(data); // this is the code added

Line graph creation using D3

Iam new to D3.i just want to draw a line chart.For that i just put the static values ,its working fine,But when i fetch data from server db graph is not getting properly.Problem iam getting is x and y axis with values are getting.But not getting the line graph.here is the code
// Line chart creation....................................................
var deviceTime = 0;
var margin = { top: 20, right: 80, bottom: 30, left: 50 };
var da = '<%=#display.to_json(:only => [:DeviceTime, :Speed])%>'
var margin = { top: 20, right: 80, bottom: 30, left: 50 };
var dataset=JSON.parse(da.replace(/"/g,'"'));
//test code TODO :need to move somewhere else
var data = [];
for (var vAlue in dataset[0]) {
var tempData = d3.values(dataset[0][vAlue]);
data.push(tempData[1]);
}
// test end
var yExtents = d3.extent(d3.merge(dataset), function (d) { return d.Speed; });
var xExtents = d3.extent(d3.merge(dataset), function (d) { return d.DeviceTime; });
//alert("xevents[0]-------"+xExtents[0] "xevents[1]-------"+xExtents[1]);
var w = 900;
var h = 400;
var padding = 30;
deviceTime = xExtents[1];
//Create scale functions
var xScale = d3.scale.linear()
.domain([xExtents[0], xExtents[1]])
.range([padding, w - padding * 2]);
var yScale = d3.scale.linear()
.domain([0, yExtents[1]])
//.domain([0, 160])
.range([h - padding, padding]);
//Define X axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(10);
//Define Y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
///////////////// need to remove
var line = d3.svg.line()
.x(function (d, i) { return xScale(i); })
.y(function (d, i) { return yScale(d); });
//Create SVG element
var svg = d3.select("#bar-demo")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Create X axis
var hh = svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
var path = svg.append("g")
//.attr("clip-path", "url(#clip)")
.append("path")
.data([data])
.attr("class", "line")
.attr("transform", "translate(" + 102 + ",0)")
.attr("d", line);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0-margin.left+10)
.attr("x",0-h/2)
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Speed km/h");
svg.append("text")
.attr("y", h)
.attr("x", h)
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Time Stamp");

Categories

Resources