How to write jqPlot values into variables? - javascript

I use jqPlot to plot a line chart out of a .csv-file.
I need to get the xmax and ymax values of the plot and use them for further processings.
How do I get this or any other values and write them inside my own variables?
EDIT
Let's say this is my plot:
What I need is not the maximum x-value from the array (here 1380). I need to get the maximum value from the plot (here 2000). For further processing I would like to add rectangles inside the plot, see second picture: and calculate their height as a x-value and not as their pixel-value.
Therefore I need to access the jqplot variables, not the array variables I give over to jqplot.

So, at some point you have an array of values that you passed to jqPlot to draw the graph, for example:
var myData = [[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]];
If you want to find the maximum x and y values, you just need to loop through the array keeping track of the largest value you've found so far.
var maxX, maxY;
for (var i=0; i < myData.length; i++) {
if (myData[i][0] > maxX || !maxX) {
maxX = myData[i][0];
}
if (myData[i][1] > maxY || !maxY) {
maxY = myData[i][1];
}
}
Here's a simple demo: http://jsfiddle.net/LAbvj/
EDIT: Ok, so I think what you are now asking for is the maximum for each axis. In that case, this is simple:
var plot1 = $.jqplot('chart1', [
[3, 7, 19, 1, 4, 6, 8, 2, 5]
]);
console.log(plot1.axes.xaxis.max);
console.log(plot1.axes.yaxis.max);
See demo: http://jsfiddle.net/KJTRF/

Related

What JavaScript code would I use to plot a trend line?

UPDATE
I managed to get trendline support added to the RGrah Line and Scatter charts. There's a demo in the download archive called demos/line-trendline.html that shows it. The Scatter chart supports trendlines too.
Assuming that I have the following values that I'm going to plot on a Line chart (these are values and not coordinates - the coordinates are calculated by my software and are shown below):
[4,4,3,2,5,5]
How would I turn those values into a set of trendline values/coordinates? (BTW I don't really have any Maths expertise beyond school level - so no fancy Maths terminology please!).
To add further details: These are a set of values that are spaced equally across a 500 pixel space (an HTML5 canvas tag). So the X coordinates are calculated for you and will come out like this (35 pixel margin on both sides of the chart): [35,121,207,293,379,465].
These are just the X coordinates, the Y coordinates are calculated automatically based on the scale, the height of the chart and the value. Here's an example Line chart that my software creates using this code:
<canvas id="cvs" width="500" height="250">
[No canvas support]
</canvas>
<script>
line = new RGraph.Line({
id: 'cvs',
data: [4,4,3,2,5,5],
options: {
xaxisLabels: ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
shadow: false,
backgroundGridBorder: false,
backgroundGridVlines: false,
xaxis: false,
yaxis: false
}
}).draw()
</script>
You can see the chart online here:
https://www.rgraph.net/demos/line-csv-reader.html
And the X/Y coordinates (that are then plotted on the canvas tag) that are generated end up as this:
[[35,71],[121,71],[207,107],[293,143],[379,35],[465,35]]
So you already know:
the X coordinates are calculated for you ... (35 pixel margin): 35, 121, 207, 293, 379, 465.
the generated result:
[[35,71], [121,71], [207,107], [293,143], [379,35], [465,35]] that's just a list of [x,y] points
From that we can remove the X we know (calculated for us) and we will get:
71, 71, 107, 143, 35, 35
we can see a pattern with the original input
4, 4, 3, 2, 5, 5
piece of cake to get the formula with that sequence:
35 + (5 - y)*36
All that remains is to put that formula into code:
<canvas id="canvas"></canvas>
<script>
canvas = document.getElementById('canvas');
canvas.width = canvas.height = 500;
ctx = canvas.getContext('2d');
x = 35
trendline = []
plot = [4, 4, 3, 2, 5, 5]
plot.forEach(function(value) {
y = 35 + (5 - value) * 36
ctx.lineTo(x, y);
trendline.push([x, y])
x += 86
});
ctx.stroke();
console.log(JSON.stringify(trendline))
</script>
Now from what you mentioned on the comments:
it just plots the values that you give it ... It doesn't generate trend lines from your data
looking at the code of rgraph on the drawLine function:
https://www.rgraph.net/libraries/src/RGraph.line.js
// Loop thru each value given, plotting the line
// (FORMERLY FIRST)
for (i=0,len=lineData.length; i<len; i+=1) {
var data_point = lineData[i];
//
// Get the yPos for the given data point
//
var yPos = this.getYCoord(data_point);
...
//
// Add the coords to an array
//
this.coords.push([xPos, yPos]);
lineCoords.push([xPos, yPos]);
That lineCoords looks like a trendline to me...

reshape 1d array to 3d for tensorflow.js lstm

I'm trying to predict future stock prices using time series data. I have an xs array of 251 timesteps, and a ys array of the corresponding stock price for that time step. I have reshaped the xs array to be 3d, but get an error
'Input Tensors should have the same number of samples as target Tensors. Found 1 input sample(s) and 251 target sample(s).'
the code for the model is below.
var xs = [];
var ys = [];
for(i in result){
xs.push(i);
ys.push(result[i].close);
}
var xt = tf.tensor3d(xs, [1,xs.length,1]);
var yt = tf.tensor2d(ys, [xs.length, 1]);
//xt.reshape([1, xs.length, 1]).print();
//yt.reshape([1, ys.length, 1]).print();
var lstm1 = tf.layers.lstm({units: 32, returnSequences: true, inputShape:[xs.length,1]});
var model = tf.sequential();
model.add(lstm1);
model.add(tf.layers.dropout({rate:0.2}));
model.add(tf.layers.lstm({units:5}));
model.add(tf.layers.dropout({rate:0.2}));
model.add(tf.layers.dense({units:1, inputShape:[32], activation:'softmax'}));
model.compile({optimizer:'adam', loss:'categoricalCrossentropy'});
model.fit(xt, yt, {epochs:1000}).then(() => {
bestfit = model.predict(tf.tensor(xs, [xs.length,1])).dataSync();
The error seems to come from model.fit(x, y) because there seems to be a mismatch in the shape of x and y.
x has the shape [1, 251, 1] and y the shape [251, 1]. This does not work because there is more features in x than there is label in y. You have to reshape whether x or y.
reshape x: x.reshape([251, 1, 1]) or x.reshape([251, 1])
or
reshape y: y.reshape([1, 251]) or y.reshape([1, 251, 1])
Note: There is almost an infinite way of reshaping that will work as long as the first two dimension sizes are equal and that the product of all dimension sizes is equal to 251. What matter in the reshaping is not to loose the correlation between the features and the labels

Display negative values in canvas line graph

I am using html5 canvas element to draw line chart. The chart works fine with positive values. But when provided negative values, the chart is not drawn correctly.
This is what I have tried. Any help will be appreciated.
http://jsfiddle.net/nshX6/142/
function getMinY () {
var min = 0;
for(var i = 0; i < data.values.length; i ++) {
if(data.values[i].Y < min) {
min = data.values[i].Y;
}
}
return Math.ceil(min);
}
Here are some tool functions you can use to build a flexible graph.
Your flexible graph will be able to show any range of data and it will always fit on the available canvas size.
calcSourceMinMax: Calculates the minimum and maximum value from a data array.
mapRange: Takes any data value and maps it into a proportional value that is guaranteed to be inside the minimum and maximum of the graphs displayable width & height. This allows your data array to contain any range of values and still never fall outside the graphing display area.
getDisplayXY: Takes a specified x,y data value and finds its display X,Y coordinate on the graph.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var xPadding = 40;
var yPadding = 30;
// Notice I changed The X values
var data = { values:[
{ X: 0, Y: -120 },
{ X: 2, Y: 28 },
{ X: 3, Y: 18 },
{ X: 4, Y: 34 },
{ X: 5, Y: 40 },
{ X: 6, Y: 80 },
{ X: 7, Y: 80 }
]};
// calc the drawable graph boundaries
var graphLeft=xPadding;
var graphRight=canvas.width-xPadding;
var graphTop=yPadding;
var graphBottom=canvas.height-yPadding;
// graph styling
var dotRadius=3;
// calc the min & max values of data.values (calc both X & Y ranges)
var rangeX=calcSourceMinMax(data.values,'X');
var rangeY=calcSourceMinMax(data.values,'Y');
// draw the graph content
var starting=getDisplayXY(data.values[0].X,data.values[0].Y);
dot(starting,dotRadius);
for(var i=1;i<data.values.length;i++){
var ending=getDisplayXY(data.values[i].X,data.values[i].Y);
connector(starting,ending);
dot(ending,dotRadius);
starting=ending;
}
// draw the graph axes
var y0=getDisplayXY(graphLeft,0).displayY;
ctx.beginPath();
ctx.moveTo(graphLeft,graphTop);
ctx.lineTo(graphLeft,graphBottom);
ctx.moveTo(graphLeft,y0);
ctx.lineTo(graphRight,y0);
ctx.strokeStyle='#D3E';
ctx.stroke();
// draw the graph legends
ctx.textAlign='right';
ctx.textBaseline='middle';
var y0=getDisplayXY(graphLeft,0).displayY;
var yMin=getDisplayXY(graphLeft,rangeY.min).displayY;
var yMax=getDisplayXY(graphLeft,rangeY.max).displayY;
var xMax=getDisplayXY(graphRight,rangeX.max).displayX;
ctx.fillText(rangeY.min,graphLeft-10,yMin);
ctx.fillText(0,graphLeft-10,y0);
ctx.fillText(rangeY.max,graphLeft-10,yMax);
ctx.fillText(rangeX.max,graphRight+10,y0);
///////////////////////////////////
// HELPER FUNCTIONS
///////////////////////////////////
//
function getDisplayXY(valueX,valueY){
// calc the display X & Y from data.values[i]
x=mapRange(valueX,rangeX.min,rangeX.max,graphLeft,graphRight);
// Note: canvas y values increase going downward
// so swap graphTop & graphBottom
y=mapRange(valueY,rangeY.min,rangeY.max,graphBottom,graphTop);
return({displayX:x,displayY:y})
}
//
function connector(starting,ending){
ctx.beginPath();
ctx.moveTo(starting.displayX,starting.displayY);
ctx.lineTo(ending.displayX,ending.displayY);
ctx.stroke();
}
//
function dot(position,radius){
ctx.beginPath();
ctx.moveTo(position.displayX,position.displayY);
ctx.arc(position.displayX,position.displayY,radius,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}
// map source values into a designated range
function mapRange(value, sourceLow, sourceHigh, mappedLow, mappedHigh) {
return mappedLow + (mappedHigh - mappedLow) * (value - sourceLow) / (sourceHigh - sourceLow);
}
// mapping helper function
function calcSourceMinMax(a,prop){
var min=1000000;
var max=-1000000;
for(var i=0;i<a.length;i++){
var value=a[i][prop];
if(value<min){min=value;}
if(value>max){max=value;}
}
return({min:min,max:max});
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=350 height=300></canvas>
You will want to style the graph according to your design needs. This minimal example shows a legend containing the x,y axes and the min,max ranges of values.
Also, the x-axis is put at the y=0 value. You will also want to check that there is indeed a y=0 in the range of your y values. If not, you might move the x-axis at the bottom of your graph.
Good luck with your project!
Try modifaying this parameters :
var xPadding = 30;
var yPadding = 30;
To :
var xPadding = 30;
var yPadding = 100;
After that you need to change Y step to be bigger.
EDIT
In case that you have dynamic data you need to normalize values.
step 1 : find min value for Y
step 2 : if min value is less than zero (0) you need to normalize all values to be positive. Add that value to every element of array
step 3 : shift Y axes to it's new position.

Tiled based pathfinding in a 1- or 2-dimensional array

As far as I know all tile based map editors export a JSON object containing one dimensional arrays. While most pathfinding libraries/tutorials are only provided for two dimensional arrays.
Also if for example I would like to do pathfinding in this one dimensional array and this array is huge i'm geussing this would cause performance issues.
So why is it that most tile based map editors output a one dimensional and how should I handle those regarding pathfinding?
example tile editor
Just google pathfinding to find all the two dimensional patfhfinding tutorials
Depending on the orientation by which it is converted to the 1-D array;
function convert(x, y, height, width) {
return x + y * width; // rows
/* return y + x * height; // cols */
}
function reverse(i, height, width) {
var x, y;
// rows
x = i % width
y = (i - x) / width
/* // cols
y = i % height;
x = (i - y) % height; */
return [x, y];
}
Now, say we have a 6 wide by 3 high map
1-D | 2-D
0 1 2 3 4 5 | x0y0 x1y0 x2y0 x3y0 x4y0 x5y0
6 7 8 9 10 11 | x0y1 x1y1 x2y1 x3y1 x4y1 x5y1
12 13 14 15 16 17 | x0y2 x1y2 x2y2 x3y2 x4y2 x5y2
Pick an index in the 1-D Array, e.g. i = 8, to convert it to it's 2-D co-ordinates we can use reverse
reverse(8, 3, 6); // [2, 1]
// i, h, w = [x, y]
Or say we picked co-ordinates x = 2, y = 1 in our 2-D Array, we can convert it to the index in the 1-D Array with convert
convert(2, 1, 3, 6); // 8
// x, y, h, w = i
Once you can convert between the two systems you can do your path finding as normal. You can name these functions however you like, I wrote them more so you can see how to switch between the two systems.
Depending on how it is made, the y axis may have 0 at the bottom, not the top, or the entire thing could be mirrored across a diagonal (which I called cols in the above functions). It really depends on how it was done, but as long as you are consistent with the conversion and have the correct height and width (read maximum y and maximum x respectively), it should not matter.
One approach might be to retrieve an offset into the 1D array, based on the 2D vector coords of the tile:
int MaxX = 100; // assumes a max row of 100;
int offset = Y * MaxX + X;
tile[offset] = ....
No need to convert, just reference the tile directly in the 1D array. I used this approach for A* in a recent game project, and works for me.

Iterate each point within a polygon

Assuming I have the vertices of some polygon in a grid environment, how can I iterate through each cell it contains (including those on the edge)?
To clarify, I have the following vertices (counted as if the topleft is (0, 0)):
//each point is [x, y]
var verts = [
[1, 1],
[3, 1],
[3, 2],
[4, 2],
[4, 4],
[0, 4],
[0, 3],
[1, 3]
];
Which would define a polygon such as this:
Where each green dot is a point I would like to iterate, based on the vertices above. There is no pattern to the direction the vertices will walk along the edge of the polygon, it could go clockwise or counter-clockwise around the polygon. However, they will be in order; that is, if you put down your pen and move to each vertex in order, without lifting up, and it would draw the outline without crossing inside the polygon.
The use case being I have the imageData from a PNG loaded via the canvas API. This PNG is split into "zones", and I need to iterate each pixel of the current "zone". Each "zone" is defined by a vertex array like above.
I tried something like the following, which will create a square to iterate through for each set of 4 vertices in the array.
for(var v = 0, vl = verts.length - 4; v < vl; ++v) {
//grabbing the minimum X, Y and maximum X, Y to define a square to iterate in
var minX = Math.min(verts[v][0], verts[v + 1][0], verts[v + 2][0], verts[v + 3][0]),
minY = Math.min(verts[v][1], verts[v + 1][1], verts[v + 2][1], verts[v + 3][1]),
maxX = Math.max(verts[v][0], verts[v + 1][0], verts[v + 2][0], verts[v + 3][0]),
maxY = Math.min(verts[v][1], verts[v + 1][1], verts[v + 2][1], verts[v + 3][1]);
for(var x = minX; x < maxX; ++x) {
for(var y = minY; y < maxY; ++y) {
//do my checks on this pixel located at X, Y in the PNG
}
}
}
Two big problems with that though:
It can repeat points within the polygon, and
It can grab points outside the polygon
I can solve the first issue by tracking which points I check, so I don't repeat a check. The second can only be solved by running a PointInPoly check on each point, which would make this solution much heavier than I want it to be.
EDIT
Iterating each pixel in the entire image and applying a PointInPoly check to each is also unacceptable; it would be even slower than the above algorithm.
If your polygons are convex, you can do the following:
Create a line for each edge of the polygon denoting one side inside and one side outside (this is based on the normal, which can be dependent on the winding direction)
For every pixel inside the bounding box that you already calculated, check to see if the pixel is on the in-side of the line. If the pixel is on the out-side of any of the lines, then it is outside the polygon. If it is inside all of them, then it is inside.
The basic algorithm is from here: https://github.com/thegrandpoobah/voronoi/blob/master/stippler/stippler.cpp#L187
If your polygons are not convex, then what I would do is to actually draw the polygon on the canvas in a known colour, and then apply the iterative flood fill algorithm. That requires that you know at least one pixel which is on the inside, but that shouldn't be an expensive test. But this may not be suitable in JavaScript if you can't do it in an offscreen buffer (not familiar with the canvas tag).

Categories

Resources