Flot: Show a minimum amount of ticks - javascript

Using flot charts I would like to specify a minimum of ticks to show on the y-axis. In my case I would like to always show at least 10 ticks (values 1-10), but if my y-axis max exceeds 10 then I would like flot to draw the chart with its normal tick algorithm. I currently have it sort of working by specifying a function for the ticks paramater.
ticks: function(axis){
var ticks = [];
if(axis.max < 10){
ticks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
}
else{
for(var i = 1; i < axis.max; i++){
ticks.push(i);
}
}
return ticks;
},
The problem with this is that I get a lot more ticks than I want when the axis.max is greater than 10. Is there a way to avoid this?
I originally thought I could return null, but flot is expecting an array to be returned. :(

If you want the default functionality for tick generation you can call the tickGenerator function of the axes. It is much cleaner than working with a piece of code you don't maintain. So in your case, you could do this:
ticks: function(axis){
return axis.max < 10 ? [1,2,3,4,5,6,7,8,9,10] : axis.tickGenerator(axis);
}

It seems that default Flot algorithm goes like that (see setupTickGeneration function:
noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight);
so maybe something like that would work here (DISCLAIMER - not really tested, not sure if you have all needed vars in this scope and the math is just to sketch the solution:
ticks: function(axis){
var ticks = [];
if(axis.max < 10){
ticks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
}
else{
noTicks = 0.3 * Math.sqrt(canvasHeight);
interval = Math.round(axis.max / noTicks);
for(var i = 1; i <= noTicks; i++){
ticks.push(i * interval);
}
}
return ticks;
},

I found this:
ticks: 16
but I haven't tried it.

Related

Skip overlapping tick labels in Plotly Javascript

We are using scatter plots in Plotly.JS to display 2D graph data over a large X range, so we use logarithmic scaling. Zooming and panning works very well, except for one small issue: the X tick labels are confusing because Plotly uses single-digit labels for minor (non-powers of 10) ticks:
I can use tickFormat: '0.1s' to show real numbers (which is what users want) instead of single digits, but then there are cases where these labels can overlap:
I can also add dtick: 'D2' which only displays subticks at positions 2 and 5 and not all digits, but this is then fixed and doesn't adjust to scaling any more.
Ideally, I could specify subtick-label digits where to skip the label (but not the vertical line) completely, without having to resort to tickmode: array and having to specify all tick labels manually, and still benefit from automatic tick adjustment depending on scaling.
For example, if all subtick digits are displayed, I would say I'd like to have tick labels at positions 1, 2, 3, 5, 7, the result would look like this:
The other display modes (digits 2 & 5 only, or just the power of 10) would not change.
Is there a way to do this? If so, how? I'm not afraid of patching Plotly if required, but right now I don't know where to start looking.
Usually I solve this by rotating the labels by some 35-45 degrees. That way they are all there and still readable.
https://plotly.com/javascript/reference/#layout-xaxis-tickangle
tickangle
Parent: layout.xaxis
Type: angle
Default: "auto"
Sets the angle of the tick labels with respect to the horizontal. For example, a `tickangle` of -90 draws the tick labels vertically.
OK, so I looked deeply into the Plotly configuration options and there are options that are semi-automatically modified 'live' depending on zoom levels, but the conditions when those modifications happen are hardcoded.
So here's a patch (for Plotly v1.36, because that's what we currently use). I modified some of the hardcoded conditions when to change from 1,10,100,... to 1,2,5,10,20,50,100,... to 1,2,3,4,5,... labeling on logarithmic axes and removed the tick labels at 4, 6, 8 and 9 for the last case to avoid text overlapping.
For my application, this now works well and I could not find any more overlapping of tick labels. Also, I got rid of the single digits between powers of ten which confused some users.
--- a/plotly-latest.v1.36.js 2021-05-24 21:45:28.000000000 +0100
+++ b/plotly-latest.v1.36.js 2022-02-02 10:21:08.000000000 +0100
## -127302,13 +127302,13 ##
if(!nt) {
if(ax.type === 'category') {
minPx = ax.tickfont ? (ax.tickfont.size || 12) * 1.2 : 15;
nt = ax._length / minPx;
}
else {
- minPx = ax._id.charAt(0) === 'y' ? 40 : 80;
+ minPx = ax._id.charAt(0) === 'y' ? 40 : 100;
nt = Lib.constrain(ax._length / minPx, 4, 9) + 1;
}
// radial axes span half their domain,
// multiply nticks value by two to get correct number of auto ticks.
if(ax._name === 'radialaxis') nt *= 2;
## -127395,14 +127395,21 ##
// Start with it cleared and mark that we're in calcTicks (ie calculating a
// whole string of these so we should care what the previous date head was!)
ax._prevDateHead = '';
ax._inCalcTicks = true;
var ticksOut = new Array(vals.length);
- for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]);
+ // if scaling == log, skip some intermediate tick labels to avoid overlapping text
+ var skipTexts = /^[4689]/;
+ var text;
+ for(var i = 0; i < vals.length; i++) {
+ text = axes.tickText(ax, vals[i]);
+ if(ax.type == "log" && ax.dtick == "D1" && text.text.match(skipTexts)) text.text = "";
+ ticksOut[i] = text;
+ }
ax._inCalcTicks = false;
return ticksOut;
};
function arrayTicks(ax) {
## -127535,18 +127542,20 ##
// ticks on a linear scale, labeled fully
roughDTick = Math.abs(Math.pow(10, rng[1]) -
Math.pow(10, rng[0])) / nt;
base = getBase(10);
ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10);
+ ax.tickformat = '';
}
else {
// include intermediates between powers of 10,
// labeled with small digits
// ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits)
- ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1';
+ ax.dtick = (roughDTick > 0.4) ? 'D2' : 'D1';
+ ax.tickformat = '0.1s';
+ ax.hoverformat = '0.2s'; // Workaround to fix hoverinfo label formatting
}
}
else if(ax.type === 'category') {
ax.tick0 = 0;
ax.dtick = Math.ceil(Math.max(roughDTick, 1));
}
The patch is hereby released as Public Domain to make it as easy as possible to use this for direct integration into Plotly (maybe as a "D3" formatting option?).

Strategy for Looping Through a Fixed Set in Javascript by Steps

I am trying to figure out a way in JavaScript to advance and rewind through a fixed set by steps.
[0,1,2,3,4,5,6,7,8,9,10]
I'm starting at 3. I want to advance by 15. That means I land on 7 because after 10, we start over again at 0.
Another example. Starting at 9, rewind by 11. I should end up on 9 again because after 0 we start over again at 10.
Basically, as you loop you fall of the edges of the set and start again on the opposite edge continuing in the same direction.
This should work for any size set, not just one with 11 elements like in the example.
Help me, math wizards! 🧙‍♂️
Generally for any language what you want is the modulus operator %. The problem the behavior for negative numbers is not defined as some would expect. One way to deal with this:
function move(start, step, size) {
return (start+step+Math.abs(step)*size)%size
}
console.log(move(9,-11,11))
console.log(move(3,15,11))
where step can be either positive or negative. Another I think is:
function move(start, step, size) {
res = (start+step)%size;
if (res < 0) return size + res;
return res;
}
console.log(move(9,-11,11))
console.log(move(3,15,11))
You could get the index, add the wanted steps and take from the sum the remainder with the length of the array as index.
If the index is negative add the length of the array.
function getValue(array, start, steps) {
var index = array.indexOf(start);
if (index === -1) return;
index += steps;
index %= array.length;
return array[index < 0 ? index + array.length : index];
}
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(getValue(array, 3, 15)); // 7
console.log(getValue(array, 9, -11)); // 9

Project Euler Q#2 in "javascript"

the sum of even Fibonacci numbers below 4 mill : i am trying to do it using JavaScript, but i am getting infinity as an answer
but if i use small number such as 10, i am getting console.log() output with the result, is it possible to do that in JavaScript??
var fib = [1, 2];
for(var i =fib.length; i<4000000; i++)
{
fib[i] = fib[i-2] + fib[i-1];
}
//console.log(fib);
var arr_sum = 0;
for(var i = 0; i < fib.length; i++){
if(fib[i] % 2 === 0){
arr_sum += fib[i] ;
}
}
console.log(arr_sum);
So this is the problem:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
The correct answer is 4613732.
On the script below, the answer would be from variable "sum". I assigned it an initial value of 2 since that is the first even number on our sequence. I used 3 more variables to traverse through this sequence. The loop runs normal Fibonacci sequence and the if statement filters even numbers and adds it to the sum.
I think this is the simplest and most intuitive code implementation.
var sum = 2;
var x = 1, y = 2, fib;
while(x < 4000000) {
fib = x + y;
x = y;
y = fib;
if(fib%2===0) sum += fib;
}
console.log(sum);
Two things before I get to coding:
You need to sum the even fibonacci numbers which VALUE below 4 Million (#Alnitak)
There is an "even fibonacci series" which can be calculated in a different manner, see here.
Alright here we go:
let fib = [0, 2];
for(let i = 2; fib[i-1] < 4000000; i++) {
fib[i] = 4 * fib[i - 1] + fib[i - 2];
}
fib.pop()
let sum = fib.reduce((a, c) => a + c, 0);
console.log(sum);
Edit
Without the fib.pop() I just added, the last element of the array would be a number > 4000000. Now you should be able to get the right result.

Having trouble with the Fibonacci Sequence

So I recently discovered this site (https://projecteuler.net). The second problem asks you to find the sum of all even Fibonacci numbers less than four million. I tried to use my limited JavaScript to solve this for me, but it doesn't seem to be working at all. Here is the jsfiddle: http://jsfiddle.net/ophunt/pnf24j7q/3/
Here is the javascript contained therein:
//variables here
var sum = 0;
var fibA = 1;
var fibB = 1;
var store = 0;
//main loop
while (fibA < 4000000) {
store = fibA;
fibA += fibB;
fibB = store;
if (fibA % 2 === 0) {
sum += fibA;
}
}
Document.getElementById("result").innerHTML = sum;
So a few things can help you out here:
The sum up to fibonacci(n) = fibonacci(n+2) - 1
This is nice since you don't have to manually do the sums since you are already doing the sums when you create the sequence
Even fibonacci numbers are every third fibonacci number. This ends up meaning that the sum of the evens is equal to the sum of all divided by two.
For example:
fibs: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
sums: 1, 2, 4, 7, 12, 20, 33, 54, 88
even_sum: 0, 0, 2, 2, 2, 10, 10, 10, 44
Also give a number you can find the which fiboncacci is closest with exceeding it based on some (semi)simple math. (You can also calculate the fibonacci numbers themselves this way, but I'll leave the original function because recursive functions are cool.
The javascript implimentation looks like this:
var findFibNum = function(num) {
var est = Math.log(num * Math.sqrt(5)) /(Math.log((Math.sqrt(5)+1)/2))
return Math.floor(est)
}
Knowing these three things you can make a pretty quick solution:
var top = 4000000;
/* function finds nth fibonacci number */
var fib = function(n){
if (n <=1) return n;
return fib(n-1) + fib(n-2);
}
/* given a number this finds closest n below it i.e. for 34 it give 9
because 34 is the nineth fibonacci number
*/
var findFibNum = function(num) {
var est = Math.log(num * Math.sqrt(5)) /(Math.log((Math.sqrt(5)+1)/2))
return Math.floor(est)
}
var n = findFibNum(top) /* fib(n) is the largest fibonacci below top */
/* the sum of fibonacci number n is fib(n)+2 -1 -- this beats looping and adding*/
var fibsum = fib(n+2) -1
/* it s a nice feature of the sequence that the sum of evens is equal to the sum off all dived by 2 */
var evensum = fibsum/2
Fiddle gives 4613732

Major and minor ticks in FLOT

Some charting/plotting libraries, e.g. matplotlib for Python, has the concept of major and minor ticks (with corresponding major and minor gridlines). I have been looking around, and I think this doesn't exist in FLOT. It seems that there is only one category of tick.
Isn't it possible to make something like below, and if yes, then how?
E.g., as in the illustration below, major ticks for every 1.0, and minor ticks for every 0.2.
You are correct that flot does not support this natively.
To replicate your drawing, I would use grid markings and add a thicker line at each whole number tick:
$.plot("#placeholder", [ d1 ], {
xaxis: {
tickSize: 0.2 // regular tick at 0.2
},
grid: {
markings: function (axes) {
var markings = [];
var xTicks = axes.xaxis.ticks;
for (var i = 0; i < xTicks.length; i++){ // loop all the ticks and add a black line at each whole number
if (xTicks[i].v % 1 === 0){
markings.push({ xaxis: { from: xTicks[i].v - 0.005, to: xTicks[i].v + 0.005 }, color: 'black' });
}
}
return markings;
}
}
});
Produces (example here):
If anyone wants to add your own unlabelled minor ticks at the minortick divisions you want you can do this. (Default minor ticks is 5 and can't easily be overidden)
My data is time based showing two years with markers every three months.
xaxis: {
mode: "time",
timeformat: "%b %Y",
minTickSize: [3, "month"],
showMinorTicks:false,
},
grid: {
markings: function (axes) {
var markings = [];
var month_time= 2629700; // year in seconds divided by 12
var offset=0;
var linespread=36000; // set line thickness
var xTicks = axes.xaxis.ticks;
for (var i = 0; i < xTicks.length; i++){
// loop all the ticks and add a dark line at each main group
markings.push({ xaxis: { from: xTicks[i].v - linespread, to: xTicks[i].v + linespread }, color: '#aaa' });
for (var k = 1; k < 3; k++){
//use subdvision to add minor ticks
offset=month_time*k;
markings.push({ xaxis: { from: xTicks[i].v + offset - linespread, to: xTicks[i].v + offset + linespread }, color: '#ddf' });
}
}
return markings;
}
}

Categories