Open-source project
welch/tdigest avatar
welch/tdigest

tdigest: Dunning's streaming quantile sketch in JavaScript, still at 0.1.3

tdigest: javascript implementation of Dunning's T-Digest for streaming quantile approximation

74 stars12 forksJavaScriptMIT

At a glance

What is it?
tdigest is a JavaScript implementation of the T-Digest, a data structure for approximating the distribution of a stream of real numbers. The algorithm makes no guarantees by its own admission, and the changelog in the README records an API rename, a discrete mode and a release whose only content was a badge.
Who is it for?
tdigest suits a browser or Node project that needs approximate percentiles over a stream and does not need a documented accuracy bound, since that is the trade the algorithm itself offers. It does not suit a project that wants a maintained dependency with a versioned release trail, because the package sits at 0.1.3, the changelog lives only in the README, and the development toolchain is several major versions behind current.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 58 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • welch/tdigest74 ★ · +2
Star history
Repository2026-08-262026-10-22
welch/tdigest7274

2 Stars in the last 57 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What the T-Digest is, in the project's own words

The definition comes first and it is carefully hedged. A T-Digest is a data structure and algorithm for constructing an approximate distribution for a collection of real numbers presented as a stream. Then the caveat that shapes every use: the algorithm makes no guarantees, but behaves well enough in practice that implementations have been included in Apache Mahout and ElasticSearch for computing summaries and approximate order statistics over a stream. Two named references follow. The overview recommended for behaviour is a blog post about percentile and quantile estimation of big data, written against a Python implementation, and the deeper material is the paper itself plus a reference implementation in Java. This implementation is described as based on a reading of the paper, with some boundary and performance tweaks, which is a candid way of saying it was ported by reading rather than by porting code.

The newest release changed a badge and nothing else

The changelog is in the README, newest first, and it reads like a project that has been stable for years rather than one that ships features. Version 0.1.3 contains no code changes at all: the npm version and downloads badges now load over https so they render on the registry page, and the dead Travis CI badge and its configuration were removed. Version 0.1.2 bumped the bintree dependency to 1.0.2 specifically to pick up its licensing declaration. Version 0.1.1 made percentile on an empty digest return undefined, or an array of undefined values, instead of NaN, upgraded bintrees for a bugfix, and corrected the discrete percentile and percentile-rank boundary conditions to match the standard definition. That sequence of three releases is documentation, a licence fix and a correctness fix, and the last push to the default branch master is dated 2026-08-26.

Discrete mode changes what a percentile means

The most consequential option arrived in 0.1.0 and it is easy to skim past. When a digest is created with delta set to false, the sample distribution is treated as discrete: T-Digest behaviour is switched off, differing samples are never merged, and they need not even be numeric, and percentiles are reported as the nearest exact data value rather than as an interpolation between two of them. That is a different question being answered, and for counts, latencies or any integer-valued measurement it is often the one you actually wanted. The same release describes the default path: a digest starts in exact histogram mode, stays in exact mode while the number of distinct values is reasonable, and switches automatically into T-Digest mode once the sample is large and looks like it came from a continuous distribution.

The API is push, compress, percentile

The 0.1.0 release renamed the central method, and the quickstart shows the current shape. Quantile became p_rank, for percentile rank, and both percentile and p_rank now accept an array or a single value:

javascript
var TDigest = require('tdigest').TDigest;
var x=[], N = 100000;
for (var i = 0 ; i < N ; i += 1) {
    x.push(Math.random() * 10 - 5);
};
td = new TDigest();
td.push(x);
td.compress();
console.log(td.summary());
console.log("median ~ "+td.percentile(0.5));

The other renames came earlier, in 0.0.5, when the API was overhauled: asArray became toArray, redigest became compress, and digest became push. That release also changed a behaviour that surprises people, since pushing an array no longer triggers compression on its own, which is why the example calls compress explicitly before asking for a summary. A compression step is the point at which clusters are merged, so skipping it after a large push means asking a question of a digest that has not been digested yet.

The browser build is generated, not committed

There is a browser story and it is a build task rather than a checked-in artefact. A grunt dist task was added in 0.0.7 to create a UMD-wrapped version of the digest and its dependencies, so it can be imported as a standalone module in client-side JavaScript, and the task is configured to emit a self-contained file at dist/tdigest.js. The documented way to use it is a script tag followed by inline code:

code
<script src="dist/tdigest.js"></script>
<script>
    var td = new this.tdigest.TDigest();
    for (var i=0; i < 1000000; i++) {
        td.push(Math.random());
    }
    td.compress();
    document.write(td.summary())
</script>

The top level of the repository holds ten entries and no dist directory, so that file exists only after you run the build, which is also why the package's main entry is the single source file at the root rather than a bundled output. A distributions file and a gruntfile sit beside it, and two example files, one for Node and one for the browser, are shipped as references.

One pinned dependency and a dated toolchain

The runtime dependency list is a single entry, bintrees, pinned to exactly 1.0.2, and the reason it was pinned to that version is recorded in the 0.1.2 entry: the bump was to pick up the library's licensing declaration. Pinning the tree structure library exactly, and letting the development tools float, is a deliberate split. The development side is where the age shows. The test runner is mocha at a version 2 range, the assertion library is chai at a version 3 range, the task runner is grunt at a version 0.4 range, with a pure CommonJS transform and an assertion helper alongside them. The test script itself is a single line that points mocha at a specs directory. Nothing here needs a build step to run under Node, which is a real advantage for a library, but the toolchain is old enough that a fresh install is where friction would appear first.

No releases, so the changelog is the README

The project publishes no GitHub releases, which means the version history exists in exactly one place: the changelog section of the README, and the version itself lives in the package manifest at 0.1.3. That is a workable arrangement for a package consumed from the registry, and an awkward one for anyone auditing what changed, since there is no tag to diff and no dated entry beyond the version numbers. The accuracy question is delegated in the same way. The project says the algorithm makes no guarantees, and the behaviour discussion is pointed at a blog post and a paper rather than at measurements in the repository, so there is no error table to check and no convergence parameter to inspect from here. The tree is correspondingly small: a licence, a readme, the source file, the examples, the specs, a gruntfile, a distributions file and a package manifest.

Editorial conclusion

tdigest suits a browser or Node project that needs approximate percentiles over a stream and does not need a documented accuracy bound, since that is the trade the algorithm itself offers. It does not suit a project that wants a maintained dependency with a versioned release trail, because the package sits at 0.1.3, the changelog lives only in the README, and the development toolchain is several major versions behind current. Before adopting it, read the discrete mode section to know whether a percentile will be interpolated or returned as an exact stored value, and remember the package is a single root file with one pinned runtime dependency, so a build step is the only thing standing between you and a browser copy.

Frequently asked questions

What is TDIGEST?

A T-Digest is a data structure and algorithm for constructing an approximate distribution for a collection of real numbers presented as a stream. The algorithm makes no guarantees, but it behaves well enough in practice that implementations have been included in Apache Mahout and ElasticSearch for summaries and approximate order statistics.

What is the tdigest package on npm?

It is a JavaScript implementation of Dunning's T-Digest for streaming quantile approximation, based on a reading of the paper with boundary and performance tweaks. It is published as tdigest, MIT licensed, and its API is a push method, an explicit compress step, and percentile and p_rank queries that accept arrays or single values.

How does t-digest compare to q-digest?

The repository makes no such comparison. Its stated references are the T-Digest paper, the Java reference implementation, and a blog post about a Python implementation, so nothing here describes how it compares to a Q-digest or to any other sketch.

Official sources

  1. Official README
  2. Project repository
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/welch-tdigest.svg)](https://hysenlabs.com/projects/welch-tdigest)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/welch-tdigest.svg)](https://hysenlabs.com/projects/welch-tdigest)