I have a scatter plot Vizframe chart in which I need to include a linear regression/trend line. Any idea how this can be done? It appears this is not something offered by vizframe 'out of box'? I can't find a solution for this!
Question:
Any suggestions on a feasible way to implement a regression line on a Scatter Plot Vizframe chart?
Here is the code I have for the setup. The scatter plot opens in a dialog/modal when a button is pressed.
sap.ui.define([
'jquery.sap.global',
'vizConcept/controller/BaseController',
'sap/ui/model/json/JSONModel',
'vizConcept/model/viewControls',
'sap/m/Button',
'sap/m/Dialog',
],
function (jQuery, BaseController, JSONModel, viewControls, Button, Dialog) {
"use strict";
var controls;
var mainController = BaseController.extend("vizConcept.controller.Main", {
onInit: function(oEvent) {
// Access/expose the defined model(s) configured in the Component.js or Manifest.json within the controller.
this.getView().setModel(this.getOwnerComponent().getModel("products"), "products");
var oModel = this.getView().getModel("products");
this.getView().setModel(oModel);
var sUrl = "#" + this.getOwnerComponent().getRouter().getURL("page2");
$(function() {
var dataset = new sap.viz.ui5.data.FlattenedDataset({
dimensions : [
{
axis : 1,
name : 'Award Date',
value : "{AwdDate}"
}
],
measures : [
{
group: 1,
name : 'Award Date',
value : '{Hist}'
},
{
group: 2,
name : 'Current PPI',
value : '{Current}'
}
],
data : {
path : "/ProductCollection"
}
});
var scatterViz = new sap.viz.ui5.Scatter({
id : "idscatter",
width : "1000px",
height : "400px",
title : {
text : 'Pricing Tool Scatter Plot Example'
},
xAxis : {
title : {
visible : true
}
},
yAxis : {
title : {
visible : true
}
},
dataset : dataset
});
scatterViz.setModel(sap.ui.getCore().getModel());
scatterViz.setModel(oModel);
var dlg = new sap.m.Dialog({
id: 'vizModal',
title: 'Scatter Plot Example Viz',
width : "1800px",
height : "600px",
content : [scatterViz],
beginButton: new Button({
text: 'Close',
press: function () {
dlg.close();
}
})
});
(new sap.m.Button({
text: 'open',
type: 'Accept',
press: function() {
dlg.open();
scatterViz.invalidate();
}
})).placeAt('content');
});
},
onToPage2 : function () {
this.getOwnerComponent().getRouter().navTo("page2");
},
});
return mainController;
});
Edit
Here is the 'products' model that is outputted on the vizframe chart. I have the products model defined in the manifest.json but the connection there is fine:
products model
{
"ProductCollection": [
{
"Item": "1",
"AwdDate": "20160715",
"Hist": 171.9,
"Current": 183
},
{
"Item": "2",
"AwdDate": "20160701",
"Hist" : 144.3,
"Current": 158.6
},
{
"Item": "3",
"AwdDate": "20150701",
"Hist": 160,
"Current": 165
},
{
"Item": "1",
"AwdDate": "20160715",
"Hist": 201,
"Current": 167
},
{
"Item": "2",
"AwdDate": "20160801",
"Hist" : 175.3,
"Current": 178.2
},
{
"Item": "3",
"AwdDate": "20150721",
"Hist": 160,
"Current": 147
},
{
"Item": "1",
"AwdDate": "20160715",
"Hist": 175.9,
"Current": 185.2
},
{
"Item": "2",
"AwdDate": "20161101",
"Hist" : 165.3,
"Current": 158.2
},
{
"Item": "3",
"AwdDate": "201700101",
"Hist": 160,
"Current": 165
},
{
"Item": "4",
"AwdDate": "201600401",
"Hist": 173,
"Current": 177
}
]
};
Edit 2
Here is my attempt at the solution offered here. But nothing appears after this is included in the onInit() function of the controller.
var regressionData = [];
for (var i = 0; i < 10; i++) {
regressionData[i] = [oData.ProductCollection[i].Current, oData.ProductCollection[i].Hist];
};
regression('linear', regressionData);
Unfortunately, you have some limitations inherent to the viz charts. Namely:
You cannot add "oblique" reference lines (= the trend line) to the chart. You can only add vertical / horizontal ones via the Reference Line feature. Check out the (non-intuitive) documentation on this: Viz Charts Documentation (look at the plotArea.referenceLine.line; it is just a number = will be a horizontal / vertical line depending on your chart type or orientation).
You cannot combine a scatter plot with a line chart. If you look in the same Viz Charts Documentation, you will see that in the Combination "chapter" on the left hand side, there is no "Line - Scatter" combination.
Also, your X values (AwDate) are ABAP-style dates as string. It is more appropriate to use a date / time chart to correctly display them. Anyway, regression makes a lot more sense when you use dates. Otherwise your data is "categorical" and the only way you can make regression is by sorting them and considering them equidistant on the X axis - not what you would normally want if you have non-equidistant data (like the one in your example).
My proposal would be to:
Use a line chart instead of a scatter chart (such that you can also display the trend line).
Convert the ABAP-style strings into dates, such that you can use a timeseries chart. You have some "wrong" dates in your example data btw: "201600401".
Do the regression for whatever you want and add it as a separate "trend" series. You will need to compute a point on the "trend" series for each point on your other series (basically, you will have to add a "trend" attribute to each line in your ProductCollection). If you are using an OData model, then you will need to either switch to an JSON client model or do some ugly workarounds with formatters.
As requested, I made an example implementation here: https://jsfiddle.net/93mx0yvt/23/. The regression algorithm I borrowed from here: https://dracoblue.net/dev/linear-least-squares-in-javascript/. The main points of the code are:
// format for parsing the ABAP-style dates
var oFormat = DateFormat.getDateInstance({
pattern: "yyyyMMdd"
});
// maps an ProductCollection entry to a new object
// which has the parsed date (and only the needed attributes)
var fnMapData = function(oEntry) {
return {
date: oFormat.parse(oEntry.AwdDate),
current: oEntry.Current,
historical: oEntry.Hist
};
};
var fnProcessData = function(oD) {
var aEntries = oD.ProductCollection.map(fnMapData),
aXs = aEntries.map(function(oE) { // get the Xs
// we take the millis to be able to do arithmetics
return oE.date.getTime();
}),
aYs = aEntries.map(function(oE) { // get the Ys (hist)
return oE.historical;
}),
//changed the function to only return only result Ys
aRs = findLineByLeastSquares(aXs, aYs);
//save the Ys into the result
for (var i = 0; i < aEntries.length; ++i) {
aEntries[i].trend = aRs[i];
}
return {
data: aEntries
};
};
You can use then the data returned by the fnProcessData function inside a JSONModel and then build a simple multi-series date/time line chart based on it.
Related
I'm trying to get the row ID of a select editor in Tabulator.
I have a few hundred rows of values, some i want to have a value of yes, some of no. When I change the value I then need to know which row it is for.
Here is my table.
adtable = new Tabulator("#adtable", {
ajaxURL: "getinfo.php",
layout: "fitColumns",
pagination:"local",
placeholder: "No Data Set available",
paginationSize: 40,
paginationSizeSelector: [100, 200, 500, 1000],
columns: [{
title: "Key Name",
field: "keyname",
formatter: "textarea",
sorter: "string",
headerFilter: "input"
},
{
title: "Active",
field: "display",
editor: "select",
editorParams: {
values: ["Yes", "No"],
sortValuesList: "asc",
defaultValue: "Yes",
elementAttributes: {
maxlength: "10",
},
},
],
});
});
Where can I put a command which will give me the row number.?
I need to update a db record. basiclaly, set display = either yes or no.
My preference was a tickCross but couldnt work that out either.
Any assistance is greatly appreciated.
using Tabulator 4.9
After a bit more thinking... it was actually blindingly simple.
{
title: "Active",
field: "display",
editor: "select",
editorParams: {
values: ["Yes", "No"], //create list of values from all values contained in this column
sortValuesList: "asc", //if creating a list of values from values:true then choose how it should be sorted
defaultValue: "Yes", //set the value that should be selected by default if the cells value is undefined
elementAttributes: {
maxlength: "10", //set the maximum character length of the input element to 10 characters
},
},
cellEdited: function(row) {
let thisrow = row.getData()
alert(thisrow.id + "is now " + thisrow.display)
}
},
Added here to help others who might be banging their head against a wall. I was obviously thinking of something more complicated.
sap.ui.core.Element.extend("custom.barNlineChartControl", { metadata : {
properties : {
"Job" : {type : "string", group : "Misc", defaultValue : null},
"Threshold" : {type : "int", group : "Misc", defaultValue : null},
}
}});
sap.ui.core.Control.extend("control.barNlinechart", {
/* the control API */
metadata : {
aggregations : {
"items" : { type: "custom.barNlineChartControl", multiple : true, singularName : "item"}
},
events: {
"select" : {},
"selectEnd": {}
}
},
//D3 Code below:
onAfterRendering: function() {
var that = this;
/* get the Items aggregation of the control and put the data into an array */
var aItems = this.getItems();
var data = [];
for (var i=0;i<aItems.length;i++){
var oEntry = {};
for (var j in aItems[i].mProperties) {
oEntry[j]=aItems[i].mProperties[j];
}
data.push(oEntry);
}
alert(JSON.stringify(data));
Code of view & control
multiBarLineGraph = new control.barNlinechart({
layoutData: new sap.ui.layout.GridData({span: "L12 M12 S12"}),
items: {
path : "/genericData",
template : new custom.barNlineChartControl({Job:"{Job}",Threshold:"{Threshold}"}),
}
}),
var multiBarData = {
"genericData":[
{
"Job": "Doctor",
"Threshold": 45,
"Hospital1": 30,
"Hospital2": 100,
"Hospital3": 90,
},
{
"Job": "Teacher",
"Threshold": 65,
"School1": 60,
"School2": 75,
},
]};
When the alert in d3 code executes I get Job & Threshold but other data from JSON array are missing which is obvious as the properties set here only accept job and threshold. As the JSON is dynamic how to write custom control so that I can pass the complete data to control everytime no matter how dynamic the data be.
You could use type: "any" for your items and dont use the element custom.barNlineChartControl at all:
Edit: as an aggregation controls the lifetime of the aggregated objects you have to use a property in this case.
sap.ui.core.Control.extend("control.barNlinechart", {
/* the control API */
metadata : {
properties : {
"items" : { type: "any" }
},
events: {
"select" : {},
"selectEnd": {}
}
},
and then in your view:
multiBarLineGraph = new control.barNlinechart({
layoutData: new sap.ui.layout.GridData({span: "L12 M12 S12"}),
items: { path : "/genericData" }
}),
this.getItems() would return an array of whatever has been been set / bound.
While creating mixed chart in Zingchart we can pass the type attribute values with values array. But I'm not sure when reading data from CSV how this can be achieved.
I want to create mixed chart as on fiddle link below but data is to be read from a csv file.
var myConfig =
{
"type":"mixed",
"series":[
{
"values":[51,53,47,60,48,52,75,52,55,47,60,48],
"type":"bar",
"hover-state":{
"visible":0
}
},
{
"values":[69,68,54,48,70,74,98,70,72,68,49,69],
"type":"line"
}
]
}
zingchart.render({
id : 'myChart',
data : myConfig,
height: 500,
width: 725
});
<script src="https://cdn.zingchart.com/zingchart.min.js"></script>
<div id="myChart"></div>
I put together a demo for you using the sample data you provided in one of your related questions. If you go to this demo page and upload the CSV you originally provided, you should get this chart:
ZingChart includes a CSV parser for basic charts, but a more complex case like this requires a bit of preprocessing to get your data where it needs to be. I used PapaParse for this demo, but there are other parsing libraries available.
Here's the JavaScript. I'm using a simple file input in the HTML to get the CSV.
var csvData;
var limit = [],
normal = [],
excess = [],
dates = [];
var myConfig = {
theme: "none",
"type": "mixed",
"scale-x": {
"items-overlap":true,
"max-items":9999,
values: dates,
guide: {
visible: 0
},
item:{
angle:45
}
},
"series": [{
"type": "bar",
"values": normal,
"stacked": true,
"background-color": "#4372C1",
"hover-state": {
"visible": 0
}
}, {
"type": "bar",
"values": excess,
"stacked": true,
"background-color": "#EB7D33",
"hover-state": {
"visible": 0
}
}, {
"type": "line",
"values": limit
}]
};
/* Get the file and parse with PapaParse */
function parseFile(e) {
var file = e.target.files[0];
Papa.parse(file, {
delimiter: ",",
complete: function(results) {
results.data.shift(); //the first array is header values, we don't need these
csvData = results.data;
prepChart(csvData);
}
});
}
/* Process the results from the PapaParse(d) CSV and populate
** the arrays for each chart series and scale-x values
*/
function prepChart(data) {
var excessVal;
//PapaParse data is in a 2d array
for (var i = 0; i < data.length; i++) {
//save reference to your excess value
//cast all numeric values to int (they're originally strings)
var excessVal = parseInt(data[i][4]);
//date, limit value, and normal value can all be pushed to their arrays
dates.push(data[i][0]);
limit.push(parseInt(data[i][1]));
normal.push(parseInt(data[i][3]));
/* we must push a null value into the excess
** series if there is no excess for this node
*/
if (excessVal == 0) {
excess.push(null);
} else {
excess.push(excessVal);
}
}
//render your chart
zingchart.render({
id: 'myChart',
data: myConfig,
height: 500,
width: 725
});
}
$(document).ready(function() {
$('#csv-file').change(parseFile);
});
i have 100000 data in my grid at first request it showing only 10 after clicking on the page selecter it showing next data............plz answer why enhanced grid displaying only 10 rows at first time
require([ "dojo/_base/lang","dojox/grid/EnhancedGrid",
"dojox/grid/enhanced/plugins/Pagination","dojo/data/ItemFileReadStore",
"dijit/form/Button","dojo/request/xhr", "dojo/dom",
"dojo/dom-construct", "dojo/json", "dojo/on", "dojox/grid/cells/dijit",
"dojo/domReady!" ],
function(lang,EnhancedGrid,Pagination,ItemFileReadStore,Button,xhr, dom, domConst, JSON, on) {
xhr("myservernameaddress/GridExample/string", {
handleAs : "json"
}).then(
function(dataa) {
/* domConst.place("<p>response: <code>"
+ JSON.stringify(dataa) + "</code></p>",
"output"); */
/* domConst.place("<p>response: <code>"
+ JSON.stringify(dataa) + "</code></p>",
"output"); */
var mydata=dataa;
var yourStore = new dojo.data.ItemFileReadStore({
data: {
identifier: "sno",
/* items: mydata.aa */
items:mydata
}
});
grid = new EnhancedGrid({
store : yourStore,
selectable:true,
query : {
sno : "*"
},
structure : [ {
name : "SNO",
field : "sno",
width : "100px"
},{
name : "SNAME",
field : "sname",
width : "100px",
editable:true
},{
name : "SALARY",
field : "salary",
width : "200px",
editable:true
} ],
rowSelector: '20px',
plugins: {
pagination: {
pageSizes: ["25","50","100"],
description: true,
sizeSwitch: true,
pageStepper: true,
gotoButton: true,
maxPageStep: 2,
position: "bottom",
search:true
}
}
});
grid.placeAt("myGrid");
grid.startup();
}, function(err) {
alert("error");
}, function(evt) {
});
});
can avoid enhanced grid default 10 rows size by this please check it
defaultPageSize: 50,.
use this in pagination plugin...
I think this might help you out:
When you define your grid you can set autoHeight
I've copied this from dojo:
autoHeight
If true, automatically expand grid’s height to fit data. If numeric,
defines the maximum rows of data displayed (if the grid contains
less than autoHeight rows, it will be shrunk).
For more informations have a look : http://dojotoolkit.org/reference-guide/1.7/dojox/grid/DataGrid.html#datagrid-options
Update1
Maybe this former Post makes it clearer: DataGrid's rowsPerPage attribute not working
Regards, Miriam
My json looks like this
var jStore = {
" identifier": "cpu",
"items": [
{
"Time": "02:52",
"Used": 100,
"Idle": 0
},
{
"Time": "02:57",
"Used": 100,
"Idle": 0
}....
]
};
I create a datastore and adding to the chart,
var realStore = new dojo.data.ItemFileReadStore({data: jStore});
var Ser = new dojox.charting.DataSeries(realStore, {query: {Idle: "*"} }, "Idle");
var Ser1 = new dojox.charting.DataSeries(realStore, {query: {Used: "*"} }, "Used");
chart.addAxis("x");
chart.addAxis("y", { vertical: true, fixLower: "major", fixUpper: "major" });
chart.addSeries("Used ",Ser);
chart.addSeries("Idle",Ser1);
the chart is rendering properly but the xaxis values are taken default (1,2,3..).
But i need to give the axis value as the Time in my json.
Xaxis should be like 02:52,02:54,02:56
Also in the tool tip it showing only the value i have given the series. But i would like to add the value+ xaxis value. Say for example 100 at 02:54.
Could someone help me on this.
You can specify custom labels for each value as follows:
mychart.addAxis("x", {fixLower: "minor", fixUpper: "minor", natural: false,
font: "normal normal 10pt Arial",
labels: [{value: 1, text: "Q2 FY11"},
{value: 2, text: "Q3 FY11"},
{value: 3, text: "Q4 FY11"},
{value: 4, text: "Q1 FY12"}]
});
You can also specify custom tooltip text as follows:
mychart.addSeries("Series A", [{ y: 2.3, tooltip: "FFFF"}, { y: 3.5, tooltip: "GGGG"}]);
In your case since you are using a store, the JSON that feeds the store needs to have a "tooltip" attribute for each value - dojo will use that to populate the tooltip