The highstock column range with dataGrouping enabled seems not to be computing dataAggregation correctly.
The aggregated values seem to change when changing the range.
March 2014 will show different values if scrolling more towards the right.
Code and jsfiddle:
dataGrouping: {
enabled: true,
approximation: function() {
const indices = _.range(this.dataGroupInfo.start, this.dataGroupInfo.start + this.dataGroupInfo.length);
const low = _.min(indices.map(i => this.options.data[i][1]));
const high = _.max(indices.map(i => this.options.data[i][2]));
return [low, high];
},
groupPixelWidth: 50
}
See jsfiddle
The columns are changed only when the navigator does not start from the beggining - and that happens because the way you defined approximation callback.
dataGroupInfo contains information according to the visible points (which fall into x axis range, cropped points) in the chart, not all the points - so to have proper indices for the initial data, you need to add this.cropStart - it is the index from which points are visible.
approximation: function() {
const start = this.cropStart + this.dataGroupInfo.start;
const stop = start + this.dataGroupInfo.length;
const indices = _.range(start, stop);
const low = _.min(indices.map(i => this.options.data[i][1]));
const high = _.max(indices.map(i => this.options.data[i][2]));
return [ low, high ];
},
example: https://jsfiddle.net/12o4e84v/7/
The same functionality can be implemented easier
approximation: function(low, high) {
return [ _.min(low), _.max(high) ];
}
example: https://jsfiddle.net/12o4e84v/8/
Or even simpler:
approximation: 'range',
However, by default approximation for columns is set to range, so you do not have to do it manually.
example: https://jsfiddle.net/12o4e84v/9/
Related
It has two inputs and one output.
Input: [Temperature, Humidity]
Output: [wattage]
I learned as follows
Even after 5 million rotations, it does not work properly.
Did I choose the wrong option?
var input_data = [
[-2.4,2.7,9,14.2,17.1,22.8,281,25.9,22.6,15.6,8.2,0.6],
[58,56,63,54,68,73,71,74,71,70,68,62]
];
var power_data = [239,224,189,189,179,192,243,317,224,190,189,202];
var reason_data = tf.tensor2d(input_data);
var result_data = tf.tensor(power_data);
var X = tf.input({ shape: [2] });
var Y = tf.layers.dense({ units: 1 }).apply(X);
var model = tf.model({ inputs: X, outputs: Y });
var compileParam = { optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError }
model.compile(compileParam);
var fitParam = {
epochs: 500000,
callbacks: {
onEpochEnd: function (epoch, logs) {
console.log('epoch', epoch, logs, "RMSE --> ", Math.sqrt(logs.loss));
}
}
}
model.fit(reason_data, result_data, fitParam).then(function (result) {
var final_result = model.predict(reason_data);
final_result.print();
model.save('file:///path/');
});
The following is the result for 5 million times.
It should be the same as power_data , but it failed.
What should I fix?
While there is rarely one simple reason to point to when a model doesn't perform the way you would expect, here are some options to consider:
You don't have enough data points. Twelve is not nearly sufficient to get an accurate result.
You need to normalize the data of the input tensors. Given that your two features [temperature and humidity] have different ranges, they need to be normalized to give them equal opportunity to influence the output. The following is a normalization function you could start with:
function normalize(tensor, min, max) {
const result = tf.tidy(function() {
// Find the minimum value contained in the Tensor.
const MIN_VALUES = min || tf.min(tensor, 0);
// Find the maximum value contained in the Tensor.
const MAX_VALUES = max || tf.max(tensor, 0);
// Now calculate subtract the MIN_VALUE from every value in the Tensor
// And store the results in a new Tensor.
const TENSOR_SUBTRACT_MIN_VALUE = tf.sub(tensor, MIN_VALUES);
// Calculate the range size of possible values.
const RANGE_SIZE = tf.sub(MAX_VALUES, MIN_VALUES);
// Calculate the adjusted values divided by the range size as a new Tensor.
const NORMALIZED_VALUES = tf.div(TENSOR_SUBTRACT_MIN_VALUE, RANGE_SIZE);
// Return the important tensors.
return {NORMALIZED_VALUES, MIN_VALUES, MAX_VALUES};
});
return result;
}
You should try a different optimizer. Adam might be the best choice, but for a linear regression problem such as this, you should also consider Stochastic Gradient Descent (SGD).
Check out this sample code for an example that uses normalization and sgd. I ran your data points through the code (after making the changes to the tensors so they fit your data), and I was able to reduce the loss to less than 40. There is room for improvement, but that's where adding more data points comes in.
I just started using Chart.js, and I have an issue.
First of all, what I need to display in my simple line chart is something like this:
This is my current chart, on the Y-axis I have possible grades (for student's exams), ranging from 1 to 6.
On the X-axis I have the possible Points that can be achieved in a given exam.
Now it gets more complicated, this chart is being updated based on the inputs and selection of a dropdown.
Grades (Y-axis) can be of 5 increment types: Integer, Halves, Quarters, Decimal and Hundredths
This is chosen with a dropdown, default value is Quarters, meaning with Quarters selected my Array of Numbers for the grades would look like:
grades = [1, 1.25, 1.50, 1.75, 2....., 5.75, 6]
meanwhile with Hundredths selected, it would look like:
grades = [1, 1.01, 1.02, 1.03, .... 5.97, 5.98, 5.99, 6]
And for each grade, a set amount of Points (X-axis) is needed to achieve it.
The points needed are calculated with a formula that I put in a function:
mySecretFormula(grades: Array<Number>) {
grades.forEach(g => {
const currentPoints = g * mySecretFormula;
this.points.push(currentPoints);
}
so basically I pass my Grades in this function, and it returns another Array of numbers with the same number of elements (as each grade corresponds to a score)
example, if I had selected Integer Grades, meaning my grade array looks like:
grades = [1, 2, 3, 4, 5, 6]
the return I would get for my scores would be:
scores = [0, 5, 10, 15, 25, 30]
if the max points were set to 30 (the max score is defined in an input)
Then finally I used chart.js to display my data like this:
this.canvas.data.labels = this.points;
this.canvas.data.datasets[0].data = this.grades;
this.canvas.update();
so everytime I change the dropdown regarding the increments of the grades, this function gets fired and updates the chart.
Let's say it's working, but it's far from optimal.
What I want to achieve is simple.
It should look like this:
This is what the Chart looks like when I select Integer grades, so only 6 different grades and 6 different scores.
I want the Chart to always look like this, no matter what increment is selected,
so always 5 or 6 grid lines and always the same tick points for the X-axis.
Then if the current increment selected is Integer, I'll have only 6 intersection, but if I were to swap to Decimal or Hundredths, so with a lot of intersections, the chart looks exactly like this, BUT when you hover on the line with the mouse, I'll get the tooltip for each valid intersection.
Now if I swap to Decimal increments, my Chart updates into this:
(ignore the rounding, forgot to round them to 2 decimals)
so as you see the tickpoints change, the grid width changes, and the height of the whole chart changes.
But the intersections work correctly, if I hover the mouse along the line, I ll get a tooltip for each point for these pairs:
decimal increments equal to:
grades = [1, 1.1, 1.2, 1.3, 1.4, .... 5.9, 6]
points = [0, 0.7, 1.4, 2.1, 2.8, .... 34.3, 35]
so to achieve this same result, BUT with the chart that is always the same, always the same tick points and height and width, because the range of the grades and the scores will always be the same, but depending on the increment chosen, there could be from a minimum of 6 intersection (integer) to over 500 intersections (hundredths)!
Hope I made myself clear, thank you very much
Edit: managed with your help to add a custom tooltip on 2 lines with this:
afterBody: function([tooltipItem], data): any {
const multistringText = ["Points: " + tooltipItem.xLabel];
multistringText.push("Grade: " + tooltipItem.yLabel);
return multistringText;
}
works perfectly, but how can I now remove the original tooltip string above it? look the image, above my 2 lines custom tooltip I have another line that I want to hide!
And finally, always looking at this image, how can I make the grid lines on the X-axis of the same width? as you can see the first 4 are bigger than the last! I want them to be always the same width! thank you
I advise to convert your chart to xy chart, set axis options and customize tooltips
var ctx = document.getElementById("myChart").getContext('2d');
var precision = 0;
var data = getData();
var myChart = new Chart(ctx, {
type:"scatter",
data: {
datasets: [{
label: 'grades',
data: data,
fill: false,
pointRadius: 0,
pointHitRadius: 3
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
max:6
}
}],
xAxes: [{
ticks: {
beginAtZero:true,
max:35,
stepSize: 1,
callback: function(value){
if (value < 5){
return value;
} else {
if (value% 5 === 0){
return value;
}
}
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = [
'X: ' + tooltipItem.xLabel.toFixed(precision),
'Y: ' + tooltipItem.yLabel.toFixed(precision)
];
return label;
}
}
}
}
});
function getData(){
var step = 10**-precision;
var arr = [];
for (var i=0; i<=6; i+=step){
arr.push({y: i, x: 35*i/6})
}
return arr;
}
setTimeout(function(){
precision = 1;
myChart.data.datasets[0].data = getData();
myChart.update();
}, 2000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.js"></script>
<canvas id="myChart" width="100" height="100"></canvas>
I'm trying to set max value axis using dataprovider, since I'm dynamically loading data in my bar chart I need to see the "progress" on one of the bars compared to the one that is supposed to be the total.
How do I achieve this?
I' tried with:
"valueAxes": [
{
"id": "ValueAxis-1",
"stackType": "regular",
"maximum": myDataProviderAttribute
}
But no luck.
Any suggestion will be much apreciated.
I've submited a ticket to AmCharts support and got this feedback:
You can set max before the chart is initialized based on the data you have, inside addInitHandler. Here is an example for a simple column chart:
AmCharts.addInitHandler(function(chart) {
// find data maximum:
var min = chart.dataProvider[0].visits;
for (var i in chart.dataProvider) {
if (chart.dataProvider[i].visits > max) {
max = chart.dataProvider[i].visits;
}
}
// set axes max based on value above:
chart.valueAxes[0].maximum = max + 100;
chart.valueAxes[0].strictMinMax = true;
});
You may need to use strictMinMax as above to enforce the value:
https://docs.amcharts.com/3/javascriptcharts/ValueAxis#strictMinMax
Example of setting minimum inside addInitHandler:
https://codepen.io/team/amcharts/pen/b4be8cb4e3c073909860720e0909a876?editors=1010
If you refresh the data, or use live data, then before you animate or validate the chart to show the updated data, you should recalculate the max and, if you want the axis to change then use chart.valueAxes[0].maximum = max; where max is something you calculate based on data input.
Here is an example:
function loop() {
var data = generateChartData();
chart.valueAxes[0].maximum = max;
// refresh data:
chart.animateData(data, {
duration: 1000,
complete: function () {
setTimeout(loop, 2000);
}
});
}
The lines above were used in this example:
https://codepen.io/team/amcharts/pen/d0d5d03cfdcc2cc256e28ec52ad8b95c/?editors=1010
I have one long unixtime, value Array which is used to initiate a flot chart, and some buttons to change the scale, what I can't seem to be able to do is get Y-axis to scale with the change in X-scale.
Here is an example chart:
http://jsfiddle.net/U53vz/
var datarows = //Data Array Here
var options = { series: { lines: { show: true }, points: { show: true } },
grid: { hoverable: true,clickable: false, },
xaxis: { mode: "time", min: ((new Date().getTime()) - 30*24*60*60*1000), max: new Date().getTime(), }
};
function castPlot() {
window.PLOT = $.plot("#placeholder", [{ data: dataRows }], options
);
};
In the official example scaling is automatic and unspecified on the Y-axis:
http://www.flotcharts.org/flot/examples/axes-time/index.html
The only alternative I can think of is looping through the dataset and calculating new Y min/max on each button press. Unless I am breaking some very obvious default function.
When calculating y-scale, flot does not look at only the "viewable" data but the whole dataset. Since the data points are still present, the y min/max respects them. So your options are:
Subset the series data down to the desired range and let flot scale both x and y.
As you suggested, calculate your own min/max on the y axis.
If you plot get any more complicated than it is now (especially if you start setting up click/hover events on it), I would also recommend you switch to redrawing instead of reiniting your plot.
var opts = somePlot.getOptions();
opts.xaxes[0].min = newXMin;
opts.xaxes[0].max = newXMax;
opts.yaxes[0].min = newYMin;
opts.yaxes[0].max = newYMax;
somePlot.setupGrid();
somePlot.draw();
EDITS
Here's one possible solution.
Dygraphs allows easy display of time series...
However, if my data contains only two data points, it automatically fills the gaps in X axis with hours. Is it possible to disable this functionality?
I searched and tried many options but not found anything useful.
Example might be the 'Time Series Drawing Demo' from the gallery - if executed on only few datapoints, it fills the 'gaps' with hours.
This is a good example:
g = new Dygraph(document.getElementById('plot'),"a,b\n2008-12-01,0.9\n2008-12-02,0.3\n2008-12-03,0.7\n")
UPDATE- this seems to be working:
ticker: function(a, b, pixels, opts, dygraph, vals) {
var chosen = Dygraph.pickDateTickGranularity(a, b, pixels, opts);
if(chosen==12) chosen=13;
if (chosen >= 0) {
return Dygraph.getDateAxis(a, b, chosen, opts, dygraph);
} else {
// this can happen if self.width_ is zero.
return [];
}
};
Your issue is not that you have two points, but that your points cover a certain amount of time. Dygraphs tries to calculate the best granularity for the x axis tick marks in a given set of data.
One way to modify the default calculation is by using the pixelsPerLabel option.
Example: http://jsfiddle.net/kaliatech/P8ehg/
var data = "a,b\n2008-12-01,0.9\n2008-12-02,0.3\n2008-12-03,0.7\n";
g = new Dygraph(document.getElementById("plot"), data, {
axes: {
x: {
pixelsPerLabel: 100
}
}
});
This requires hard coding a pixel width though, and it is still ultimately dependent on the data set that you are graphing. A more flexible approach might be to use the ticker option, allowing you to supply your own function for calculating label granularity. See the documentation and built-in functions of dygraph-tickers.js.
See also:
How to set specific y-axis label points in dygraphs?
EDIT: Example using ticker. This requires that you are familiar with the data and the data range is somewhat constant, otherwise you could end up with unreadable x-axis labels.
var g = new Dygraph(document.getElementById("demodiv3"), data(), {
title: 'Example for changing x-axis label granularity 3',
axes: {
x: {
ticker: function(a, b, pixels, opts, dygraph, vals) {
var chosen = Dygraph.pickDateTickGranularity(a, b, pixels, opts);
//Force to DAILY if built-in calculation returned SIX_HOURLY
//if(chosen==Dygraph.SIX_HOURLY)
// chosen=Dygraph.DAILY;
//or
//Force to DAILY always
chosen = Dygraph.DAILY;
if (chosen >= 0) {
return Dygraph.getDateAxis(a, b, chosen, opts, dygraph);
} else {
return [];
}
}
}
}
});