I'm trying to learn how to code with the d3.js by working on my first d3 mini project based on the Free Code Camp curriculum. I am trying to make a simple bar graph with this json file. I got stuck trying to format the dates in the file. I've tried looking at the d3.js API and I am still lost. I would be very grateful for any advice that comes my way. Here is my code
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//then make a function to parse the time
var parseDate = d3.timeFormat("%Y-%m-%d");
// set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().range([height, 0],0.5);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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 + ")");
//setup axis
// get the data
d3.json("https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json").get(function(error,dataset){
var d =dataset.data;
d.forEach(function(datum, i) {
d[0]=parseDate(datum[0]);
//console.log(datum[1]);
});
//console.log(d[3][0]);
});
You want timeParse, not timeFormat:
var parseDate = d3.timeParse("%Y-%m-%d");
Using timeParse,
the returned function parses a specified string, returning the corresponding date or null if the string could not be parsed according to this format’s specifier.
And this is how your forEach function should be:
data.forEach(function(d) {
d[0] = parseDate(d[0]);
});
Here is a demo:
var parseDate = d3.timeParse("%Y-%m-%d");
d3.json("https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json", function(json) {
var data = json.data;
data.forEach(function(d) {
d[0] = parseDate(d[0]);
});
console.log(data);
});
<script src="https://d3js.org/d3.v4.min.js"></script>
Related
I have a csv that has the following structure:
date
A
B
'2022-01-02'
120
150
'2022-01-03'
160
170
Now, I am trying to build a symbol chart that has the x value as the date and the y value as the value of either A or B.
I would like to have the symbols in A to have the same colors as each other, and group B as well.
The problem is whenever I use my code to do this it gives me only 2 symbols instead of 4. i.e. it gives me one symbol per column not per data point. Also I am not sure how to force all points in the same column to be the same color?
I am using d3 v5
This is my code
var margin = {top: 20, right: 10, bottom: 20, left: 10};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
const timeConv = d3.timeParse("%Y-%m-%d");
const dataset = d3.csv("xxx.csv");
dataset.then(function(data) {
var slices = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d){
return {
date: timeConv(d.date),
measurement: +d[id]
};
})
};
});
var svg2 = d3.select("body").append("svg")
.attr("width", (width + margin.left + margin.right))
.attr("height", (height + margin.top + margin.bottom))
.attr("transform", "translate(" + margin.left/4 + "," + margin.top/4 + ")");
// scales
const xScale = d3.scaleTime().range([0,width]);
const yScale = d3.scaleLinear().rangeRound([height, 0]);
xScale.domain(d3.extent(data, function(d){
return timeConv(d.date)}));
yScale.domain([(0), d3.max(slices1, function(c) {
return d3.max(c.values, function(d) {
return d.measurement; });
})
]);
// axis
const yaxis = d3.axisLeft()
.ticks(9)
.scale(yScale);
const xaxis = d3.axisBottom()
.ticks(d3.timeMonth.every(3))
.tickFormat(d3.timeFormat('%b %y'))
.scale(xScale);
var slices2 = slices.filter(slice => RegExp('someRegex').test(slice.id))
//console.log(xScale(timeConv(new Date(d.value.date))))
const symbol = d3.symbol().size(500)
const symbols = svg2_2.append('g').attr('id','symbols').selectAll("symbols")
.data(slices2)
.enter()
symbols.append("path").attr("d", symbol)
I am trying to update a d3 graph when I click on a County on a map. Instead of updating the existing chart, a new graph is created each time. Any ideas of how to rectify this? Code is here: https://github.com/careyshan/d3-graphUpgade
Thanks
This is the part of the code that I am having difficulty with:
on("click", function(d,i){
if(document.getElementById('linechart').childNodes===null){
console.log("Working")
dataNew = data.filter(function( obj ) {
return obj.County == d.properties.NAME_TAG;
console.log(data);
});
dataNew.sort(function(a,b){
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return b.date - a.date;
});
for (var i=1; i<dataNew.length;i++){
dataSel.push(dataNew[i]);
}
}else if(document.getElementById('linechart').childNodes!=null){
//console.log("check")
dataNew = data.filter(function( obj ) {
return obj.County == d.properties.NAME_TAG;
});
dataNew.sort(function(a,b){
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return b.date - a.date;
});
for (var i=1; i<dataNew.length;i++){
dataSel.push(dataNew[i]);
}
}
linechart(dataNew);
console.log(dataNew);
});
A new graph is created each time because on every call to the function linechart() you are appending a new graph (an svg element) to the body of the page.
This is the snippet from your code which appends a new graph each time.
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("id","linechart")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
If you want to update the same chart, just modify your linechart() function to refer to the same svg element. You can achieve this by declaring a global variable for the svg element and use it in your linechart() method like below:
// If graph is not there, create it
if(!svg) {
// append the svg object to the body of the page
//appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
svg= d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("id","linechart")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
}
Note: Declare the svg as a global variable (may be at the starting of the script tag):
var svg;
I am making a scatterplot in D3 with the code below and the textlabels of the right circles are being cut off, after putting textlabels on the X and Y axis.
Here is a working jsFiddle:
https://jsfiddle.net/chemok78/1vcat0s8/4/
Adding the X and Y axis labels seems to move the whole chart to the right and up, making it move a bit out of the containing div. Anyone can help me how to fix this?
var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json";
d3.json(url, function(json) {
var data = json;
var margin = {
top: 40,
right: 100,
bottom: 80,
left: 80
};
var w = 1000 - margin.left - margin.right;
var h = 800 - margin.top - margin.bottom;
var svg = d3.select("#chart")
.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 + ")");
var maxRank = d3.max(data, function(d) {
return d.Place;
});
var minSeconds = d3.min(data, function(d) {
return d.Seconds;
});
var maxSeconds = d3.max(data, function(d) {
return d.Seconds;
});
var formatTime = d3.time.format("%M:%S");
var formatSeconds = formatMinutes = function(d) {
return formatTime(new Date(2016, 0, 0, 0, 1, d));
};
var xScale = d3.scale.linear()
.domain([maxSeconds + 5, minSeconds])
.range([0, w]);
var yScale = d3.scale.linear()
.domain([0, maxRank + 2])
.range([0, h]);
This has nothing to do with "moving the chart". Here is the problem.
When you do this:
var labels = svg.selectAll("text")
You're selecting text elements that already exist in your SVG. Because of that, your "enter" selection will have less elements compared to what it should contain.
The solution is simple: select something that doesn't exist:
var labels = svg.selectAll("foo")
Or, alternatively, move the blocks that append the axes' labels to the bottom of the code.
Here is your updated fiddle: https://jsfiddle.net/mp7LxsL4/
I'm trying to make a scatterplot that appears below an interactive worldmap with the help of D3. The scatterplot contains data from the country that the user clicked on in the worldmap. The problem is that when the user clicks on another country, the scatterplot of the previous country should disappear. This is not the case unfortunately, the second scatterplot just appears under the first scatterplot. Does anyone know how I can fix this? Any help would be greatly appreciated.
A part of the code I use for the scatterplot:
function ScatterCorruption(dataset, title){
var xValue = function(d) { return d.GDP;}
var yValue = function(d) { return d.Variable;}
// determine parameters
var margin = {top: 20, right: 20, bottom: 200, left: 70},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// formatters for axis and labels
var currencyFormat = d3.format("0.2f");
var decimalFormat = d3.format("0.2f");
// determine x scale
var x = d3.scale.linear()
.range([0, width]);
// determine y scale
var y = d3.scale.linear()
.range([height, 0]);
// determine x-axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
// determine y-axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// make svg
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 + ")");
// add the tooltip area to the webpage
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
console.log(dataset)
// load in data
d3.tsv(dataset, function(error, data) {
if (error) throw error;
// convert data
data.forEach(function(d) {
d.GDP = +d.GDP;
d.Variable = +d.Variable;
});
You'll need to call this before rendering a new scatterplot: d3.selectAll("svg > *").remove(); so that your svg is clear again. Alternatively you can also do d3.select("svg").remove(); and then recreate the svg.
I'm trying to make some basic d3 charts, which I have a little experience doing. Here is a sample of the JSON I am working with (I have 100 objects, left most out for brevity):
var data = [
{
"OrderID": 1,
"ShipCountry": "USA",
"Freight": 168.0,
"Version": 2
},
{
"OrderID": 2,
"ShipCountry": "USA",
"Freight": 336.0,
"Version": 2
},
{
"OrderID": 3,
"ShipCountry": "USA",
"Freight": 504.0,
"Version": 2
}]
and here is my d3 code:
var margin = { top: 10, bottom: 30, left: 30, right: 20 };
var width = 700 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
//svg for chart with margins
var svg = d3.select('#chart-wrapper')
.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//xScale will be based on orderID attribute
var xScale = d3.scale.linear().domain([0, 99]).range([0, width]);
var yScale = d3.scale.linear().domain([16632, 0]).range(height, 0);
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function (d) {
return xScale(d.Freight)
})
.attr('cy', function (d) {
return yScale(d.OrderID);
})
.attr('r', 2)
.attr('fill', 'green');
For some reason, the cy property of each circle gets set to NaN- which obviously keeps it from rendering. I have switched around Freight and OrderID fields, and still only the cy property does not get set correctly. While debugging, I have logged the values of each, and they appear to be real numbers. (This also happened while trying to create a line graph- the second number of the 'd' attribute for my path was NaN, which is when I decided to attempt a scatterplot instead)
Any idea what is going on? Thanks
You need to make the argument for .range() an array.
var yScale = d3.scale.linear().domain([16632, 0]).range(height, 0);
is now
var yScale = d3.scale.linear().domain([16632, 0]).range([height, 0]);