npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

swstats

v0.8.1

Published

Sliding window statistics

Downloads

488

Readme

swstats

Sliding window statistics for Nodejs and browser.

Build Status

Time and Size sliding windows, capable of calculating incremental statistics, such as count, sum, avg, stdev, freq, etc...

Features

  • Nodejs and Browser compatible
  • Time or size windows (not both)
  • Global time or custom specified timestamp
  • Numeric or category stats
  • Stats are calculated incrementally, using online algorithms
  • Core category stats: sum, frequency, mode
  • Core numeric stats: count, sum, min, max, avg, stdev
  • Plugable custom stats functions
  • Time windows are slided each second

Installation

npm install swstats

Quick start

Nodejs:

const	SWindow = require("swstats");

// Create a time window of 10 seconds. Each value pushed to
// the window will be stored in a unique slot (step:1)
// Window will slide on each second
var sw = new SWindow.TimeStats(10000,{step:1});
var i=0;

setInterval(()=>{
	sw.push(Math.random());
},1);

setInterval(()=>{
	console.log(sw.stats);
},100);

or browser:

<!doctype html>
<html>
<head>
	<script src="swstats.min.js"></script>
</head>
<body>
	<textarea id="values" readonly="readonly" cols="40" rows="15">
	</textarea>

	<script>
		// Create a time window of 10 secons.
		// Window will slide on each second
		var sw = new SWindow.TimeStats(10000,{step:1});
		var i=0;
		var ta = document.getElementById("values");

		setInterval(()=>{
			sw.push(Math.random());
		},10);

		setInterval(()=>{
			ta.value = JSON.stringify(sw.stats,null,2);
		},100);
	</script>
</body>
</html>

The results will be something like:

{ count: 9,
  sum: 4.383476926850223,
  avg: 0.48705299187224704,
  max: 0.9865126881294235,
  min: 0.0023880687887007923,
  stdev:
   { avg: 0.48705299187224704,
     sqsum: 2.6772528231211963,
     sum: 4.383476926850223,
     stdev: 0.24546266317028345 } }

....

{ count: 994,
  sum: 495.03542685194975,
  avg: 0.4980235682615189,
  max: 0.9865126881294235,
  min: 0.0023880687887007923,
  stdev:
   { avg: 0.4980235682615189,
     sqsum: 332.1783737578395,
     sum: 495.03542685194975,
     stdev: 0.29352342336095866 } }

Time Window API

new SWindow.TimeStats(time,[options])

Creates a new time window of time duration. The window will slide on each second. options is optional, and can take the following parameters:

  • step: Time step for each time slot. When a new value is added to the window, it can be stored in a new slot, or use the last one, depending on the time that value was inserted. If step is 10, that means that if a new value is inserted after 10 ms. from the last one, a new slot will be created for this value. Otherwise, the value will be added and grouped to the last slot. By default, time step is 1000 ms. High values will prevent the window increasing and consuming memory over time for long windows, but some stats will lose accuracy (such as stdev).
  • timestamp Defines how time is measured. Can take the following values:
    • TimeStats.TS.ABSOLUTE This is the default value. When a value is pushed to the window, it takes the current machine timestamp. In this mode, windows are naturally slided in real-time.
    • TimeStats.TS.RELATIVE With this mode, you can push values that has an associated timestamp that is independent of the machine time. Windows are slided based on the max and min inserted timestamps. This mode is less performant, but allows you to insert values out of time order.
  • type: Value types for this window. Can take the values "numeric" or "category". By default, windows are numeric.
  • ops: Statistic operations to perform on the window values. If you don't need all the core functions, or want to add a new custom operation, you can pass an array of operation names. By default, for numeric stats, ops are ["count","sum","avg","stdev"], and for category ["sum","freq","mode"]
  • stats: Object with pre-initialized stats values.

Example:

const SWindow = require("swstats");

// Time window of 10 seconds
var tw1 = new SWindow.TimeStats(10000,{
	step:1000,      // Values will be accumulated in slots of 1 second
	type:"numeric", // Numeric values
	ops:["sum"],    // We only want to sum values
	stats : {
		sum : 233     // Initial value where sum will start
	}
});

// Time window of 10 seconds with relative timestamp
var tw2 = new SWindow.TimeStats(10000,{
	timestamp:TimeStats.TS.RELATIVE,
	step:1000,      // Values will be accumulated in slots of 1 second
	type:"numeric" // Numeric values
});

timeWindow.push(val)

Adds a new value to the window. It will consume a new slot or be added to the current one, depending on the step value. If timestamp mode is absolute, simply push the value, but if relative is activated, you must push an object with value and timestamp:

tw1.push(10);
tw2.push({ts:Date.now(),v:10});

timeWindow.push([vals])

Adds multiple values at once, in the same manner as push(val).

timeWindow.clean()

Resets window and stats.

timeWindow.pause()

Pauses the window, so no slide occurs, but no new values will be added.

timeWindow.resume(shift)

Resumes a paused window. If shift is true, the timestamps of each slots will be shifted relative to the time the window is resumed, otherwise they keep their original timestamps. This will affect how the window is slided on the next second.

destroy()

Kill the window, so no new values will be added, and no slide will occur. You can still access to its stats.

timeWindow.length

Gets the current size of the window (number of slots)

timeWindow.stats

Gets the current calculated stats

{ count: 9,
  sum: 4.638591015963949,
  max: 0.8745422003577794,
  min: 0.04623850021742104,
  avg: 0.5153990017737722,
  stdev:
   { avg: 0.5153990017737722,
     sqsum: 3.044764166996456,
     sum: 4.638591015963949,
     stdev: 0.26957558983867996 } }

timeWindow.window

Gets the current calculated stats per slot

[ { t: 1517499437649,
    sum: { David: 571, John: 404 },
    freq: { David: 0.5856410256410256, John: 0.41435897435897434 },
    mode: 'David',
    threshold: { David: false, John: false } },
  { t: 1517499438649,
    sum: { John: 398, David: 580 },
    freq: { John: 0.4069529652351738, David: 0.5930470347648262 },
    mode: 'David',
    threshold: { John: false, David: false } },
  { t: 1517499439649,
    sum: { David: 449, John: 274 },
    freq: { David: 0.6210235131396957, John: 0.3789764868603043 },
    mode: 'David',
    threshold: { David: true, John: false } } ]

Size Window API

new SWindow.SizeStats(size,[options])

Creates a new size window with size slots. The window will slide when the maximum slots have been reached. options is optional, and can take the following parameters:

  • type: "numeric" or "category"
  • ops: Same a s TimeStats.
  • stats: Object with pre-initialized stats values.

Example:

const SWindow = require("swstats");

// Size window of 100 values
vat sw = new SWindow.SizeStats(100,{
	type:"category", // Category values
	ops:["freq"],    // We only want the value frequency
	stats : {
		freq : {       // Initial value where freq will start
			"john" : 0.3,
			"david" : 0.7
		}
	}
});

sizeWindow.push(val)

Adds a new value to the window. It will consume a new slot.

sizeWindow.push([vals])

Adds multiple values at once. In case of category values, unlike the temporary window, all the values pushed in one call will be added together in the same slot:

sizeWindow.push(["value1","value2"]);

will consume only one slot, whereas:

sizeWindow.push("value1");
sizeWindow.push("value2");

will consume two slots.

sizeWindow.clean()

Resets window and stats.

sizeWindow.length

Gets the current size of the window (number of slots)

sizeWindow.stats

Gets the current calculated stats

timeWindow.window

Gets the current calculated stats per slot

Custom Statistics API

SWindow.register(type,name,deps,fn,def)

It is possible to implement and plug a custom function to calculate any stats you need. To register a new stats function, you must call the register function with the following parameters:

  • type: "numeric" or "category"
  • name: Name of the operation
  • deps: Array of dependencies. If the custom function depends on previous stats to be performed, you can pass the names in this array.
  • fn: The stats function. It will be described later.
  • default: If true, new created windows without the ops options specified, will perform by default this stats operation.

The stats function fn

fn(currval,newitems,olditems,allitems,newstats,oldstats)

The stats function passed to the register method takes the following arguments:

  • currval: The current stats value for your function, prior to the next calculation when an item has been added.
  • newitems: Array of new values added to the window since the last function call.
  • olditems: Array of removed items from the window since the last function call.
  • allitems: Array of all the values that are currently in the window (allitems includes newitems and excludes olditems)
  • newstats: Current calculated stats by the functions called before this.
  • oldstats: Previous calculated stats.

The items in the newitems, olditems and allitems arrays, have the following format:

  • For numeric values:
{
	t : 1496843741554,   // Timestamp in unix format (ms)
	v : 12.34,           // Total value for this slot
	min : 2.4						 // Minimum of the accumulated values
	max : 4.5						 // Maximum of the accumulated values
	l : 4                // Values accumulated in this slot
}
  • for category values:
{
	t : 1496843741554,   // Timestamp in unix format (ms)
	v : {                // Total values for this slot
		"john" : 4,
		"david" : 6
	}
}

Note: Timestamp only appears in time windows.

Examples:

const SWindow = require("swstats");

// Calculates the variance
SWindow.register("numeric","variance",["stdev"],
	(currval,newitems,olditems,allitems,newstats,oldstats)=>{
		return Math.pow(newstats.stdev.stdev,2);
	}
);

// A Weighted sum, where value decreases as more items are pushed
// to the window
SWindow.register("numeric","decsum",[],
	(currval,newitems,olditems,allitems,newstats,oldstats)=>{
		currval = currval || {};
		if(!currval.ratio) currval.ratio = 0.99;
		if(!currval.weight) currval.weight = 1.0;
		if(!currval.sum) currval.sum = 0;

		var oldWeight = currval.weight;
		var newWeight = oldWeight * currval.ratio;

		var olen = olditems.length;
		var nlen = newitems.length;

		// Adds the new items, and append the weight values, so
		// we can fetch them in the substract phase
		for(let i=0;i<nlen;i++) {
			newitems[i].weight = newWeight;
			currval.sum += newitems[i].v * newWeight;
		}

		// Substract the removed items
		for(let i=0;i<olen;i++) {
			currval.sum -= olditems[i].v * olditems[i].weight;
		}

		currval.weight = newWeight;
		return currval;
	}
);

var sw = new SWindow.SizeStats(10,{
	ops : ["variance","decsum"],
	stats : {decsum : {ratio : 0.99}}
});

for(let i=0;i<100;i++) {
	sw.push(10);
	sw.push(5);
}
console.log(sw.stats);

Results:

{ decsum:
   { ratio: 0.99,
     weight: 0.13397967485796175,
     sum: 10.53536530978331 },
  avg: 7.5,
  stdev: { avg: 7.5, sqsum: 625, sum: 75, stdev: 2.5 },
  variance: 6.25 }