D3 Grouped Bar chart Dynamic domain and range- Remove bandwidth without data - javascript

I created a grouped barchart in the code jsfiddle as explained here. My need is to adjust the size of the bandwidth in the X axis dynamically based on the values different than zero. For example the x axis with the value poacee to display only two value(bars) without space in between the blue and red barcharts and accordingly to adjust the bandwidth of X axis, to have size of the two elements.
code:
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<script>
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
// Parse the Data
var data = [
{"group": "banana", "Nitrogen": "12", "normal": 1, "stress": 13},
{"group": "poacee", "Nitrogen": "6", "normal": 0, "stress": 33},
{"group": "sorgho", "Nitrogen": "11", "normal": 28, "stress": 12},
{"group": "triticum", "Nitrogen": "19", "normal": 6, "stress": 1}]
// List of subgroups = header of the csv files = soil condition here
var subgroups = ["group","Nitrogen", "normal", "stress"]
// List of groups = species here = value of the first column called group -> I show them on the X axis
// var groups = d3.map(data, function(d){return(d.group)}).keys()
var groups = ["banana", "poacee", "sorgho", "triticum"]
// Add X axis
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(0));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 40])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
// Another scale for subgroup position?
var xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, x.bandwidth()])
.padding([0.05])
// color palette = one color per subgroup
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a'])
// Show the bars
svg.append("g")
.selectAll("g")
// Enter in data = loop group per group
.data(data)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + x(d.group) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return xSubgroup(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.key); });
</script>

Related

d3 ("rect") not updating

Working on a simple d3-graph with changing values via two buttons. after adding some tooltip functionality i came to understand that the transition basically chokes tooltip functionality, so i had to remove the "transition()" and put it at the very end in order to get tooltip functionality, now im stuck with a graph that updates its axes but not the rect´s themselves. anyone to point me in the right direction?
full code:
function openPage(pageUrl){
window.open(pageUrl);
}
//Best So Far
var data1 = [
{group: "Niger", value: 264257},
{group: "Mali", value: 350110},
{group: "Chad", value: 406573},
{group: "Burkina Faso", value: 1814283}
];
var data2 = [
{group: "Niger", value: 1.018},
{group: "Mali", value: 1.636},
{group: "Chad", value: 2.343},
{group: "Burkina Faso", value: 8.239}
];
// set the dimensions and margins of the graph
var margin = {top: 30, right: 30, bottom: 60, left: 70},
width = 500 - margin.left - margin.right;
height = 440 - margin.top - margin.bottom;
var svg = d3.select("#VizBox")
.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 + ")");
// Initialize the X axis
var x = d3.scaleBand()
.range([ 0, width ])
.padding(0.2);
var xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
// Initialize the Y axis
var y = d3.scaleLinear()
.range([ height, 0]);
var yAxis = svg.append("g")
.attr("class", "myYaxis")
// By Tomas S.(etter akser)
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
// END by Tomas S.
// A function that create / update the plot for a given variable:
function update(data) {
// Update the X axis
x.domain(data.map(function(d) { return d.group; }))
xAxis.call(d3.axisBottom(x))
// Update the Y axis
y.domain([0, d3.max(data, function(d) { return d.value }) ]);
yAxis.transition().duration(1000).call(d3.axisLeft(y));
// Create the u variable
var u = svg.selectAll("rect")
.data(data)
u
.enter()
.append("rect") // Add a new rect for each new elements
.attr("x", function(d) { return x(d.group); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", "#ADD8E6")
// By Tomas S.
// Based on this example:
// https://bl.ocks.org/alandunning/274bf248fd0f362d64674920e85c1eb7
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 75 + "px")
.style("top", d3.event.pageY + 25 + "px")
.style("display", "inline-block")
.html("Land: " + (d.group) + "<br>" + "Verdi: " + (d.value));
})
.on("mouseout", function(d){ tooltip.style("display", "none");})
// END by Tomas S.
.merge(u) // get the already existing elements as well
.transition() // and apply changes to all of them
.duration(1000)
}
// Initialize the plot with the first dataset
update(data1)

How to make a scatterplot based upon certain criteria using d3.js

For learning how to use d3.js, I was trying to use titanic dataset for learning available on kaggle.
I am trying to achieve the objective:
Make a scatterplot of age vs fare with age in x axis and fare in y axis
Use the sex column to have male as square and female as circles in the scatterplot
Have opacity to indicate the condition - survived or not survived.
I have used the following code:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.age);
})
.attr("cy", function(d) {
return y(d.fare);
})
.attr("r", 1.5)
.style("fill", "#69b3a2")
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
The condition I want to implement is:
if(d.sex == "female"){
return d3.symbolCircle;
} else if (d.sex == "male"){
return d3.symbolSquare;
}
But, being absolute new to the syntax, I am not understanding how. Also, How to have two colors for the 3rd objective of indicating survivor vs dead.
Can anyone help me please. I really thank you in advance.
In case you do not have the dataset, it can also be found here.
Firstly, you need to look at your data. Your properties are all uppercase, and reading a CSV file always means that you need to parse your rows: numbers and dates are still strings, you need to cast them as such:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.age);
})
.attr("cy", function(d) {
return y(d.fare);
})
.attr("r", 1.5)
.style("fill", "#69b3a2")
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
Secondly, circles can only be circles. To draw both squares and circles, you'll need to use <path>. Your d3.symbol* were correct, but you need to access their .draw() function. d3.path is a generator to easily draw the d attribute of a path:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + [x(d.age), y(d.fare)] + ")";
})
.attr("d", function(d) {
const path = d3.path();
const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
shape.draw(path, 8);
return path.toString();
})
.style("fill", "#69b3a2")
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
(As an aside, you also could have drawn rects for both, but given the female ones rounded corners with the rx attribute).
Finally, you can use a colour scale for fill, with d3.scaleOrdinal, but if you have only two colours and don't use it to colour multiple things (like a line chart and a legend), just use an if statement:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + [x(d.age), y(d.fare)] + ")";
})
.attr("d", function(d) {
const path = d3.path();
const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
shape.draw(path, 8);
return path.toString();
})
.style("fill", function(d) {
return d.survived ? "#69b3a2" : "#ddd";
})
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
One interesting thing in SVG is that a <rect> with the rx and ry attributes equal to half its width or height (those being the same, of course) becomes effectively a circle.
So, supposing you have
var diameter = 3;
All you need is:
.attr("rx", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
.attr("ry", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
And, of course, subtract the x and y positions by half the diameter (i.e., the radius).
That seems like a hack, but the advantage of that approach is that it's quite easy to transition between the square and the "circle" by just changing the rx/ry values (one can transition paths, but that's a bit more complicated). Have a look at this transition, with an exaggerated radius and a clipped domain:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 10,
left: 60
},
width = 660 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var diameter = 12;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", row, function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 300])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.Age) - diameter / 2;
})
.attr("y", function(d) {
return y(d.Fare) - diameter / 2;
})
.attr("width", diameter)
.attr("height", diameter)
.style("fill", "#69b3a2")
.transition()
.duration(2000)
.attr("rx", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
.attr("ry", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
.style("fill", function(d) {
return +d.Survived ? "#69b3a2" : "tan"
});
})
function row(d) {
d.Age = +d.Age;
d.Fare = +d.Fare;
return d;
}
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

How to add labels to my scatterplot from data using d3.js

I am learning d3.js for visualization and using titanic dataset
My aim is to add the names of the passengers to my visualization so that when one moves cursor over a point - the name of the person is displayed.
So far, I have achieved the goals:
plot the x axis and y axis on log scale and the data from the table which has age on x-axis and fare on y-axis
The female is circle and male is square
the survived has light color while the dead have brighter color.
Now I want to append text and I am not sure if it should be:
// Add Text Labels
svg.selectAll("text")
.data(data_scatter)
.enter()
.append("text")
.text(function(d) {
return d.name;
})
.attr("font_family", "sans-serif") // Font type
.attr("font-size", "11px") // Font size
.attr("fill", "darkgreen"); // Font color
I am using this code for the visualization:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLog()
.domain([1e+0, 1e+3])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + [x(d.age), y(d.fare)] + ")";
})
.attr("d", function(d) {
const path = d3.path();
const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
shape.draw(path, 8);
return path.toString();
})
.style("fill-opacity", function(d) {
return d.survived ? "0.3" : "1";
})
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
I got so far:
Now I want to add labels of names to the points.
Can anyone please help me.
To add a title attribute to each path, do something like this:
Remember to uncomment name: d.Name to make sure name is known.
Also note that if you open the generated HTML in the DOM inspector, you can see that every path now has a title child node. The DOM inspector should - after the console - always be the first thing you check when debugging d3.js
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLog()
.domain([1e+0, 1e+3])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + [x(d.age), y(d.fare)] + ")";
})
.attr("d", function(d) {
const path = d3.path();
const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
shape.draw(path, 8);
return path.toString();
})
.style("fill-opacity", function(d) {
return d.survived ? "0.3" : "1";
})
.append("title")
.text(function(d) {
return d.name;
});
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
In order to solve what I wanted to do, I just needed to do the following:
using name: d.Name read the name column data and then in the final part: add
.append("svg:title")
.text(function(d) { return d.name});
after the styling component.
That gets me to displaying names when I hover over the points.

Assign distinct color to each row in a heat map

I'm using code that can be found at:
https://www.d3-graph-gallery.com/graph/heatmap_basic.html
In particular, I want to change the following snippet of code:
// Build color scale
var myColor = d3.scaleLinear()
.range(["white", "#69b3a2"])
.domain([1,100])
So it accepts a color as an argument. My intention is to have a separate color for each row of the heat map. Is this possible?
Given what you described here:
My intention is to have a separate color for each row of the heat map.
I'm assuming that, for each row, you want a different range in the colour scale, going from "white" to a specific colour.
If that is correct, you can simply set an array of colours, for instance...
var colorArray = d3.schemeCategory10;
//d3.schemeCategory10 is this array of colours:
//["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"]
... and then, using the myVars array, set the scale range dynamically inside the anonymous function:
.style("fill", function(d) {
var thisColor = colorArray[myVars.indexOf(d.variable)]
myColor.range(["white", thisColor])
return myColor(d.value)
})
Here is the code you linked with that change:
// set the dimensions and margins of the graph
var margin = {
top: 30,
right: 30,
bottom: 30,
left: 30
},
width = 450 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
// Labels of row and columns
var myGroups = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
var myVars = ["v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10"]
// Build X scales and axis:
var x = d3.scaleBand()
.range([0, width])
.domain(myGroups)
.padding(0.01);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
// Build X scales and axis:
var y = d3.scaleBand()
.range([height, 0])
.domain(myVars)
.padding(0.01);
svg.append("g")
.call(d3.axisLeft(y));
var colorArray = d3.schemeCategory10;
// Build color scale
var myColor = d3.scaleLinear()
.domain([1, 100])
//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/heatmap_data.csv", function(data) {
svg.selectAll()
.data(data, function(d) {
return d.group + ':' + d.variable;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.group)
})
.attr("y", function(d) {
return y(d.variable)
})
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.style("fill", function(d) {
var thisColor = colorArray[myVars.indexOf(d.variable)]
myColor.range(["white", thisColor])
return myColor(d.value)
})
})
<script src="https://d3js.org/d3.v4.js"></script>
<div id="my_dataviz"></div>

Uncaught TypeError: Cannot read property 'length' of undefined in d3

I am trying to make a line chart in d3, with time on x-axis. I am using json data in variable. I used d3.time.format() function to format the time, but it gives me above error. I am learning d3 so please help. my code is :
<div id="viz"></div>
<script>
var width = 640;
var height = 480;
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().domain([height, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var data = [
{ "at": "2014-11-18T07:29:03.859Z", "value": 0.553292},
{ "at": "2014-11-18T07:28:53.859Z", "value": 0.563292},
{ "at": "2014-11-18T07:28:43.859Z", "value": 0.573292},
{ "at": "2014-11-18T07:28:33.859Z", "value": 0.583292},
{ "at": "2014-11-18T07:28:13.859Z", "value": 0.553292},
{ "at": "2014-11-18T07:28:03.859Z", "value": 0.563292}];
var line = d3.svg.line()
.x(function(d, i) { return x(d.x_axis); })
.y(function(d, i) { return y(d.y_axis); })
.interpolate("linear");
var vis = d3.select("#viz")
.append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g").attr("transform","translate(" + margin.left + "," + margin.top + ")");
data.forEach(function(d) {
d.xAxis = d3.time.format("%d-%b-%y").parse(d.xAxis);
d.yAxis = +d.value;
});
x.domain(d3.extent(data, function(d) { return d.x_axis; }));
y.domain(d3.extent(data, function(d) { return d.y_axis; }));
vis.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
vis.append("g")
.attr("class", "y axis")
.call(yAxis);
vis.append("svg:path")
.datum(data)
.attr("class", "line")
.attr("d", line);
There are several issues in your code so I have created a new one below with some comments on the changes.
<style>
/* You need some styling for your line */
.line{
stroke:steelblue;
fill:none
}
</style>
<script>
var width = 640;
var height = 480;
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var x = d3.time.scale().range([0, width]);
// You had a mistake filling the domain structure of your scale.
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 data = [
{ "at": "2014-11-18T07:29:03.859Z", "value": 0.553292},
{ "at": "2014-11-18T07:28:53.859Z", "value": 0.563292},
{ "at": "2014-11-18T07:28:43.859Z", "value": 0.573292},
{ "at": "2014-11-18T07:28:33.859Z", "value": 0.583292},
{ "at": "2014-11-18T07:28:13.859Z", "value": 0.553292},
{ "at": "2014-11-18T07:28:03.859Z", "value": 0.563292}];
//You were using non existing variables
var line = d3.svg.line()
.x(function(d) { return x(d.xAxis); })
.y(function(d) { return y(d.yAxis); })
.interpolate("linear");
var vis = d3.select("#viz")
.append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g").attr("transform","translate(" + margin.left + "," + margin.top + ")");
//You are using ISO format, use the correct time parser
var iso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");
data.forEach(function(d) {
d.xAxis = iso.parse(d.at); //You were parsing a non existing variable
d.yAxis = parseFloat(d.value); //You were parsing a non existing variable
});
console.log(data);
//The variables for the domain were not correct
x.domain(d3.extent(data, function(d) { return d.xAxis; }));
y.domain(d3.extent(data, function(d) { return d.yAxis; }));
vis.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis);
vis.append("g").attr("class", "y axis").call(yAxis);
vis.append("svg:path").datum(data).attr("class", "line").attr("d", line);
</script>
Let me know if this helps
Here's a fiddle which shows what I think you're after: http://jsfiddle.net/henbox/n9s542w0/1/
There were a couple of changes to make.
Firstly, there's some inconsistency about how you use d.y_axis vs d.yAxis (same with x) to set \ refer to the new elements you add to the data set which you want to plot. I've set these keys to d.y_axis and d.x_axis.
Next:
d.xAxis = d3.time.format("%d-%b-%y").parse(d.xAxis);
Your format here should refer to the input format, rather than the output, and you should be parsing d.at rather than d.xAxis (see this answer for more), so you should have:
d.x_axis = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse(d.at);
Thirdly, when you append the line you should handle the data differently. Change:
vis.append("svg:path")
.datum(data)
.attr("class", "line")
.attr("d", line);
to
vis.append("svg:path")
.attr("class", "line")
.attr("d", line(data));
As per this similar example
And finally, you were setting the y domain twice but never setting the range. The line:
var y = d3.scale.linear().domain([height, 0]);
should read
var y = d3.scale.linear().range([height, 0]);

Categories

Resources