d3 Reusable histogram - javascript

I've been trying to implement Reusability on a histogram plotted using d3.
I want that after plotting of the dataset, I want to plot statistical mean, variance etc. on the same plot.These would be user driven, basically I want to use the same plot.
Here's my attempt on coding the skeleton histogram code
function histogram(){
//Defaults
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 760,
height = 200;
function chart(selection){
selection.each(function(d,i){
var x = d3.scale.linear()
.domain( d3.extent(d) )
.range( [0, width] );
var data = d3.layout.histogram()
//Currently generates 20 equally spaced bars
.bins(x.ticks(20))
(d);
var y = d3.scale.linear()
.domain([0, d3.max(d) ])
.range([ height - margin.top - margin.bottom, 0 ]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select(this).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 bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar");
/*
Corrected bars
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", x(data[0].dx) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.y); });
*/
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class","y axis")
.call(yAxis);
bar.append("rect")
.attr("x", function(d,i){ return x(d.x); })
.attr("width", x(data[0].dx) - 1)
.attr('y',height)
.transition()
.delay( function(d,i){ return i*50; } )
.attr('y',function(d){ return y(d.y) })
.attr("height", function(d) { return height - y(d.y); });
});
}
//Accessors//
chart.width = function(value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function(value) {
if (!arguments.length) return height;
height = value;
return chart;
};
return chart;
}
It's assigning a negative width for bars. My input dataset would simply be an array of numbers and I need to plot the frequency distribution

If you're asking how to implement the avg, standard deviation, once you have your histogram you can draw lines and text on it to represent the avg. I would calculate which bar the average is in, and the % of the way through the bar and then something like this:
var averageBar = vis.selectAll("g.bar:nth-child(" + (averageBarIndex + 1) + ")");
averageBar.append("svg:line")
.attr("x1", 0)
.attr("y1", y.rangeBand()*averageBarPercentage)
.attr("x2", w)
.attr("y2", y.rangeBand() * averageBarPercentage)
.style("stroke", "black");
averageBar.append("svg:text")
.attr("x", w-150)
.attr("y", y.rangeBand() * averageBarPercentage-15)
.attr("dx", -6)
.attr("dy", "10px")
.attr("text-anchor", "end")
.text("Average");
That will give you a line marking the average, you can do similar for the standard deviation.

Related

Creating A Bar chart in d3.js from an JSON

I have this JSON data
{"1960":"133.55501327769","1961":"134.159118941963","1962":"134.857912280869","1963":"134.504575565342","1964":"134.105211273476","1965":"133.569625896451","1966":"132.675635192775","1967":"131.665502129354","1968":"129.190980115918","1969":"126.736756382819","1970":"124.382808900193","1971":"122.133431342027","1972":"120.020185557559","1973":"118.087531093609","1974":"116.132988067096","1975":"114.100918174437","1976":"111.980005447216","1977":"109.783821762662","1978":"106.033489239906","1979":"102.341720681455","1980":"98.7390023274647","1981":"95.2412508672801","1982":"91.7911923993222","1983":"88.0011769487606","1984":"84.2072557839419","1985":"80.3593225600132","1986":"76.4415956498419","1987":"72.5145803648751","1988":"71.1706639452677","1989":"69.8887679924858","1990":"69.0044133814268","1991":"67.7559924352118","1992":"66.9284506867798","1993":"64.9489678572737","1994":"62.9227777228154","1995":"60.7070695260477","1996":"58.5966308804751","1997":"56.4401276304142","1998":"55.5315395528949","1999":"54.6587808352011","2000":"53.8314102398679","2001":"52.9015276443892","2002":"51.9907926813042","2003":"51.5228563035101","2004":"51.1032496482833","2005":"50.7325902239383","2006":"50.3291352282938","2007":"49.9998514069402","2008":"49.8870459355469","2009":"49.7812066054555","2010":"49.6729747116906","2011":"49.5360469363113","2012":"49.3837446924523","2013":"48.7965576984378","2014":"48.1964180547578","2015":"47.5501940499984","Country Name":"Arab World","Country Code":"ARB","Indicator Name":"Adolescent fertility rate (births per 1,000 women ages 15-19)","Indicator Code":"SP.ADO.TFRT","":""}
which I convert from CSV to JS object.
How do I build a bar chart with d3.js where the key is the X axis and the values are the Y axis?
here's what I came up with
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// set the ranges
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
// define the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
// add the SVG element
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 + ")");
d3.queue()
.defer(d3.csv, "http://localhost:8000/HNP_Data.csv")
.defer(d3.csv, "http://localhost:8000/HNP_Series.csv")
.await(analyze);
var dataToVis;
var array = []
function analyze(error,data,series) {
dataToVis = data[2];
console.log(JSON.stringify(dataToVis));
for (var key in dataToVis) {
if(!isNaN(key) && key.length >0) {
var p = new Object;
p.year = key;
p.frequency = p(dataToVis[key]);
array.push(p);
}
}
array.forEach(function(d){
d.year = d.year;
d.frequency = +d.frequency;
});
x.domain(data.map(function(d) { return d.year; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
// add axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
// Add bar chart
svg.selectAll("bar")
.data(array)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.year); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
}
but I end up with this error.
Error: <rect> attribute y: Expected length, "NaN".
Error: <rect> attribute height: Expected length, "NaN".

D3 bar chart with variable width and variable length data up to 1000 data points

I am having a lot of trouble with D3 bar chart where the length of the data I get from search is variable. I used the D3 bar chart example and built this. However as data length goes up, the graph gets less and less legible. The problem seems to be the range gets confused when it is beyond 100 points or so.
Code is below:
function makegraph(data,ctype) {
//var data=xdata.splice(-900)
var gwidth=data.length*2;
if(gwidth < 800) gwidth=800;
//gwidth=800
var margin = {top: 9, right: 20, bottom: 30, left: 90},
width = gwidth - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
//x=d3.scale.linear().domain([0,1]).range(0,width)
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(function(d) { if (d % 10) return ""; else return d;})
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// .ticks(10, "%");
d3.select("svg").remove()
var svg = d3.select("#graphchart").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 line = d3.svg.line()
.x(function(d) { return x(d.rowid); })
.y(function(d) { return y(d[ctype]); });
x.domain(data.map(function(d) { return d.rowid; }));
var ymax=d3.max(data, function(d) { return d[ctype]; });
var ymin=d3.min(data, function(d) { return d[ctype]; });
y.domain([ymin, ymax]);
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", Math.log10(ymax))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Bytes");
// console.log(x.rangeBand())
var bar = svg.selectAll(".bar")
.data(data);
bar.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.rowid); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d[ctype]); })
.attr("height", function(d) { var xya= height - y(d[ctype]); /* console.log(xya+":"+d[ctype]); */ return height - y(d[ctype]); })
/* .attr("title", function(d) { return d['bytes']+" Bytes at "+d.stime; }) */
.on("mouseover", function(d) { $("#graphinfo").html(d3.format('0,000')(d['bytes'])+" Bytes at "+d.stime) })
.on("mouseout",function(d) { $("#graphinfo").html(' ')})
.on("click",function(d) { tablegraph(d)});
$('.bar').tooltip()
}
Do you have suggestions how I can make this graph so that the bar chart will grow horizontally for larger data sets? Thanks for any help and suggestions!
Vijay

Bar chart scale

I have this bar chart on D3.js... It works fine..
But I'm having problems with the scale... When a data series has a value much grater than the others, the <rect> does not fit into the scale.
Any idea how to solve this matter?
Here is the code:
var data = [
{"Anio":"1999","CONTRAVENCIONAL":"78484","PENAL":"0","FALTAS":"0","MULTAS":"0","OTROS":"0","TOTAL":"78484"},
{"Anio":"2000","CONTRAVENCIONAL":"92879","PENAL":"0","FALTAS":"0","MULTAS":"0","OTROS":"0","TOTAL":"92879"},
{"Anio":"2001","CONTRAVENCIONAL":"100018","PENAL":"0","FALTAS":"1818","MULTAS":"0","OTROS":"0","TOTAL":"101836"},
{"Anio":"2002","CONTRAVENCIONAL":"101380","PENAL":"0","FALTAS":"3692","MULTAS":"0","OTROS":"0","TOTAL":"105072 "},
{"Anio":"2003","CONTRAVENCIONAL":"86791","PENAL":"0","FALTAS":"7417","MULTAS":"0","OTROS":"0","TOTAL":"94208"},
{"Anio":"2004","CONTRAVENCIONAL":"47870","PENAL":"255","FALTAS":"1105","MULTAS":"1811","OTROS":"0","TOTAL":"51041"},
{"Anio":"2005","CONTRAVENCIONAL":"33013","PENAL":"348","FALTAS":"1473","MULTAS":"634","OTROS":"0","TOTAL":"35468"},
];
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 860 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
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");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var arr_data = [];
for (var i = 0; i < data.length; i++) {
var o = {'name':data[i].Anio,'value':data[i].TOTAL};
arr_data.push(o);
};
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom )
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(arr_data.map(function(d) { return d.name; }));
y.domain([0, d3.max(arr_data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(arr_data)
.enter().append("g");
bar.append("rect")
.attr("class", "rect")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value) ; })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand())
.attr("fill","#632423")
.on('mouseover',function(d){
var a = d3.select(this)
.attr("fill","#733A39");
var a = d3.select("#tooltip")
.style("left","100px")
.style("top","20px")
.select("#value")
.text(d.value);
}).on('mouseout',function(d){
var a = d3.select(this)
.attr("fill","#632423"); //old color: #790018
});
bar.append("text")
.attr("class", "label")
.text(function(d) { return d.value; })
.attr("x", function(d) { return x(d.name)+4; })
.attr("y", function(d) { return y(d.value)+20 ; });
Your scale will actually handle series with larger values for you because you're dynamically setting it to go from 0 to the max value in your data:
y.domain([0, d3.max(arr_data, function(d) { return d.value; })]);
However, you're not converting your data to numbers from strings, so right now the scale is from [0, 94208] instead of [0, 105072]. This is because the character "9" is greater than "1". You can fix this by converting the values to numbers when you construct your arr_data, like this:
var o = {'name':data[i].Anio,'value':+data[i].TOTAL}; // <---- notice the '+'
This produces a better looking graph:

Horizontal bar chart d3.js not showing label

I've an horizontal bar chart in d3.js and I would like to add the name like "y-label" for every bar of the chart.
The original example of my bar chart is http://bl.ocks.org/mbostock/2368837
without negative values.
So I modified it for my purpose
var margin = {top: 40, right: 20, bottom: 100, left: 60},
width = 720 - margin.left - margin.right,
height = 480 - margin.top - margin.bottom;
var x_4 = d3.scale.linear()
.range([0, width])
var y_4 = d3.scale.ordinal()
.rangeRoundBands([0, height], .2);
var xAxis_4 = d3.svg.axis()
.scale(x_4)
.orient("top");
var tip_4 = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Value:</strong> <span style='color:red'>" + d.ln_numfollowers + "</span>";
})
var sampleSVG_4 = d3.select("#LinkedIn").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 + ")")
.call(tip_4);
d3.csv("#routes.Assets.at("d3/linkedin_competitor_prova.csv")", type, function(error, data) {
x_4.domain(d3.extent(data, function(d) { return d.ln_numfollowers; })).nice();
y_4.domain(data.map(function(d) { return d.organization_name; }));
sampleSVG_4.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) { return d.ln_numfollowers < 0 ? "bar negative" : "bar positive"; })
.attr("x", function(d) { return x_4(Math.min(0, d.ln_numfollowers)); })
.attr("y", function(d) { return y_4(d.organization_name); })
.attr("width", function(d) { return Math.abs(x_4(d.ln_numfollowers) - x_4(0)); })
.attr("height", y_4.rangeBand())
.on('mouseover', tip_4.show)
.on('mouseout', tip_4.hide);;
sampleSVG_4.append("g")
.attr("class", "x axis")
.call(xAxis_4);
sampleSVG_4.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x_4(0))
.attr("x2", x_4(0))
.attr("y2", height)
});
function type(d) {
d.ln_numfollowers = +d.ln_numfollowers;
return d;
}
The csv data file is:
organization_name,ln_numfollowers
Carrot.mx,100
CarJump,45
I don't know why the organization_name is not showing.
As you can see, not even in the original example the label on the y axis are showing.
Couple of problems:
1.) You probably don't want to create your x-axis using extent. With your sample data this would create a chart from 45 to 100. You probably want to start it at zero.
x_4.domain([0,d3.max(data, function(d) { return d.ln_numfollowers; })]);
2.) You don't actually create a conventional y-axis. This code:
sampleSVG_4.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x_4(0))
.attr("x2", x_4(0))
.attr("y2", height)
Is creating a y-axis that's just a line. It's not using the built-in d3axis creation. What you need is:
var yAxis_4 = d3.svg.axis()
.scale(y_4)
.orient("left");
....
sampleSVG_4.append("g")
.attr("class", "y axis")
.call(yAxis_4);
Example here.

D3 - ticks wrong positioned

I tried now couple of things but I can not figure out why my ticks are wrong positioned. I used different sources to make this stacked barchart.
Here is the fiddle of my code: http://jsfiddle.net/azj7guec/
And here is the code itself:
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 1000 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
x = d3.scale.ordinal().rangeRoundBands([0, width], .50);
y = d3.scale.linear().range([height, 0]);
z = d3.scale.ordinal().range(["darkblue", "blue", "lightblue"])
console.log("RAW MATRIX---------------------------");
// 4 columns: ID,c1,c2,c3
var matrix = [
[22,45,34,65],
[23,66,12,22],
[24,32,44,76],
[25,12,76,32],
[26, 67, 34, 56]
];
console.log(matrix)
var keys = matrix.map(function(item){return item[0]});
console.log("REMAP---------------------------");
var remapped =["c1","c2","c3"].map(function(dat,i){
return matrix.map(function(d,ii){
return {x: d[0], y: d[i+1] };
})
});
console.log(remapped)
console.log("LAYOUT---------------------------");
var stacked = d3.layout.stack()(remapped)
console.log(stacked)
//var yMax= d3.max(stacked)
x.domain(keys);
y.domain([0, d3.max(stacked[stacked.length - 1], function(d) { return d.y0 + d.y; })]);
// show the domains of the scales
console.log("x.domain(): " + x.domain())
console.log("y.domain(): " + y.domain())
console.log("------------------------------------------------------------------");
var yAxis = d3.svg.axis()
.scale(y)
.ticks(10)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.tickValues(keys)
.orient("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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
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("Open Issues");
// Add a group for each column.
var valgroup = svg.selectAll("g.valgroup")
.data(stacked)
.enter().append("g")
.attr("class", "valgroup")
.style("fill", function(d, i) { return z(i); })
.style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
// Add a rect for each date.
var rect = valgroup.selectAll("rect")
.data(function(d){return d;})
.enter().append("rect")
.attr("width", 20)
.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());
Hope someone can help me.
The ordinal scale divides its output range into intervals based on the input domain. Your current positioning puts the bars at the beginning of those intervals, whereas the ticks are in the center. To match up the positions, add half the interval minus half the bar width to the beginning of the interval:
.attr("x", function(d) { return x(d.x) + (x.rangeBand() - 20) / 2; })
Complete demo here.

Categories