I'm pretty new into this. I'm trying to understand how to develop a bubble chart using the DC.js library. The reason I'm using DC.js library because all my other row charts are crossfiltering. If I select any bars in the row charts, I want it to crossfilter the bubble chart also.
Currently I'm trying to develop the bubble chart and its not working. It is not displaying properly. I noticed that most of the dc bubble charts are using measurements for x and y axis. For my case, I need to use text instead of integers or date, etc. Here is a sample data:
var data = [
{Failure: "test1", topic: "a", count: "2"},
{Failure: "test1", topic: "b", count: "2"},
{Failure: "test1", topic: "c", count: "95"},
{Failure: "test1", topic: "c", count: "2"},
{Failure: "test2", topic: "a", count: "75"},
{Failure: "test2", topic: "b", count: "2"},
{Failure: "test2", topic: "c", count: "10"},
{Failure: "test2", topic: "a", count: "2"},
{Failure: "test3", topic: "a", count: "51"},
{Failure: "test3", topic: "b", count: "40"},
{Failure: "test3", topic: "c", count: "20"},
{Failure: "test3", topic: "b", count: "15"}
];
When I create a bubble chart, I want the Failure column be the X axis which will display "Test1, Test2, Test3" and the y axis, display "a, b, c". The count column will be the size of the bubble. So i can view a bubble that is how many count would be for test3 and topic C.
Is this possible in dc.bubblechart? because all I see in the examples when x and y axis are ranges in numbers or date.
Here is my code so far to develop this bubble chart. It's the best I can do...
var failure= ndx.dimension(function(d) {return d.failure;});
var topic= ndx.dimension(function(d) {return d.topic;});
var bubbleChart = dc.bubbleChart("#bubble-chart");
//debugger;
bubbleChart
.dimension(failure)
.group(dateGroup)
.x(d3.scale.ordinal().domain(failure))
.y(d3.scale.ordinal().domain(topic))
.width(400)
.height(400)
.yAxisPadding(50)
.xAxisPadding(50)
.xAxisLabel('X') // (optional) render an axis label below the x axis
.yAxisLabel('Y') // (optional) render a vertical axis lable left of the y axis
.renderLabel(true)
.title(function (p) {
return [
"Failure: " + p.value.Failure,
"Topic: " + p.value.topic,
"count: " + p.value.count,
]
.join("\n");
})
.renderTitle(true)
.renderHorizontalGridLines(true) // (optional) render horizontal grid lines, :default=false
.renderVerticalGridLines(true)
.maxBubbleRelativeSize(0.3)
.keyAccessor(function (p) {
return p.value.Failure;
})
.valueAccessor(function (p) {
return p.value.topic;
})
.radiusValueAccessor(function (p) {
return p.value.count;
})
.elasticRadius(true)
.elasticY(true)
.elasticX(true);
I switched over to heatmap and it works what i expected to be! And the client likes it =) Thanks for the advised #Gordon
I used a heatmap that is provided in DC.js. The crossfiltering works in both ways.
Related
How can I define locations within my SVG elements that contain nodes?
I'm trying to create an abstract map where nodes are contained within locations using D3. The nodes will then be linked to other nodes (sometimes many nodes in the same location +/or other locations).
So sample data may look something like this:
{"nodes":[
{"id": "a", "location": "1"},
{"id": "b", "location": "1"},
{"id": "c", "location": "2"},
{"id": "d", "location": "2"},
{"id": "e", "location": "3"},
{"id": "f", "location": "3"},
{"id": "g", "location": "4"},
{"id": "h", "location": "4"}]
}
I want to create 4 rectangles/bubbles, with 2 nodes (circles) in each.
I'm new to D3 and guess I'm struggling to go from simple datasets to JSON objects. So sorry if I'm missing the obvious.
If you're creating a force directed chart, you can use forceX and forceY to arrange the nodes in the screen. According to the API:
The x- and y-positioning forces push nodes towards a desired position along the given dimension with a configurable strength. The strength of the force is proportional to the one-dimensional distance between the node’s position and the target position.
In this demo, I'm taking your data array and positioning in the x coordinates according to location. First, I set an scale:
var xScale = d3.scalePoint()
.domain([1, 2, 3, 4])
.range([100, width - 100]);
And use this scale in forceX:
var force = d3.forceSimulation(data)
.force('x', d3.forceX((d) => xScale(d.location)).strength(2))
Here is a demo:
var data = [{
"id": "a",
"location": "1"
}, {
"id": "b",
"location": "1"
}, {
"id": "c",
"location": "2"
}, {
"id": "d",
"location": "2"
}, {
"id": "e",
"location": "3"
}, {
"id": "f",
"location": "3"
}, {
"id": "g",
"location": "4"
}, {
"id": "h",
"location": "4"
}];
var width = 500,
height = 200;
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scalePoint()
.domain([1, 2, 3, 4])
.range([100, width - 100]);
var circles = svg.selectAll(".bigCircles")
.data(xScale.domain())
.enter()
.append("circle")
.attr("cx", d=>xScale(d))
.attr("cy", height/2)
.attr("fill", d=>color(d))
.attr("r", 40)
.attr("opacity", 0.2);
var node = svg.selectAll(".circles")
.data(data)
.enter().append("circle")
.attr("r", 10)
.attr("fill", (d) => color(d.location));
var force = d3.forceSimulation(data)
.force('x', d3.forceX((d) => xScale(d.location)).strength(2))
.force('center', d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide(12));
force.nodes(data)
.on('tick', function() {
node
.attr('transform', (d) => {
return 'translate(' + (d.x) + ',' + (d.y) + ')';
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
I created a row chart to show some negative values. I'd like to place the chart right next to another row chart with positive values since stacking isn't supported for the row chart.
The problem is, that the rows aren't shown completely. elasticX seems to be the problem. The x-axis scale range is limited from the lowest value to the highest.
I've created an snippet to demonstrate the bahaviour.
In the example the range is from -2000 to -800 which is the lowes value. But I obviously need it to be -2000 to 0.
I didn't get a solution. So any help would be much appreciated!
var data = [{
"name": "A",
"out": 1000
}, {
"name": "B",
"out": 1200
}, {
"name": "C",
"out": 1500
}, {
"name": "D",
"out": 800
}, {
"name": "E",
"out": 2000
}];
var rowChart = dc.rowChart("#rowChart");
var ndx = crossfilter(data),
nameDimension = ndx.dimension(function(d) {
return d.name;
}),
outGroup = nameDimension.group().reduceSum(function(d) {
return -d.out;
});
rowChart.width(300)
.height(500)
.margins({top: 10, right: 10, bottom: 30, left: 50})
.dimension(nameDimension)
.group(outGroup)
.elasticX(true)
.xAxis().ticks(2);
dc.renderAll();
<script src='https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.5/crossfilter.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.0-dev/dc.min.js'></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.0-dev/dc.css"/>
<div id="rowChart"> </div>
This bug was reported as #879 and was fixed in 2.0 beta 32.
Problem is, that the 2.1.0-dev version is quite old, can't be updated, and can't be deprecated due to an apparent bug in npm.
A development tag makes absolutely no sense! So the develop branch can't be distributed via a real CDN. If you want to use the development version of dc.js, you need to specify the dependency in your package.json using github url syntax:
"dc": "dc-js/dc.js#develop"
Or if you're not using npm, and a fake CDN is acceptable, you can use:
https://cdn.rawgit.com/dc-js/dc.js/develop/dc.js
Snippet corrected by using 2.0 beta 33 below.
Hopefully we'll push a real version 2.1 soon and 2.1.0-dev will get buried.
var data = [{
"name": "A",
"out": 1000
}, {
"name": "B",
"out": 1200
}, {
"name": "C",
"out": 1500
}, {
"name": "D",
"out": 800
}, {
"name": "E",
"out": 2000
}];
var rowChart = dc.rowChart("#rowChart");
var ndx = crossfilter(data),
nameDimension = ndx.dimension(function(d) {
return d.name;
}),
outGroup = nameDimension.group().reduceSum(function(d) {
return -d.out;
});
rowChart.width(300)
.height(500)
.margins({top: 10, right: 10, bottom: 30, left: 50})
.dimension(nameDimension)
.group(outGroup)
.elasticX(true)
.xAxis().ticks(2);
dc.renderAll();
<script src='https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.5/crossfilter.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/dc/2.0.0-beta.33/dc.js'></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.0.0-beta.33/dc.css"/>
<div id="rowChart"> </div>
I notice that the external scripts are in the wrong order - crossfilter.js relies on d3.js and therefore it should come after but before dc.js; perhaps that is why the chart isn't rendering correctly.
I took code from this answer and I'm tying to do something like in first picture, to see if it's possible (I know it's is, it's just some JavaScript, HTML and CSS;) ).
Tha second picture is my progress so far.
I put Details text in header:
$("#list_subgrid").append("Details").css('width', '100px');
I changed width of first column:
$(".jqgfirstrow").find("td:first").css({"height":"0px", "width":"100px"});
I can get to result in third picture if I change width of bunch of elements all over the place, but not sure that's correct way. And I can't get rid off horizontal scroll bar.
Have no idea how to put Details text into every cell in first column instead of plus sign, but plus sign can stay there.
And how to switch "subgrid" column to be last instead of first is completely beyond my knowledge...
I wrote the answer, which you used as an example, many years ago. Now I would just to place the subgrid data together with the main data of the row, like details property below:
var myGridData = [
// main grid data
{id: "10", col1: "11", col2: "12", details: [
// data for subgrid for the id=10
{id: "10", c1: "aa", c2: "ab", c3: "ac"},
{id: "20", c1: "ba", c2: "bb", c3: "bc"},
{id: "30", c1: "ca", c2: "cb", c3: "cc"}
]},
{id: "20", col1: "21", col2: "22", details: [
// data for subgrid for the id=20
{id: "10", c1: "xx", c2: "xy", c3: "xz"}
]}
];
The expression $(this).jqGrid("getLocalRow", rowid) get the item of the data and $(this).jqGrid("getLocalRow", rowid).details is the subgrid data of the row. As the result we can rewrite the original example like on the demo.
To have the column with the fixed text Details we can use simple formatter
formatter: function () {
return details;
}
where details is defined for example like
var details = "<span class='fa fa-fw fa-plus'></span> " +
"<span class='mylink'>Details</span>";
(I used Font Awesome icon) and class mylink defined like
.mylink { text-decoration: underline; }
Now we can hide the "subgrid" column and to open/close the subgrid by simulation of click event on the hidden cell with + or - icon. We receive the following full code
var myGridData = [
// main grid data
{id: "10", col1: "11", col2: "12", details: [
// data for subgrid for the id=10
{id: "10", c1: "aa", c2: "ab", c3: "ac"},
{id: "20", c1: "ba", c2: "bb", c3: "bc"},
{id: "30", c1: "ca", c2: "cb", c3: "cc"}
]},
{id: "20", col1: "21", col2: "22", details: [
// data for subgrid for the id=20
{id: "10", c1: "xx", c2: "xy", c3: "xz"}
]}
],
$grid = $("#list"),
details = "<span class='fa fa-fw fa-plus'></span> " +
"<span class='mylink'>Details</span>";
$grid.jqGrid({
data: myGridData,
colModel: [
{ name: "col1", label: "Column 1" },
{ name: "col2", label: "Column 2" },
{ name: "details", label: "Details",
align: "center", width: 70,
formatter: function () {
return details;
} }
],
cmTemplate: { width: 200 },
iconSet: "fontAwesome",
autoencode: true,
sortname: "col1",
sortorder: "desc",
pager: true,
caption: "Demonstrate how to create subgrid from local data",
beforeSelectRow: function (rowid, e) {
var $self = $(this),
p = $self.jqGrid("getGridParam"),
$td = $(e.target).closest("tr.jqgrow>td"),
cm = $td.length > 0 ? p.colModel[$td[0].cellIndex] : null,
cmName = cm != null ? cm.name : null;
if (cmName === "details") {
// simulate opening the subgrid
$($td.parent()[0].cells[p.iColByName.subgrid]).click();
// inverse +/-
var $plusMinus = $td.find("span.fa");
if ($plusMinus.hasClass("fa-minus")) {
$plusMinus.removeClass("fa-minus").addClass("fa-plus");
} else {
$plusMinus.removeClass("fa-plus").addClass("fa-minus");
}
}
return true;
},
subGrid: true,
subGridRowExpanded: function (subgridDivId, rowid) {
var $subgrid = $("<table id='" + subgridDivId + "_t'></table>"),
$subgridDiv = $("#" + subgridDivId),
subgridData = $(this).jqGrid("getLocalRow", rowid).details;
$subgridDiv.closest(".subgrid-data").prev(".subgrid-cell").remove();
var colspan = $subgridDiv.closest(".subgrid-data").attr("colspan");
$subgridDiv.closest(".subgrid-data").attr("colspan", parseInt(colspan, 10) + 1);
$subgridDiv.append($subgrid);
$subgrid.jqGrid({
idPrefix: rowid + "_",
data: subgridData,
colModel: [
{ name: "c1", label: "Col 1" },
{ name: "c2", label: "Col 2" },
{ name: "c3", label: "Col 3" }
],
iconSet: "fontAwesome",
autowidth: true,
autoencode: true,
sortname: "c1"
});
$subgrid.jqGrid("setGridWidth", $subgridDiv.width() - 1);
}
}).jqGrid("hideCol", "subgrid");
The corresponding demo one can see here. After clicking of "+ Detailes" one will see the following:
As I keep adding dimensions to my data set, I encounter an issue of having my bar chart not displaying values of my "account" dimension.
Here is my current fiddle: https://jsfiddle.net/eugene_goldberg/yv3nnred/18/
var data = [
{date: "2015-03-25T12:10:00", resolver: "Group 1", escalation_pct: 20, bubble: 5,account: "Aon", region: "Americas", x: 1},
{date: "2015-03-25T12:15:00", resolver: "Group 2", escalation_pct: 10, bubble: 5,account: "Boeing", region: "AMEA", x: 1},
{date: "2015-03-25T12:25:00", resolver: "Group 3", escalation_pct: 50, bubble: 10,account: "Zurich", region: "Nordics", x: 1},
{date: "2015-03-25T12:40:00", resolver: "Group 4", escalation_pct: 30, bubble: 1,account: "Aon", region: "Americas", x: 1},
{date: "2015-03-25T12:35:00", resolver: "Group 5", escalation_pct: 5, bubble: 10,account: "Boeing", region: "Australia", x: 1},
{date: "2015-03-25T12:45:00", resolver: "Group 6", escalation_pct: 13, bubble: 1,account: "Zurich", region: "UK&I", x: 1}
];
var dimAccount = ndx.dimension(function(d) {return + d.account;});
var accountGroup = dimAccount.group().reduceSum(function(d) {return d.x});
var accountChart = dc.barChart("#account-chart");
accountChart.width(480)
.height(150)
.dimension(dimAccount)
.group(accountGroup)
.x(d3.scale.linear().domain([0, 10]))
.elasticY(true)
.xAxis().tickFormat();
It must be something very basic, I'm just not grasping it yet...
In my case, going with a row chart was what I needed.
A fully working fiddle is below:
https://jsfiddle.net/eugene_goldberg/yv3nnred/25/
var dimAccount = ndx.dimension(function(d) {return d.account;});
var accountGroup = dimAccount.group().reduceSum(function (d) {
return d.x;
});
var accountChart = dc.rowChart("#account-chart");
accountChart
.width(500)
.height(500)
.dimension(dimAccount)
.group(accountGroup);
A row chart works good. But another option is a selectmenu, because the height of the bar doesnt represent anything you could have a simple dropdown menu with the options.
(dc.selectMenu on 2.1 branch)
I'm using a NVD3 and D3 to create some simple visuals, one of which is a horizontal bar chart. The options are:
{
"type": "multiBarHorizontalChart",
"height": 600,
"showControls": false,
"showValues": true,
"duration": 500,
"xAxis": {
showMaxMin: false,
axisLabelDistance: 400,
axisLabelWidth: 500,
},
"yAxis": {
"axisLabel": "",
"tickFormat": function (d) { return d; }
},
"yDomain" : [0, 10000],
x : (function(d) { return d.label }),
y : (function(d) { return d.value }),
showLegend: false,
valueFormat: d3.format(".0f"),
}
And the output looks like:
Default output
However, the project style police don't like that the range of the graph appears unbounded and they'd like it to look like this:
Desired output
Is this possible?
Thanks
The actual problem here was how the library build on top of D3 (NVD3) chose to draw it's bar charts.
Rather than re-invent the wheel, a decision was made to use Amcharts instead.