How to add label above the graph using dc.js? - dc.js

When I select any particular field in the bar graph, all the graphs changes accordingly.
I want to introduce a heading above the graphs which should show which data is currently displayed in the graphs and the count.
So what changes should I make in my code?
var data = [{
"city": "New York",
"neighborhood": "N/A",
"hits": 200
}, {
"city": "New York",
"neighborhood": "Brooklyn",
"hits": 225
}, {
"city": "New York",
"neighborhood": "Queens",
"hits": 1
}, {
"city": "San Francisco",
"neighborhood": "Chinatown",
"hits": 268
}, {
"city": "San Francisco",
"neighborhood": "Downtown",
"hits": 22
}, {
"city": "Seattle",
"neighborhood": "N/A",
"hits": 2
}, {
"city": "Seattle",
"neighborhood": "Freemont",
"hits": 25
}];
var pieChart = dc.pieChart("#pieChart"),
rowChart = dc.rowChart("#rowChart");
var ndx = crossfilter(data),
cityDimension = ndx.dimension(function (d) {
return d.city;
}),
cityGroup = cityDimension.group().reduceSum(function (d) {
return d.hits;
}),
neighborhoodDimension = ndx.dimension(function (d) {
return d.neighborhood;
}),
neighborhoodGroup = neighborhoodDimension.group().reduceSum(function (d) {
return d.hits;
});
pieChart.width(200)
.height(200)
.slicesCap(4)
.dimension(cityDimension)
.group(cityGroup);
pieChart.filter = function() {};
rowChart.width(500)
.height(500)
.dimension(neighborhoodDimension)
.group(neighborhoodGroup);
dc.renderAll();
<div id="pieChart"> </div>
<div id="rowChart"> </div>

For displaying the current filters, you can use spans with class='reset' andclass='filter'` within the charts, as demonstrated in the annotated stock example
For displaying the counts of records filtered, you can use the dataCount widget or the numberDisplay widget.
Anything else, you'll have to implement the display itself, probably hooking the filtered event to determine when the filters have changed, and changing your own div using e.g. d3 or jQuery.

Related

Retrieving object from TreeMap

I've been working with few charts of amCharts 4, and everytime I struggled to subscribe to the event that returns the "selected/clicked" element. I don't know what I've been missing from the doc, but for example, I need to retrieve the selected item from "hit" event but i can't find anywhere (here is a simple i'd like to try this on https://codepen.io/team/amcharts/pen/erojQb)
var chart = am4core.create("chartdiv", am4charts.TreeMap);
chart.data = [{
"name": "First",
"value": 190
}, {
"name": "Second",
"value": 289
}, {
"name": "Third",
"value": 635
}, {
"name": "Fourth",
"value": 732
}, {
"name": "Fifth",
"value": 835
}];
/* Set color step */
chart.colors.step = 2;
/* Define data fields */
chart.dataFields.value = "value";
chart.dataFields.name = "name";
I tried this:
chart.seriesTemplates.template.columns.events.on('hit', function(ev) {
console.log('mlkmlz');
});
but not called, and this:
chart.seriesContainer.events.on('hit', function(ev) {
console.log(ev.target.dataItem)
});
but no dataItem attached
OK, so it's a bit more complicated than that. To attach events, you'll need to to actually create series for the specific level, then attach events on its column template:
var series = chart.seriesTemplates.create("0");
series.columns.template.events.on('hit', function(ev) {
console.log(ev.target.dataItem);
});

Using d3.js v4 - using parentId to resolve ambiguity

I am trying to understand the right usage to achieve my collapsible tree d3 but unable to establish the proper parent/child references since I cannot use "parent". Attempting to use parentID.
This is my dataset I am testing with:
var result = [
{ "id": 1, "name": "Top Level", "parent": null, "parentId": "" },
{ "id": 2, "name": "PROD", "parent": "Top Level", "parentId": 1 },
{ "id": 3, "name": "QAT", "parent": "Top Level", "parentId": 1 },
{ "id": 4, "name": "App1", "parent": "PROD", "parentId": 2 },
{ "id": 5, "name": "App1", "parent": "QAT", "parentId": 3 },
{ "id": 6, "name": "ServerPROD001", "parent": "App1", "parentId": 4 },
{ "id": 7, "name": "ServerQAT001", "parent": "App1", "parentId": 5 }
];
and based on the collapsible tree:
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function (d) { return d.name; })
.parentId(function (d) { return d.parent })
(result);
This works fine if I do not include items 6 and 7. If I do include these I get an ambiguity error which makes sense because it cannot determine which "App1" to associate to.
I tried changing the code to use the parentId but just get an error of "missing:1" now.
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function (d) { return d.name; })
.parentId(function (d) { return d.parentId })
(result);
Note - I cannot change the "App1" name values to something unique as they will exist in multiple areas with that given name.
Since you have the id that is unique and not the name:
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function (d) { return d.id; }) // return the id instead of the name
.parentId(function (d) { return d.parentId })
(result);
and then set the name you need to be displayed like this:
// assign the name to each node as the initial name
treeData.each(function(d) {
d.name = d.data.name;
});
A working example can be found here, based on this
:)
Good luck!

D3.js nesting and rollup at the same time in v4

I have a very similar task as D3.js nesting and rollup at same time and solution provided by #altocumulus for d3.js v3 works perfectly fine for me (tested). However, I am using d3.js v4 across my website and I am having difficulties replicating the same approach with v4 - I am not getting the same results. Perhaps, because I don't understand the sumChildren function. Please feel free to provide a better or different approach on how to restructure the loaded csv file as json with subtotals at every node level using d3.js v4. In my case, I need to have Population at the Country, State and the City levels.
Disclaimer: I have been using SO for many years and in most of the cases I got my answers from questions posted by other people, but this is my first question. In addition to this I am noob in d3.js
population.csv:
Country,State,City,Population
"USA","California","Los Angeles",18500000
"USA","California","San Diego",1356000
"USA","California","San Francisco",837442
"USA","Texas","Austin",885400
"USA","Texas","Dallas",1258000
"USA","Texas","Houston",2196000
index.html:
<!DOCTYPE html>
<html>
<head>
<title> Test D3.js</title>
</head>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.4.0/d3.min.js"></script>
<body>
<script>
d3.csv("population.csv", function(error, data) {
if (error) throw error;
var nested = d3.nest()
.key(function(d) { return d.Country; })
.key(function(d) { return d.State; })
.rollup(function(cities) {
return cities.map(function(c) {
return {"City": c.City, "Population": +c.Population };
});
})
.entries(data);
// Recursively sum up children's values
function sumChildren(node) {
node.Population = node.values.reduce(function(r, v) {
return r + (v.values ? sumChildren(v) : v.Population);
},0);
return node.Population;
}
// Loop through all top level nodes in nested data,
// i.e. for all countries.
nested.forEach(function(node) {
sumChildren(node);
});
// Output. Nothing of interest below this line.
d3.select("body").append("div")
.style("font-family", "monospace")
.style("white-space", "pre")
.text(JSON.stringify(nested,null,2));
});
</script>
</body>
</html>
Results:
[
{
"key": "USA",
"values": [
{
"key": "California",
"value": [
{
"City": "Los Angeles",
"Population": 18500000
},
{
"City": "San Diego",
"Population": 1356000
},
{
"City": "San Francisco",
"Population": 837442
}
]
},
{
"key": "Texas",
"value": [
{
"City": "Austin",
"Population": 885400
},
{
"City": "Dallas",
"Population": 1258000
},
{
"City": "Houston",
"Population": 2196000
}
]
}
],
"Population": null
}
]
Desired Results:
[
{
"key": "USA",
"values": [
{
"key": "California",
"values": [
{
"City": "Los Angeles",
"Population": 18500000
},
{
"City": "San Diego",
"Population": 1356000
},
{
"City": "San Francisco",
"Population": 837442
}
],
"Population": 20693442
},
{
"key": "Texas",
"values": [
{
"City": "Austin",
"Population": 885400
},
{
"City": "Dallas",
"Population": 1258000
},
{
"City": "Houston",
"Population": 2196000
}
],
"Population": 4339400
}
],
"Population": 25032842
}
]
The v4 changelog tells us that
When used in conjunction with nest.rollup, nest.entries now returns {key, value} objects for the leaf entries, instead of {key, values}.
It is this little renaming from values to value within the leaf nodes which eventually breaks the code. Changing the helper function accordingly should get you back on track:
// Recursively sum up children's values
function sumChildren(node) {
if (node.value) {
node.values = node.value; // Ensure, leaf nodes will also have a values array
delete node.value; // ...instead of a single value
}
node.Population = node.values.reduce(function(r, v) {
return r + (v.value? sumChildren(v) : v.Population);
},0);
return node.Population;
}

tickformat for d3.js time scale

I am using a d3.js time.scale like this:
var xScale = d3.time.scale()
.domain(d3.extent(data, function(d) { return d.date; }))
.range([0, chartWidth]);
The data is like this:
var testData = [
{
"date": "2015-04-01T00:00:00.000Z",
"value": 200.00
},
{
"date": "2015-05-01T00:00:00.000Z",
"value": 2000.00
},
{
"date": "2015-06-01T00:00:00.000Z",
"value": 4000.01
},
{
"date": "2015-07-01T00:00:00.000Z",
"value": 1000.00
},
{
"date": "2015-08-01T00:00:00.000Z",
"value": 750.00
},
{
"date": "2015-09-01T00:00:00.000Z",
"value": 1568.00
},
{
"date": "2015-10-01T00:00:00.000Z",
"value": 3789.00
},
{
"date": "2015-11-01T00:00:00.000Z",
"value": 5678.00
},
{
"date": "2015-12-01T00:00:00.000Z",
"value": 4898.00
},
{
"date": "2016-01-01T00:00:00.000Z",
"value": 9002.00
},
{
"date": "2016-02-01T00:00:00.000Z",
"value": 3320.00
},
{
"date": "2016-03-01T00:00:00.000Z",
"value": 12000.00
}
];
But the ticks and the ordering are not as I would expect:
I would expect the ticks to be ordered from april 2015 to march 2016 and I do not understand where the 2016 tick is coming from.
How can I order the ticks as I would expect and also where is the 2016 tick coming from?
Here is a jsbin that shows the problem
If you want the ticks to show the month name and the year, add this to the axis:
.tickFormat(d3.time.format("%B %Y"));
Alternatively, you can display the abbreviated month name:
.tickFormat(d3.time.format("%b %Y"));
Here is the updated jsbin: http://jsbin.com/wikafuluqu/1/edit?js,output
PS: To show the first tick, add nice() to the scale. This is the jsbin with nice():
http://jsbin.com/loyupelazu/1/edit?js,output

Parsing errors when i try to create pie chart using d3js

I am trying to create pie charts using d3js. I dont face any issues when i used simple json file. But later when i tried to use slightly more complex json, i am getting these 2 errors.
Error: Problem parsing d="M0,-300A300,300 0 1,1 NaN,NaNLNaN,NaNA20,20 0 1,0 0,-20Z"
Error: Problem parsing d="MNaN,NaNA300,300 0 1,1 NaN,NaNLNaN,NaNA20,20 0 1,0 NaN,NaNZ"
from my understanding the error is at arcs.append("path").attr("d", arc). but donno how to fix it.
here is the d3js code
<div id="sample1">
<script>
var r = 300;
var color = d3.scale.category10()
var canvas = d3.select("#sample1").append("svg")
.attr("width", 700)
.attr("height", 700);
var group = canvas.append("g")
.attr("transform", "translate(350,350)");
var arc = d3.svg.arc()
.innerRadius(20)
.outerRadius(r);
var pie = d3.layout.pie()
.value(function(d) {
d.children.forEach(function(x) {
return x.value;
});
})
.sort(null);
d3.json("js/mydata.json", function(data) {
var arcs = group.selectAll(".arc")
.data(pie(data.children))
.enter()
.append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", function(d) {
d.data.children.forEach(function(x) {
return color(x.name);
});
});
})
</script>
</div>
here is my json
{
"name": "All",
"children": [
{
"name": "Main1",
"children": [
{
"name": "Sub1",
"value": 6.25
},
{
"name": "Sub2",
"value": 12.50
},
{
"name": "Sub3",
"value": 6.25
}
]
},
{
"name": "Main2",
"children": [
{
"name": "Sub4",
"value": 6.25
},
{
"name": "Sub5",
"value": 12.50
},
{
"name": "Sub6",
"value": 6.25
}
]
},
{
"name": "Main3",
"children": [
{
"name": "Sub7",
"value": 6.25
},
{
"name": "Sub8",
"value": 12.50
},
{
"name": "Sub9",
"value": 6.25
}
]
},
{
"name": "Main4",
"children": [
{
"name": "Sub10",
"value": 6.25
},
{
"name": "Sub11",
"value": 12.50
},
{
"name": "Sub12",
"value": 6.25
}
]
}
]
}
Updated:
The approach given by "Lars Kotthoff" worked. how ever this approach does not fix my actual problem which i dint mentioned here. The problem is, i want to give a border to all the Main1,Main2,Main3,Main4. it means although there are 12 colors here borders will be only 4 i.e grouping all three colors into one based on ite parent.
Below picture shows my requirement.

Resources