Related
I have an AJAX call which brings me back to data in JSON format.
[{"KEY":"IA","VALUE":"8"},{"KEY":"GE","VALUE":"1"}]
However, the labelling may change for this data depending on some user interaction (selecting from a drop down may invoke a search for some other data) leading to :
[{"NAME":"STEVE","AGE":"54"},{"NAME":"PETE","AGE":"22"}]
So I need some way to just get the first label and data and push it to the X axis like:
|
|
|
|
|_____________
Steve Pete
Name
and then stick the second label and data up the Y Axis.
so most of the code examples I have seen use some form of d3.name to identify the labels in the returned data but as I need it to dynamically name the axis keys/values Im not sure how I can achieve this.
Also, the JSON data I have is stored in a variable called jdata so I wouldnt use
the d3.json method.
The examples im working from is on : http://codepen.io/mrev/pen/waKvbw
JS:
var margin ={top:20, right:30, bottom:30, left:40},
width=960-margin.left - margin.right,
height=500-margin.top-margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width+(2*margin.left)+margin.right) //set width
.attr("height", height+margin.top+margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("http://codepen.io/superpikar/pen/kcJDf.js", function(error, data){
x.domain(data.map(function(d){ return d.letter}));
y.domain([0, d3.max(data, function(d){return d.frequency})]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i){
return "translate("+x(d.letter)+", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.frequency);
})
.attr("x", function(d,i){
return x.rangeBand()+(margin.left/2);
})
.attr("height", function(d) {
return height - y(d.frequency);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand()+margin.left )
.attr("y", function(d) { return y(d.frequency) -10; })
.attr("dy", ".75em")
.text(function(d) { return d.frequency; });
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+margin.left+","+ height+")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate("+margin.left+",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
});
function type(d) {
d.letter = +d.letter; // coerce to number
return d;
}
HTML :
<div id="chart"></div>
EDITED CODE
var jsplit = jdata.split('"');
var keyX = jsplit[1];
var keyY = "";
var data = JSON.parse(jdata);
data[0].keys().forEach(function(k) {
if (k!=keyX) keyY=k;
});
var margin ={top:20, right:30, bottom:30, left:40},
width=960-margin.left - margin.right,
height=500-margin.top-margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width+(2*margin.left)+margin.right) //set width
.attr("height", height+margin.top+margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
x.domain(data.map(function(d){ return d[keyX]}));
y.domain([0, d3.max(data, function(d){return d[keyY]})]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i){
return "translate("+x(d[keyX])+", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d[keyY]);
})
.attr("x", function(d,i){
return x.rangeBand()+(margin.left/2);
})
.attr("height", function(d) {
return height - y(d[keyY]);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand()+margin.left )
.attr("y", function(d) { return y(d[keyY]) -10; })
.attr("dy", ".75em")
.text(function(d) { return d[keyY]; });
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+margin.left+","+ height+")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate("+margin.left+",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
function type(d) {
d[keyX] = +d[keyX]; // coerce to number
return d;
}
Quick & dirty trick to find the names of the first and second keys: simply split the json text data around ":
var jsplit = jdata.split('"');
var keyX = jsplit[1];
var keyY = jsplit[5];
this is assuming your data format doesn't change, and that the " character does not appear within the values
Edit: taking comments into account:
var jsplit = jdata.split('"');
var keyX = jsplit[1];
var keyY = "";
var data = JSON.parse(jdata);
for (k in data[0]) {
if (k!=keyX) keyY=k;
}
Note that all this code, as well as the rest of the graph building parts, should appear in the callback function from your ajax method.
You need to use d[keyX] and d[keyY], respectively, instead of d.letter and d.frequency in your example.
For the labels, .text("Frequency") should be .text(keyY), and you need to add an x label, maybe with (untested):
.call(xAxis) //add the following lines:
.append("text")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(keyX);
I am trying to create a log-normal plot with an array of data, but when I apply a log scale to the y axis, it only scales the axis ticks and not the actual data being plotted. In summary, all the data is plotted linearly, but the axis scale is shown as log. Below is my axis code:
var y = d3.scale.log()
.domain([.001,maxData])
.range([graphHeight, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")
.ticks(20, ".2")
.tickSize(-graphWidth,0,0);
svg.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(" +graphWidth + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 70)
.attr("x", -graphHeight/2)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("YLabel");
var line = d3.svg.line()
.x(function(d,i) {
return (i-0.5)*horizontalBarDistance;
})
.y(function(d) {
return graphHeight - d*100;
})
for (names in dataArrays)
{
svg.append("svg:path").attr("class","line").attr("d", line(dataArrays[names]));
}
You have to use the scale in your code for it to make a difference. At the moment, your code contains no reference to it apart from when you're assigning it to the axis. You probably want something like
.y(function(d) {
return y(d);
})
in the definition of your line generator.
Having issues with getting JSON data on a line chart, i can do this for a bar chart totally fine but for some reason it gives me an error when doing a line chart. There isn't much documentation around so see if anyone knows.
I get this error
Error: Problem parsing d="MNaN,400LNaN,258.65447419986936LNaN,221.90289571086436LNaN,183.32244720226433LNaN,134.29131286740693LNaN,149.70607446113652LNaN,63.1395602003048LNaN,37.44829087742215LNaN,69.40997169605924LNaN,0LNaN,169.91073372523405LNaN,643.2397126061397"
JSON data is as follows:
[{"logins":"3333","month_name":"January"},{"logins":"4956","month_name":"February"},{"logins":"5378","month_name":"March"},{"logins":"5821","month_name":"April"},{"logins":"6384","month_name":"May"},{"logins":"6207","month_name":"June"},{"logins":"7201","month_name":"July"},{"logins":"7496","month_name":"August"},{"logins":"7129","month_name":"September"},{"logins":"7926","month_name":"October"},{"logins":"5975","month_name":"November"},{"logins":"540","month_name":"December"}]
Now the code:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = $("svg").parent().width();
height = $("svg").parent().height();
aspect = 500 / 950;
var x = d3.time.scale()
.range([0, width]);
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 line = d3.svg.line()
.x(function(d) { return x(d.month_name); })
.y(function(d) { return y(d.logins); });
var svg = d3.select(document.createElement("div")).append("svg")
.attr("preserveAspectRatio", "xMidYMid")
.attr("viewBox", "0 0 950 500")
.attr("width", width)
.attr("height", width * aspect)
.attr("id", "art_chart")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(data, function(d) { return d.month_name; }));
y.domain(d3.extent(data, function(d) { return d.logins; }));
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("Logins");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
Anyone know how to fix it so it displays the line chart?
You need to parse your dates and pass in the numbers as numbers:
data.forEach(function(d) {
d.month_name = d3.time.format("%B").parse(d.month_name);
d.logins = +d.logins;
});
If you run this code just after loading the JSON, everything should work fine.
I have some number of amount (in dollar) which I am showing on y-axis and year on x-axis. now, I want to show original number on y-axis but not able to do.
i mean on y axis i want to show number from 1 to 100000 as amount but now, with .ticks(10) i can only used between 0 to 8000 amount at y axis.
and one more thing that if i want to show name as string at x axis then how can i show please let me know. i am stuck here and newly with d3.
function test() {
var graph_data;
// in graph_data it should contain value like this
// [Object { letter="2011", frequency="10000"},
// Object { letter="2012", frequency="8200"}]
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, 110], .3);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickPadding(10)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(3)
.orient("left")
.ticks(10);
var svg = d3.select("#award_by_year_chart").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.domain(graph_data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(graph_data, function(d) { return d.frequency; })]);
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("Total dollar");
svg.selectAll(".bar")
.data(graph_data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
function type(d) {
d.frequency = +d.frequency;
return d;
}
}
now, this code is working as below chart image. and i want to change value of y axis according to total dollar value and it's coming from table and it can also -ve as well +ve and upper limit not fixed. then how can resolve.
please give me valuable solution.
thanks
Ok, from what I understand from your question here's what I got:
i mean on y axis i want to show number from 1 to 100000 as amount but
now, with .ticks(10) i can only used between 0 to 8000 amount at y
axis.
For this I would check out https://github.com/mbostock/d3/wiki/SVG-Axes#wiki-tickValues, which documents the function tickValues. With this you can specify exactly what you want to show on the y axis. Say you want to show just 0 and 100000 then you can simply pass the array [0, 10000].
and one more thing that if i want to show name as string at x axis
then how can i show please let me know. i am stuck here and newly with
d3.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
.selectAll('text')
.text(function(d) {
return 'Some string: ' + d.letter
})
This will let you customize any of the tick values on the x axis. In case I didn't understand the first part of the question, you can also use this trick on the y axis to customize the output of the tick value.
I'm trying out d3js and I have a problem with getting my first basic column(vertical bar) chart work. The only thing I find a bit difficult to understand is the scaling thing. I want to make the x and y axis ticks with labels but I have the following problems:
First of all here is my data:
{
"regions":
["Federal","Tigray","Afar","Amhara","Oromia","Gambella","Addis Ababa","Dire Dawa","Harar","Benishangul-Gumuz","Somali","SNNPR "],
"institutions":
[0,0,34,421,738,0,218,22,22,109,0,456]
}
On the y-axis the values are there but the order is reversed. Here is the code:
var y = d3.scale.linear().domain([0, d3.max(data.institutions)]).range([0, height]);
then I use this scale to create a y-axis:
var yAxis = d3.svg.axis().scale(y).orient("left");
and add this axis to the svg element
svgContainer.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("Institutions");
the problem here is that the y-axis start from 0 at the top and with 700 at the bottom which is OK but it should be in reverse order.
The other problem I have it the x-axis. I want to have an ordinal scale since the values I want to put are in the regions names I have above. So here's what I've done.
var x = d3.scale.ordinal()
.domain(data.regions.map(function(d) { return d.substring(0, 2); }))
.rangeRoundBands([0, width], .1);
then the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
and finally add it to the svg element
svgContainer.append("g")
.attr("class", "x axis")
.attr("transform", "translate( 0," + height + ")")
.call(xAxis);
Here the problem is the ticks as well as the labels appear but they are not spaced out evenly and do not correspond with the center of the rectangles I'm drawing. Here is the complete code so you can see what's happening.
$(document).ready(function(){
d3.json("institutions.json", draw);
});
function draw(data){
var margin = {"top": 10, "right": 10, "bottom": 30, "left": 50}, width = 700, height = 300;
var x = d3.scale.ordinal()
.domain(data.regions.map(function(d) { return d.substring(0, 2); }))
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.domain([0, d3.max(data.institutions)])
.range([0, height]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svgContainer = d3.select("div.container").append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" +margin.left+ "," +margin.right+ ")");
svgContainer.append("g")
.attr("class", "x axis")
.attr("transform", "translate( 0," + height + ")")
.call(xAxis);
svgContainer.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("Institutions");
svgContainer.selectAll(".bar")
.data(data.institutions)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i) {return i* 41;})
.attr("y", function(d){return height - y(d);})
.attr("width", x.rangeBand())
.attr("height", function(d){return y(d);});
}
I put the code to Fiddle: http://jsfiddle.net/GmhCr/4/
Feel free to edit it! I already fixed both problems.
To fix the upside-down y-axis just swap the values of the range function arguments:
var y = d3.scale.linear().domain([0, d3.max(data.institutions)]).range([height, 0]);
Do not forget to adjust the code for the bars if you change the scale!
The source of the mismatch between bars and the x-axis can be found here:
var x = d3.scale.ordinal()
.domain(data.regions.map(function(d) {
return d.substring(0, 2);}))
.rangeRoundBands([0, width], .1);
svgContainer.selectAll(".bar")
.data(data.institutions)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i) {return i* 41;})
.attr("y", function(d){return height - y(d);})
.attr("width", x.rangeBand())
.attr("height", function(d){return y(d);});
You specify the padding for rangeRoundBands at 0.1 but you ignore the padding when computing the x and width values for the bars. This for example is correct with a padding of 0:
var x = d3.scale.ordinal()
.domain(data.regions.map(function(d) {
return d.substring(0, 2);}))
.rangeRoundBands([0, width], 0);
svgContainer.selectAll(".bar").data(data.institutions).enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) {
return i * x.rangeBand();
})
.attr("y", function(d) {
return y(d);
})
.attr("width", function(){
return x.rangeBand();
})
.attr("height", function(d) {
return height -y(d);
});
The padding determines how much of the domain is reserved for padding. When using a width of 700 and a padding of 0.1 exactly 70 pixels are used for padding. This means you have to add 70 / data["regions"].length pixels to every bar's x value to make this work with a padding.