{"id":30141964,"url":"https://github.com/solzimer/swstats","last_synced_at":"2025-08-11T05:21:14.585Z","repository":{"id":65512086,"uuid":"93106703","full_name":"solzimer/swstats","owner":"solzimer","description":"Time and Size sliding window, numeric and category statistics","archived":false,"fork":false,"pushed_at":"2018-07-14T14:52:49.000Z","size":115,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-09T16:56:06.913Z","etag":null,"topics":["avg","count","deviation","frequency","size","slide","sliding-windows","stats","sum","time","window"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/solzimer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-01T23:06:37.000Z","updated_at":"2022-11-02T20:15:29.000Z","dependencies_parsed_at":"2023-01-26T19:25:11.622Z","dependency_job_id":null,"html_url":"https://github.com/solzimer/swstats","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/solzimer/swstats","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solzimer%2Fswstats","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solzimer%2Fswstats/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solzimer%2Fswstats/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solzimer%2Fswstats/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/solzimer","download_url":"https://codeload.github.com/solzimer/swstats/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solzimer%2Fswstats/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269803855,"owners_count":24477658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["avg","count","deviation","frequency","size","slide","sliding-windows","stats","sum","time","window"],"created_at":"2025-08-11T05:21:10.605Z","updated_at":"2025-08-11T05:21:14.565Z","avatar_url":"https://github.com/solzimer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# swstats\nSliding window statistics for Nodejs and browser.\n\n[![Build Status](https://travis-ci.org/solzimer/swstats.svg?branch=master)](https://travis-ci.org/solzimer/swstats)\n\nTime and Size sliding windows, capable of calculating incremental statistics, such as count, sum, avg, stdev, freq, etc...\n\n## Features\n* Nodejs and Browser compatible\n* Time or size windows (not both)\n* Global time or custom specified timestamp\n* Numeric or category stats\n* Stats are calculated incrementally, using online algorithms\n* Core category stats: sum, frequency, mode\n* Core numeric stats: count, sum, min, max, avg, stdev\n* Plugable custom stats functions\n* Time windows are slided each second\n\n## Installation\n```\nnpm install swstats\n```\n\n## Quick start\nNodejs:\n```javascript\nconst\tSWindow = require(\"swstats\");\n\n// Create a time window of 10 seconds. Each value pushed to\n// the window will be stored in a unique slot (step:1)\n// Window will slide on each second\nvar sw = new SWindow.TimeStats(10000,{step:1});\nvar i=0;\n\nsetInterval(()=\u003e{\n\tsw.push(Math.random());\n},1);\n\nsetInterval(()=\u003e{\n\tconsole.log(sw.stats);\n},100);\n```\nor browser:\n```html\n\u003c!doctype html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n\t\u003cscript src=\"swstats.min.js\"\u003e\u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\t\u003ctextarea id=\"values\" readonly=\"readonly\" cols=\"40\" rows=\"15\"\u003e\n\t\u003c/textarea\u003e\n\n\t\u003cscript\u003e\n\t\t// Create a time window of 10 secons.\n\t\t// Window will slide on each second\n\t\tvar sw = new SWindow.TimeStats(10000,{step:1});\n\t\tvar i=0;\n\t\tvar ta = document.getElementById(\"values\");\n\n\t\tsetInterval(()=\u003e{\n\t\t\tsw.push(Math.random());\n\t\t},10);\n\n\t\tsetInterval(()=\u003e{\n\t\t\tta.value = JSON.stringify(sw.stats,null,2);\n\t\t},100);\n\t\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe results will be something like:\n```javascript\n{ count: 9,\n  sum: 4.383476926850223,\n  avg: 0.48705299187224704,\n  max: 0.9865126881294235,\n  min: 0.0023880687887007923,\n  stdev:\n   { avg: 0.48705299187224704,\n     sqsum: 2.6772528231211963,\n     sum: 4.383476926850223,\n     stdev: 0.24546266317028345 } }\n\n....\n\n{ count: 994,\n  sum: 495.03542685194975,\n  avg: 0.4980235682615189,\n  max: 0.9865126881294235,\n  min: 0.0023880687887007923,\n  stdev:\n   { avg: 0.4980235682615189,\n     sqsum: 332.1783737578395,\n     sum: 495.03542685194975,\n     stdev: 0.29352342336095866 } }\n```\n\n## Time Window API\n### new SWindow.TimeStats(time,[options])\nCreates a new time window of *time* duration. The window will slide on each second.\n*options* is optional, and can take the following parameters:\n* **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).\n* **timestamp** Defines how time is measured. Can take the following values:\n\t* **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.\n\t* **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.\n* **type**: Value types for this window. Can take the values *\"numeric\"* or *\"category\"*. **By default, windows are numeric**.\n* **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\"]*\n* **stats**: Object with pre-initialized stats values.\n\nExample:\n```javascript\nconst SWindow = require(\"swstats\");\n\n// Time window of 10 seconds\nvar tw1 = new SWindow.TimeStats(10000,{\n\tstep:1000,      // Values will be accumulated in slots of 1 second\n\ttype:\"numeric\", // Numeric values\n\tops:[\"sum\"],    // We only want to sum values\n\tstats : {\n\t\tsum : 233     // Initial value where sum will start\n\t}\n});\n\n// Time window of 10 seconds with relative timestamp\nvar tw2 = new SWindow.TimeStats(10000,{\n\ttimestamp:TimeStats.TS.RELATIVE,\n\tstep:1000,      // Values will be accumulated in slots of 1 second\n\ttype:\"numeric\" // Numeric values\n});\n```\n\n### timeWindow.push(val)\nAdds a new value to the window. It will consume a new slot or be added to the current one, depending on the *step* value.\nIf timestamp mode is absolute, simply push the value, but if relative is activated, you must push\nan object with value and timestamp:\n```javascript\ntw1.push(10);\ntw2.push({ts:Date.now(),v:10});\n```\n### timeWindow.push([vals])\nAdds multiple values at once, in the same manner as *push(val)*.\n\n### timeWindow.clean()\nResets window and stats.\n\n### timeWindow.pause()\nPauses the window, so no slide occurs, but no new values will be added.\n\n### timeWindow.resume(shift)\nResumes 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.\n\n### destroy()\nKill the window, so no new values will be added, and no slide will occur. You can still access to its stats.\n\n### timeWindow.length\nGets the current size of the window (number of slots)\n\n### timeWindow.stats\nGets the current calculated stats\n```javascript\n{ count: 9,\n  sum: 4.638591015963949,\n  max: 0.8745422003577794,\n  min: 0.04623850021742104,\n  avg: 0.5153990017737722,\n  stdev:\n   { avg: 0.5153990017737722,\n     sqsum: 3.044764166996456,\n     sum: 4.638591015963949,\n     stdev: 0.26957558983867996 } }\n```\n\n### timeWindow.window\nGets the current calculated stats per slot\n```javascript\n[ { t: 1517499437649,\n    sum: { David: 571, John: 404 },\n    freq: { David: 0.5856410256410256, John: 0.41435897435897434 },\n    mode: 'David',\n    threshold: { David: false, John: false } },\n  { t: 1517499438649,\n    sum: { John: 398, David: 580 },\n    freq: { John: 0.4069529652351738, David: 0.5930470347648262 },\n    mode: 'David',\n    threshold: { John: false, David: false } },\n  { t: 1517499439649,\n    sum: { David: 449, John: 274 },\n    freq: { David: 0.6210235131396957, John: 0.3789764868603043 },\n    mode: 'David',\n    threshold: { David: true, John: false } } ]\n```\n\n## Size Window API\n### new SWindow.SizeStats(size,[options])\nCreates 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:\n* **type**: \"numeric\" or \"category\"\n* **ops**: Same a s *TimeStats*.\n* **stats**: Object with pre-initialized stats values.\n\nExample:\n```javascript\nconst SWindow = require(\"swstats\");\n\n// Size window of 100 values\nvat sw = new SWindow.SizeStats(100,{\n\ttype:\"category\", // Category values\n\tops:[\"freq\"],    // We only want the value frequency\n\tstats : {\n\t\tfreq : {       // Initial value where freq will start\n\t\t\t\"john\" : 0.3,\n\t\t\t\"david\" : 0.7\n\t\t}\n\t}\n});\n```\n\n### sizeWindow.push(val)\nAdds a new value to the window. It will consume a new slot.\n\n### sizeWindow.push([vals])\nAdds 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:\n```javascript\nsizeWindow.push([\"value1\",\"value2\"]);\n```\nwill consume only one slot, whereas:\n```javascript\nsizeWindow.push(\"value1\");\nsizeWindow.push(\"value2\");\n```\nwill consume two slots.\n\n### sizeWindow.clean()\nResets window and stats.\n\n### sizeWindow.length\nGets the current size of the window (number of slots)\n\n### sizeWindow.stats\nGets the current calculated stats\n\n### timeWindow.window\nGets the current calculated stats per slot\n\n## Custom Statistics API\n### SWindow.register(type,name,deps,fn,def)\nIt 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:\n* **type**: *\"numeric\"* or *\"category\"*\n* **name**: Name of the operation\n* **deps**: Array of dependencies. If the custom function depends on previous stats to be performed, you can pass the names in this array.\n* **fn**: The stats function. It will be described later.\n* **default**: If true, new created windows without the *ops* options specified, will perform by default this stats operation.\n\n### The stats function *fn*\n**fn(currval,newitems,olditems,allitems,newstats,oldstats)**\n\nThe stats function passed to the *register* method takes the following arguments:\n* **currval**: The current stats value for your function, prior to the next calculation when an item has been added.\n* **newitems**: Array of new values added to the window since the last function call.\n* **olditems**: Array of removed items from the window since the last function call.\n* **allitems**: Array of all the values that are currently in the window (allitems includes newitems and excludes olditems)\n* **newstats**: Current calculated stats by the functions called before this.\n* **oldstats**: Previous calculated stats.\n\nThe items in the newitems, olditems and allitems arrays, have the following format:\n* For numeric values:\n```javascript\n{\n\tt : 1496843741554,   // Timestamp in unix format (ms)\n\tv : 12.34,           // Total value for this slot\n\tmin : 2.4\t\t\t\t\t\t // Minimum of the accumulated values\n\tmax : 4.5\t\t\t\t\t\t // Maximum of the accumulated values\n\tl : 4                // Values accumulated in this slot\n}\n```\n* for category values:\n```javascript\n{\n\tt : 1496843741554,   // Timestamp in unix format (ms)\n\tv : {                // Total values for this slot\n\t\t\"john\" : 4,\n\t\t\"david\" : 6\n\t}\n}\n```\n**Note**: Timestamp only appears in time windows.\n\nExamples:\n```javascript\nconst SWindow = require(\"swstats\");\n\n// Calculates the variance\nSWindow.register(\"numeric\",\"variance\",[\"stdev\"],\n\t(currval,newitems,olditems,allitems,newstats,oldstats)=\u003e{\n\t\treturn Math.pow(newstats.stdev.stdev,2);\n\t}\n);\n\n// A Weighted sum, where value decreases as more items are pushed\n// to the window\nSWindow.register(\"numeric\",\"decsum\",[],\n\t(currval,newitems,olditems,allitems,newstats,oldstats)=\u003e{\n\t\tcurrval = currval || {};\n\t\tif(!currval.ratio) currval.ratio = 0.99;\n\t\tif(!currval.weight) currval.weight = 1.0;\n\t\tif(!currval.sum) currval.sum = 0;\n\n\t\tvar oldWeight = currval.weight;\n\t\tvar newWeight = oldWeight * currval.ratio;\n\n\t\tvar olen = olditems.length;\n\t\tvar nlen = newitems.length;\n\n\t\t// Adds the new items, and append the weight values, so\n\t\t// we can fetch them in the substract phase\n\t\tfor(let i=0;i\u003cnlen;i++) {\n\t\t\tnewitems[i].weight = newWeight;\n\t\t\tcurrval.sum += newitems[i].v * newWeight;\n\t\t}\n\n\t\t// Substract the removed items\n\t\tfor(let i=0;i\u003colen;i++) {\n\t\t\tcurrval.sum -= olditems[i].v * olditems[i].weight;\n\t\t}\n\n\t\tcurrval.weight = newWeight;\n\t\treturn currval;\n\t}\n);\n\nvar sw = new SWindow.SizeStats(10,{\n\tops : [\"variance\",\"decsum\"],\n\tstats : {decsum : {ratio : 0.99}}\n});\n\nfor(let i=0;i\u003c100;i++) {\n\tsw.push(10);\n\tsw.push(5);\n}\nconsole.log(sw.stats);\n```\n\nResults:\n```javascript\n{ decsum:\n   { ratio: 0.99,\n     weight: 0.13397967485796175,\n     sum: 10.53536530978331 },\n  avg: 7.5,\n  stdev: { avg: 7.5, sqsum: 625, sum: 75, stdev: 2.5 },\n  variance: 6.25 }\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolzimer%2Fswstats","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolzimer%2Fswstats","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolzimer%2Fswstats/lists"}