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 🙏

© 2025 – Pkg Stats / Ryan Hefner

d3-scale-break

v0.1.11

Published

Domain/range scale breaking for handling highly skewed data in d3

Downloads

16,656

Readme

What is this?

d3-scale-break is a plugin that removes the complexity from adding scale breaks to graphics built in Mike Bostock's D3. It's useful when working with highly skewed data that can't be handled through normal continuous scales.

d3-scale-break allows multiple scales to be defined for a single dataset. It does this by interpolating an array of input domains (one for each scale) to a single range.

This:

d3.scaleLinear()
    .domain([0, 10000])
    .range(0, 100);

Becomes this:

d3sB.scaleLinear()
    .domain([ [0, 1000], [1000, 10000] ])
    .scope([ [0, 0.5], [0.5, 1] ])
    .range([0, 100]);

The .scope method denotes the percentage of the range that each subscale should occupy – in the above example it's 50% for each domain. You can denote gaps in the range with scope too, eg: .scope([ [0, 0.2], [0.5, 1] ]).

The plugin wraps d3-scale and d3-axis and provides all the same methods and functionality (with a couple caveats explained below).

This initial version only works with scaleLinear, but the plugin wrap's d3-scale's continuous.js and, if demand arises, by design could be expanded to function with the other continuous scales.

For usage examples, check out this blog post.

Installation

npm i -S d3-scale-break

This will install d3-scale, d3-axis and d3-interpolate as dependencies, but you'll need to install any other D3 libraries separately.

API Reference

scaleLinear

scaleLinear([[domain, ]range])

Defines a continuous linear scale in exactly the same fashion as d3.scaleLinear with a specified domain and range.

# continuous.domain([domain])

Domain, if specified, should be an array of subdomains, each of which will set a subscale's domain to the specified array of numbers. All of the subdomains, collectively will interpolate to a single range. Domains can overlap or have gaps between them. Data points that fall into gaps between subdomains will not be render, but those outside of the entire array of subdomains will be treated according to standard d3.clamp() settings.

Unlike D3's standard domain, d3-scale-break does not support domain polymapping, thus each subdomain array must contain only two numbers, aka its extent.

d3sB.scaleLinear()
    .domain([ [0, 1000], [1000, 10000], [10000, 100000] ]);

If no domain is specified, the method returns the current array of subdomains.

# continuous.scope([scope])

Scope, if specified, should be an array of subscopes. Each subscope should be an array of two values between 0 and 1, representing the percentage of the range that the scope's respective subscale should occupy.

d3sB.scaleLinear()
    .domain([ [-10000, -100], [-100, 100], [100, 10000] ])
    .scope([ [0, 0.25], [0.25, 0.75], [0.75, 1] ])
    .range([0, 1000])

In the above example, the scope method delineates that within the range of 0 to 1000, the first subdomain should occupy the first 25% (0-250), the second subdomain the middle 50% (250-750) and the final subdomain the final 25% (750-1000).

If no scope is specified, the method returns the current array of subscopes.

# continuous.range([range])

There is no difference between D3's range method and d3-scale-break's – all subscales will be interpolated to the single range.

axis

Axis in d3-scale-break is designed to operate exactly like the d3.axis and is instantiated using the same four method names: d3sB.axisLeft, d3sB.axisRight, d3sB.axisTop and d3sB.axisBottom.

Under the hood, d3-scale-break generates a new d3.axis for each of subscales defined in the given scale:

const x = d3sB.scaleLinear()
    .domain([ [0, 1000], [1000, 10000] ])
    .scope([ [0, 0.5], [0.5, 1] ])
    .range([0, 100])

const xAxis = d3sB.axisLeft(x)

In the above example, two axes will be drawn, one for each of the subscales. Each axis will also be given a class pertaining to its index (eg. .axis-0 and .axis-1) so that they can be styled individually.

All of the standard d3.axis methods still exist. However, you have the ability to either pass a single set of parameters to them that will be transposed to each subaxis, or you can pass an array of parameters for each subaxis. For example, given two subaxis, you could pass in:

d3sB.axisTop(x).tickVales([1,5,10], [10,100,1000,10000]);

# The exceptions to these rules are ticks, which will only allow you to pass the same rules for ticks to each subaxis and will not accept an array of subaxis parameters (use tickArguments, tickFormat, etc to bypass this) and scale, which cannot be used to set a new scale (do so instead on instantiation) and only returns the current array of subscales.

One major difference between d3-scale-break axis and D3's axis is that you must define your scale's domain before you pass it to your axes, otherwise the plugin will not know how many subaxes to generate.

breakers

d3-scale-break provides two helper methods most useful for dealing with dynamic data.

# d3sB.breakDomain(data, breakpoints)

Pass an array of values – the dataset you intend to interpolate with your scale – and an array of breakpoints to d3sB.breakDomain and it will return an array of subdomains that you can pass to your scale. For instance:

const data = [-1000, -965, -400, -20, 40, 99, 325];
const breakpoints = [-100, 100];

const domain = d3sB.breakDomain(data, breakpoints)
// [ [-1000, -965, -400], [-20, 40, 99], [325] ]

I've found it is useful to use a one dimensional clustering algorithm – like something found in the Simple Statistics library – to define your break points.

# d3sB.breakScope(data, breakpoints)

Pass a an array of values (the dataset you intend to interpolate with your scale) and an array of breakpoints to d3sB.breakScope and it will return an array of subscopes that you can pass to your scale. The percentage given to each subscope will be defined by the number of datapoints that fall within its expected subscale. For instance:

const data = [-1000, -965, -400, -20, 40, 99, 325];
const breakpoints = [-100, 100];

const scope = d3sB.breakScope(data, breakpoints)
// [ [0, 0.43], [0.43, 0.86], [0.86, 1] ]

# d3sB.breakData(data, breakpoints)

This is simply a wrapper method for breakDomain and breakScope that returns them both in the format:

{
    domain: Array<Array>,
    scope: Array<Array>
}

Again, check out examples of how to use d3-scale-break here.