In this codepen I am trying to create a column chart with scaleBand for the x and width. But, I have a large gap between the second and third columns. Why is it doing that? The full D3 code is below but the codepen also has the data that I am using for the column chart. Thank you.
data.forEach(function(d) {
d.height = +d.height;
});
var w = 400;
var h = 400;
var xScale = d3.scaleBand()
.domain(["Burj Khalifa", "hanghai Tower", "Abraj Al-Bait Clock Tower",
"Ping An Finance Centre", "Lotte World Tower", "One World Trade Center",
"Guangzhou CTF Finance Center"])
.range([0, 400])
.paddingInner(0.3)
.paddingOuter(0.3);
var yScale = d3.scaleLinear()
.domain([0,828])
.range([0,400]);
var svg = d3.select("#chart-area").append("svg")
.attr("width", w)
.attr("height", h);
var rects = svg.selectAll("rect")
.data(data);
rects.enter().append("rect")
.attr("width", xScale.bandwidth)
.attr("height", (d) => yScale(d.height))
.attr("x", (d) => xScale(d.name))
.attr("y", (d) => h - yScale(d.height))
.attr("fill", "blue");
It seems your domain array was not mapped correctly. Rather use your xScale using computed arrays which will avoid any spelling mistakes / special character interpretation.
Using this solves it.
var xScale = d3.scaleBand()
.domain(data.map(d => d.name))
.range([0, 400])
.paddingInner(0.3)
.paddingOuter(0.3);
Here is the working codepen.
Related
I want to create a barchart displaying C02 emission.
The Problem (see picture below):
Why are the bars "pushed" to the right? Why are the years in the x-axis displayed without the first integer?
I am using Version 3 of d3.
Given some JSON data like this:
[
{
"Cement": 0.0,
"Gas Flaring": 0.0,
"Gas Fuel": 0.0,
"Liquid Fuel": 0.0,
"Per Capita": null,
"Solid Fuel": 3.0,
"Total": 3.0,
"Year": 1751
},
and so on…
]
To prepare for scaling I did:
var minDate = dataset[0].Year;
var maxDate = dataset[dataset.length - 1].Year;
var maxValue = d3.max(dataset, function(d) {
return d["Per Capita"];
});
I append the svg
var svg = d3
.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
I sacled the xAxis and the yAxis:
var xAxisScale = d3.time
.scale()
.domain([minDate, maxDate])
.range([0, w]);
var yAxisScale = d3.scale
.linear()
.domain([0, maxValue])
.range([h, 0]);
The I finally builded these axisses…
var xAxis = d3.svg
.axis()
.scale(xAxisScale)
.orient("bottom");
var yAxis = d3.svg
.axis()
.scale(yAxisScale)
.orient("left");
svg
.append("g")
.attr("class", "axis")
.attr("transform", "translate(92," + (h - padding) + ")")
.call(xAxis);
svg
.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",-90)")
.call(yAxis);
I also than addeded the rects…
svg
.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.style("fill", "teal")
.attr({
x: function(d, i) {
return i * (w / dataset.length);
},
y: function(d) {
return yAxisScale(d["Per Capita"]);
},
width: w / dataset.length,
height: function(d) {
return h - yAxisScale(d["Per Capita"]);
}
});
The result is not the intended one.
Could you please elaborate what went wrong?
Why are the bars "pushed" to the right?
Why are the years in the x-axis displayed without the first integer?
I am using Version 3 of d3.
Thank you very much!
The main problem here is that this...
"Year": 1751
... is not a date object. That's just a number. If you look at your axis you'll realise that.
So, you have to parse it. For instance:
const format = d3.time.format("%Y");
dataset.forEach(function(d){
d.Year = format.parse(d.Year);
});
Also, when you do this...
var minDate = dataset[0].Year;
var maxDate = dataset[dataset.length - 1].Year;
... you're blindly trusting that the array is sorted. Don't do that. Instead, do:
var minDate = d3.max(dataset, function(d){
return d.Year
});
var maxDate = d3.min(dataset, function(d){
return d.Year
});
Or, if you want to use destructuring:
var [minDate, maxDate] = d3.extent(dataset, d => d.Year);
Finally, now that you have a proper scale, don't use the indices for the x position. Use the scale:
x: function(d) {
return xAxisScale(d.Year);
},
This covers the problem regarding the x position. For fixing the y position, just set a proper margin.
histogram
I want to plot histogram using d3.js where i have dataset of around 13000 points which is divided into 2 clusters . I want to color both of them but when i use category color it only shows first one.In the input file i have Droplet_no, Amplitude, Cluster.
Here is my code :
<script type="text/javascript">
d3.csv("test_F06.csv",function type(d){
d.Droplet_no = +d.Droplet_no;
d.Amplitude = +d.Amplitude;
return d;} , function(data){
var width = 600;
height = 500;
padding = 50;
var colorColumn = "Cluster";
var map = data.map(function(i){return parseInt(i.Amplitude);})
var x = d3.scale.linear()
.domain([0, d3.max(map)])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x);
var numbins = 3000;
var histogram = d3.layout.histogram()
.bins(x.ticks(numbins))
(map);
var y = d3.scale.linear()
.domain([0, d3.max(histogram.map(function(i){return i.length;}))])
.range([0, height/2]);
var colorScale = d3.scale.category10();
var canvas = d3.select("body").append("svg")
.attr("width", width+padding)
.attr("height", height+ padding)
.append("g")
.attr("transform", "translate(20,0)")
var bars = canvas.selectAll(".bar")
.data(histogram)
.enter()
.append("g")
.attr("class", "bar")
var group = canvas.append("g")
.attr("tansform","translate(0, " + height + ")")
bars.append("rect")
.attr("x", function(d){return x(d.x);})
.attr("y", function(d){return 500-y(d.y);})
.attr("width", function(d){return d.dx;})
.attr("height", function(d){ return y(d.y);})
.attr("fill", function(d){return colorScale(d[colorColumn]);});
})
</script>
Can anyone help me?
I am attaching image of the plot as well
Recently I have began exploring D3 and I'm having some issues with scales.
I'm in a earlier stages of a simple bar chart and my yScale is outputting some stranges values.
I've notice that this doesn't happen if I simple define the domain like .domain([0, 8000]) which is not very dynamic…
Here's the link for the csv file:
Google Transparency Report: User data requests
And here's the code:
var dataset;
var w = 500;
var h = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
d3.csv("google-user-data-requests.csv", function(data) {
dataset = data;
generateVis();
});
var generateVis = function () {
var barValue = function(d) {
return d["User Data Requests"];
};
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, barValue)])
.range([0, h]);
var bars = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
var barsAttr = bars
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(barValue(d));
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(barValue(d));
});
};
What am I missing?
What you need to do is convert your strings to numbers, i.e.
d3.csv("google-user-data-requests.csv", function(data) {
dataset = data;
dataset.forEach(function(d) {
d['User Data Requests'] = +d['User Data Requests'];
});
generateVis();
});
You probably also want to parse the dates and format them as such.
I'm attempting to make a Paired Bar Graph between glob and local within my JS Object/Array. I've made bar graphs in D3 previously, but haven't used objects. I'm finding it difficult to access the correct data.
Eventually, the keyword data will be used in the axis. And the cpc will be used as a tooltip.
Here's the code that I have so far: (or see my JSFiddle)
var w = 600;
var h = 400;
var colors = ["#377EB8", "#4DAF4A"];
var dataset = {"keyword": ["payday loans", "title loans", "personal loans"],
"glob": ["1500000", "165000", "550000"],
"local": ["673000", "165000", "301000"],
"cpc": ["14.11", "12.53", "6.14"]
};
var series = 2; // Global & Local
var x0Scale = d3.scale.ordinal()
.domain(d3.range(dataset.glob.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {return d.glob;})])
.range([0, h]);
var glob = function(d) {
return d.glob;
};
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w)
.attr("height", h);
// Graph Bars
svg.selectAll("rect")
.data(dataset, glob) //access the series here?
.enter()
.append("rect")
.attr("x", function(d, i){
return x0Scale(i);
})
.attr("y", function(d) {
return h - yScale(d.glob);
})
.attr("width", x0Scale.rangeBand())
.attr("height", function(d) {
return yScale(d.glob); // ***************
})
.attr("fill", colors[1]);
Currently, the chart doesn't get populated. I assume I am not accessing values correctly. I'm simply trying to get data from glob to make sure I'm accessing things correctly - and then from there I was going to populate both series, etc. Is my issue not accessing key/values correctly?
Take a look at this: http://jsfiddle.net/juY5E/2/
I was able to get three bars by changing .data(dataset, glob) to .data(dataset.glob) and then changing d.glob to +d for the 'y' attr, the 'height' attr and in yScale.domain
to be able to switch between glob and local, you may want to restructure the data.
I'm using d3 to build a scatter plot and the set of values I have for the x and y axes both have positive and negative values. This is my first attempt at using d3 and I've gathered from introductory tutorials such as On the tenth day of Xmas, get dirty with data using d3.js, that the key is setting up the x and y scales correctly.
Next, I found this tutorial: Bar Chart with Negative Values, which has almost helped me get it right (I think). My data set is too hefty to place here, but here is the code I have a sample of my data to work with:
<div id="compareAll"></div>
<script>
window.onload = function() {
var dataSet = [ [4,4], [-4,4], [-4,-4], [4,-4], ];
var x0 = Math.max(-d3.min(dataSet[0]), d3.max(dataSet[0]));
var xScale = d3.scale.ordinal()
.domain([-x0,x0])
.range([0,10]);
var yScale = d3.scale.ordinal()
.domain(d3.range(dataSet[1])
.rangeRoundBands([0,10]);
var svg = d3.select("#compareAll")
.append("svg")
.attr("width", 10)
.attr("height",10)
svg.selectAll("circle")
.data(dataSet)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
})
.attr("r",4);
var xAxis = d3.svg.axis()
.scale(xScale)
.tickSize(1);
var yAxis = d3.svg.axis()
.scale(yScale)
.tickSize(1);
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0,10)")
.call(xAxis);
svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(10,0)")
.call(yAxis);
}
</script>
Frankly, I don't understand the use of the ordinal scale in the Bar Chart example, however I got seemly worse results when I took it out and made the yScale with the same syntax as the xScale.
Any explanation of the best way to go about working with positive and negative values for both axes would be appreciated. Let me know if I'm missing something obvious. I through together this example from a much larger project as to make it as specific and easy to read as possible.
Thanks!
There's nothing special about using negative values for your axes - you should simply be using linear scales for your axes, and setting the domain to [min, max], where min might well be negative.
See a working version of your code here: http://jsfiddle.net/nrabinowitz/qN5Sa/
A few notes on this:
You should definitely use linear scales for a scatterplot, unless you want more of a matrix. A standard x-scale might look like this:
var xScale = d3.scale.linear()
.domain([-5, 5])
.range([0,w]);
The d3.svg.axis() component includes an .orient() setting - by default, this is "bottom", i.e. a horizontal axis with numbers below the line. To make a correctly oriented y-axis, use .orient('left').
I didn't include the code to determine the min/max domain for each axis based on your data, because I figured it would complicate the example. But you can do this fairly easily if you don't know the bounds of your data:
// assuming data like [[x0,y0], [x1,y1]]
var xmin = d3.min(data, function(d) { return d[0] }),
xmax = d3.max(data, function(d) { return d[0] }),
ymin = d3.min(data, function(d) { return d[1] }),
ymax = d3.max(data, function(d) { return d[1] });