Showing sum of multiple values with amCharts - amcharts

I would like to show sum of multiple values as one chart output with amCharts. I am using dataLoader with JSON to get the data. I know I have to create a function for but I couldn't understand how to get the data from the dataLoader to calculate
{
"balloonText": "[[title]] of [[valueAxis]]:[[value]]",
"lineThickness": 3,
"id": "sumValue",
"title": "sum Value",
"valueField": (function() {
var sumValues = "calculation";
return sumValues
}
this attempt is probably not correct but this is how I started
{
"balloonText": "[[title]] of [[valueAxis]]:[[value]]",
"lineThickness": 3,
"id": "LoadigTime",
"title": "Loadig Time",
"valueField": (function() {
var sumValues = (HomePageLoad + LoginToParametersLoad + ParametersLoad + AlarmsLoad + SwitchSideLoad + LoginToAdminLoad + AdminLoad) / 7;
return sumValues
})
}

valueField cannot be a function, only a string reference to a field in your data.
If the chart is meant to be displaying the sum of all of those fields in your data as a chart, simply add logic to your postProcess callback to create a new dataset containing your sums, e.g.
postProcess: function(data) {
var newData = [];
data.forEach(function(dataItem) {
var item = {
YOUR_CATEGORY_FIELD: dataItem.YOUR_CATEGORY_FIELD, //replace with your category field name
sum: 0
};
//loop through your item's keys and sum everything up, filtering out
//your category property
item.sum = Object.keys(dataItem).reduce(function(sum, key) {
if (key !== "YOUR_CATEGORY_FIELD") {
sum += dataItem[key]
}
return sum;
}, 0);
newData.push(item);
});
return newData;
},
// ...
graphs: [{
valueField: "sum",
// other props here
}]

Related

How to render timeseries/categorical stacked bar chart in dc.js, filtering stack and bar

Most examples I have found are using data that has time and number
var data = [
{
"Time": "19-Jan-2018 11:24:49.000 UTC",
"Speed": 1.885
},
{
"Time": "19-Jan-2018 11:24:59.000 UTC",
"Speed": 1.875
},
{
"Time": "19-Jan-2018 11:25:00.000 UTC",
"Speed": 1.878
},
{
"Time": "19-Jan-2018 11:25:01.000 UTC",
"Speed": 1.876
}
]
I am looking to stack type
var data = [
{
"Time": "19-Jan-2018 11:24:49.000 UTC",
"type": "CAT"
},
{
"Time": "19-Jan-2018 11:24:59.000 UTC",
"type": "DOG"
},
{
"Time": "19-Jan-2018 11:25:00.000 UTC",
"type": "CAT"
},
{
"Time": "19-Jan-2018 11:25:01.000 UTC",
"Type": "BAT"
}
]
How can I stack categorical data, while allowing the user to select time/category pairs, as in the following Example?
I adapted the example to time/category data in this fiddle.
Those dates would only parse in Chrome, so
const parseDate = d3.utcParse("%d-%b-%Y %H:%M:%S.%L UTC");
data.forEach(d => {
d.Time = parseDate(d.Time);
})
I changed the key functions to use ,
function multikey(x,y) {
return x + ',' + y;
}
function splitkey(k) {
return k.split(',');
}
I also changed fake group stack_second to convert string-dates from the multikeys back into Dates, and to initialize categories to 0 (since every stack has to be present for every X).
function stack_second(group, categories) {
return {
all: function() {
var all = group.all(),
m = {};
// build matrix from multikey/value pairs
all.forEach(function(kv) {
var ks = splitkey(kv.key);
m[ks[0]] = m[ks[0]] || Object.fromEntries(categories.map(c=>[c,0]));
m[ks[0]][ks[1]] = kv.value;
});
// then produce multivalue key/value pairs
return Object.keys(m).map(function(k) {
return {key: new Date(k), value: m[k]};
});
}
};
}
Get the array of categories from the source data:
const categories = Array.from(new Set(data.map(d => d.Type)).values());
When dealing with date/time, you have to choose a d3 time interval appropriate for your data. Here minutes looked right. Using UTC d3-time methods everywhere because your source data is UTC.
const interval = d3.utcMinute;
Calculate xscale domain and apply:
let extent = d3.extent(data, d=>d.Time);
extent[1] = interval.offset(extent[1], 1)
chart
.x(d3.scaleTime().domain(extent))
.xUnits(interval.range)
Right number of ticks, also formatted in UTC:
chart.xAxis().ticks(d3.utcMinute).tickFormat(d3.utcFormat('%H:%M'))
Match colors between stacks and wedges with
.colors(d3.scaleOrdinal().domain(categories).range(d3.schemeCategory10))
Crossfilter initialization, using the interval and categories:
const interval = d3.utcMinute; // choose appropriate to your data
var cf = crossfilter(data),
timeTypeDim = cf.dimension(function(d) { return multikey(interval(d.Time), d.Type); }),
timeTypeGroup = timeTypeDim.group(), // reduceCount by default
stackedGroup = stack_second(timeTypeGroup, categories);
And here's the chart code for completeness, although we've already discussed the relevant parts:
function sel_stack(i) {
return function(d) {
return d.value[i];
};
}
chart
.width(600)
.height(400)
.colors(d3.scaleOrdinal().domain(categories).range(d3.schemeCategory10))
.controlsUseVisibility(true)
.x(d3.scaleTime().domain(extent))
.xUnits(interval.range)
.margins({left: 80, top: 20, right: 10, bottom: 20})
.brushOn(false)
.clipPadding(10)
.title(function(d) {
return d.key + '[' + this.layer + ']: ' + d.value[this.layer];
})
.legend(dc.legend().x(540).y(50))
.dimension(timeTypeDim)
.group(stackedGroup, categories[0], sel_stack(categories[0]))
.renderLabel(true);

How to integrate KendoUI chart with SignalR

i want to create a real time chart, using Kendo ui chart and signalr. I see this example, but has no code. So i try alone.
A little demonstration of my code:
At first I created a kendo chart
function queueActivityChart() {
$("#queueActivityChart").kendoChart({
legend: {
visible: true
},
seriesDefaults: {
labels: {
visible: true,
format: "{0}",
background: "transparent"
}
},
series: [{
type: "line",
field: "Incoming",
categoryField: "DateTime",
}],
valueAxis: {
labels: {
format: "{0}"
},
line: {
visible: false
}
},
categoryAxis: {
labels:
{
rotation: -90,
dateFormats:
{
seconds: "ss",
minutes: "HH:mm:ss",
hours: "HH:mm",
days: "dd/MM",
months: "MMM 'yy",
years: "yyyy"
}
}, type: "Date", field: "DateTime", baseUnit: "seconds"
}
});
var chart = $("#queueActivityChart").data("kendoChart");
chart.options.transitions = false;
}
$(document).ready(queueActivityChart);
$(document).bind("kendo:skinChange", queueActivityChart);
Then I have this part of code, that get from server data
$scope.signalRData = [];
$scope.signalR.on('receiveCounters', function (data) {
$scope.queueData = data;
for (var i = 0; i < data.length; i++) {
$scope.signalRData.push(data[i]);
}
while ($scope.signalRData.length > 12) {
$scope.signalRData.splice(0, 1);
}
$("#queueActivityChart").data("kendoChart").setDataSource(
new kendo.data.DataSource({
group: {
field: "Name"
},
data: $scope.signalRData
}));
});
This works! And I get a picture of the latest updated items.
But the problem is that this chart is like to put one picture in front of other. I mean that this is the first time that load Data Source; that creates a chart of my data, the second time my data has changed, some values are still in my array some others has move out, the third too.
It seems like it puts a picture of my current data in front of the
previous data. It's not smoothie and cannot use chart's legend
property because I initialize my Data Source everytime.
Can someone help me how can create a smoothie kendo chart with real time data like the kendo official example? Also can somehow to add scroller to bottom?
I looked at the code for the benchmark and I think you may be missing in your chart which is renderAs: "canvas"
Also, in the example, the data is kept locally (saved) and then moved so it creates that "smooth" effect you may be talking about.
Here is the code that you can be of interest:
function step() {
addPoint();
$("#chart").data("kendoChart").refresh();
frames++;
if (playing) {
kendo.animationFrame(step);
}
}
function addPoint() {
var stockData,
change,
lastValue;
// Shift existing categories to the left and add the next date at the end
lastDate = new Date(lastDate.getTime() + TICKS_PER_DAY);
categoryList.push((lastDate.getMonth() + 1) + "/" + (lastDate.getDay() + 1));
if (categoryList.length > POINTS) {
categoryList.shift();
}
for (var i = 0; i < stocks.length; i++) {
stockData = stocks[i];
change = (Math.random() > 0.5 ? 1 : - 1) * Math.random() * 10;
lastValue = stockData[stockData.length - 1] || Math.random() * 10;
// Add a new pseudo-random data point
stockData.push(Math.min((i + 1) * 20, Math.max((i + 1) * 10, lastValue + change)));
// Shift the data points of each series to the left
if (stockData.length > POINTS) {
stockData.shift();
}
}
}
Check out the source code of your example for the full source code and use the dojo to test our their code and play around with it easily

ZoomRange Highstock works not correct?

I made a Highstock diagramm and got aproblem with zooming on the yAxis.
I have a Button and 2 textfield to get the wanted min/max values for the axis. With min:0, max: 100 it works well. With min:0, max:80 it doesn't (max will still be 100 in the Diagramm).
If I use the mouse for zooming it works well (even a min of: 3.7 and a max of 3.894 is possible). But using the mouse is not an Option, because in the later Diagramm there will be 3 yAxes with individual zoom.
$(function () {
var seriesOptions = [],
seriesCounter = 0,
names = ['MSFT', 'AAPL', 'GOOG'];
/**
* Create the chart when all data is loaded
* #returns {undefined}
*/
function createChart() {
$('#container').highcharts('StockChart', {
rangeSelector: {
selected: 4
},
chart:{
zoomType: 'xy'
},
yAxis: [
{
labels: {
format: '{value}',
},
height: '100%',
opposite: false,
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
],
plotOptions: {
series: {
compare: 'percent'
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2
},
series: seriesOptions
},
function(chart){
$('#btn').click(function(){
var min = temp_min.value,
max = temp_max.value;
chart.yAxis[0].setExtremes((min),(max));
});
});
}
$.each(names, function (i, name) {
$.getJSON('https://www.highcharts.com/samples/data/jsonp.php?filename=' + name.toLowerCase() + '-c.json&callback=?', function (data) {
if(seriesCounter==0){
seriesOptions[i] = {
name: name,
data: data,
yAxis: 0
};
} else {
seriesOptions[i] = {
name: name,
data: data,
yAxis: 0
};
}
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
});
JSFiddle
Another Question: Is it possible to set up a scrollbar for the yAxis as well?
Thanks for your help, Patrick
This is related with fact that tickInterval is not regular, so is rounded to value (like 100). The solution is using tickPositioner which calculates ticks, based on extremes which you define.
tickPositioner: function (min,max) {
var positions = [],
tick = Math.floor(min),
increment = Math.ceil((max - min) / 5);
for (tick; tick - increment <= max; tick += increment) {
positions.push(tick);
}
return positions;
},
http://jsfiddle.net/6s11kcwd/
The scrollbar is supported only for xAxis.

Handsontable: Updating a cell renderer at runtime

I'm using handsontable, and I want to change the background color of a cell if its value is edited and changed. I can do this easily if my data source is an array of arrays (see fiddle: http://jsfiddle.net/chiman24/3o2c3c7m/).
document.addEventListener("DOMContentLoaded", function() {
// Row Styles
var blank = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = '#ABAAAA'
};
var align = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.verticalAlign = 'middle';
td.style.fontWeight = 'bold';
};
var highlight1 = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = '#BDD7EE';
td.style.textAlign = 'right';
};
var changedBackgroundColor = '#cbd9e4';
var defaultBackgroundColor = 'white';
var hasChanged = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = changedBackgroundColor;
};
var noChange = function(instance, td, row, col, prop, value,
cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
td.style.backgroundColor = defaultBackgroundColor;
};
var data = [
["1", "Hear us from heaven", "New Life Worship",
"Anderson, Jared", "something"
],
["2", "Spirit Break Out", "Kim Walker", "Walker, Kim",
"Still Believe"
]
],
dataCopy = [
["1", "Hear us from heaven", "New Life Worship",
"Anderson, Jared", "something"
],
["2", "Spirit Break Out", "Kim Walker", "Walker, Kim",
"Still Believe"
]
],
container = document.getElementById('example1'),
hot1;
//Table Row and Col Options
hot1 = new Handsontable(container, {
data: data,
fixedColumnsLeft: 1,
columnSorting: true,
colHeaders: ["id", "title", "artist", "author", "album"],
columns: [{
type: "text"
}, {
type: "text"
}, {
type: "text"
}, {
type: "text"
}, {
type: "text"
}]
});
hot1.addHook('afterChange', afterChange);
function afterChange(changes, source) {
if (source == 'edit' || source == 'autofill') {
$.each(changes, function(index, element) {
var change = element;
var rowIndex = change[0];
var columnIndex = change[1];
var oldValue = change[2];
var newValue = change[3];
var cellChange = {
'rowIndex': rowIndex,
'columnIndex': columnIndex
};
if (oldValue != newValue) {
var cellProperties = hot1.getCellMeta(
rowIndex, columnIndex);
if (newValue != dataCopy[rowIndex][
columnIndex
]) {
cellProperties.renderer = hasChanged;
} else { //data changed back to original value.
cellProperties.renderer = noChange;
}
hot1.render();
}
});
}
}
});
// noSideScroll class added to fix some containers while side scrolling the table
$(window).scroll(function() {
$('.noSideScroll').css({
'left': $(this).scrollLeft()
});
});
However, when using an array of objects, I can't get it to work. (see fiddle: http://jsfiddle.net/chiman24/24mpavga/).
var data = [{
"id": 1,
"title": "First Loved Me",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}, {
"id": 2,
"title": "One Thing Remains",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}],
dataCopy = [{
"id": 1,
"title": "First Loved Me",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}, {
"id": 2,
"title": "One Thing Remains",
"artist": "Israel and New Breed",
"author": "Houghton, Israel",
"album": "Covered: Alive In Asia"
}],
container = document.getElementById('example1'),
hot1;
//Table Row and Col Options
hot1 = new Handsontable(container, {
data: data,
fixedColumnsLeft: 1,
columnSorting: true,
colHeaders: ["id", "title", "artist", "author", "album"],
columns: [{
data: "id"
}, {
data: "title"
}, {
data: "artist"
}, {
data: "author"
}, {
data: "album"
}]
});
hot1.addHook('afterChange', afterChange);
function afterChange(changes, source) {
if (source == 'edit' || source == 'autofill') {
$.each(changes, function(index, element) {
var change = element;
var rowIndex = change[0];
var columnIndex = change[1];
var oldValue = change[2];
var newValue = change[3];
var cellChange = {
'rowIndex': rowIndex,
'columnIndex': columnIndex
};
if (oldValue != newValue) {
var cellProperties = hot1.getCellMeta(
rowIndex, columnIndex);
if (newValue != dataCopy[rowIndex][
columnIndex
]) {
cellProperties.renderer = hasChanged;
} else { //data changed back to original value.
cellProperties.renderer = noChange;
}
hot1.render();
}
});
}
}
Is there a way to accomplish this? I want to get it working using an array of objects because my data coming from the server will be in JSON format. I've scoured the handsontable documentation for a couple of days to no avail. Any help will be much appreciated. Thanks.
I got some help from the handsontable github forum.
Apparently, if the data source is an array of objects, then when calling "getCellMeta", instead of passing in a numerical column index, you have to pass in the column index as a property like this:
hot.getCellMeta(2, hot.propToCol(columnIndex));
Here's the updated demo
Other way to change background color of cell is use the Cell Option
...
if (oldValue != newValue){
aCell.push(
{ row: rowIndex,
col: hot.propToCol(columnIndex),
className: "cssWithBackgroundColor" });
hot.updateSettings({ cell: aCell });
}
If the user undo change you can
if ( source == 'UndoRedo.undo'){
aCell.pop();
hot.updateSettings({ cell: aCell });
}

Issue in Sorting Integer values In AngularJs Custom Sort function

I am having problem in my custom sorting method. I have a json structure wherein integer values are being passed as String. While sorting I need to sort the list based on the integer value, but it is being sorted as String values.
I have created a JsFiddle for the same:
http://jsfiddle.net/ayan_2587/vjF2D/14/
The angular code is as follows:-
var app = angular.module('myModule', []);
app.controller('ContactListCtrl', function ($scope, $timeout, $filter) {
var sortingOrder = 'price';
$scope.sortingOrder = sortingOrder;
$scope.sortorder = '-sort';
$scope.contacts = [{
"name": "Richard",
"surname": "Stallman",
"price": "7200"
}, {
"name": "Donald",
"surname": "Knuth",
"price": "34565"
}, {
"name": "Linus",
"surname": "Torvalds",
"price": "23454"
}];
$scope.setLoading = function (loading) {
$scope.isLoading = loading;
}
$scope.layoutDone = function (value) {
console.log(value);
$scope.setLoading(true);
$timeout(function() {
// take care of the sorting order
if ($scope.sortingOrder !== '') {
if(value == 'sort'){
$scope.contacts = $filter('orderBy')($scope.contacts, $scope.sortingOrder, false);
}
else if(value == '-sort'){
$scope.contacts = $filter('orderBy')($scope.contacts, $scope.sortingOrder, true);
}
}
$scope.setLoading(false);
}, 1000);
}
$scope.loadFeed = function(url) {
$scope.setLoading(true);
}
$scope.loadFeed();
});
app.directive('repeatDone', function() {
return function(scope, element, attrs) {
if (scope.$last) { // all are rendered
scope.$eval(attrs.repeatDone);
}
}
})
Please help me out !!!
SInce you sort in JS, you can allways use Array.sort() - at least on modern browsers-, passing it a sort function like:
function sortAsc(a,b) {
a = parseInt(a.price);
b = parseInt(b.price);
return a > b ? 1 : (a === b ? 0 : -1);
}
And then execute the sorting like:
if(value == 'sort'){
$scope.contacts.sort(sortAsc);
}
See forked fiddle: http://jsfiddle.net/sRruf/ (the initial state is not sorted)
The sort property (here price) can be parameterized with a little extra work.
And here is a version using the orderBy filter and a custom predicate: http://jsfiddle.net/sRruf/1/

Resources