How can I dynamically graph points from a table? The table has two columns dedicated to the x and y coordinates and an additional column for duration.
Example
Say I have the table shown below. The Date and Effort columns would compose the x,y coordinates (Timestamp, Effort) for a line chart. This line chart would have data-points based on cumulative totals from the Timestamp and Effort columns. The Duration column would determine how long the entry had an impact on the data-points of the line chart. So based on this table, I would want a line chart with the coordinates listed under Data-Points.
Timestamp Effort Duration
4/13/2016 12:13:12.15 10 100
4/13/2016 12:13:12.80 12 100
4/13/2016 12:13:13.15 30 100
4/13/2016 12:13:13.80 50 100
Data-Points:
(4/13/2016 12:13:12.15, 10)
(4/13/2016 12:13:12.80, 22)
(4/13/2016 12:13:13.15, 42)
(4/13/2016 12:13:13.80, 80)
One of the Highcharts demos shows precisely how you can accomplish this using a static HTML table: http://www.highcharts.com/demo/column-parsed.
I've created a fiddle using your sample data and plotted it as a line chart, per your requirement: http://jsfiddle.net/brightmatrix/qgvkp0t0/
Your data table would be coded similar to the following:
<table id="datatable">
<thead>
<tr>
<th>Time</th>
<th>Effort</th>
</tr>
</thead>
<tbody>
<tr>
<th>4/13/2016 12:13:12.15</th>
<td>10</td>
</tr>
<tr>
<th>4/13/2016 12:13:12.80</th>
<td>22</td>
</tr>
<tr>
<th>4/13/2016 12:13:13.15</th>
<td>42</td>
</tr>
<tr>
<th>4/13/2016 12:13:13.80</th>
<td>80</td>
</tr>
</tbody>
</table>
You would then create your chart using the following code. Note the data attribute that calls to your HTML table's ID:
$(function () {
$('#container').highcharts({
data: {
table: 'datatable'
},
chart: {
type: 'line'
},
title: {
text: 'Data extracted from a HTML table in the page'
},
xAxis: {
type: 'category'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Units'
}
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' + this.point.y;
}
}
});
});
One thing to note: without any modifications, your timestamp data would be read into your x-axis as a datetime type. This would leave you with empty points for those dates or times in between.
In my example, I explicitly set the x-axis type to category, so that there is exactly one plot on the chart for each data point.
Now, to get the x-axis labels to appear in a more readable format, you can explore the xAxis.labels.formatter attribute (http://api.highcharts.com/highcharts#xAxis.labels.formatter) and the Highcharts.dateFormat() function (http://api.highcharts.com/highcharts#Highcharts.dateFormat).
Update (July 13, 2016): I've been able to solve the original poster's requirement of adding cumulative points based on data in an HTML table, but not the removal of dates that are older than the current data point's duration. My modifications are below.
In order to take the data from an HTML table and work with it prior to plotting the chart, I used the data.columns attribute (see http://api.highcharts.com/highcharts#data.columns).
Before your chart options are declared, I wrote this code to go through the HTML table and add the content to arrays.
// read through the HTML table and calculate cumulative effort
// solution inspired by:
// 1. http://stackoverflow.com/questions/3248869/how-do-i-get-data-from-a-data-table-in-javascript
// 2. http://stackoverflow.com/questions/10057226/how-to-get-html-table-td-cell-value-by-javascript
// set the series name as the default; this is the first entry read by data.columns
var seriesTime = ['Time'];
var seriesEffort = ['Effort'];
var seriesDuration = ['Duration'];
var table = document.getElementById('datatable');
var noRows = table.rows.length;
// go through the table and assign values to the series arrays
// start with the second row (r = 1) to omit the table's header
for (var r = 1; r < noRows; r++) {
seriesTime.push(Date.parse(table.rows[r].cells[0].innerHTML));
seriesEffort.push(parseInt(table.rows[r].cells[1].innerHTML));
seriesDuration.push(parseInt(table.rows[r].cells[2].innerHTML));
}
I then use those arrays as follows:
data: {
// refer to demo link at: http://api.highcharts.com/highcharts#data.columns
columns: [
seriesTime, // categories
seriesEffort // first series
]
},
The way this works is that the first item expected in these arrays is the series name, which is why I have as the default value when the arrays are first declared.
Now, once you have the data in arrays, you would need to run through them to calculate your cumulative totals and subtract those whose duration has passed. Unfortunately, I wasn't successful on the duration piece. Here's what I wrote in my fiddle:
// next, go through the series arrays and tally up the cumulative totals based on duration
for (var t = 2; t < seriesTime.length; t++) {
seriesEffort[t]+=seriesEffort[t-1];
for (var e = 1; e < seriesEffort.length; e++) {
if (seriesTime[e] < seriesTime[t] - seriesDuration[t]) { // NOTE: this is where I'm getting stuck
seriesEffort[t]-=seriesEffort[e];
} else {
//seriesEffort[t]+=seriesEffort[e];
}
}
}
I hope this gets you closer to your solution. I apologize that I couldn't get this 100% mocked up for you.
I ended up preprocessing the data. First, I got all of the data points into the series cumulatively. Next, I updated the datetime x values with the durations. Finally, I subtracted each individual y value whose x value was less than the cumulative data point's x value.
Related
I have a chart that is filled with content from HTTP requests. And data always come with this code
chartComponent.chartInstance
.getDefaultAxisX()
.onAxisInteractionAreaMouseDragStop((axis: Axis, event: MouseEvent, button: number, startLocation: Point): void => {
if (axis.getInterval().start / 2 < 10000) {
this.chartService.loadHistorySeriesData(chartComponent.selectedTimeRangeMinutes).subscribe((data: SeriesMapResponse) => {
this.chartService.addNewDataToTheSeriesInstances(chartComponent.chartId, chartComponent.seriesInstances);
})
}
})
So I have to implement conditions like if the offset is less than some number load me new data... Like pagination on the Instagram feed but with carts. How can I get this offset, I tried a lot of approaches and nothing works.
You can get the "left side of chart" information by referencing the X Axis interval start, like follows:
Axis.getInterval().start
You can probably get the needed information of "last old point" by LineSeries.getXMin() method.
In icCube reporting tool 6.1
is there a possibility to retrieve data like a cellValue(rowindex,columnindex) while i'm in a diffrent widget like chart box for example and wanna get a cell value from different table in the report? with Widget's JavaScript...? and also do other functions like column count or row count as i'm in the Widget's JavaScript of the actual table...?
UPDATED QUESTION
Example:
i wanna be able for example to get in the chart palette by Expression to
retrieve the number of the 1 row and column 2 (value 12)
and then for the example i wanna use this number and see if the number is greater then 10 then i will want the chart color to be Green and if less the blue... so i wanna know how to retrieve a specific cell from the table when the chart loads... and the chart will wait for a click on row from the table
so it will load after the table... so the rendering problem you've mentioned you have won't be a problem here.
You can share Table's data with global variable.
In On Data Received hook:
/**
* Return data object
*/
function(context, data, $box) {
window.ic3Data = {};
window.ic3Data.tableContext = context;
return data;
}
Then you can easily use PublicTableContext API from table widget in other charts. For example in a coloring expression for an AmChart:
return window.ic3Data.tableContext.cellValue(0,1) > 10 ? 'green' : 'red';
I am building a Meteor app where I have to display a variable length table of calculation results. The calculations are done in Meteor and displayed in cells of the rows - each cell in the table is a numeric result based on a complex calculation. Finally I want to display a total calculation for each row.
calcresult1 calcresult2 row1sum
calcresult3 calcresult4 row2sum
:
(variable number of rows)
How can I efficiently calculate the row sums reactively from the calcresults on each row?
Can I setup a single session variable, sum to it when rendering the cells in the row, and then flush the total as each rowsum is to be rendered?
If the rows have the same number of cells each time, you could pass the results from each cell helper to a final helper.
<template name="calcTable">
<table>
{{#each calcRow}}
<tr>
<td>{{calcresult1}}</td>
<td>{{calcresult2}}</td>
<td>{{rowsum calcresult1 calcresult2}}</td>
</tr>
{{/each}}
</table>
</template name="calcTable">
-
Template.calcTable.helpers({
calcresult1: function() {
return result;
},
calcresult2: function() {
return result;
},
rowsum: function(calcresult1, calcresult2) {
return calcresult1 + calcresult2;
}
});
We're displaying time series data (utilisation of a compute resource, sampled hourly over months) on a stacked area chart using D3.js:
d3.json("/growth/instance_count_1month.json", function( data ) {
data.forEach(function(d) {
d.datapoints = d.datapoints.map(
function(da) {
// NOTE i'm not sure why this needs to be multiplied by 1000
return {date: new Date(da[1] * 1000),
count: da[0]};
});
});
x.domain(d3.extent(data[0].datapoints, function(d) { return d.date; }));
y.domain([0,
Math.ceil(d3.max(data.map(function (d) {return d3.max(d.datapoints, function (d) { return d.count; });})) / 100) * 100
]);
The result is rather spiky for my tastes:
Is there an easy way to simplify the data, either using D3 or another readily available library? I want to reduce the spikiness, but also reduce the volume of data to be graphed, as it will get out of hand.
I have a preference for doing this at the UI level, rather than touching the logging routines (even though redundant JSON data will have to be transferred.)
You have a number of options, you need to decided what is the best way forward for the type of data you have and the needs of it been used. Without knowing more about your data the best I can suggest is re-sampling. Simply report the data at longer intervals ('rolling up' the data). Alternatively you could use a rolling average or look at various line smoothing algorithms.
I'm using jsRender and I wanted to display my data as columns rather then the rows I am returning. I want to pivot the data - is this something that can be done with jsRender. I can't get the data pivoted in SQL so my only option is to do it myself.
This is basically what I am after. I want to write the column names myself.
Header | Row 1 | Row 2 | Row 3 |
Column Blah | Row data | Row data | Row data
More blah | Row data | Row data | Row data
I have tried to use the {{for}} loop for table cell but I just don't know where to start.
UPDATE:
After Boris's suggestion I have tried the suggested code. Although I as it's not formatting properly I have included it here.
This is an excerpt of my JSON source:
{
"Layers": {
"Layer": [
{
"#LayerID": "1",
"RiskRef": {
"#ColVal": "Contract/Section Number",
"#text": "PUSNA11000392/1"
},
"ContractStatus": {
"#ColVal": "New, Renewal or NTU?",
"#text": "New"
},
"AdjustRate": {
"#ColVal": "Adjustable Rate",
"#text": "0.53%"
},
And my jsRender javaScript code is:
<script id="xolDetailsTemplate" type="text/x-jsrender">
{{for Layers}}
{{for >#data["Layer"]}}
<td>{{>#data["#LayerID"]}}</td>
{{/for}}
{{/for}}
</script>
If your data is
model = { people: [{firstName: "Jo", lastName: "Colby"}, ...] }
you can use the following template to render a people array, pivoted rows to columns:
<tbody>
<tr><td>First Name</td>{{for people}}<td>{{>firstName}}</td>{{/for}}</tr>
<tr><td>Last Name</td>{{for people}}<td>{{>lastName}}</td>{{/for}}</tr>
</tbody>
In your comment below you say that your data is has field names like "#foo". Lets consider this example:
model = { "people": [{"#firstName": "Jo", "#lastName": "Colby"}}, ...] }.
You can render this with a template as follows:
<tbody>
<tr><td>First Name</td>{{for people}}<td>{{>#data["#firstName"]}}</td>{{/for}}</tr>
<tr><td>Last Name</td>{{for people}}<td>{{>#data["#lastName"]}}</td>{{/for}}</tr>
</tbody>
If the field name has non JavaScript name characters, such as "#" that is where you need to use the syntax #data["#xxx"].