Home > database >  How to make data points fit a curve with random variation from main curve?
How to make data points fit a curve with random variation from main curve?

Time:02-25

So basically, say you have a curve like y = log(x).

const y = x => Math.log(x)
console.log(generate(10))
// [0, 0.6931471805599453, 1.0986122886681096, 1.3862943611198906, 1.6094379124341003, 1.791759469228055, 1.9459101490553132, 2.0794415416798357, 2.1972245773362196]

function generate(max) {
  let i = 1
  let ypoints = []
  while (i < max) {
    ypoints.push(y(i  ))
  }
  return ypoints
}

enter image description here

Now with these points, we want to diverge off of them by a random amount as well, that is also following some sort of a curve in terms of variation or how much it randomly diverges. So for example, as the curve flattens out up and to the right, the y moves slightly up or slightly down, with decreasing probability of being further away from the log(x) mark for y. So say that fades off at 1/x.

enter image description here

That means, it is more likely to be directly on log(x) or close to it, than it is to be further from it. But we can swap with any similar equation.

How can you make a simple JavaScript function give you the final set of coordinates (x, y), an array of 2-tuples containing the x and y coordinate? My attempt is not getting the part right about the "variational-decay according to a secondary curve" that I've been trying to describe.

// calling `u` the thing that converts x into a y point.
const u = x => Math.log(x)
// here, `v` is the equation for variation from the main curve, given a y.
const v = y => 1 / y
// `r` is the random variation generator
const r = y => y   (Math.random() * v(y))

const coordinates = generate(10, u, v, r)

console.log(coordinates)

function generate(max, u, v, r) {
  let i = 1
  let coordinates = []
  while (i < max) {
    const x = i  
    const y = r(u(x))
    coordinates.push([ x, y ])
  }
  return coordinates
}

How can this be made to take the two curves and generate the randomish variation away from the curve that has probability/decay-rate to be away from the main curve according to the second curve?

The expected output is to be - some small amount away from that first array in the comments (if we have 10 points along the x axis).

// [0, 0.6931471805599453, 1.0986122886681096, 1.3862943611198906, 1.6094379124341003, 1.791759469228055, 1.9459101490553132, 2.0794415416798357, 2.1972245773362196]

So it might be like (just making these up, they would be more randomly determined):

// [0, 0.69, 1.1, 1.3, 1.43, 1.71, 1.93, 2, 2.21]

Notice how they are more likely to be close to the log(x) curve (because 1/x, and randomness), than further away, and it goes -.

The main reason for asking (which is tangential to this abstracted question), is for generating dummy data during development, to test out UI data visualization features, simulating somewhat realistic looking data. I would pick a much more complicated equation for the main curve, and a similar decay equation for variation, and generate potentially millions of points, so this is just a simplification of that problem.

I am talking like given a curve equation like the one in the next picture, generate random points that are like the points in this next picture too.

enter image description here

CodePudding user response:

An approach:

  • Take a random x.
  • Calculate y = f(x).
  • Get a random offset of this point with wanted distribution.
  • Return this point.

const
    f = x => 10 * Math.log(x),
    offset = () => (1 / Math.random() - 1) * (Math.random() < 0.5 || -1),
    canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d');

for (x = 0; x < 100; x  ) {
    const
        y = f(x),
        dx = offset(),
        dy = offset();
        
    console.log(x.toFixed(2), y.toFixed(2), dx.toFixed(2), dy.toFixed(2));
    ctx.beginPath();
    ctx.strokeStyle = '#000000';
    ctx.arc(x * 4 , (100 - y) * 4, 0.5, 0, Math.PI * 2, true);
    ctx.stroke();        

    ctx.beginPath();
    ctx.strokeStyle = '#ff0000';
    ctx.arc((x   dx) * 4 , (100 - y   dy) * 4, 2, 0, Math.PI * 2, true);
    ctx.stroke();        
}
<canvas id="canvas" style="border-width: 0; display: block; padding: 0; margin: 0;" width="400" height="400"></canvas>

  • Related