I tried a lot of datasets and I don't know why, I have a issue with data_histo.csv. The x axis seems reverse and then, bars can't be displayed. With data_histo2.csv or data_histo3.csv, all is good.
An explanation could be nice!
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/d3#5.0.0/dist/d3.min.js"></script>
</head>
<body>
<svg class="histogramme" width="960" height="600"></svg>
<script>
//select
let svgHisto = d3.select(".histogramme")
//dimension
let margin = {top: 20, right: 10, bottom: 20, left: 80}
let width = +svgHisto.attr("width") - margin.left - margin.right
let height = +svgHisto.attr("height") - margin.top - margin.bottom;
let g1 = svgHisto.append("g")
.attr("transform",`translate(${margin.left}, ${margin.top})`);
//data
d3.csv("data_histo.csv").then(function(data) {
console.log(data);
//define x and y axis
let x = d3.scaleLinear();
let y = d3.scaleBand();
x.domain(d3.extent(data, function(d) { return d.value0; })).nice()
.rangeRound([0, width]);
y.domain(data.map(function(d) { return d.libreg; }))
.rangeRound([0, height]).padding(0.1);
//add x axis
g1.append("g")
.attr("class", "axis x_axis")
.attr("transform",`translate(0,${height})`)
.call(d3.axisBottom(x));
//add y axis
g1.append("g")
.attr("class", "axis y_axis")
.call(d3.axisLeft(y));
//bar chart
g1.selectAll(".bar1")
.data(data)
.enter().append("rect")
.attr("class", "bar bar1")
.attr("x", function(d) {return x(Math.min(0,d.value0)); })
.attr("y", function(d) { return y(d.libreg) + 10; })
.attr("width", 0)
.attr("height", y.bandwidth() - 20);
//animate
g1.selectAll(".bar1")
.transition()
.duration(1000)
.delay(function(d,i){return i*100})
.attr("width", function(d) { return x(d.value0); });
});
</script>
</body>
</html>
With data_histo.csv
"codreg","libreg","year0","value0","year1","value1"
"03","Guyane",2009,4,2014,4.6
"04","La Réunion",2009,8.2,2014,9.8
"11","Île-de-France",2009,12.6,2014,13.9
"01","Guadeloupe",2009,13.3,2014,15.8
"32","Hauts-de-France",2009,14.7,2014,16.1
"02","Martinique",2009,14.7,2014,17.6
"44","Grand Est",2009,16.5,2014,18
"84","Auvergne-Rhône-Alpes",2009,16.8,2014,18.3
"52","Pays de la Loire",2009,17.1,2014,18.6
"28","Normandie",2009,17.2,2014,19
"53","Bretagne",2009,18.5,2014,20.2
"24","Centre-Val de Loire",2009,18.7,2014,20.4
"27","Bourgogne-Franche-Comté",2009,18.8,2014,20.7
"76","Occitanie",2009,19.3,2014,20.8
"93","Provence-Alpes-Côte d''Azur",2009,19.5,2014,21.3
"94","Corse",2009,20.2,2014,21.5
"75","Nouvelle-Aquitaine",2009,20.2,2014,21.8
With data_histo2.csv
"codreg","libreg","year0","value0","year1","value1"
"84","Auvergne-Rhône-Alpes",2013,39.1,2017,41.7
"27","Bourgogne-Franche-Comté",2013,42.3,2017,44.4
"53","Bretagne",2013,39.6,2017,44.7
"24","Centre-Val de Loire",2013,40.5,2017,46.8
"94","Corse",2013,24.2,2017,30.8
"44","Grand Est",2013,41.3,2017,45.4
"01","Guadeloupe",2013,55.5,2017,56.5
"03","Guyane",2013,33.1,2017,33.2
"32","Hauts-de-France",2013,45.8,2017,47.3
"11","Île-de-France",2013,40.1,2017,42.6
"02","Martinique",2013,52.5,2017,50.2
"28","Normandie",2013,42.6,2017,46.2
"75","Nouvelle-Aquitaine",2013,40,2017,44.4
"76","Occitanie",2013,40.3,2017,43.7
"52","Pays de la Loire",2013,40.6,2017,45.8
"93","Provence-Alpes-Côte d''Azur",2013,38.5,2017,42.6
"04","La Réunion",2013,54.2,2017,54.6
"06","Mayotte",2013,,2017,
Here is my code : https://plnkr.co/edit/B8qEQ4dlUdRHhkQvzjZx?p=preview
There are two issues with your code:
D3 parses csv/tsv/dsv entries as text. So when you load your csv, you get rows that look like this:
{
"codreg": "03",
"libreg": "Guyane",
"year0": "2009",
"value0": "4",
"year1": "2014",
"value1": "4.6"
}
When you set your scale with extent, you aren't using the numerical extent. You could coerce your data to a number like so:
data.forEach(function(d) {
d.value0 = +d.value0;
})
Secondly, if you do this you'll notice some peculiar behavior in the placement of the bars:
You can see that the bars start to the left of the plot area. The reason is that you are using a linear scale, and plot the start of the bars like so:
.attr("x", function(d) {return x(Math.min(0,d.value0)); })
You want your bars to start at x(4) - which is where the x value that marks the interception with the y axis. Instead you are using x(0), which will naturally be to the left of where you want. This works in your second example, because x(0) also happens to be the x value that intercepts the y axis. Instead, you can simply use:
.attr("x",0)
This marks the left edge of the plot area, which is likely where you want all your bars to be anchored to.
Here's a forked plunkr.
One thing to note, is that your shortest bar will always be non-visible: the start and end points will be the same. This is because the extent of the scale goes from the smallest value to the largest value - and the smallest value, marking the left boundary of the plot area, is the value of the shortest bar. To change this you can modify the scale's domain, perhaps using 0 as the first value and then using d3.max to find the uppermost value.
I had a D3js code which produces bar graphs and works fine with version 3.x. I wanted to upgrade the code to version 5 in the interest of being updated. Upon doing so I was able to correct a number of syntax updates such as scaleLinear, scaleBand, etc. The data is imported via tsv. The code is able to show the graph on the page with the correct x axis widths for the bars. However, the yAxis bars go out of bounds and the scale on the y-axis is very short. For example, the data shows the maximum value of the data to be 30000, but the yaxis is only from 0-90. Upon further investigation the d.RFU values from which the y data is generated seems to be not converted from string to integers. In the v3 code, I had a function at the end which converted the type of d.RFU to integer using the unary operator
d.RFU = +d.RFU
However, it seems to be not working in v5. Could this be due to the promises implementation in replacement of the asynchronous code?
Any solutions on how to fix this in version 5?
Please let me know if you need any more information and forgive me if I have missed out anything as I am new to programming and this website. Any help is appreciated.
Here is parts of the code which I have right now:
//set dimensions of SVG and margins
var margin = { top: 30, right: 100, bottom: 50, left: 100, },
width = divWidth - margin.left - margin.right,
height = 250 - margin.top - margin.bottom,
x = d3.scaleBand()
.range([0, width - 20], 0.1),
y = d3.scaleLinear()
.range([height,0]);
//setup the axis
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var svg = d3.select("#bargraphID")
.append("svg")
.attr("width", width + margin.left + margin.right - 100)
.attr("height", height + margin.top + margin.bottom - 10)
.append("g")
.attr("transform", "translate (" + margin.left + "," + margin.top + ")");
d3.tsv(filename).then(function(data) {
// get x values from the document id
x.domain(data.map(function(d) {
return d.ID;
}));
yMax = d3.max(data, function(d) {
return d.RFU;
});
// get the y values from the document RFU tab
y.domain([0, yMax]);
//create the x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate (0, " + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "middle")
.attr("dx", "0em")
.attr("dy", "-0.55em")
.attr("y", 30)
.attr("class", "x-axisticks");
//create the y-axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
//add the data as bars
var bar = svg.selectAll("bar")
.data(data)
.enter()
.append("rect")
.style("fill", barColor)
.attr("fill-opacity", "0.3")
.attr("x", function(d) {
return x(d.ID);
})
.attr("width", x.bandwidth())
//set initial coords for bars for animation.
.attr("y", height)
.attr("height", 0)
//animate bars to final heights
.transition()
.duration(700)
.attr("y", function(d) {
return y(d.RFU);
})
.attr("height", function(d) {
return height - y(d.RFU);
})
.attr("fill-opacity", "1.0")
.attr("class", "y-data");
});
//convert RFU to integers
function type(d) {
d.RFU = +d.RFU;
return d;
}
Just like with the old v3 and v4 versions, you have to pass the row conversion function to d3.tsv in D3 v5:
d3.tsv(filename, type)
Then, use the promise with then. Have in mind that d3.tsv always return strings (be it D3 v3, v4 or v5), because:
If a row conversion function is not specified, field values are strings.
Here is the demo with fake data:
var tsv = URL.createObjectURL(new Blob([
`name RFU
foo 12
bar 42
baz 17`
]));
d3.tsv(tsv, type).then(function(data) {
console.log(data)
})
function type(d) {
d.RFU = +d.RFU;
return d;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
PS: Since SO snippet may have a problem loading that blob in some browsers, here is the same code in JSFiddle, check the console: https://jsfiddle.net/kv0ea0x2/
I'm following this example by Mike himself. The timeFormat in the example is ("%d-%b-%y"), but using my own data uses just the year. I've made all the necessary changes (I think). The y-axis shows, but the x-axis doesn't. There are also no errors showing, so I'm not sure where to go. Below is my code. Thanks!
<!DOCTYPE html>
<meta charset="utf-8">
<p></p>
<style>
.axis--x path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<!--We immediately define the variables of our svg/chart-->
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// Now we give our svg some attributes. We use conventional margins as set out by Mike Bostock himself.
// Sets width and height minus the margins.
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Here we set out the time format: date-month-year.
//var parseTime = d3.timeParse("%d-%b-%y");
var formatTime = d3.timeFormat("%Y");
formatTime(new Date); // "2015"
// Now we set our axis. X is according to the time, and y is linear.
// We use rangeRound to round all the values to the nearest whole number.
// We don't use rangeBands or rangePoints as we're not creating a bar chart or scatter plot.
var x = d3.scaleTime()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
// Now we tell what we want our line to do/represent.
// x is the date, and y is the close price.
var line = d3.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
// This is where we load our tsv file.
d3.tsv("/LineCharts/Line Chart 2 - MO Capital Punishment/data/data.tsv", function(d) {
d.date = formatTime(d.date);
d.close = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
// The .extent function returns the minimum and maximum value in the given array.
// Then, function(d) { return d.date; } returns all the 'date' values in 'data'.
// The .domain function which returns those maximum and minimum values to D3 as the range for the x axis.
x.domain(d3.extent(data, function(d) {
return d.date;
}));
//Same as above for the x domain.
y.domain(d3.extent(data, function(d) {
return d.close;
}));
// Note that we use attr() to apply transform as an attribute of g.
// SVG transforms are quite powerful, and can accept several different kinds of transform definitions, including scales and rotations.
// But we are keeping it simple here with only a translation transform, which simply pushes the whole g group over and down by some amount, each time a new value is loaded onto the page.
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Doing the same as above but for the y axis.
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
//This is where we append(add) text labels to our y axis.
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.style("text-anchor", "end")
.text("Total");
g.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
In the following line chart, though the chart is plotted properly but the x and y axis with labels is not plotted properly. Can someone help me out with that?
SNIPPET:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<svg width="500" height="300"></svg>
<script>
//module declaration
var app = angular.module('myApp',[]);
//Controller declaration
app.controller('myCtrl', function($scope){
var data = [
{x: "2016-01-10", y: "10.02"},
{x: "2016-02-10", y: "15.02"},
{x: "2016-03-10", y: "50.02"},
{x: "2016-04-10", y: "40.02"},
{x: "2016-05-10", y: "10.02"}
];
var parseTime = d3.timeParse("%Y-%m-%d");
var xScale = d3.scaleTime().range([0,500]).domain(d3.extent(data, function(d){ return parseTime(d.x)}));
var yScale = d3.scaleLinear().range([300,0]).domain([0,50]);
//Plotting domain on x and y axis
var xAxis = d3.scaleBand().rangeRound([0, 500]).padding(0.6);
var yAxis = d3.scaleLinear().rangeRound([300, 0]);
xAxis.domain(data.map(function(d) { return d.letter; }));
yAxis.domain([0, d3.max(data, function(d) { return d.frequency; })]);
//Final printing of elements on svg
//Plortting of x-axis
d3.select("svg")
.append("g")
.attr("transform", "translate(0," + 300 + ")")
.call(d3.axisBottom(xAxis));
//Plotting of y-axis
d3.select("svg")
.append("g")
.call(d3.axisLeft(yAxis).ticks(10, "%"));
//the line function for path
var lineFunction = d3.line()
.x(function(d) {return xScale(parseTime(d.x)); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveLinear);
//Main svg container
var mySVG = d3.select("svg");
//defining the lines
var path = mySVG.append("path");
//plotting lines
path
.attr("d", lineFunction(data))
.attr("stroke",function() { return "hsl(" + Math.random() * 360 + ",100%,50%)"; })
.attr("stroke-width", 2)
.attr("fill", "none");
});
</script>
</body>
</html>
RESULT:
ISSUES:
The X-Axis is not coming
Labels on X-Axis missing
Lables on Y-Axis missing
Please, help me out to get the chart properly.
Regarding the y axis: you're not translating it from the origin position. It should be:
d3.select("svg")
.append("g")
.attr("transform", "translate(30, 0)")//30 here is just an example
.call(d3.axisLeft(yAxis).ticks(10, "%"));
Regarding the x axis: you're translating it all the way down to the height. It should be less than that:
d3.select("svg")
.append("g")
.attr("transform", "translate(0," + (height - 30) + ")")//30 is just an example
.call(d3.axisBottom(xAxis));
In a nutshell: set the margins properly and translate the axis according to those margins.
PS: nothing will show up in the ticks, because you don't have letter or frequency in your data.
I've been looking around a while for an answer to this, and I haven't been able to figure it out.
I'm ultimately creating a TopoJSON file from grid based data (GRIB files).
I can pretty easily interpolate the data down to a finer resolution grid so the plot points appear smoother when zoomed out, but when zoomed in, it's inevitable to see the blocky grid points.
I've also looked into simplification, which does help a bit but its not quite smoothing.
I'm using D3 to render the data.
Is this something that can be done on the front end or should/can it be done in the raw TopoJSON data?
I essentially don't want you to be able to tell that it's a grid, even if you zoom in 10,000%.
Here's an example of what I'm after:
Is this something that can be done on the front end or should/can it be done in the raw TopoJSON data?
This is something that should be done on the front end. If you were to smooth the data before you wrote it to the JSON file, the file would be needlessly big.
If you're using D3.js, and you're working with lines, the built-in interpolate() function is the way to go.
Here is a working example of D3's line.interpolate() using "cardinal" smoothing:
http://codepen.io/gracefulcode/pen/doPmOK
Here's the code:
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.interpolate("cardinal")
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
// Adds the svg canvas
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 + ")");
// Get the data
d3.json('https://api.myjson.com/bins/175jl', function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
// Starting with a basic graph 14
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.close;
})]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
Maybe d3 line.interpolate() is what you're looking for?
More info:
http://www.d3noob.org/2013/01/smoothing-out-lines-in-d3js.html