Stress Majorization
Stress Majorization implementation in Typescript, for 2 dimensional data. Was concieved primary for graph drawing.
A future implementation may include n dimensional data.
Features
- Distance helpers
- Weight helpers
- Pure js, no dependencies
- allows for custom datasets.
- allows for partial iteration of pairs
- 🚧 tests coming soon
Installing
Using npm:
$ npm install stress-majorization
Using yarn:
$ yarn add stress-majorization
Examples
Simple case :
const StressMajorization = ;const result = ;// returns data with updated positions and an array containing epsilon for each iteration// => [data, epsilons]
⚠️ Note : in this example, points are named
i
andj
, their indexes areiIdx
andjIdx
Custom datasets
Note:
dataset
can be an object with attributes{a: {x: 10, y: 10}, b: {x: 11, y: 12}, /*...*/}
or an array :[a, b, c, ...]
. If dataset is an object, keys will be retrieved usingObject.keys
.
To allow custom data, define fromPoint and toPoint callbacks
const weight = 1; // example fillconst stress = 1; // not realcase const customData = x: 0 y: 0 z: 5 x: 1 y: 10 z: 'Hello' /*, ...*/; const result epsilons = ;
Ignore pairs
By default StressMajorization
is solved over all possible pairs of items in data
(O(n²
)), a pair of the same item will be ignored.
It is possible to skip pairs by passing an ignore
function in the options.
// ignoring same position nodesconst result = ;
⚠️ Note: Be careful when defining custom a ignore function, iterating over a pair
(i, i)
(same item in the pair) can result in funny behaviour
Helpers
Several helpers come alongside the algorithm.
Distance
which allows for custom distance calculations.
DistanceFactory
was created to allow for distance calculations over custom data.
; // Retrieve euclidean distance computationconst euclidean = Distancedefault;// euclidean =// (i: [number, number], j: [number, number]) => number // custom dataconst distance = ;// distance = (i, j) => number// coordinates of (i, j) will be retrieved using the accessors
Weight
Different weight were defined for stress majorization, they are put together in this object. Weight Calculation depend on distance computation.
; // Retrieve euclidean distance computationconst distance = ;// distance = (i, j) => number const weight = ;/* weight = { one: () => 1, distance: (i, j) => distance(i, j), distanceInversePow: (i, j) => Math.pow(distance(i, j), -1), distanceInverse2Pow: (i, j) => Math.pow(distance(i, j), -2), exponentialInverseDistance: (i, j) => Math.exp(-distance(i, j)) }*/
This weight can be then injected
const result = ;
Note: custom distances can be passed to the factory, not only
Distance
functions:const hamilton = Math + Math;const weight = ;
API
StressMajorization(data, options)
data
can be anything as long as it is an array of things with number coordinates
const data = 0 0 0 1 /*...*/; // worksconst data = x: 1 y: 1 x: 0 y: 5 /*...*/; // also works (with custom accessors)const data = a: 1 z: 1 a: 0 z: 5 /*...*/; // also works (with custom accessors)const data = a: 0 0 b: 0 1 ; // works, not continous natural numbersconst data = ;data0 = x: 1 y: 1 ;data5 = x: 1 y: 10 ; // will work
options
: Configuration
i
andj
are items of the data array. They have the same type, information other that those accessible via coordinate accessors are also available.
const options = // weight between two points. Xs and Ys are the position of the last iteration, i, j the items from the dataset number // Stress function, i, j represents items from the dataset number // converts `Point` (internal object structure) to coordinates of the output object. Passes new coordinates (Point ) and the current item (n) x y // default to (i) => i // converts data item to `Point` (internal) x y // default to (i) => i // will ignore stress on this pair if yields true, iIdx and jIdx are the position of i, j in the data array boolean // default to (iIdx, jIdx) => iIdx === jIdx // Threshold of change between each iteration, if the change is less that epsilon, the aglorithm will stop and return the solution epsilon: number // default to Math.pow(10, -6) // Maximum iterations allowed, if the algorithm either reaches this or epsilon, it will return whatever the result was computed up to this point. If set to 0, it will ignore the maximum iteration check maxIterations: number // default to 10000;
Distance
Distances measurement algorithm
const Distance = // euclidean distance euclidean: i: number number j: number number : number // squared distance squared: i: number number j: number number : number
DistanceFactory(distance, x, y)
Note
Point
will be used instead of[number, number]
because it's easier to read
- distance:
(i: Point, j:Point) => number
- x:
(i: any) => number
- y:
(i: any) => number
returns
number;
Note Defined in Typescript as :
;
WeightFactory(distance)
Creates weight functions based on the distance measurement
- distance :
(i: any, j: any) => number
. Need to be adapter for the data (by usingDistanceFactory
for example)
returns
1 Math Math Math
Point
contains basic operations for vector manipulation
Point.add(i: Point, j: Point)
: i + j
Point.sub(i: Point, j: Point)
: i - j
Point.mult(a: number, i: Point)
: a * i
Point.div(i: Point, a: number)
: i/a
Point.lenght(i: Point)
: lenght of the vector using euclidean distance
License
see LICENSE <3
Authors
Me and a lot of reading.