Colour not rendering properly Pie Chart d3 - javascript

I am working on a pie chart today in D3 (fun!).
Alright, the issue I am having is the colours aren't being assigned properly.
Here's the jsfiddle:
https://jsfiddle.net/zh34ud25/5/
Most(?) relevant code:
var color = d3.scale.ordinal()
.range(["#71b2b9", "#dcdcdc"]);
color.domain(d3.keys(dataUnbilledRevenue[0].values[0]).filter(function(key) {
if (key === 'Unbilled_Revenue'
|| key === 'Billed_Revenue') {
return key
}
}));
// This returns the data into two separate objects which can be graphed.
// In this case, Amount and Quantity.
var datasets = color.domain().map(function(name) {
return {
name: name,
values: dataUnbilledRevenue.map(function(d) {
return {
Value: +d.values[0][name]
};
})
};
});

This is simple, doing this will give the colors to pie:
pieValues.append("path")
.attr("d", arc)
.attr('class', 'pie-point')
.style("fill", function(d) {
return color(d.data.name)
})
working code here

Related

Hide legend items if value is equal to 0 in NVD3.js Pie Chart

Looking to hide label from legend of a Pie Chart when return value is equal to 0. Can anyone point me in the right direction in NVD3.js?
nv.addGraph(function () {
var donutChart = nv.models.pieChart()
.x(function (d) {
return d.label
})
.y(function (d) {
return d.value
})
d3.select("#chart-devices svg")
.datum(data)
.transition().duration(1200)
.call(donutChart);
nv.utils.windowResize(donutChart.update);
return donutChart;
});
A possible answer is to remove items with value=0 on renderEnd event:
chart.dispatch.on('renderEnd', function () {
console.log("renderEnd");
d3.selectAll(".nv-legend .nv-series")[0].forEach(function (d) {
//get the data
var t = d3.select(d).data()[0];
// remove item from legend
if (t.value == 0)
d3.select(d).remove();
});
});
Another possibility is to remove items after a timeout:
setTimeout(function () {
d3.selectAll(".nv-legend .nv-series")[0].forEach(function (d) {
//get the data
var t = d3.select(d).data()[0];
// remove item from legend
if (t.value == 0)
d3.select(d).remove();
});
}, 1);
In both cases there are gaps among remaining items.
Here is a fiddle (first option): https://jsfiddle.net/beaver71/yt7vrohk/

Trouble Loading Data for Titanic Visualization

Having some massive trouble getting my D3 scatter plot visualization running. Didnt know how to reference the data, so its available from a dropbox link here.
There are a few problems.
I am a bit confused about loading my data.
I cannot seem to get the data loaded. I have been successful before, but I am trying to load the data without having to reference a function (i.e., global). However, as you will see - I am getting nothing - [].
Do I need to load it at the bottom of my script and then reference the function within the d3.csv(function(d) {...}, FUNCTION);? Why cant I simple load it to a variable (as I am trying to) at the top of my script. Such that its the first object available?
I also felt like I had a good handle on the Mike Bostock tutorial about .enter(), update(), .exit(). But I know that I have an issue in the comment section of "//ENTER + UPDATE" where I have circle.circle(function(d) {return d;});. I dont understand this.
Overall, I am looking to create a scatter plot with fare as my x-axis, age as my y-axis, then loop through the options of "Female Only", "Male Only", "Children Only" and "All" (starting and ending with All).
I plan to add more to this as I get a better understanding of where I am currently stumbling.
d3.csv("titanic_full.csv", function(d) {
return {
fare: +d.fare,
age: d.age == '' ? NaN : +d.age,
sibsp: +d.sibsp,
pclass: +d.pclass,
sex: d.sex,
name: d.name,
survived: d.survived
};
}, function(error, d) {
//Filter out erroneous age values (263 rows are removed)
var dataset = d.filter(function(d) {
if (d['age'] >= 0) {
return d;
}
});
//*Main Elements Setup*
//Width and height
var w = 650;
var h = 650;
var padding = 20;
//Create scale functions
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.fare;
})])
.range([padding, w - padding * 2]); // introduced this to make sure values are not cut off
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.age;
})])
.range([h - padding, padding]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Show Axes
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + (h - padding) + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + padding + ',0)')
.call(yAxis);
function update(dataset) {
//DATA JOIN
//Join new data with old elements, if any.
var circle = svg.selectAll('circle')
.data(dataset);
//UPDATE
//Update old elements as needed.
circle.attr('class', 'update');
//ENTER
//Create new elements as needed.
circle.enter().append('circle')
.attr('class', 'enter')
.transition()
.duration(1000)
.attr("cx", function(d) {
return xScale(d.fare);
})
.attr("cy", function(d) {
return yScale(d.age);
})
.attr("r", 6)
.attr('fill', function(d) {
if (d.survived === '0') {
return 'green';
} else {
return 'red';
}
})
//ENTER + UPDATE
//Appending to the enter selection expands the update selection to include
//entering elements; so, operations on the update selection after appending to
//the enter selection will apply to both entering and updating nodes.
circle.circle(function(d) {
return d;
});
//EXIT
//Remove old elements as needed.
circle.exit().remove();
};
//The initial display.
update(dataset);
//Work through each selection
var options = ['Female Only', 'Male Only', 'Children Only', 'All']
var option_idx = 0;
console.log('next')
var option_interval = setInterval(function(options) {
if (options == 'Female Only') {
var filteredData = dataset.filter(function(d) {
if (d['sex'] == 'female') {
return d;
}
})
} else if (options == 'Male Only') {
var filteredData = dataset.filter(function(d) {
if (d['sex'] == 'male') {
return d;
}
})
} else if (options == 'Children Only') {
var filteredData = dataset.filter(function(d) {
if (d['age'] <= 13) {
return d;
}
})
} else if (options == 'All') {
var filteredData = dataset.filter(function(d) {
return d;
})
};
console.log('interval')
option_idx++; // increment by one
update(filteredData);
if (option_idx >= options.length) {
clearInterval(option_interval);
};
}, 1500);
});
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 8px;
}
<title>Titanic Visualization - Fare and Age Survival</title>
You should write the whole code inside the d3.csv callback function. Try this way.
d3.csv("titanic_full.csv", function(d) {
return {
fare: +d[fare],
age: d.age == '' ? NaN : +d.age,
sibsp: +d.sibsp,
pclass: +d.pclass
};
}, function(error, dataset) {
//Filter out erroneous age values (263 rows are removed)
var dataset = dataset.filter(function(d) {
if (d['age'] >= 0) {
return d;
}
});
//Remaining code
});
Refer here for more details.
Okay, so I have an answer, but I probably will not explain this as well.
1. I am a bit confused about loading my data.
With the help of #Gilsha, I was able to reconfigure my script to load the data first, with the rest of my script being the 'callback' portion of the d3.csv function. That worked smoothly. I also was able to filter my data to remove the blanks right away. Here is that first part:
d3.csv("titanic_full.csv", function(d) {
return {
fare: +d.fare,
age: d.age == '' ? NaN : +d.age,
sibsp: +d.sibsp,
pclass: +d.pclass,
sex: d.sex,
name: d.name,
survived: d.survived
};
}, function(error, d) {
//Filter out erroneous age values (263 rows are removed)
var dataset = d.filter(function(d) {
if (d['age'] >= 0) {
return d;
}
});
//Rest of script here.
**2. I also felt like I had a good handle on the Mike Bostock tutorial about .enter(), update(), .exit(). Link to Bostock Tutorial I was following **
Couple things that I did wrong here that was holding me up. The main item that I was stuck on was this portion:
//ENTER + UPDATE
//Appending to the enter selection expands the update selection to include
//entering elements; so, operations on the update selection after appending to
//the enter selection will apply to both entering and updating nodes.
circle.circle(function(d) {return d;});
Basically, I was following the tutorial too closely and didnt realize that when he was using "text.text(function(d) {return d;});", he was setting the text attribute (?) to something. This is where I believe I should be setting any changes to my ENTER and UPDATE selections (all the items that I expect to be in the DOM). So, when Mike was doing text.text and I replicated with circle.circle, I should have had circle.text(.....). Or whatever I want there. Anyone care to comment or explain that better??
PS - I had many other errors... throughout, especially in the section of establishing my interval!

Set Colors of zoomable treemap conditionally

I am using the D3 library to create a Zoomable Treemap for my application data using Javascript and JSON. I see online that many times d3 category for Color is being used to determine the colors of each section. However, I wish to color the sections of treemap using my application logic. Like below:
If conditionA
color = red
If conditionB
color=green
....
Is there any way to achieve this.. Check the values of my JSON Object and set the color of a section only on the basis of some conditions; and have all other sections set to a default color?
Your question is a little vague (next time include some code!), but in general, say you have data like this:
var data = [{
conditionA: true,
conditionB: false
}, {
conditionA: false,
conditionB: true
}, {
conditionA: false,
conditionB: false
}];
then it's as simple as:
svg.selectAll('.SomeCircles')
.data(data)
.enter()
.append('circle')
.attr('r', 20)
.attr('cx', function(d, i) {
return i * 25 + 25;
})
.attr('cy', function(d, i) {
return i * 25 + 25;
})
.attr('class', 'SomeCircles')
.style('fill', function(d) { //<-- filling based on an attribute of my data
if (d.conditionA) {
return 'red';
} else if (d.conditionB) {
return 'green';
} else {
return 'blue';
}
});
Here is an example.

d3.js scatterplot with different colors and symbols - issues encountered

I am trying to create a scatterplot of hundreds of datapoints, each with about 5 different attributes.
The data is loaded from a .csv as an array of objects, each of which looks like this:
{hour: "02",yval: "63",foo: "33", goo:"0", bar:"1"},
I want to display the scatterplot with the following attributes:
Shape for bar:
-circle to represent all points where bar=0, and a triangle-down to represent those where bar=1 (this is a dummy variable).
Color for foo and goo:
All points start as grey. goo is categorical with values [0,1,2] while foo is quantitative with a range from 0-50. foo and goo are mutually exclusive, so only one of them has a value. In other words, for each data point either foo=0 or goo=0.
Points with goo=1 should be orange; points with goo=2 should be red.
foo should be mapped onto a linear color scale from light blue to dark blue, ie d3.scale.linear().domain([0, 50]).range(["#87CEFF", "#0000FF"]);
I can do each of these individually, but defining everything together is creating issues for me.
My code with reproducible data is here: http://jsfiddle.net/qy5ohw0x/3/
Issues
For the symbol, i tried
.append("svg:path")
.attr("d", d3.svg.symbol())
which did not work. I tried a different approach altogether, but this did not map the values correctly:
var series = svg.selectAll("g.series")
.data(dataSet, function(d, i) { return d.bar; })
.enter()
.append("svg:g")
series.selectAll("g.point")
.data(dataSet)
.enter()
.append("svg:path")
.attr("transform", function(d, i) { return "translate(" + d.hour + "," + d.yval + ")"; })
.attr("d", function(d,i, j) { return d3.svg.symbol().type(symbolType[j])(); })
.attr("r", 2);
For the goo colors (grey/orange/red), i mapped the values to the 3 colors manually:
First define var colors = ["grey", "orange", "red"];
Then while drawing the data points chain
.style("fill", function (d) { return colors[d.type]; })
This worked alone, but not with the different symbols.
Finally, can i chain a second color .attr for foo? d3.scale.linear().domain([0, 50]).range(["#87CEFF", "#0000FF"]); would probably work if this is possible.
Again, the jsfiddle is here: http://jsfiddle.net/qy5ohw0x/3/
Thanks!!
Just do all the logic and comparisons in a function(d) for each attribute.
First set up some helpers:
// symbol generators
var symbolTypes = {
"triangleDown": d3.svg.symbol().type("triangle-down"),
"circle": d3.svg.symbol().type("circle")
};
// colors for foo
var fooColors = d3.scale
.linear()
.domain([0, 50])
.range(["#87CEFF", "#0000FF"]);
Then append a path for each symbol:
svg.selectAll("path")
.data(dataSet)
.enter().append("path")
.attr("class", "dot")
// position it, can't use x/y on path, so translate it
.attr("transform", function(d) {
return "translate(" + (x(d.hour) + (Math.random() * 12 - 6)) + "," + y(d.yval) + ")";
})
// assign d from our symbols
.attr("d", function(d,i){
if (d.bar === "0") // circle if bar === 0
return symbolTypes.circle();
else
return symbolTypes.triangleDown();
})
// fill based on goo and foo
.style("fill", function(d,i){
if (d.goo !== "0"){
if (d.goo === "1")
return "red";
else
return "orange";
}else{
return fooColors(d.foo);
}
});
Updated fiddle.
On a side note, I actually think straight d3 is way more intuitive than nvd3 for this situation.
It's much simplier with nvd3.js
function prepareData (data) {
return [{
key: 'Group 1',
values: data.map(function (item) {
item.shape = item.bar == "0" ? 'circle' : 'triangle-down';
item.x = Number(item.hour);
item.y = Number(item.yval);
item.size = 0.1;
item.disabled = Math.random() > 0.4;
return item;
})
}]
}
nv.addGraph(function() {
var chart = nv.models.scatterChart()
.showDistX(false)
.showDistY(true)
.showLegend(false)
//Axis settings
chart.xAxis.tickFormat(d3.format('3.0f'));
chart.yAxis.tickFormat(d3.format('3.0f'));
d3.select('#chart svg')
.datum(prepareData(dataSet))
.call(chart)
// A bit hacky but works
var fooscale = d3.scale.linear().domain([0, 50]).range(["#87CEFF", "#0000FF"]);
function colorer(d) {
if (d.goo == '1')
return 'orange';
else if (d.goo == '2')
return 'red';
else if (d.goo == '0')
return fooscale(d.foo);
return 'gray';
}
d3.selectAll('.nv-point')
.attr({
'stroke': colorer,
'fill': colorer
})
nv.utils.windowResize(chart.update);
return chart;
});
See https://jsfiddle.net/qy5ohw0x/4/
PS Unfortunately Nvd3 lacks docs, so use it's github instead

How can an NVD3 pie chart be made without using "label" and "value" in the JSON object?

The code below requires the JSON object to specify "value" and "label". I want to make this so that a pie chart can be created with any key names.
//Regular pie chart example
nv.addGraph(function() {
var chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.showLabels(true);
d3.select("#chart svg")
.datum(exampleData())
.transition().duration(350)
.call(chart);
return chart;
});
The above function can be modified to return d.fruit and d.number to create a pie chart of a JSON object [{"fruit": "apple", "number": 1}], but I would like this to work for any JSON object, regardless of the key names.
#chart svg {
height: 400px;
}
</style>
<div id="chart">
<svg></svg>
</div>
</head>
<body>
<script>
//Regular pie chart example
nv.addGraph(function() {
var chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.showLabels(true);
d3.select("#chart svg")
.datum(exampleData())
.transition().duration(350)
.call(chart);
return chart;
});
//Donut chart example
nv.addGraph(function() {
var chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.showLabels(true) //Display pie labels
.labelThreshold(.05) //Configure the minimum slice size for labels to show up
.labelType("percent") //Configure what type of data to show in the label. Can be "key", "value" or "percent"
.donut(true) //Turn on Donut mode. Makes pie chart look tasty!
.donutRatio(0.35) //Configure how big you want the donut hole size to be.
;
d3.select("#chart2 svg")
.datum(exampleData())
.transition().duration(350)
.call(chart);
return chart;
});
//Pie chart example data. Note how there is only a single array of key-value pairs.
function exampleData() {
return [
{"value":"1","label":"apples"},{"value":"2","label":"bananas"}
];
}
</script>
</body>
</html>
var chart = nv.models.pieChart()
.x(function(d) { //always spits out first memeber of object
var val, x;
val = 0;
x = 0;
for (i in d) {
if (x++ === val)
{
return d[i];
}
}
})
.y(function(d) { //always spits out second member of object
var val, x;
val = 1;
x = 0;
for (i in d) {
if (x++ === val)
{
return d[i];
}
}
})
The following lines define what properties should be used by the chart:
var chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
So, you can change d.label to d.whatever, and if you have a whatever property, it will use that for x.
You could run your data through something before you pass it into the chart. Something along the lines of:
d3.map(data, function(item) {
return {
label: item.car,
value: item.speed
};
}).values();
You could easily wrap that in a function something like:
function transform(data, x, y) {
x = x || "label";
y = y || "value";
return d3.map(data, function(item) {
return {
label: item[x],
value: item[y]
};
}).values();
}
d3.select("#chart2 svg")
.datum(transform(exampleData(), "car", "speed"));
There is no reliable way other than transforming your data or changing your x and y accessors to guarantee you will see the data you're expecting to see. The chart has no way to understand your data without you expressing what it means.

Categories

Resources