I am having String values in x-axis and integer values in y-axis Using nvd3 how to plot the graph - nvd3.js

How to plot the stackedarea chart using x-axis as string values and y-axis should be number using nvd3
ex:-
$scope.data = [
{
"key": "2016-2017",
"values": [['Cse', 100], ['ECE', 200], ['IT', 85], ['EEE', 65]]
}
]

Wouldn't you just do:
$scope.x = function(d) {
return d[0];
}
$scope.y = function(d) {
//coerce to number
return +d[1];
}

Related

How to create curved lines connecting nodes in D3

I would like to create a graphic in D3 that consists of nodes connected to each other with curved lines. The lines should be curved differently depending on how far apart the start and end point of the line are.
For example (A) is a longer connection and therefore is less curved than (C).
Which D3 function is best used for this calculation and how is it output as SVG path
A code example (for example on observablehq.com) would help me a lot.
Here is a code example in obserbavlehq.com
https://observablehq.com/#garciaguillermoa/circles-and-links
I will try to explain it, let me know if there is something I am not clear enough:
Lets start with our circles, we use d3.pie() to position this circles, passing the data defined above, it will return us some arcs, but as we want circles instead of arcs, we use arc.centroid to get the coordinates of our circles
Value is required for the spacing in the pie layout that we use to calculate the position, if you want more circles, you will need to reduce the value, here is the related code:
pie = d3
.pie()
.sort(null)
.value((d) => {
return d.value;
});
arc = d3.arc().outerRadius(300).innerRadius(50);
data = [
{ id: 0, value: 10 },
{ id: 1, value: 10 },
{ id: 2, value: 10 },
{ id: 3, value: 10 },
{ id: 4, value: 10 },
{ id: 5, value: 10 },
{ id: 6, value: 10 },
{ id: 7, value: 10 },
{ id: 8, value: 10 },
{ id: 9, value: 10 },
];
const circles = [];
for(let item of pieData) {
const [x, y] = arc.centroid(item);
circles.push({x, y});
}
Now we can render the circles:
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
const mainGroup = svg
.append("g")
.attr("id", "main")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Insert lines and circles groups, lines first so they are behind circles
const linesGroup = mainGroup.append("g").attr("id", "lines");
const circlesGroup = mainGroup.append("g").attr("id", "circles");
circlesGroup
.selectAll("circle")
.data(circles, (_, index) => index)
.join((enter) => {
enter
.append("circle")
.attr("id", (_, index) => {
return `circle-${index}`;
})
.attr("r", 20)
.attr("cx", (d) => {
return d.x;
})
.attr("cy", (d) => {
return d.y;
})
.style("stroke-width", "2px")
.style("stroke", "#000")
.style("fill", "#963cff");
});
Now we need to declare the links, we could do this with an array specifying the id of the source and destination (from and to). we use this to search each circle, get its coordinates (the source and destination of our links) and then create the links, in order to create them, we can use a path and the d3 method quadraticCurveTo, this function requires four parameters, the first two are "the control point" which defines our curve, we use 0, 0 as it is the center of our viz (it is the center because we used a translate in the parent group).
lines = [
{
from: 1,
to: 3,
},
{
from: 8,
to: 4,
},
];
for (let line of lines) {
const fromCircle = circles[line.from];
const toCircle = circles[line.to];
const fromP = { x: fromCircle.x, y: fromCircle.y };
const toP = { x: toCircle.x, y: toCircle.y };
const path = d3.path();
path.moveTo(fromP.x, fromP.y);
path.quadraticCurveTo(0, 0, toP.x, toP.y);
linesGroup
.append("path")
.style("fill", "none")
.style("stroke-width", "2px")
.style("stroke-dasharray", "10 10")
.style("stroke", "#000")
.attr("d", path);
}

nvd3 - force all xaxis labels to show on line chart

how can I force all xAxis labels to show up on my graph without defining each tick via .tickvalues ?
I'm totally new to nvd3 and this is my first try.
Maybe there is someone with a good heart who can have a look at my code and help me out.
I did my research but nothing worked for me.
Here is my code:
var data = [
{
"key": "www.WebsiteA.com",
"values": [
{date:"20151221",rank:1},
{date:"20151222",rank:3},
{date:"20151223",rank:2},
{date:"20151224",rank:4},
{date:"20151225",rank:2},
{date:"20151226",rank:5},
{date:"20151227",rank:3},
{date:"20151228",rank:2},
{date:"20151229",rank:2},
{date:"20151230",rank:1},
{date:"20151231",rank:2},
{date:"20160101",rank:4},
{date:"20160102",rank:5},
{date:"20160103",rank:3},
] },
{
"key": "www.WebsiteB.com",
"values": [
{date:"20151221",rank:2},
{date:"20151222",rank:1},
{date:"20151223",rank:3},
{date:"20151224",rank:5},
{date:"20151225",rank:1},
{date:"20151226",rank:4},
{date:"20151227",rank:1},
{date:"20151228",rank:5},
{date:"20151229",rank:3},
{date:"20151230",rank:4},
{date:"20151231",rank:2},
{date:"20160101",rank:1},
{date:"20160102",rank:3},
{date:"20160103",rank:2},
] },
]
nv.addGraph(function() {
var chart = nv.models.lineChart()
.x(function(d) {return d3.time.format("%Y%m%d").parse(d.date) })
.y(function(d) {return d.rank})
.yDomain([6, 1])
.color(d3.scale.category10().range())
.useInteractiveGuideline(true)
.margin({left: 100})
.margin({right: 50})
.margin({bottom: 100})
;
chart.legend.margin({top: 10, right:60, left:80, bottom: 100});
chart.xAxis
.tickFormat(function(d) {return d3.time.format('%Y-%m-%d')(new Date(d)) })
.rotateLabels(-45)
;
chart.xScale(d3.time.scale()); //fixes misalignment of timescale with line graph
chart.yAxis
.axisLabel('Rank')
.tickFormat(d3.format('d'))
;
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart)
;
nv.utils.windowResize(chart.update);
return chart;
});
You can find the fiddle here: http://jsfiddle.net/Marei/1azqmx1L/3/
Thank you!
I think (I have only worked directly with d3 before, not nvd3) that you need to specify a list of xValues that you want to display using tickValues().
So the first thing you need to do is to get a list of all your xValues (the order and/or duplicates do not matter):
//Map all xValues for each dataset to an array (tmp)
var tmp = data.map(function(e) {
return e.values.map(function(d) {
return d3.time.format('%Y%m%d').parse(d.date);
});
});
//And flatten out that array, so you have all your xValues in a 1D-array
var xValues = [].concat.apply([], tmp);
Then use this to set that you want to display all xValues:
chart.xAxis
.tickFormat(function(d) {return d3.time.format('%Y-%m-%d')(new Date(d)) })
.rotateLabels(-45)
.tickValues(xValues)
.showMaxMin(false)
;
showMaxMin needs is set to false because otherwise all the end-values must be displayed
I have string values for x,
so, for tickValues() i created an index based array with items length like,
axisLabel: graph.x_label,
tickValues: Array.from({ length: graph.values.length }, (x, i) => i),

D3js pie animation on load not working

I'm trying to use attrTween in d3 to animate a pie chart when the page is loaded but it's not working for me. I've used attrTween before to animate a change in data and it's worked fine but this time I want to 'grow' the pie chart when the page is loaded first but it's not behaving as expected and I'm not getting any information as to why this is.
If I remove the line .attrTween('d' arcTweenStart); then everything works fine except of course it does not animate. If the line is left in then nothing is displayed and the arcTweenStart function is never entered. Can anyone spot where I'm going wrong?
function drawCharts()
{
// Create the chart and bind the data to it and position it
var pieChart = d3.select("#groupRisk").selectAll("svg")
.data(dataSet) // Bind the data to the chart
.enter().append("svg")
.attr("id", "pie")
.attr("width", w) // Set th width
.attr("height", h) // Set the height
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")"); // Position the chart
// Create the pie chart layout
var pie = d3.layout.pie()
.value(function(d) { return d.count; })
.sort(null); // Sort is set to null to allow for better looking tweens
// Create "slices" for each data element
var arcs = pieChart.selectAll("g.slice")
.data(pie) // Bind the pie layout to the slices
.attr("id", "arcs")
.enter()
.append("g")
.attr("class", "slice");
// Create the graphics for each slice and colour them
arcs.append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { this._current = d; })
.transition()
.duration(500)
.attrTween('d' arcTweenStart);
}
function arcTweenStart(b)
{
var start =
{
startAngle: b.startAngle,
endAngle: b.endAngle
};
var i = d3.interpolate(start, b);
return function(t)
{
return arc(i(t));
};
}
EDIT:
My data set looks like this:
var dataSet=
[
[
{ "label": "Green", "count": 40 },
{ "label": "Amber", "count": 50 },
{ "label": "Red", "count": 10 }
],
[
{ "label": "Green", "count": 20 },
{ "label": "Amber", "count": 30 },
{ "label": "Red", "count": 50 }
],
[
{ "label": "Green", "count": 50 },
{ "label": "Amber", "count": 20 },
{ "label": "Red", "count": 30 }
]
];
I have an array of data sets so I want to draw a chart for each one.
You don't show what your dataSet variable holds (that would have really helped answer the question!) but assuming your data looks like this:
var dataSet = [{
count: 4
}, {
count: 5
}, {
count: 6
}];
You don't need to do the first bind/enter:
d3.select("#groupRisk").selectAll("svg")
.data(dataSet) // Bind the data to the chart
.enter()
...
This would give you a pie chart for each entry in the data. Getting rid of that, your bind then becomes:
var arcs = pieChart.selectAll("g.slice")
.data(pie(dataSet)) //<-- call pie with the dataSet
.attr("id", "arcs")
.enter()
.append("g")
.attr("class", "slice");
But really to the heart of your question, your tween var start, has the same start/end angle as where you want to end. So, you animate the same thing over and over again. What I think you meant is:
function arcTweenStart(b) {
var start = {
startAngle: b.startAngle,
endAngle: b.startAngle //<-- set end to start and adjust on each call
};
var i = d3.interpolate(start, b);
return function(t) {
return arc(i(t));
};
}
Oh, and one typo in there too:
.attrTween('d' arcTweenStart); //<-- comma missing between 'd' and arcTweenStart
Example here.

How to set the domain and scale on an yaxis on a discreteBarChart nvd3.js

I'm using d3.js charts in one of my applications.
Here they are in this image
See Charts
For Y axis on Money chart (See in the Image), I want maximum value rounded to 400, no matter what maximum bar size is here it is $358.72, but I want to keep bar at 358.72 but on Y Axis it would be 400 Limit.
Code for it is here
tradesChart = [
{
key: "Cumulative Return",
values: [
{
"label" : "6E",
"value" : 100 },
{
"label" : "NG",
"value" : 100 },
{
"label" : "TF",
"value" : 67 } ]
}
];
nv.addGraph(function() {
var chart = nv.models.discreteBarChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value/100 })
.staggerLabels(true)
.showValues(true)
.valueFormat(d3.format('.0%'))
chart.yAxis.tickFormat(function(d) { return d3.format('d')(d*100) + '%'; });
d3.select('#chart-trades svg')
.datum(tradesChart)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
Kindly Help me to solve this out
You need to modify the domain of the Y-axis scale. Usually it is derived from the maximum value of the data with a statement like the following:
yScale.domain([0, d3.max(data, function (d) { return d.v; }) ]);
In your case, you should modify it to be more like this instead:
yScale.domain([0, 400]);
Alternatively, if you want to set the maximum value from the data or a minimum static value, you could do something like the following:
yScale.domain([0, d3.max(data, function (d) { return d.v > 400 ? d.v : 400; }) ]);
A full example jsfiddle is here.
That's how to do it with D3.js, I'm not familiar with the venerable nvd3.js lib, so I'm not sure how to access the scale, but i'll take a look and see if I can find it.
I use nvd3 and for me this worked instead:
nv.models.discreteBarChart().yDomain([0,400])
I could'nt get it to work with yScale.domain
Force Y
List of numbers to Force into the Y scale (ie. 0, or a max / min, etc.). The numbers tell the d3.js the values to use in the scale, rather than d3.js determining the values.
Datatype: Array of Numbers (i.e. [0, 50]
http://cmaurer.github.io/angularjs-nvd3-directives/line.chart.html

How to use this data with D3js Scatter Plot?

I have data in following format and want to use with D3.js Scatter Plot:
{
"0": [
{"X":"1", "Y":"1"},
{"X":"2", "Y":"2"},
{"X":"3", "Y":"3"},
{"X":"4", "Y":"4"}
],
"1": [
{"X":"1", "Y":"1"},
{"X":"2", "Y":"2"},
{"X":"3", "Y":"3"},
{"X":"4", "Y":"4"}
],
"2": [
{"X":"1", "Y":"1"},
{"X":"2", "Y":"2"},
{"X":"3", "Y":"3"},
{"X":"4", "Y":"4"}
],
...
}
Provided that, I want each 0, 1, 2 and N to be treated as a new series for the Scatter Plot while want to draw dots using X,Y provided in that N (0, 1, 2 or N).
My apparent confusions are around following points:
Is this data good for Scatter Plot? if not, then what should be the best graph (an existing example will be great in either case).
How to use this data with d3.min() and d3.max()?
How to use this data to define the scaling for X and Y axis, as well?
Any help is highly appreciated. Thanks in advance.
There is a fiddle to accompany this code: http://jsfiddle.net/GyWpN/13/
Crude, but the elements are there.
Is this data good for Scatter Plot? if not, then what should be the
best graph (an existing example will be great in either case).
This data will work in a scatter plot. The 'best' graph very much depends on what the data represents, and how your users will interpret it.
How to use this data with d3.min() and d3.max()?
See code below
How to use this data to define the scaling for X and Y axis, as
well?
See code below
var myData = {
"0": [ {"X":"1", "Y":"1"},
{"X":"2", "Y":"2"},
{"X":"3", "Y":"3"},
{"X":"4", "Y":"4"} ],
"1": [ {"X":"1", "Y":"2"},
{"X":"2", "Y":"3"},
{"X":"3", "Y":"4"},
{"X":"4", "Y":"5"} ],
"2": [ {"X":"1", "Y":"7"},
{"X":"2", "Y":"6"},
{"X":"3", "Y":"5"},
{"X":"4", "Y":"4"} ]};
var width = 625,
height = 350;
// A way to look more easily across all 'inner' arrays
var myDataDrill = d3.values( myData );
var x = d3.scale.linear()
.domain([0,
// max over all series'
d3.max( myDataDrill, function(d) {
// within a series, look at the X-value
return d3.max( d, function(v) { return v.X } )
} ) ])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, d3.max( myDataDrill, function(d) {
return d3.max( d, function(v) { return v.Y } ) } )])
.range([height, 0]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
var series = svg.selectAll( "g" )
// convert the object to an array of d3 entries
.data( d3.map( myData ).entries() )
.enter()
// create a container for each series
.append("g")
.attr( "id", function(d) { return "series-" + d.key } );
series.selectAll( "circle" )
// do a data join for each series' values
.data( function(d) { return d.value } )
.enter()
.append("circle")
.attr( "cx", function(d) { return x(d.X) } )
.attr( "r", "10" )
.attr( "cy", function(d) { return y(d.Y) } );
I'm new to D3 to but I can direct you to alignedleft his tutorials are really helpfull and your questions accept from key,value-objects are explained.

Resources