Related
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.
Let's say I have this formula, for example:
function getExperience(level) {
let a = 0;
for (let x = 1; x < level; x += 1) {
a += Math.floor(x + (200 * (2 ** (x / 3))));
}
return Math.floor(a / 4);
}
for (var i = 1; i < 100; i++) {
console.log(`Level ${i}: ${getExperience(i)}`);
}
To get the experience needed for level 50, you'd do: getExperience(50).
But, how would you reverse that and get the LEVEL needed for experience? So, getLevel(20010272) would output 50.
Short answer
You can use 4.328085 * Math.log(0.00519842 * xp + 1.259921045) as a very good approximation of the corresponding level.
If you need an exact value, you could iterate over all levels until you find the desired range, as in this answer.
Long answer
Slightly modified function
I don't think it's possible to find an exact, closed-form expression for the inverse of this function. It should be possible if you modify getExperience(level) a bit, though.
First, you can notice that x grows much slower than 2 ** (x / 3).
Then, Math.floor doesn't have much influence over large numbers.
So let's remove them! Here's the slightly modified function:
function getExperienceEstimate(level) {
let a = 0;
for (let x = 1; x < level; x += 1) {
a += 200 * (2 ** (x / 3));
}
return a / 4;
}
The advantage of this method is that it's now a geometric series, so it's possible to calculate the sum directly, without any loop:
function getExperienceEstimate(level) {
let a = 50;
let r = 2 ** (1 / 3);
return a * (r**level - r) / (r - 1);
};
getExperienceEstimate(50) returns 20011971.993575357, which is only 0.0015% smaller than getExperience(50).
Inverse function
According to Wolfram Alpha, here's the inverse function of getExperienceEstimate:
function getLevelEstimate(xp){
let a = 50;
let r = 2 ** (1 / 3);
return Math.log(xp * (r - 1) / a + r) / Math.log(r);
};
With some minor precision loss, you can simplify it further:
function getLevelEstimate(xp){
return 4.328085 * Math.log(0.00519842 * xp + 1.259921045)
};
It's only an estimate, but it works pretty well and doesn't require any loop!
Test
For 20012272 XP, the approximate inverse function returns 50.00006263463371, which should be a good starting point if you want to find the exact result.
function getExperience(level) {
let a = 0;
for (let x = 1; x < level; x += 1) {
a += Math.floor(x + (200 * (2 ** (x / 3))));
}
return Math.floor(a / 4);
}
function getLevelEstimate(xp){
return 4.328085 * Math.log(0.00519842 * xp + 1.259921045)
};
for (var i = 1; i < 100; i++) {
console.log(`Level ${i} (XP = ${getExperience(i)}). Estimated level : ${getLevelEstimate(getExperience(i))}`);
}
You can use a binary search algorithm to avoid to loop over all possibilities.
Here is an example that I have adapted to your case.
You first need to create an array to map all your level => experience, this action should be done only ONCE, then you never have to do it again.
As you can see in my example, even with 1000 levels, you never have to iterate more than 9 times whatever level you are trying to find.
// You first have to create an array with all your levels.
// This has to be done only ONCE because it's an expensive one!
const list = [];
for (let i = 1; i <= 1000; i++) {
list[i] = getExperience(i);
}
function getExperience(level) {
let a = 0;
for (let x = 1; x < level; x += 1) {
a += Math.floor(x + (200 * (2 ** (x / 3))));
}
return Math.floor(a / 4);
}
function getLevel(value) {
// initial values for start, middle and end
let start = 0
let stop = list.length - 1
let middle = Math.floor((start + stop) / 2)
let iterations = 0;
// While the middle is not what we're looking for and the list does not have a single item.
while (list[middle] !== value && start < stop) {
iterations++;
if (value < list[middle]) {
stop = middle - 1
} else {
start = middle + 1
}
// Recalculate middle on every iteration.
middle = Math.floor((start + stop) / 2)
}
console.log(`${value} is Level ${middle} (Result found after ${iterations} iterations)`);
return middle;
}
// Then you can search your level according to the experience
getLevel(0);
getLevel(72);
getLevel(20010272);
getLevel(getExperience(50));
getLevel(33578608987644589722);
A brute-force (but inelegant) solution would be to just call getExperience for levels until you reach a level that requires more experience than the passed exp:
function getLevel(exp) {
if (exp === 0) return 0;
let level = 0;
let calcExp = 0;
while (exp > calcExp) {
calcExp = getExperience(level);
if (calcExp > exp) break;
level++;
}
return level - 1;
}
console.log(getLevel(20012272)); // experience required for 50 on the dot
console.log(getLevel(20012270));
console.log(getLevel(20012274));
console.log(getLevel(0));
function getExperience(level) {
let a = 0;
for (let x = 1; x < level; x += 1) {
a += Math.floor(x + (200 * (2 ** (x / 3))));
}
return Math.floor(a / 4);
}
You can use binary search to locate level value faster - in 7 steps max.
(while I doubt that gain is significant for length 100 list)
I am get a formula to calculate the Net Present Value using Rent, Number of periods, Discount Rate and Future Value.I am able to get the Present Value however, I need a formula to calculate the Net Present Value on today's date or any date a user inputs. My code is as below:
function PV() {
var future = 5000;
var type = 1;
periods = 12;
rate = document.getElementById("rate").value;
var ratePercent = periods * 100;
rate = Math.pow(1 + rate / 100, 1 / 365) - 1;
rate = eval(rate);
periods = eval(periods);
// Return present value
if (rate === 0) {
document.getElementById("presentResultValue").value = -payment * periods - future;
} else {
document.getElementById("presentResultValue").value = (
(((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) /
Math.pow(1 + rate, periods)
).toFixed(2);
}
}
I am also using Excel to calculate this but need a way to convert it to Javascript. I am also attaching my work with excel. ExcelNPV I am still learning JavaScript so any help will be greatly appreciated. Thank you.
Came across NPV formula on my project and had to create a function for it. Finance.js is great but I didn't want to clutter my code with it because my problem was just getting this formula.
ES5 Function
/**
* Calculates the Net Present Value of a given initial investment
* cost and an array of cash flow values with the specified discount rate.
*
* #param {number} rate - The discount rate percentage
* #param {number} initialCost - The initial investment
* #param {array} cashFlows - An array of future payment amounts
* #return {number} The calculated Net Present Value
*/
function getNPV(rate, initialCost, cashFlows) {
var npv = initialCost;
for (var i = 0; i < cashFlows.length; i++) {
npv += cashFlows[i] / Math.pow(rate / 100 + 1, i + 1);
}
return npv;
}
Using the function:
var rate = 10;
var initialCost = -25000;
var cashFlows = [-10000, 0, 10000, 30000, 100000];
console.log(getNPV(rate, initialCost, cashFlows));
// expected output: 56004.77488497429
JavaScript ES6 Function:
https://www.evermade.fi/net-present-value-npv-formula-for-javascript/
/**
* Calculates the Net Present Value of a given initial investment
* cost and an array of cash flow values with the specified discount rate.
* #param {number} rate - The discount rate percentage
* #param {number} initialCost - The initial investment
* #param {array} cashFlows - An array of future payment amounts
* #return {number} The calculated Net Present Value
*/
function getNPV(rate, initialCost, cashFlows) {
return cashFlows.reduce(
(accumulator, currentValue, index) =>
accumulator + currentValue / Math.pow(rate / 100 + 1, index + 1),
initialCost
);
}
I've explained most of it on my blogpost.
Hope this helps :)
One big thing you are missing with this is the cost.
You have periods, you have rate, you have rate percent. But there is nothing there for the "cost".
You say you have this in Excel, and other languages. If you understand the calculations in Excel, then you may understand how you should do it in JavaScript.
In Excel you use the nifty function =NPV(.... where it takes 2 arguments. The first is the "RATE" and the second is the "VALUE" which can be multiple values. When you do this in Excel, one of the values you pass in would be the total cost. So you have something like this....
=NPV(2%,[total cost],[Year1Value],[Year2Value],[Year3Value],....) or =NPV(A1,A2:A12)
The "Total Cost" would go where you spend the money....assuming its at the end of Year 1, it would go before the "value" / return from year one.
With that being said, another thing to consider is determining WHEN the cost was needed.
If the cost is upfront, then it would be taken out of the "array" of values section and added to the NPV calculations such as....
=NPV(A1,A3:A12) + A2
Where cell A2 is the Cost (upfront) and A1 is the rate and A3-A12 are all the returns.
As a simple example, if you have the rate somewhere on the page, you can loop through the arguments that are going to be passed to it such as below....
function NPV() {
var args = [];
for (i = 0; i < arguments.length; i++) {
args = args.concat(arguments[i]);
}
var rate = document.getElementById("rate").value;
var value = 0;
// loop through each argument, and calculate the value
// based on the rate, and add it to the "value" variable
// creating a running total
for (var i = 1; i < args.length; i++) {
value = value + ((args[i])/(Math.pow(1 + rate, i)));
}
return value;
}
Additionally, you could also look for a library such as Finanace.JS http://financejs.org/
This worked for me
function getNPV(rate, a, payment) {
var npv = 0;
for (var i = 0; i < a.length; i++) {
npv += payment / Math.pow(1 + rate, a[i]);
console.log(payment / Math.pow(1 + rate, a[i]))
}
return npv;
}
I am trying to calculate the consistency of an array of values which output a percentage. So for example an array of [1,1,1,1,1] would output 100% consistency, and array of [1,12,23,5,2] would output something like 25% consistency.
I have tried a simple percentage difference calculation but is only for two numbers whereas this needs to work for a dynamic array and this only outputs the difference. Any help would be appreciated so thanks in advance.
function getPercentage(n1, n2){
var newValue = n1 - n2;
return (newValue / n1) * 100;
}
getPercentage(1, 12)
Based on your comments, I refined the base value used in calculating the overall consistency between members of the array.
The new results calculated (which on visual inspection seems plausible, again it depends on the definition of consistency over all members but I think this is getting closer):
$ node jsscratch.js
Consistency for 1,1,1,1,1 is 100.000% fixed to 3 decimal places
Consistency for 1,12,23,5,2 is 33.360% fixed to 3 decimal places
Consistency for 1,12,23,15,12 is 50.160% fixed to 3 decimal places
Consistency for 1,2,10 is 83.778% fixed to 3 decimal places
/**
* Calculate the expected value
*/
function expectancy(arrayOfValues) {
let sumTotal = function(previousValue, currentValue) {
return previousValue + currentValue;
};
let u = arrayOfValues.reduce(sumTotal);
// Assume each member carries an equal weight in expected value
u = u / arrayOfValues.length;
return u;
}
/**
* Calculate consistency of the members in the vector
* #param {Array<number>} The vector of members to inspect for similarity
* #return {number} The percentage of members that are the same
*/
var similarity = function(arrayOfValues) {
let sumTotal = function(previousValue, currentValue) {
return previousValue + currentValue;
};
// Step 1: Calculate the mean value u
let u = expectancy(arrayOfValues); // Calculate the average
// Step 2: Calculate the standard deviation sig
let sig = [];
let N = 1/arrayOfValues.length;
for (let i = 0; i < arrayOfValues.length; i++) {
sig.push(N*(arrayOfValues[i] - u) * (arrayOfValues[i] - u));
}
// This only works in mutable type, such as found in JavaScript, else sum it up
sig = sig.reduce(sumTotal);
// Step 3: Offset from 100% to get the similarity
return 100 - sig;
}
answer = similarity(ar1);
console.log(`Consistency for ${ar1} is ${answer.toFixed(3)}% fixed to 3 decimal places`);
answer = similarity(ar2);
console.log(`Consistency for ${ar2} is ${answer.toFixed(3)}% fixed to 3 decimal places`);
answer = similarity(ar3);
console.log(`Consistency for ${ar3} is ${answer.toFixed(3)}% fixed to 3 decimal places`);
answer = similarity(ar4);
console.log(`Consistency for ${ar4} is ${answer.toFixed(3)}% fixed to 3 decimal places`);
Ok this is my first stab I'll take at it, tried to brake it up into smaller units of code to make it easier to work through. If I had enough time this could be further refined but then the readability goes south ;-)
Tried to avoid using TypeScript and to keep it as ECMAScript 5 friendly as possible. (Sorry about the messy formatting)
let ar100 = [1,1,1,1,1];
let ar25 = [1,12,23,5,2];
/**
* Calculate the lack of difference between two values (ie consistency)
* #param {number} valA
* #param {number} valB
* #returns {number} The consistency as a decimal
* where 1 is '100% consistent'
* and 0.5 is '50% consistent'
*/
var calcConsistency = function( valA, valB) {
let diff = Math.abs(valA - valB);
let consistency = (valA - diff) / valA;
// assumption: you can't be less than zero consistent
return consistency < 0 ? 0 : consistency;
}
/**
* A predicate used by the map operation in 'overallConsistency'
* #param {*} val
* #param {*} index
* #param {*} values
* #return {number} the average consistency of the current 'val' to the other 'values'
*/
var mapEachConsistency = function(val, index, values) {
let predicateGetOnlyOthers = function(currentValue, othersIndex) {
return index !== othersIndex;
};
let others = values.filter(predicateGetOnlyOthers);
const otherCount = others.length;
let totalConsistency = others.reduce(function(previousValue, currentValue) {
return previousValue + calcConsistency(val, currentValue);
});
return (totalConsistency / otherCount);
}
/**
* Calculate the overall average consistency of an array of numbers
* #param {Array<number>} arrayOfValues
* #return {number} The average consistency score as percentage
*/
var overallConsistency = function(arrayOfValues) {
let consists = arrayOfValues.map(mapEachConsistency);
let sumTotal = function(previous, current){ return previous + current; };
let avgAnswer = consists.reduce(sumTotal);
return (avgAnswer / consists.length) * 100;
};
// Here's using it to see the results
var answer = overallConsistency(ar100);
console.log(`Consistency: ${answer.toFixed(3)}% (fixed to 3 dicimal place)`);
answer = overallConsistency(ar25);
console.log(`Consistency: ${answer.toFixed(3)}% (fixed to 3 dicimal place)`);
I am searching for a way to calculate the Cumulative distribution function in Javascript. Are there classes which have implemented this? Do you have an idea to get this to work? It does not need to be 100% percent accurate but I need a good idea of the value.
http://en.wikipedia.org/wiki/Cumulative_distribution_function
I was able to write my own function with the help of Is there an easily available implementation of erf() for Python? and the knowledge from wikipedia.
The calculation is not 100% correct as it is just a approximation.
function normalcdf(mean, sigma, to)
{
var z = (to-mean)/Math.sqrt(2*sigma*sigma);
var t = 1/(1+0.3275911*Math.abs(z));
var a1 = 0.254829592;
var a2 = -0.284496736;
var a3 = 1.421413741;
var a4 = -1.453152027;
var a5 = 1.061405429;
var erf = 1-(((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*Math.exp(-z*z);
var sign = 1;
if(z < 0)
{
sign = -1;
}
return (1/2)*(1+sign*erf);
}
normalcdf(30, 25, 1.4241); //-> 0.12651187738346226
//wolframalpha.com 0.12651200000000000
The math.js library provides an erf function. Based on a definition found at Wolfram Alpha , the cdfNormalfunction can be implemented like this in Javascript:
const mathjs = require('mathjs')
function cdfNormal (x, mean, standardDeviation) {
return (1 - mathjs.erf((mean - x ) / (Math.sqrt(2) * standardDeviation))) / 2
}
In the node.js console:
> console.log(cdfNormal(5, 30, 25))
> 0.15865525393145707 // Equal to Wolfram Alpha's result at: https://sandbox.open.wolframcloud.com/app/objects/4935c1cb-c245-4d8d-9668-4d353ad714ec#sidebar=compute
This formula will give the correct normal CDF unlike the currently accepted answer
function ncdf(x, mean, std) {
var x = (x - mean) / std
var t = 1 / (1 + .2315419 * Math.abs(x))
var d =.3989423 * Math.exp( -x * x / 2)
var prob = d * t * (.3193815 + t * ( -.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))))
if( x > 0 ) prob = 1 - prob
return prob
}
This answer comes from math.ucla.edu
Due to some needs in the past, i put together an implementation of distribution function in javascript. my library is available at github. You can take a look at https://github.com/chen0040/js-stats
it provides javascript implementation of CDF and inverse CDF for Normal distribution, Student's T distribution, F distribution and Chi-Square Distribution
To use the js lib for obtaining CDF and inverse CDF:
jsstats = require('js-stats');
//====================NORMAL DISTRIBUTION====================//
var mu = 0.0; // mean
var sd = 1.0; // standard deviation
var normal_distribution = new jsstats.NormalDistribution(mu, sd);
var X = 10.0; // point estimate value
var p = normal_distribution.cumulativeProbability(X); // cumulative probability
var p = 0.7; // cumulative probability
var X = normal_distribution.invCumulativeProbability(p); // point estimate value
//====================T DISTRIBUTION====================//
var df = 10; // degrees of freedom for t-distribution
var t_distribution = new jsstats.TDistribution(df);
var t_df = 10.0; // point estimate or test statistic
var p = t_distribution.cumulativeProbability(t_df); // cumulative probability
var p = 0.7;
var t_df = t_distribution.invCumulativeProbability(p); // point estimate or test statistic
//====================F DISTRIBUTION====================//
var df1 = 10; // degrees of freedom for f-distribution
var df2 = 20; // degrees of freedom for f-distribution
var f_distribution = new jsstats.FDistribution(df1, df2);
var F = 10.0; // point estimate or test statistic
var p = f_distribution.cumulativeProbability(F); // cumulative probability
//====================Chi Square DISTRIBUTION====================//
var df = 10; // degrees of freedom for cs-distribution
var cs_distribution = new jsstats.ChiSquareDistribution(df);
var X = 10.0; // point estimate or test statistic
var p = cs_distribution.cumulativeProbability(X); // cumulative probability
This is a brute force implementation, but accurate to more digits of precision. The approximation above is accurate within 10^-7. My implementation runs slower (700 nano-sec) but is accurate within 10^-14. normal(25,30,1.4241) === 0.00022322110257305683, vs wolfram's 0.000223221102572082.
It takes the power series of the standard normal pdf, i.e. the bell-curve, and then integrates the series.
I originally wrote this in C, so I concede some of the optimizations might seem silly in Javascript.
function normal(x, mu, sigma) {
return stdNormal((x-mu)/sigma);
}
function stdNormal(z) {
var j, k, kMax, m, values, total, subtotal, item, z2, z4, a, b;
// Power series is not stable at these extreme tail scenarios
if (z < -6) { return 0; }
if (z > 6) { return 1; }
m = 1; // m(k) == (2**k)/factorial(k)
b = z; // b(k) == z ** (2*k + 1)
z2 = z * z; // cache of z squared
z4 = z2 * z2; // cache of z to the 4th
values = [];
// Compute the power series in groups of two terms.
// This reduces floating point errors because the series
// alternates between positive and negative.
for (k=0; k<100; k+=2) {
a = 2*k + 1;
item = b / (a*m);
item *= (1 - (a*z2)/((a+1)*(a+2)));
values.push(item);
m *= (4*(k+1)*(k+2));
b *= z4;
}
// Add the smallest terms to the total first that
// way we minimize the floating point errors.
total = 0;
for (k=49; k>=0; k--) {
total += values[k];
}
// Multiply total by 1/sqrt(2*PI)
// Then add 0.5 so that stdNormal(0) === 0.5
return 0.5 + 0.3989422804014327 * total;
}
You can also take a look here, it's a scientific calculator implemented in javascript, it includes erf and its author claims no copyright on the implementation.