plotting negative numbers with flot / jquery - javascript

I know Im missing something retarded here... All Im trying to do is graph f(x) = 2500 for a range of x from -75 to 75. This should make a horizontal line. Right now, I think its a misunderstanding on my part of some specifics of arrays. It starts at 0 and goes to 75 fine, it does not graph lower than 0. (I get half the line)
for(x = -75; x<75; x++)
{
a_const[x] = [x, 2250];
}
Im about certain the problem is there. Heres my .plot function, just to be sure.
$.plot(
$("#mydiv"),
[
//{label : "f(x) = x^2", data : a_exp},
//{label : "f(x) = sqrt(x)", data : a_sqroot},
//{label : "f(x) = 3root(x)", data : a_cuberoot}
{label: "constant", data : a_const}
],
{
//yaxis: {min:-5000},
xaxis: {min:-75},
yaxis: {min:-1000},
yaxis: {max:4000},
grid: {hoverable:true, clickable:true },
series: { points: {show:true}, lines:{show:true}}
}
);

You can't have negative array subscripts. Just do
for (x = -75, x < 75; x++ )
{
a_const.push( [x,2250] );
}
This will end up with elements with indices from 0 to 149, but containing pairs from [-75,2250] to [75,2250].

NM, figured it out. a[-75] a[-74] (etc...) is not seen by flot do to it being negative. Solution :
for(x = -75; x<75; x++)
{
a_const[x+75] = [x, 2250];
}
Would be nice to find / quote the official rule on negative indices.

Related

Chart's Y-axis does not re-render properly in Billboard.js

There is a chart generated in Billboard.js which has the possibility to introduce custom values as minimum and maximum for the Y-axis.
The problem that I encounter is that even the axis is changing when the values are modified, they are not very close to the introduced value.
For example this screenshot:
On the maximum value I don't think it is a great problem. But on the minimum one it is. The default value was 0, after there was introduced 300 I would expect to start from a closer value to it, not 0 as before. The Y-axis just moved few pixels after that change.
The html code:
<input type="number" ng-change="$ctrl.updateY('max')" ng-model="$ctrl.max">
The JS code:
updateY(val) {
if(val=== 'max') {
if(this.max || this.max === 0) {
this.maxValue = this.max;
}
} else {
if(this.min || this.min === 0) {
this.minValue = this.min;
}
}
const maxNumber = Number(this.maxValue);
const minNumber = Number(this.minValue);
this.lineView.chart.axis.range({max: {y: maxNumber}, min: {y: minNumber}});
this.resetY = false;
}
This is the code for max, same one for min.
I think that maybe the problem is from range, it doesn't generate the Y-axis with the values introduced.
Any suggestions?
It is rendering much closer to the introduced values if it is added a padding in bb.generate:
const chart = bb.generate({
...
"axis": {
...
"y": {
...
"padding" : {
top: 0,
bottom: 10
}
...

Generate random coordinates (excluding some specific ones)

I have a multidimensional array, which I'm using as a very simple coordinate system. To generate random coordinates, I came up with this very simple function:
var coords = [
[1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1],
[0,0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1],
[1,0,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1],
[0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,1],
[1,0,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,0,1,1,0,1,1]
];
function getRandomInt( min, max ) {
return Math.floor( Math.random() * (max - min + 1) ) + min;
}
function randomCoords() {
var x, y;
do {
x = getRandomInt( 0, coords[ 0 ].length - 1 );
y = getRandomInt( 0, coords.length - 1 );
}
while ( coords[ y ][ x ] !== 1 );
return [ x, y ];
}
As you might see, I only want to get random coordinates that are 1 in my array. Although this is working, I was wondering if there's a better / more effective way to do it? Sometimes (especially if there are lots of 0s in my coordinate system) it takes a bit to return a value. In that time (as far as I know) javascript can't do anything else... so everything will just pause...
If you are looking to get a random coordinate only once or twice, then your solution is the best.
If you use it often, you can put the coordinates of the 1's in an array. So you will only have to use random() once on the array
coordPairs1[Math.floor(Math.random() * coordPairs1.length)]
var coords = [
[1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1],
[0,0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1],
[1,0,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1],
[0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,1],
[1,0,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,0,1,1,0,1,1]
];
// make coord-pairs:
var coordPairs1 = []
for(var x=0; x<coords[0].length; ++x) {
for(var y=0; y<coords.length; ++y) {
if(coords[y][x] == 1)
coordPairs1.push([x,y])
}
}
function randomCoords() {
return coordPairs1[Math.floor(Math.random() * coordPairs1.length)]
}
// Example:
document.body.innerHTML = randomCoords()

How to access the value of a custom attribute in data from highcharts

I have this in my code
series: [{
name: 'Velocidad',
turboThreshold: 5000,
data: (function() {
// generate an array of random data
var data = [],
i;
for (i = 0; i < valor; i++) {
data.push({
x: dataUtc[i],
y: dataVel[i],
id: i
});
}
return data;
})(),
tooltip: {
valueDecimals: 0,
valueSuffix: 'Kms/H'
}
}]
When I try to get the maximum value I only get the x or y value.
I need to get the id value though.
What can I do?
I tried the following:
var chart = $('#containerVelocidad').highcharts();
min = chart.xAxis[0].min;
max = chart.xAxis[0].max;
Unfortunately that didn't help.
First of all, use yAxis.dataMax not yAxis.max. The second one is returning actual extremes, when sometimes you may have different extremes, than just from one series (zooming, setting extremes, multiple series etc.).
Demo: http://jsfiddle.net/ez4hjazk/1/
And code:
var max = chart.yAxis[0].dataMax,
maxIndex = chart.series[0].yData.indexOf(max), //get index from yData, read more about .indexOf() !
data = chart.series[0].data;
// make sure point is within actual extremes
if(maxIndex > -1) {
console.log("max = " + max, 'id = ' + data[maxIndex].id);
}
Do you need to get the maximum/minimum of id?
If so, try chart.series[0].data[i].id. Here's an example: http://jsfiddle.net/ez4hjazk/

Long array of small data points is rendered as a jagged line

The Goal
I'm attempting to render a long series of data (around 200 ticks, from small float values like 1.3223) into a line chart.
The Issue
When I use a series of data that changes only a small amount (around 0.0001 every tick), the chart is rendered as very jagged (scissor like). I would like to somehow fix it to have a "saner" radius between each point on the graph.
A Good Example
On the other hand, when rendering higher values (around 1382.21) with bigger difference between ticks (from 0.01 to 0.05 +/-) the graph is rendered more smooth and aesthetically pleasing.
Edit: As user Arie Shaw pointed out, the actual low or high values don't make a difference and it remains an issue of representing small "monotonous" changes is a less jagged form.
The Code
var initChart = function(data, container) {
new Highcharts.Chart({
chart: {
type: "area",
renderTo: container,
zoomType: 'x'
},
title: {
text: ''
},
xAxis: {
labels: {
enabled: false
}
},
yAxis: {
title: {
text: ''
}
},
legend: {
enabled: false
},
color: '#A3D8FF',
plotOptions: {
area: {
fillColor: '#C6E5F4',
lineWidth: 1,
marker: {
enabled: false
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
exporting: {
enabled: false
},
series: [{
name: "TEST",
data: data
}]
});
};
Both graphs, and sample data sets are presented in the following fiddle:
http://jsfiddle.net/YKbxy/2/
The problem you're experiencing is unavoidable: If you have a lot of small variations over time, the graph is going to appear jagged provided that you show each data point.
The key point is that last phrase.
One way to 'smooth out' the rough parts would be to average the data. For example:
var myData = []; //... Some array of data; assuming just numbers
var averageData = function (data, factor) {
var i, j, results = [], sum = 0, length = data.length, avgWindow;
if (!factor || factor <= 0) {
factor = 1;
}
// Create a sliding window of averages
for(i = 0; i < length; i+= factor) {
// Slice from i to factor
avgWindow = data.slice(i, i+factor);
for (j = 0; j < avgWindow.length; j++) {
sum += avgWindow[j];
}
results.push(sum / avgWindow.length)
sum = 0;
}
return results;
};
var var initChart = function(data, container) {
new Highcharts.Chart({
series: [{
name: "TEST",
data: averageData(myData, 2)
}]
});
});
This method also has the advantage that you could (potentially) reuse the function to compare the averaged data to the regular data, or even toggle between how much to average the data.
You can always use areaspline instead of area, see: http://jsfiddle.net/YKbxy/3/
why dont you treat you .00001 data as 1, so times 10000, and then write it in your legend like that.
You should even do that as a test, since if the chart looks fine then, it means there is a problem in the dataset numbers when you return it to normal, since high charts takes the difference between high and low...
Either you must approximate your data by only using a few decimal places or you must average out the values using something like:
var data = new Array(200);
var smallArray = new Array(5);
var averagedData = new Array(20);
for (var index=0; index<=averagedData.length; index++){
for(var i = 0; i<=smallArray.length; i++){
smallArray[i] = data[i + index * 5];
}
averagedData[index] = (smallArray[1] + smallArray[2] + smallArray[3] + smallArray[4] + smallArray[5])/smallArray.length;
}
Then you will only need to plot 20 averaged points on an array of 200 data points. You can change the values for what you need.
In the end the issue is in the frequency of the points or their plotting on yAxis.
When I provide more realistic positioning (e.g timestamp) it will look good.
Meaning that jaggedness is a result of the small changes over constant yAxis progression, which is most similar to nt3rp's answer

Given a degree x, find the nearest degree in an array of degrees

I have an array of degrees, [10, 90, 200, 280, 355] for a circle.
I'm given a degree, let's say 1. How do I determine that 1 is closest to 355 degrees?
Subtract the two numbers. If the difference is larger above 180 [or below -180], subtract [or add] 360. Now you can just compare absolute values of the difference.
Here is an actual formula:
degreediff = min(abs(x-y),360-abs(x-y))
This is more compact and efficient:
function difference(a, b) {
var d = Math.abs(a - b);
return d > 180 ? 360 - d : d;
};
function closest(a, bs) {
var ds = bs.map(function(b) { return difference(a, b); });
return bs[ds.indexOf(Math.min.apply(null, ds))];
};
> difference(1, 355)
6
> closest(1, [10, 90, 200, 280, 355])
355
You have one value which will contain the closes degree found found_degree and one for the actual difference degree_difference.
Next, iterate over the whole array and calculate two values: abs(degree_at_position - target_degree) and abs(degree_at_position - 360 - target_degree). If one of those values is smaller than degree_difference, you have a closer degree - store it in found_degree and update degree_difference accordingly.
That's it.
You maybe should initialise found_degree with -1 and degree_difference with 360, just to make sure you can properly interpret the result in case of an empty given array as well - or you simply handle the case of an empty input array separately.
Is this a homework, by the way?
The brute force approach would be something like this:
var closestElement;
var closestDivergence = 360;
var toCompare = 355;
var choices = [1, 90, 200, 280, 355];
for(i=0;i<choices.length;i++){
var currentDivergence=choices[i] - toCompare;
if (currentDivergence<0) {
currentDivergence+=360;
}
if (currentDivergence < closestDivergence){
closestDivergence = currentDivergence;
closestElement = i;
}
}
if (closestElement != NaN){
alert('Closest value is '+choices[closestElement]);
}
Here's a nice little quickie
function closest(deg,ar) {
return ar.sort(function(a,b){var c = deg; return Math.min(360 - (a-c),Math.abs(a-c)) - Math.min(360 - (b-c),Math.abs(b-c))})
}
var myArray = [355, 280, 200, 181, 90, 30];
alert(closest(180,myArray));
Sorts and returns the array according to which one is closest to the provided degree. Index 0 is closest. It does wrap making 355 closer to 0 than 10.
First check the array (check which element is closest) using the given degree (1 in your example), then add 360 and check with that degree (361). Compare which result is better:
x the given degree, y the first result, z the second result
if (abs(x-y) < 360+x-z)
choose y;
else
choose z;
If the array is sorted you can check it with a binary sort which gives you O(log n) time in worst case scenario. Otherwise you have to browse through the whole array two times.
This formula will only work with circles. It's pseudo code of course.
degree diff = min(abs(x-y),360-abs(x-y))
Using the comments from this page I managed to come up with this code:
function closest(deg, degs) {
deg = (deg / 360 > 1 ? deg - (Math.floor(deg / 360)*360) : deg);
var difference = 360;
var closest = -1;
for(i=0;i<degs.length;i++) {
var x = degs[i];
var diff = Math.min(Math.abs(x-deg),360-Math.abs(x-deg))
if(diff <= difference) {
closest = i;
difference = diff;
}
};
return closest;
}
closest(1000, [10, 90, 200, 280, 355]);

Categories

Resources