I am trying to create realtime linechart based on notifcations that i am getting. I have looked in examples - http://jsfiddle.net/kaliatech/4TMMD/
however i cannot make it work, the rate is being updated and data is inserted into the array, but i am getting in the plot only one point which is always the first one
this.data = [{
key: "New Incidents Rate",
values: getData()
}];
function getData() {
return that.newIncidentRate;
}
redraw();
function redraw() {
nv.addGraph(function() {
that.chart = nv.models.lineChart()
.x(function(d) { return d.x })
.y(function(d) { return d.y });
//.color(d3.scale.category10().range());
that.chart.xAxis
.axisLabel('Time')
.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
});
that.chart.yAxis
.axisLabel('Rate')
.tickFormat(d3.format(',r'));
d3.select('#chart svg')
.datum(that.data)
//.transition().duration(500)
.call(that.chart);
nv.utils.windowResize(that.chart.update);
return that.chart;
});
}
amplify.subscribe("newIncident", function (rate) {
$scope.newRate = rate;
$scope.$apply();
if (that.newIncidentRate.length > 20) {
that.newIncidentRate.shift();
}
var currentTime = new Date();
that.newIncidentRate.push({
x: new Date(currentTime.getTime()),
y: rate
});
redraw();
});
i have tested it more and it seems to be related to the number of points, when i take the sample all is ok (adding 30 point - fresh every time) same when adding 2 points , so i tested to keep all the points in container and always copy it to new one - falied, it seems there is a problem to increase the number of points
function getData() {
var arr = [];
var theDate = new Date(2012, 01, 01, 0, 0, 0, 0);
newIncidentRate.push({x: new Date(theDate.getTime()), y: Math.random() * 100});
theDate.setDate(theDate.getDate() + moveDate);
++moveDate;
for (var d in newIncidentRate) {
arr.push(d) ;
}
return arr;
}
full code - http://jsfiddle.net/lirazrom/Wx5bG/
Related
Hello i have a question about dc/d3, i would like to show on the xAxis the monthname, Januar till December (in correct order)
The Value should be sum up for each year (i mean on January shoud be the Values from Year 2020 till 2022 - only one Januar)
the following code works fine for Bars because the reorder of the x in this way works fine, but for line chart i get a funny chart.
function Dimension() {
return Data.ndx.dimension(function (d) { return moment.localeData()._months[d["DATE"].getMonth()]; });
}
function Group(thisDim) {
var thisVal = thisDim.group().reduceSum(function (d) { return d["VALUE"]; });
return thisVal;
}
var thisDim = Dimension();
var thisVal = Group();
chartObject.dimension(thisDim).group(thisVal)
.title(function (d) { return d.key; })
.ordinalColors(['#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#dadaeb'])
.label(function (d) { return d.key;})
.margins({top: 10, right: 10, bottom: 30, left: 70})
.elasticY(true)
;
for (var i=0;i<12;i++) x.push(moment.localeData()._months[i]);
chartObject.x(d3.scaleBand().domain(x)).xUnits(dc.units.ordinal);
This is a simple code (i hope i have put all in)
Here is a screenshot of the chart:
Goodmorning everyone,
I'm trying to implement the "candlestick" graph from the Chartjs library;
I took the example shown in the documentation as a guide and everything works correctly: the 'x' and 'y' axes show the correct values;
The problem is that the actual graph is not displayed, even if the Cartesian axes are present
this is the code used for implement Chart and the result image
var barCount = 5;
var initialDateStr = '01 Apr 2017 00:00 Z';
var barData = getRandomData(initialDateStr, barCount);
//console.log(barData)
//function lineData() { return barData.map(d => { return { x: d.x, y: d.c} }) };
var chart = new Chart(ctx, {
type: 'candlestick',
data: {
datasets: [{
label: 'CHRT - Chart.js Corporation',
data: barData
}]
}
});
var getRandomInt = function(max) {
return Math.floor(Math.random() * Math.floor(max));
};
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function randomBar(date, lastClose) {
var open = +randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
var close = +randomNumber(open * 0.95, open * 1.05).toFixed(2);
var high = +randomNumber(Math.max(open, close), Math.max(open, close) * 1.1).toFixed(2);
var low = +randomNumber(Math.min(open, close) * 0.9, Math.min(open, close)).toFixed(2);
return {
x: date.valueOf(),
o: open,
h: high,
l: low,
c: close
};
}
I have a script that originated in D3 V3 and I'm trying to rebuild it in V5.
I'll go through the code and then the problem. This is the relevant part of code.
var height = 570
var width = 510
var svg = d3.select("#chart").append("svg")
.attr("width", width + 160)
.attr("height", height + 90)
.append("g")
.attr("transform", "translate(" + 160 + "," + 90 + ")");
// this has a count for how many are in each group
var grpcnts = {
met: { '1960': 0, '1970': 0, '2010': 0, label: "First Met", x: 1, y: 1 },
romantic: { '1960': 0, '1970': 0, '2010': 0, label: "Romantic", x: 2, y: 1 },
lived: { '1960': 0, '1970': 0, '2010': 0, label: "Live Together", x: 3, y: 1 },
married: { '1960': 0, '1970': 0, '2010': 0, label: "Married", x: 4, y: 3 },
}
var x = d3.scaleBand()
.domain([1950, 1970, 1980, 2010])
.range([0, width]);
var y = d3.scaleBand()
.domain(d3.keys(grpcnts))
.range([height, 0]);
var sched_objs = [],
curr_index = -1;
// Load data
d3.tsv("timelines.tsv")
.then(function(data) {
data.forEach(function(d) {
var day_array = d.timeline.split(",");
var activities = [];
for (var i=0; i < day_array.length; i++) {
// Duration
if (i % 2 == 1) {
activities.push({'act': day_array[i-1], 'duration': +day_array[i]});
}
}
sched_objs.push(activities);
});
// A node for each person's schedule
var nodes = sched_objs.map(function(o,i) {
var act = o[0].act;
var init_x = x(+data[i].decade) + Math.random();
var init_y = y('met') + Math.random();
var col = "#cccccc";
grpcnts[act][data[i].decade] += 1;
return {
act: act,
radius: maxRadius,
x: init_x,
y: init_y,
decade: data[i].decade,
color: color(act),
married: false,
moves: 0,
next_move_time: o[0].duration,
sched: o
}
});
}) // end tsv
This is a sample of the dataset.
"decade" "timeline"
1970 "met,4,romantic,14,lived,1,married,-99"
1970 "romantic,2,married,-99"
1970 "met,9,romantic,48,married,-99"
1970 "romantic,20,married,-99"
1970 "met,2,romantic,10,married,-99"
1970 "met,13,romantic,16,married,-99"
The problem is that the x and y fields show up as NaN.
I've added console.log statements before the return clause and the init_x and init_y values print out the right numbers.
I've tested the x() and y() functions with all the valid inputs and they return the right values. I've tested Math.random() which appears to work just fine.
None of the other fields show up as NaN which leads me to believe that the syntax for returning multiple values is right. I've also tried wrapping init_x and init_y in Number().
Unfortunately your code is full of errors, to the point that we cannot make it run without a major refactor.
However, just to answer your current specific question ("where are the NaNs coming from?"), this is the problem (line 212 in your pasteBin):
var k = 0.03 * this.alpha;
Since there is no alpha, but alpha() instead, when you use k (which is undefined) latter on...
o.x += (x(+o.decade) - o.x) * k * damper;
... you get that nice and beautiful NaN.
I'd like to emphasise again that this change will not make your code work, you have a lot of other problems to fix.
I'm experimenting with D3 version 4 force directed graphs and have looked at Jim Vallandingham's tutorial and code as a starting point.
http://vallandingham.me/bubble_chart_v4/
and am attempting to produce an animation similar to the example here from Nathan Yau
https://flowingdata.com/2016/08/23/make-a-moving-bubbles-chart-to-show-clustering-and-distributions/
I've stripped the bubble chart from Jim Vallandingham's code to what I think I need and can display the individual states by changing the index value, but for some reason the code does not want to animate between the different states. I assume the redraw function isn't working. It may be an obvious error or one made through complete ignorance, but if you can help it would be great.
Here's my code:
function bubbleChart() {
var width = 940;
var height = 600;
var center = { x: width / 2, y: height / 3 };
var years = ["0","2008", "2009", "2010"];
var yearCenters = {
2008: { x: width / 3, y: 2 * height / 3 },
2009: { x: width / 2, y: 2 * height / 3 },
2010: { x: 2 * width / 3, y: 2 * height / 3 }
};
// #v4 strength to apply to the position forces
var forceStrength = 0.03;
// These will be set in create_nodes and create_vis
var svg = null;
var bubbles = null;
var nodes = [];
var index= 0;
function charge(d) {
return -Math.pow(d.radius, 2.3) * forceStrength;
}
// Here we create a force layout
var simulation = d3.forceSimulation()
.velocityDecay(0.2)
.force('x', d3.forceX().strength(forceStrength).x(center.x))
.force('y', d3.forceY().strength(forceStrength).y(center.y))
.force('charge', d3.forceManyBody().strength(charge))
.on('tick', ticked);
// #v4 Force starts up automatically, which we don't want as there aren't any nodes yet.
simulation.stop();
// Nice looking colors
var fillColor = d3.scaleOrdinal()
.domain(['low', 'medium', 'high'])
.range(['#d84b2a', '#beccae', '#7aa25c']);
function createNodes(rawData) {
var myNodes = rawData.map(function (d) {
return {
id: d.id,
radius: 5,
value: +d.total_amount,
name: d.grant_title,
org: d.organization,
group: d.group,
year: d.start_year,
x: Math.random() * 900,
y: Math.random() * 800
};
});
// sort them to prevent occlusion of smaller nodes.
myNodes.sort(function (a, b) { return b.value - a.value; });
return myNodes;
}
/*
* Main entry point to the bubble chart.
*/
var chart = function chart(selector, rawData) {
// convert raw data into nodes data
nodes = createNodes(rawData);
// Create a SVG element inside the provided selector
// with desired size.
svg = d3.select(selector)
.append('svg')
.attr('width', width)
.attr('height', height);
// Bind nodes data to what will become DOM elements to represent them.
bubbles = svg.selectAll('.bubble')
.data(nodes, function (d) { return d.id; });
// Create new circle elements each with class `bubble`.
// There will be one circle.bubble for each object in the nodes array.
// Initially, their radius (r attribute) will be 0.
// #v4 Selections are immutable, so lets capture the
// enter selection to apply our transtition to below.
var bubblesE = bubbles.enter().append('circle')
.classed('bubble', true)
.attr('r', 0)
.attr('fill', function (d) { return fillColor(d.group); })
.attr('stroke', function (d) { return d3.rgb(fillColor(d.group)).darker(); })
.attr('stroke-width', 2)
// #v4 Merge the original empty selection and the enter selection
bubbles = bubbles.merge(bubblesE);
// Fancy transition to make bubbles appear, ending with the
// correct radius
bubbles.transition()
.duration(2000)
.attr('r', function (d) { return d.radius; });
// Set the simulation's nodes to our newly created nodes array.
// #v4 Once we set the nodes, the simulation will start running automatically!
simulation.nodes(nodes);
chart.redraw();
};
// Callback function that is called after every tick of the force simulation.
// These x and y values are modified by the force simulation.
function ticked() {
bubbles
.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; });
}
chart.redraw = function (index){
simulation.force('x', d3.forceX().strength(forceStrength).x(nodePosX));
simulation.force('y', d3.forceY().strength(forceStrength).y(nodePosY));
simulation.alpha(1).restart();
}
function nodePosX(d) {
if (+d.year <= +years[index]) {
return yearCenters[d.year].x;
} else {
return center.x;
}
}
function nodePosY(d) {
if (+d.year <= +years[index]) {
return yearCenters[d.year].y;
} else {
return center.y;
}
}
// return the chart function from closure.
return chart;
}
var myBubbleChart = bubbleChart();
myBubbleChart('#vis', data);
for (i=0;i<4;i++){
setInterval(function(){myBubbleChart.redraw(i);}, 100);
}
I misunderstood how to use setInterval to redraw the chart, so it should be as follows:
var i = 0;
setInterval(function(){myBubbleChart.redraw(i++);}, 1000);
I have a D3 chart that is supposed to look like this:
But instead it looks like this:
This is the code i'm using:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
</head>
<body>
<svg id="chart"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var NSW = "NSW";
var QLD = "QLD";
var width = 600;
var height = 400;
var years = [];
var getStat = function(year, volatility, basis) {
return {
d: year,
x: basis,
vol: volatility,
value: 45 * Math.pow(basis, year),
high: 45 * Math.pow(basis+volatility, year),
low: 45 * Math.pow(basis-volatility, year),
}
}
for(i = 0; i < 25; i++) {
years.push(i);
}
var data = years.map(function(year){ return [getStat(year, 0.03, 1.08),getStat(year, 0.02, 1.08), getStat(year, 0.01, 1.08)]; }); // generate bogus data
var set_one = data.map(function(d) { return d[0];});
var set_two = data.map(function(d) { return d[1];});
var set_three = data.map(function(d) { return d[2];});
var chart = d3.select("#chart").attr("width", width).attr("height", height).append("g");
var x = d3.scale.linear().domain([0, years.length]).range([0, width]);
var y = d3.scale.linear().domain([0, d3.max(data, function(d){ return Math.max(d[0].high, d[1].high); })]).range([0, height]);
var area = d3.svg.area().x(function(d,i) { return x(i); })
.y0(function(d, i){ return d.low}) //FUNCTION FOR BASE-Y
.y1(function(d, i){ return d.high * 0.99;}); //FUNCTION FOR TOP-Y
chart
.selectAll("path.area")
.data([set_one,set_two,set_three]) // !!! here i can pass both arrays in.
.enter()
.append("path")
.attr("fill", "rgba(0,0,0,0.5)")
.attr("class", function(d,i) { return [NSW,QLD,"T"][i]; })
.attr("d", area);
</script>
What am I doing wrong?
Actually your doing nothing wrong the y-axis goes downwards starting at 0 from the top down to height. So to flip it you can set the y values to height - yValue:
var area = d3.svg.area().x(function(d,i) { return x(i); })
.y0(function(d, i){ return (height - (d.low))}) //FUNCTION FOR BASE-Y
.y1(function(d, i){ return (height - (d.high * 0.99))}); //FUNCTION FOR TOP-Y
Fiddle Example
the y ordinate in SVG increases downwards. Try this...
var y = d3.scale.linear().domain([d3.max(data, function(d){ return Math.max(d[0].high, d[1].high); }), 0]).range([0, height]);
Like everything in HTML / CSS / Canvas, the Y axis starts with 0 at the top and goes down to height at the bottom.
So according to your setup, the graph behaves correctly.
There are multiple ways to change the graphs direction.
a) You can change the range of your axis var y = d3.scale.linear().domain([...]).range([height, 0]);
b) You can change the domain of your axis var y = d3.scale.linear().domain([d3.max(data, function(d){ return Math.max(d[0].high, d[1].high); }), 0]).range([...]);
or c) change the way the graph gets its y-values with d3.svg.area().y0(...) and d3.svg.area().y1(...)
I would recommend the first option, because this actually specifies the range your domain gets projected on.
I think there was an issue with your y-scaling.I have inverted the range from range([height, 0] which was initially range([0,height]) as this should be the way as per d3 norms otherwise you have to change the logic while calculating the height of plot.
Here I am attaching the fixed code.
<!DOCTYPE html>
<meta charset="utf-8">
<head>
</head>
<body>
<svg id="chart"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var NSW = "NSW";
var QLD = "QLD";
var width = 600;
var height = 400;
var years = [];
var getStat = function(year, volatility, basis) {
return {
d: year,
x: basis,
vol: volatility,
value: 45 * Math.pow(basis, year),
high: 45 * Math.pow(basis+volatility, year),
low: 45 * Math.pow(basis-volatility, year),
}
}
for(i = 0; i < 25; i++) {
years.push(i);
}
var data = years.map(function(year){ return [getStat(year, 0.03, 1.08),getStat(year, 0.02, 1.08), getStat(year, 0.01, 1.08)]; }); // generate bogus data
var set_one = data.map(function(d) { return d[0];});
var set_two = data.map(function(d) { return d[1];});
var set_three = data.map(function(d) { return d[2];});
var chart = d3.select("#chart").attr("width", width).attr("height", height).append("g");
var x = d3.scale.linear().domain([0, years.length]).range([0, width]);
var y = d3.scale.linear().domain([0, d3.max(data, function(d){ return Math.max(d[0].high, d[1].high); })]).range([height, 0]);
var area = d3.svg.area().x(function(d,i) { return x(i); })
.y0(function(d, i){ return y(d.low)}) //FUNCTION FOR BASE-Y
.y1(function(d, i){ return y(d.high * 0.99);}); //FUNCTION FOR TOP-Y
chart
.selectAll("path.area")
.data([set_one,set_two,set_three]) // !!! here i can pass both arrays in.
.enter()
.append("path")
.attr("fill", "rgba(0,0,0,0.5)")
.attr("class", function(d,i) { return [NSW,QLD,"T"][i]; })
.attr("d", area);
</script>