Upsampling a time series in Javascript - javascript

Let's assume a time series with n elements similar to this one:
[{value: 33209.203948532944, time: 1620178750},
{value: 33209.203948532944, time: 1620208647},
{value: 33610.0948868934, time: 1620219519},
{value: 34220.523450350825, time: 1620241262},
{value: 34220.523450350825, time: 1620242621}]
What is a simple algorithm in JavaScript to create another time series with m elements (m > n) which would have a similar visual when plotted on a chart?

The following function uses linear interpolation between each consecutive points by simply creating a segment between each 2 consecutive points and adding more points in-between as needed. The first and last point of the series are not modified.
const upSample = (series, threshold) => {
const l = series.length;
if (threshold <= l) { return series; }
const sampled = [];
// always add the first point
sampled.push(series[0]);
const lerp = (x, y, a) => x * (1 - a) + y * a;
for (var i = 1; i < threshold - 1; ++i) {
const delta = (i / (threshold - 2)) * (l - 2);
const left = Math.floor(delta);
const right = Math.min(Math.ceil(delta), l - 1);
const a = delta - left;
const value = lerp(series[left].value, series[right].value, a);
const time = Math.floor(lerp(series[left].time, series[right].time, a));
sampled.push({ value, time });
}
// always add last point
sampled.push(series[l - 1]);
return sampled;
};

Related

How to get a random value from truncated normal distribution with mean and std in JavaScript?

What is a JS alternative to the same Python implementation?
import matplotlib.pyplot as plt
from scipy.stats import truncnorm
import numpy as np
mean = 1
std = 2
clip_a = -4
clip_b = 3
a, b = (clip_a - mean) / std, (clip_b - mean) / std
x_range = np.linspace(-3 * std, 3 * std, 1000)
plt.plot(x_range, truncnorm.pdf(x_range, a, b, loc = mean, scale = std));
I'd like to get a random value according to the distribution (in JS the same code with size=1):
dist = truncnorm.rvs(a, b, loc = mean, scale = std, size=1000000)
plt.hist(dist);
Here is a JS function that implements a truncated, skew-normal pseudo random number generator (PRNG). It is based on this blog post by Tom Liao and has been extended to consider lower and upper bounds (truncation).
Essentially, the function is called recursively until a variate within the desired bounds is found.
You can pass your own random number generator using the rng property, though Math.random will be used by default. Also, as you didn't ask for a skew-normal distribution, you can just ignore the skew property as it defaults to 0. This will leave you with a truncated normal PRNG, just as you asked for.
function randomTruncSkewNormal({
rng = Math.random,
range = [-Infinity, Infinity],
mean,
stdDev,
skew = 0
}) {
// Box-Muller transform
function randomNormals(rng) {
let u1 = 0,
u2 = 0;
//Convert [0,1) to (0,1)
while (u1 === 0) u1 = rng();
while (u2 === 0) u2 = rng();
const R = Math.sqrt(-2.0 * Math.log(u1));
const Θ = 2.0 * Math.PI * u2;
return [R * Math.cos(Θ), R * Math.sin(Θ)];
}
// Skew-normal transform
// If a variate is either below or above the desired range,
// we recursively call the randomSkewNormal function until
// a value within the desired range is drawn
function randomSkewNormal(rng, mean, stdDev, skew = 0) {
const [u0, v] = randomNormals(rng);
if (skew === 0) {
const value = mean + stdDev * u0;
if (value < range[0] || value > range[1])
return randomSkewNormal(rng, mean, stdDev, skew);
return value;
}
const sig = skew / Math.sqrt(1 + skew * skew);
const u1 = sig * u0 + Math.sqrt(1 - sig * sig) * v;
const z = u0 >= 0 ? u1 : -u1;
const value = mean + stdDev * z;
if (value < range[0] || value > range[1])
return randomSkewNormal(rng, mean, stdDev, skew);
return value;
}
return randomSkewNormal(rng, mean, stdDev, skew);
}
Calling this function in the following manner
const data = [];
for (let i = 0; i < 50000; i++) {
data.push({
x: i,
y: randomTruncSkewNormal({
range: [-4,3],
mean: 1,
stdDev: 2
})
});
}
and plotting the data using your charting library of choice should give your the desired output.
I also made a small Observable notebook interactively demonstrating the function which you might want to check out as well.

Linear regression with two independent variables in javascript

The following will output the slope, intercept and correlation coefficient R^2 for a given set of x and y values.
let linearRegression = (y,x) => {
let lr = {}
let n = y.length
let sum_x = 0
let sum_y = 0
let sum_xy = 0
let sum_xx = 0
let sum_yy = 0
for (let i = 0; i < y.length; i++) {
sum_x += x[i]
sum_y += y[i]
sum_xy += (x[i]*y[i])
sum_xx += (x[i]*x[i])
sum_yy += (y[i]*y[i])
}
lr['slope'] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x)
lr['intercept'] = (sum_y - lr.slope * sum_x)/n
lr['r2'] = Math.pow((n*sum_xy - sum_x*sum_y)/Math.sqrt((n*sum_xx-sum_x*sum_x)*(n*sum_yy-sum_y*sum_y)),2)
return lr
}
How can I adapt this to accept two independent variables x1, x2 rather than one?
This page goes into the modified formulas:
http://faculty.cas.usf.edu/mbrannick/regression/Reg2IV.html
But i've been struggling to adapt this to the above function.
Step-by-step
First, look at the input line:
let linearRegression = (y,x) => {. You have 2 variables, so we could call them x1 and x2: let linearRegression = (y,x1,x2) => {
Now the formulae for the regression depend on dot products between the pairs of variables - x1.x1, x1.x2, x1.y etc.
So instead of calculating sum_xx, sum_xy, sum_yy, we need to calculate the sums for all of those pairs (with 3 variables instead of 2, there are 6 sums to calculate).
Finally, the two-variable equation is y=a + b1.x1 + b2.x2, so there are 2 slopes to calculate rather than 1, and the page you linked to gives all the formulae you would need there.

d3.js How to simplify a complex path - using a custom algorithm

I've got a very basic example here. http://jsfiddle.net/jEfsh/57/ that creates a complex path - with lots of points. I've read up on an algorithm that may look over the points and create a simpler set of coordinates. Does anyone have any experience with this - examples on how to loop through the path data and pass it through the algorithm - find the shortest set of points to create a more rudimentary version of the shape?
http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
var points = "M241,59L237,60L233,60L228,61L224,61L218,63L213,63L209,65L204,66L199,67L196,68L193,69L189,70L187,72L184,72L182,74L179,75L177,76L175,78L173,79L170,81L168,83L165,85L163,87L161,89L159,92L157,95L157,97L155,102L153,105L152,110L151,113L151,117L151,123L150,137L148,180L148,185L148,189L148,193L148,197L148,202L148,206L149,212L151,218L152,222L154,229L154,232L155,235L157,239L158,241L160,245L162,247L163,249L165,251L167,254L169,256L172,258L175,260L178,261L183,265L188,268L193,270L206,273L213,275L220,275L225,275L232,276L238,277L243,277L249,277L253,277L259,277L266,277L271,277L277,277L281,277L284,277L288,277L293,277L297,276L302,274L305,272L308,271L311,268L313,267L315,264L318,261L322,257L324,254L326,249L329,244L331,241L332,239L334,234L338,230L339,226L341,222L343,218L345,213L347,211L348,207L349,201L351,196L352,192L353,187L353,183L353,180L353,178L354,176L354,173L354,170L354,168L354,167L354,166L354,164L354,162L354,161L354,159L354,158L354,155L354,152L354,149L352,143L349,137L347,133L343,125L340,119 M241,59L340,119";
d3.select("#g-1").append("path").attr("d", points);
//simplify the path
function DouglasPeucker(){
}
/*
//http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
function DouglasPeucker(PointList[], epsilon)
// Find the point with the maximum distance
dmax = 0
index = 0
end = length(PointList)
for i = 2 to ( end - 1) {
d = shortestDistanceToSegment(PointList[i], Line(PointList[1], PointList[end]))
if ( d > dmax ) {
index = i
dmax = d
}
}
// If max distance is greater than epsilon, recursively simplify
if ( dmax > epsilon ) {
// Recursive call
recResults1[] = DouglasPeucker(PointList[1...index], epsilon)
recResults2[] = DouglasPeucker(PointList[index...end], epsilon)
// Build the result list
ResultList[] = {recResults1[1...end-1] recResults2[1...end]}
} else {
ResultList[] = {PointList[1], PointList[end]}
}
// Return the result
return ResultList[]
end
*/
It's not clear what your problem is exactly. Do you have problems to turn the SVG data string into a list of points? You can use this:
function path_from_svg(svg) {
var pts = svg.split(/[ML]/);
var path = [];
console.log(pts.length);
for (var i = 1; i < pts.length; i++) {
path.push(pts[i].split(","));
}
return path;
}
It is a very simple approach: It splits the string on all move (M) and line (L) commands and treats them as lines. It then splits all substrings on the comma. The first "substring" is ignored, because it is the empty string before the first M. If there is a way to do this better in d3 I haven't found it.
The reverse operation is easier:
function svg_to_path(path) {
return "M" + path.join("L");
}
This is equivalent to svg.line.interpolate("linear").
You can then implement the Douglas-Peucker algorithm on this path data recursively:
function path_simplify_r(path, first, last, eps) {
if (first >= last - 1) return [path[first]];
var px = path[first][0];
var py = path[first][1];
var dx = path[last][0] - px;
var dy = path[last][1] - py;
var nn = Math.sqrt(dx*dx + dy*dy);
var nx = -dy / nn;
var ny = dx / nn;
var ii = first;
var max = -1;
for (var i = first + 1; i < last; i++) {
var p = path[i];
var qx = p[0] - px;
var qy = p[1] - py;
var d = Math.abs(qx * nx + qy * ny);
if (d > max) {
max = d;
ii = i;
}
}
if (max < eps) return [path[first]];
var p1 = path_simplify_r(path, first, ii, eps);
var p2 = path_simplify_r(path, ii, last, eps);
return p1.concat(p2);
}
function path_simplify(path, eps) {
var p = path_simplify_r(path, 0, path.length - 1, eps);
return p.concat([path[path.length - 1]]);
}
The distance to the line is not calculated in a separate function but directly with the formula for the distance of a point to a 2d line from the normal {nx, ny} on the line vector {dx, dy} between the first and last point. The normal is normalised, nx*nx + ny*ny == 1.
When creating the paths, only the first point is added, the last point path[last] is implied and must be added in path_simplify, which is a front end to the recursive function path_simplify_r. This approach was chosen so that concatenating the left and right subpaths does not create a duplicate point in the middle. (This could equally well and maybe cleaner be done by joining p1 and p2.slice(1).)
Here's everything put together in a fiddle: http://jsfiddle.net/Cbk9J/3/
Lots of good references in the comments to this question -- alas they are comments and not suggested answers which can be truly voted on.
http://bost.ocks.org/mike/simplify/
shows interactive use of this kind of thing which references Douglas-Peucker but also Visvalingam.

Weighted average from an array

I have to write the following weighted average formula in JavaScript:
Average = (p1*p2*x1 + p3*p4*x2 + ... +p(n-2)*p(n-1)*xn) / (p1*p2 + p3*p4 + ... + p(n-2)p(n-1) )
The formula gives the average of x values.
I also have an array populated with n elements in JavaScript:
Array = (p1,p2,x1,p3,p4,x2....)
...where pi are the weights and xi the values I want to find the average for.
How can I write the formula using this array?
I would probably use the following strategy:
Create two new arrays (probably weights and values).
Iterate over the original array in steps of 3; multiplying the pn's and pushing the result into weights and pushing the xn into values.
Iterate over the new arrays, creating the weighted total (the left hand of the division) and the total weight (right hand of the division).
Divide one by the other. Done.
In other words, something like this:
function weighted_average(input) {
var weights = [];
var values = [];
var weighted_total = 0;
var total_weight = 0;;
if (input.length % 3 !== 0) {
throw new Error("Input array length is not a multiple of 3.");
}
for (var i = 0; i < input.length; i += 3) {
weights.push(input[i] * input[i + 1]);
values.push(input[i + 2]);
}
for (var i = 0; i < weights.length; i += 1) {
weighted_total += weights[i] * values[i];
total_weight += weights[i];
}
return weighted_total / total_weight;
}
You'll have to verify whether this does exactly what you're after, though. No guarantees. ;)
JSFiddle demo: jsfiddle.net/Z8seZ
Of course, you could skip the intermediary arrays to make it a bit faster. But the above is more explicit and more readable, and therefore more maintainable (e.g. you could easily split out the actual algorithm and create different "wrapper" functions for different forms of input). I would only optimize it if working with (really) large data sets.
Here's a functional approach, requiring ES5:
var w = a.unzip(3).map(function(v, i, a) {
var weight = v[0] * v[1];
var sum = weight * v[2];
return [sum, weight];
}).reduce(function(p, c, i, a) {
return [p[0] + c[0], p[1] + c[1]];
}, [0, 0]);
var aw = w[0] / w[1];
which in pseudo-code is:
split the array into chunks of three
convert each three [p1, p2, x ] into a pair [ p1 * p2 * x , p1 * p2 ]
sum the pairs (along the array, not within each pair)
divide one by the other
and where the (non-standard) unzip function which chunks the array is:
Object.defineProperty(Array.prototype, 'unzip', {
value: function(n) {
n = n || 2;
return this.reduce(function(p, c, i, a) {
if (i % n === 0) {
p.push(a.slice(i, i + n));
}
return p;
}, []);
}
});
ES6 one-liner for an array of objects xs containing the keys w as weight and v as value:
((_w, _v) => _v / _w)(...xs.reduce((r, o) => [r[0] + o[w], r[1] + o[w] * o[v]], [0, 0]))

A fast method for computing combination and permutation ratio for a large set in JS

Trying to compute a ratio in a fast efficient method when confronted with a large set of numbers. The idea is an array with numbers example Box=[1, 2, 3, 4] and naming name each item in the array to 1=A, 2=B, 3=C, 4=D. then using a number call step. Am trying to achieve this for every element in the box, for example in case of the first element A with 3 steps only.
At step 1 take the value of A and divide it by box sum
step 1 => 1/10 = 0.1
At step 2 take combination BA, CA, DA and divide by box sum subtracting each time by previous value
=> (2/10)(1/8) + (3/10)(1/7) + (4/10)(1/6) = 0.1345
At step 3 take combination BCA, BDA, CBA, CDA, DBA, DCA and divide by box sum subtracting each time by previous value
=> (2/10)(3/8)(1/5) + (2/10)(4/8)(1/4) + (3/10)(2/7)(1/5) + (3/10)(4/7)(1/3) + (4/10)(2/6)(1/4) + (4/10)(3/6)(1/3) = 0.2143
and finally return the sum of all the steps for A=0.1 + 0.1345 + 0.2143 = 0.4488
if the same procedural is done to all elements in the box array B= 0.7587 , C= 0.8702, D= 0.9222 to test if calculation was correct the sum of the ratios is equal to 3 the same number of steps used.
If steps and elements in the box are increased it should do all combination in the same order. Can this calculation run fast and not run out of memory if the Box size is 1000 and with 10 steps for every element in the box.
recurrence formula for an array length 4 with 3 steps
[A,B,C,D]
for element A
step 1 : A
step 2 : BA CA DA
step 3 : BCA BDA CBA CDA DBA DCA
for element B
step 1 : B
step 2 : AB CB DB
step 3 : ACB ADB CAB CDB DAB DCB
for element C
step 1 : C
step 2 : AC BC DC
step 3 : ABC ADC BAC BDC CAD CBD
for element D
step 1 : D
step 2 : AD BD CD
step 3 : ABD ACD BAD BCD DAC DBC
for an array of 5 elements and 4 steps [A, B, C, D, E]
for element A :
step 1: A
step 2: BA CA DA EA
step 3: BCA BDA BEA CBA CDA CEA DBA DCA DEA EBA ECA EDA
step 4:
BCDA
BCEA
BDCA
BDEA
BECA
BEDA
CBDA
CBEA
CDBA
CDEA
CEBA
CEDA
DBCA
DBEA
DCBA
DCEA
DEBA
DECA
EBCA
EBDA
ECBA
ECDA
EDBA
EDCA
Here is my try in Matlab code if JS is not possible is there a way to improve the Matlab code instead
% sample code to test
userTickets = [1, 2, 3, 4];
users = [0, 0, 0, 0];
userCount = 4;
draws = 3;
for player = 1 : length(users)
userLuck = 0;
for draw = 1 : prizes
userLuck = userLuck + cardCombo(player, userTickets, draw);
end
users(player) = userLuck;
end
total = sum(users);
**** npermutek
function [A, I] = npermutek(v,k)
narginchk(2,3);
nck = nchoosek(1:numel(v),k);
permutes = perms(1:k);
I = nck(:,permutes);
I = reshape(I,[],k);
I = sortrows(I,1:k);
A = v(I);
end
**** cardCombo
function [result] = cardCombo(player, cards, draw)
playerCards = cards(player);
opponentCards = cards(setdiff(1:end,player));
cardCount = sum(cards);
if draw == 1
result = playerCards/cardCount;
elseif draw == 2
result = 0;
for n = 1 : length(opponentCards)
result = result + ((opponentCards(n)/cardCount) * (playerCards/ (cardCount - opponentCards(n))));
end
else
combo = npermutek(opponentCards, draw -1);
combWithPlayer = repmat(playerCards, size(combo,1),1);
combo = [combo combWithPlayer];
rowSize = size(combo, 1);
colSize = size(combo,2);
rowSum = 0;
for row = 1 : rowSize
rowMul = 1;
sub = 0;
for col = 1 : colSize
num = combo(row, col);
rowMul = rowMul * (num / (cardCount - sub));
sub = sub + num;
end
rowSum = rowSum + rowMul;
end
result = rowSum;
end
end
Original method
proba of player P winning at rank exactly k is:
p(k) = sum_{a={A(k-1, N-1)}}( p(a) * p(P|a) )
where
N is the number of players
{A(k-1, N-1)} is the set of all arrangements of k-1 elem among N-1
p(a) the proba of an arrangement from {A(k-1, N-1)}
proba g of winning in [1:k]
g(k) = sum_{i=1}^k p(i)
Alternative method
An other way: to win either step 1 or ... or step 10 is the opposite of losing in every step
that is...take all arrangements of size k such that no P appear: A(k, N-1)
g(k) = 1 - sum_{a={A(k, N-1)}} p(a)
while shorter formula, the arrangements have a bigger size (k instead of k-1) so it will take more time... than original method
At this point, optimization could be to focus on evaluating the arrangements
we might try an exponentiation:
compute all {A(2, N-1)}
then {A(4, N-1)} by reusing {A(2, N-1)} (join them when no two letters in common)
then concatenate {A(4, N-1)} x {A(4, N-1)} x {A(2, N-1)} still with the condition of no two letters in common
But: notice the card of |{A(4, N-1)}| is 999*998*997*996 ~= 1e12 (and I don't dare taking such cartesian product)
Approximation
Maybe there is better way, but as a fallback, one can simply use a monte carlo approach:
Make N simu
Draw k random numbers. If any number from the draw lands in the playerInterval, success
return nbSuccess/N
To avoid resizing an array, consider a set([1,2,3,...]) and construct virtually the cumulative interval
]0;1] U ]1;1+2] U ]3;3+3] ... etc
draw initially from den == (n*(n+1)/2), and check the landing i-th interval
If i !== player, remove i from the set and reconsider the new cumulative interval, redraw from den-i
and so forth until k (or success)
function A (k, v, cbk) {
function rec(hand, depth){
if (depth == k) return cbk(hand)
for (let i = 0; i < v.length; ++i) {
let x = v[i]
hand[depth] = x
v.splice(i, 1)
rec(hand, depth + 1)
v.splice(i, 0, x)
}
}
return rec(Array(k), 0)
}
function p (v, den) {
return v.reduce((acc, num) => {
acc.res *= num / acc.den
acc.den -= num
return acc
}, { den, res: 1 })
}
//original method
function orig (box, player, steps) {
const boxWithoutPlayer = box.slice(0)
boxWithoutPlayer.splice(box.indexOf(player), 1)
const total = box.reduce((s, x) => s + x, 0)
let s = player / total
for (let step = 1; step < steps; ++step) {
A(step, boxWithoutPlayer, arr => {
let { den, res } = p(arr, total)
s += res * p([player], den).res
})
}
return s
}
//alternative method
function neg (box, player, steps) {
const boxWithoutPlayer = box.slice(0)
boxWithoutPlayer.splice(box.indexOf(player), 1)
const total = box.reduce((s, x) => s + x, 0)
let s = 0
A(steps, boxWithoutPlayer, arr => {
s += p(arr, total).res
})
return 1 - s
}
//montecarlo approx
function mc (box, player, steps) {
const NSimu = 1e5
let success = 0
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
const getRandomInt = max => Math.floor(Math.random() * Math.floor(max))
const total = box.reduce((s, x) => s + x, 0)
for (let i = 0; i < NSimu; ++i) {
let set = new Set(box)
let den = total
for (let s = 0; s < steps; ++s) {
// n in [1; den]
const n = getRandomInt(den) + 1
let idx = 0
let it = 0
for(const x of set){
if (n <= it + x) {
idx = x
break
}
it += x
}
if (idx == player) {
success++
break
}
den -= idx
set.delete(idx)
}
}
return success / NSimu
}
function main(box, player, steps, meths) {
const f = function(key, meth){
return _=> {
console.time(key)
console.log(`player ${player} winning: `,meth(box, player, steps))
console.timeEnd(key)
}
}
const dic = {
orig: f('orig', orig),
neg: f('neg', neg),
mc: f('mc', mc),
}
meths.forEach(m => dic[m]())
console.log('----')
}
main([...Array(6)].map((x,i) => i + 1), 3, 3, ['orig', 'neg', 'mc'])
main([...Array(10)].map((x,i) => i + 1), 8, 7, ['orig', 'neg', 'mc'])
//takes 1000; 900; 10 about 8s on node on my machine
main([...Array(500)].map((x,i) => i + 1), 250, 10, ['mc'])

Categories