NVD3: Setting the x & y axis values to Line chart for intervals - nvd3.js

3 library for graph generation & i am facing problems while setting the X & Y axis for the intervals based upon values. I want to know whether NVD3's java script has inbuilt capabilities to adjust the X axis intervals based upon the number on records inside the data?
I am generating the graph & it possibly sets the y axis's first value to the lowest value of the sample data. If i need to start the y axis from 0 too then how should it be done?
sample data:
var data = [{
"key": "30 Day",
"values": [{
"x": 0,
"y": 18
}, {
"x": 1,
"y": 24
}, {
"x": 2,
"y": 23
}, {
"x": 3,
"y": 27
},{
"x": 4,
"y": 24
},{
"x": 5,
"y": 31
},{
"x": 6,
"y": 37
},{
"x": 7,
"y": 46
},{
"x": 8,
"y": 32
},{
"x": 9,
"y": 23
},{
"x": 10,
"y": 30
}]
}];

Several NVD3 charts support xDomain/yDomain options. For example:
var chart = nv.models.lineChart();
chart.xDomain( [ 0, 10 ] ).yDomain( [ 0, 50 ] );

Related

C3 Extent not working as expected

I'm creating a pretty simple line graph and want the initial load to be a section of all available data, so am setting extent on the x axis:
This is how I am setting the extent:
axis: {
"x": {
"type": "timeseries",
"tick": {
"format": "%d/%m/%Y"
},
"label": {
"text": "X Label",
"position": "outer-center"
},
"padding": {
"left": 0
},
"extent": ["2017-10-01", "2017-10-05"]
},
"y": {
"label": {
"text": "Y Label",
"position": "outer-middle"
}
}
},
But it is ignored. The chart just shows the full extent of the data.
Is this the correct way to show a subset of the data when the chart is generated?
This is the full code and here's a fiddle (I tried a code snippet but didn't work)
const columnData = [
["x", "2017-10-01", "2017-10-02", "2017-10-03", "2017-10-04", "2017-10-05", "2017-10-06", "2017-10-07", "2017-10-08", "2017-10-09", "2017-10-10", "2017-10-11", "2017-10-12", "2017-10-13", "2017-10-14", "2017-10-15", "2017-10-16"],
["data0", -55, -50, 11, -18, 39, 65, -84, -15, 14, 81, -79, 67, -48, 38, 99, -60],
["data1", 28, 14, -99, -33, 55, 71, 58, 66, 7, -88, 99, -37, -7, 59, -13, -57],
["data2", 14, 6, -9, 25, 42, -93, -6, 67, -35, 88, 36, 45, 42, 78, 51, -88],
["data3", 31, -73, -69, 45, 55, 15, -48, 41, -64, -12, -6, 14, -69, 16, -65, -73],
["data4", 98, 60, 82, 80, -62, -47, 55, 87, -65, 37, 22, 30, 93, -69, -88, 33],
["data5", -98, 57, 71, -25, -40, 13, 72, -90, 71, -71, -21, -9, -90, 73, -94, 100]
];
const generateChart = function() {
const chart = c3.generate({
data: {
"x": "x",
"columns": columnData,
"type": "line"
},
axis: {
"x": {
"type": "timeseries",
"tick": {
"format": "%d/%m/%Y"
},
"label": {
"text": "X Label",
"position": "outer-center"
},
"padding": {
"left": 0
},
"extent": ["2017-10-01", "2017-10-05"]
},
"y": {
"label": {
"text": "Y Label",
"position": "outer-middle"
}
}
},
zoom: {
enabled: true
},
transition: {
duration: 100
},
legend: {
show: false
},
subchart: {
show: true
},
size: {
height: 500
},
grid: {
x: {
show: true
},
y: {
show: true
}
}
});
};
generateChart();
As of 11th May 2018 it seems this is a bug: https://github.com/c3js/c3/issues/2357
I switched to earlier versions (see below) of C3 and D3 and it works as it should.
I am now using C3 version 0.4.10 and D3 version 3.5.0

Implementing a flood algorithm in javascript

Suppose I have a grid in javascript represented as such: ** Note this is just a tiny grid to serve as an example.
{
"width": 5,
"height": 5,
"nodes": [
[{
"x": 0,
"y": 0,
"walkable": true
}, {
"x": 1,
"y": 0,
"walkable": true
}, {
"x": 2,
"y": 0,
"walkable": true
}, {
"x": 3,
"y": 0,
"walkable": true
}, {
"x": 4,
"y": 0,
"walkable": true
}],
[{
"x": 0,
"y": 1,
"walkable": true
}, {
"x": 1,
"y": 1,
"walkable": true
}, {
"x": 2,
"y": 1,
"walkable": true
}, {
"x": 3,
"y": 1,
"walkable": true
}, {
"x": 4,
"y": 1,
"walkable": true
}],
[{
"x": 0,
"y": 2,
"walkable": true
}, {
"x": 1,
"y": 2,
"walkable": true
}, {
"x": 2,
"y": 2,
"walkable": true
}, {
"x": 3,
"y": 2,
"walkable": true
}, {
"x": 4,
"y": 2,
"walkable": true
}],
[{
"x": 0,
"y": 3,
"walkable": true
}, {
"x": 1,
"y": 3,
"walkable": true
}, {
"x": 2,
"y": 3,
"walkable": true
}, {
"x": 3,
"y": 3,
"walkable": true
}, {
"x": 4,
"y": 3,
"walkable": true
}],
[{
"x": 0,
"y": 4,
"walkable": true
}, {
"x": 1,
"y": 4,
"walkable": true
}, {
"x": 2,
"y": 4,
"walkable": true
}, {
"x": 3,
"y": 4,
"walkable": true
}, {
"x": 4,
"y": 4,
"walkable": true
}]
]
}
The "walkable" boolean will determine which areas are blocked off so to speak.
How would I flood this grid to mark the isolated areas? In the above example, a flood fill would fill the entire grid with a single color, because all areas are walkable. But supposing the grid had some areas which were unreachable from other areas (based on the walkable bool), how would I mark the different areas? I basically want to set a color property for each node. If the node isn't the same color as another node, then I know it can't be reached from that node.
EDIT:
Here's what I have so far. Can't run this on a node without getting a maximum call stack error:
function floodFill(node, grid) {
if (node.walkable == false) {
return;
}
if ((node.floodColor != undefined) && (node.floodColor == 'red')) {
return;
}
node.floodColor = 'red';
if ((grid.nodes[node.y + 1] != undefined) && (grid.nodes[node.y + 1][node.x] != undefined)) {
floodFill(grid.nodes[node.y + 1][node.x], grid);
}
if ((grid.nodes[node.y - 1] != undefined) && (grid.nodes[node.y - 1][node.x] != undefined)) {
floodFill(grid.nodes[node.y - 1][node.x], grid);
}
if ((grid.nodes[node.y] != undefined) && (grid.nodes[node.y][node.x + 1] != undefined)) {
floodFill(grid.nodes[node.y][node.x + 1], grid);
}
if ((grid.nodes[node.y] != undefined) && (grid.nodes[node.y][node.x - 1] != undefined)) {
floodFill(grid.nodes[node.y][node.x - 1], grid);
}
}
For those of you who did answer, the above is the sort of thing i am looking for. Descriptions of what to do don't help me, as I've already read quite a bit of descriptions of what to do. Explicit code please :p
Simply repeat flood-fill from many different locations. Just make sure to not fill the same area twice and your algorithm would still work in linear time, assuming reasonable representation of neighborhood (for example a matrix).
n_colors = 0;
for field in grid:
if field has no color assigned yet:
floodFill(fromField: field, color: n_colors)
n_colors = n_colors + 1
I will try this :
first create a resultMap which is a copy of your current grid with a color attribute by point, set all the walkable === false points to color: black
const floodMap = yourData.nodes
.map(n => n.map(p => {...p, {color: p.walkable ? 'black': undefined ));
create a function flood(x, y, color) which take a point in input. If this point is already colored, then do nothing (and return false), otherwise apply color take the (4 or 8 according to your rule) connected points if they aren't colored, and run flood(x', y', color) on them (and return true).
function flood(x, y, color) {
if(!floodMap[y][x].color) {
floodMap(x + 1, y, color);
floodMap(x - 1, y, color);
floodMap(x, y + 1, color);
floodMap(x, y - 1, color);
return true;
}
return false;
}
apply this function for each point of the matrix by changing the color each time the previous call have returned true.
const colors = ['red', 'blue', 'yellow', 'green', 'purple'];
const currentColor = 0;
floodMap.forEach(n => n.forEach(p => {
const flooded = flood(p.x, p.y, colors[currentColor]);
if(flooded) currentColor++;
}));

displaying custom x axis labels with multi bar chart using Nvd3

I'm trying to display labels on the x axis on the bar chart as shown below but it only shows every other one. Does anyone know how to display all?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.7.0/nv.d3.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.7.0/nv.d3.min.js"></script>
</head>
<body>
<h2>multiBarChart</h2>
<div id="multibarchart"><svg style="height:350px;width:800px;"></svg></div>
<script>
data_multibarchart = [{
"values": [{
"y": 4,
"x": 'a'
}, {
"y": 9,
"x": 'b'
}, {
"y": 9,
"x": 'c'
}, {
"y": 5,
"x": 'd'
}, {
"y": 6,
"x": 'e'
}, {
"y": 4,
"x": 'f'
}, {
"y": 1,
"x": 'g'
}, {
"y": 7,
"x": 'h'
}, {
"y": 6,
"x": 'i'
}, {
"y": 2,
"x": 'j'
}],
"key": "Count",
"yAxis": "1"
}, {
"values": [{
"y": 8,
"x": 'a'
}, {
"y": 18,
"x": 'b'
}, {
"y": 18,
"x": 'c'
}, {
"y": 10,
"x": 'd'
}, {
"y": 12,
"x": 'e'
}, {
"y": 8,
"x": 'f'
}, {
"y": 2,
"x": 'g'
}, {
"y": 14,
"x": 'h'
}, {
"y": 12,
"x": 'i'
}, {
"y": 4,
"x": 'j'
}],
"key": "Duration",
"yAxis": "1"
}];
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
chart.margin({
top: 30,
right: 60,
bottom: 70,
left: 60
});
var datum = data_multibarchart;
chart.yAxis.tickFormat(d3.format(',.2f'));
chart.tooltipContent(function(key, y, e, graph) {
var x = String(graph.point.x);
var y = String(graph.point.y);
if (key == 'Count') {
var y = String(graph.point.y) + ' call';
}
if (key == 'Duration') {
var y = String(graph.point.y) + ' min';
}
tooltip_str = '<center><b>' + key + '</b></center>' + y + ' at ' + x;
return tooltip_str;
});
chart.showLegend(true);
d3.select('#multibarchart svg').datum(datum).transition().duration(500).attr('height', 350).call(chart);
});
</script>
</body>
</html>
I figured out. I just had to make them visible:
d3.selectAll('#multibarchart svg .tick text').style("opacity", 1);
Note that I also tried setting:
chart.reduceXTicks = false;
chart.staggerLabels = false;
chart.wrapLabels = false;
but this did NOT help.

D3.js : Set multiple attributes of SVG shape

My data array looks like this:
circleData = {
"x": [10,20,30,40], "r":[1,2,3,4], "y": [0,0,0,0]
},
{
"x": [15,25,35,45], "r":[5,6,7,8], "y": [20,20,20,20]
}
I want to create two rows of four circles with the above x, r, and y parameters. But how? The problem is, if the x values are bound in order to create the circles, then the circles have "access" to the x data, but no longer to the r or y data.
As mentioned in Lars Kotthoff's comment, the easiest way will probably be to restructure your data. Because D3's data binding relies on arrays, one approach would be something like this:
circleData = [{
"x": [10,20,30,40], "r":[1,2,3,4], "y": [0,0,0,0]
},
{
"x": [15,25,35,45], "r":[5,6,7,8], "y": [20,20,20,20]
}];
var circles = d3.merge(circleData.map(function(d) {
return d3.zip(d.x, d.y, d.r);
}));
This will give you a 2-dimensional array containing 8 arrays representing all 8 circles with their cx, cy, and r parameters.
You may then bind circles as your data and operate very D3-ish on it.
var circleData = [{
"x": [10, 20, 30, 40],
"r": [1, 2, 3, 4],
"y": [0, 0, 0, 0]
}, {
"x": [15, 25, 35, 45],
"r": [5, 6, 7, 8],
"y": [20, 20, 20, 20]
}];
var circles = d3.merge(circleData.map(function(d) {
return d3.zip(d.x, d.y, d.r);
}));
var svg = d3.select("body")
.append("svg").append("g")
.attr("transform", "translate(50,50)"); // Translate for the sake of visibility
svg.selectAll("circle")
.data(circles).enter()
.append("circle")
.attr({
"cx": function(d) {
return d[0];
},
"cy": function(d) {
return d[1];
},
"r": function(d) {
return d[2];
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

How do I define a value accessor for a stacked bar graph?

I'm trying to create a stack bar graph using the stack layout.
I can make it work only if I pass it an array of x,y coordinates. But I want to be able to add meta data to it, such as series title.
I've read the docs (https://github.com/mbostock/d3/wiki/Stack-Layout), and seen how it's done on a steamgraph (Correct usage of stack.values([accessor]) in D3 Streamgraph?). The problem with these examples is that they don't take into account things like y scale, making it difficult to establish variables such as yStackMax.
I also need the data to be passed to the stack() function early on, because I'm planning to redraw this and other things when the data is refreshed. In short, instead of:
var data = [
[
{ "x": 0, "y": 91},
{ "x": 1, "y": 290}
],
[
{ "x": 0, "y": 9},
{ "x": 1, "y": 49}
],
[
{ "x": 0, "y": 10},
{ "x": 1, "y": 25}
]
];
var layers = d3.layout.stack()(data);
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
... which works, I want to be able to do:
var data = [
{
"name": "apples",
"values": [
{ "x": 0, "y": 91},
{ "x": 1, "y": 290}
]
},
{
"name": "oranges",
"values": [
{ "x": 0, "y": 9},
{ "x": 1, "y": 49}
]
},
{
"name": "potatoes",
"values": [
{ "x": 0, "y": 10},
{ "x": 1, "y": 25}
]
}
];
var layers = d3.layout.stack()(data).values(function(d) { return d.values; });
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
... which doesn't work.
This is the working code fiddle: http://jsfiddle.net/StephanTual/E6FeP/
This is the fiddle for the code that doesn't work: http://jsfiddle.net/StephanTual/Tnj8W/
Here's an updated fiddle.
The key part was:
// define the accessor before adding in the data
var layers = d3.layout.stack().values(function(d) { return d.values; })(data);
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); });
And then I made a couple other adjustments as necessary to access the .values.

Resources