SciChart.js JavaScript 2D Charts API > Axis APIs > Axis Labels > Custom LabelProviders: Readable Numbers
Custom LabelProviders: Readable Numbers

Customisation in SciChart.js can go a level deeper than built-in label formatting by creating a custom labelprovider class.

In this page we're going to show a worked example of how we can create a custom label provider to handle formatting of numbers with thousands/millions commas, or to format large numbers such as 1,000 as 1K, 1,000,000 as 1M and 1,000,000,000 as 1Bn

To create a custom labelprovider to handle dynamic dates, first a class which inherits one of the LabelProvider classes listed here and override formatLabel or formatCursorLabel.

Inside the formatLabel function, determine which formatting to apply based on properties and format the label value.

In this example below, we show two ways to format large numeric values in SciChart.js.

Method 1: K,M,B,T formatting

Large numbers are formatted as follows:

  • 1,000 = 1K
  • 1,000,000 = 1M
  • 1,000,000,000 = 1B
  • 1,000,000,000,000 = 1T

Label prefix and postfix, decimal places are supported by passing ILabel2DOptions to the constructor of CustomNumericLabelProvider, which in turn passes to the base class constructor NumericLabelProvider.

Method 2: Comma formatting

Large numbers are formatted as follows:

  • 1000 => 1,000
  • 1000000 => 1,000,000
  • 1000000000 => 1,000,000,000

etc...

const {
  NumericLabelProvider
} = SciChart;

// A custom class which inherits NumericLabelProvider for advanced numeric formatting
// You can also inherit DateLabelProvider for date formatting
class CustomNumericLabelProvider extends NumericLabelProvider {

  customFormat = "Commas"; // Options are "Default", "Commas" and "KMBT"

  // Options accepts values from ILabel2DOptions or 'customFormat' e.g.
  // { customFormat: "Commas", labelPrefix: "$", labelPrecision: 2 } or { customFormat: "KMBT" }
  constructor(options) {
    super(options);
    this.customFormat = options?.customFormat ?? "Commas";
  }

  // Called for each label
  formatLabel(dataValue) {
    if (this.customFormat === "Default") {
      return super.formatLabel(dataValue);
    } else if (this.customFormat === "Commas") {
      // Format numbers using the default format, but with commas e.g. 1,000,000
      return this.formatNumberWithCommas(dataValue);
    } else if (this.customFormat === "KMBT") {
      // Format large numbers with K, M, B abbreviations e.g. 1.5M
      return this.formatNumberKMBT(dataValue);
    }
  }

  // Called for each tooltip/cursor label
  formatCursorLabel(dataValue) {
    return this.formatLabel(dataValue);
  }

  // Formats a label with commas e.g. 1000000 becomes 1,000,000
  formatNumberWithCommas(dataValue) {
    const labelValue = super.formatLabel(dataValue);
    const commasRegex = /\B(?=(\d{3})+(?!\d))/g;
    const output = labelValue.replace(commasRegex, ",");

    // Log what happened for educational purposes
    console.log(`formatNumberWithCommas: ${dataValue} => ${labelValue} => ${output}`);
    return output;
  }

  // Returns a number formatted as K, M, B, T for thousands, millions, billions, trillions
  formatNumberKMBT(dataValue) {
    // formatLabel applies decimal, significant figure formatting and adds prefix, postfix
    let originalLabel = super.formatLabel(dataValue);
    let result = originalLabel;
    // Now we need to inject K, M, B, T into the label before the postfix

    // e.g. formatLabel(1000000) with prefix="$", postfix="USD" = "$1000000 USD" => "$1M USD"
    if (dataValue >= 1_000_000_000_000) {
      result = super.formatLabel(dataValue / 1_000_000_000_000).replace(this.postfix, "T" + this.postfix);
    } else if (dataValue >= 1_000_000_000) {
      result = super.formatLabel(dataValue / 1_000_000_000).replace(this.postfix, "B" + this.postfix);
    } else if (dataValue >= 1_000_000) {
      result = super.formatLabel(dataValue / 1_000_000).replace(this.postfix, "M" + this.postfix);
    } else if (dataValue >= 1_000) {
      result = super.formatLabel(dataValue / 1_000).replace(this.postfix, "K" + this.postfix);
    }

    // Log what happened for educational purposes
    console.log(`formatNumberKMBT: ${dataValue} => ${originalLabel} => ${result}`);

    return result
  }

}

Applying the Custom LabelProvider to an Axis

Next, apply the custom LabelProvider to an axis as follows:

// Apply the custom labelprovider we created before to different axis

sciChartSurface.yAxes.add(new LogarithmicAxis(wasmContext, {
  axisTitle: "Y Axis with K,M,B,T abbreviations",
  // Enable K,M,B,T abbreviations for large labels
  labelProvider: new CustomNumericLabelProvider({
    customFormat: "KMBT",
    labelPrefix: "$",
    labelPostfix: " USD",
    labelPrecision: 2,
    labelFormat: ENumericFormat.SignificantFigures
  }),
  visibleRange: new NumberRange(1, 1e12)
}));

sciChartSurface.xAxes.add(new NumericAxis(wasmContext, {
  axisTitle: "X Axis with comma separators",
  // Enable comma formatting for large labels
  labelProvider: new CustomNumericLabelProvider({ customFormat: "Commas", labelPrecision: 1 }),
  visibleRange: new NumberRange(0, 1e10)
}));
const { wasmContext, sciChartSurface } = await chartBuilder.build2DChart(divElementId, {
  surface: { theme: { type: EThemeProviderType.Dark } },
  yAxes: {
    type: EAxisType.LogarithmicAxis,
    options: {
      axisTitle: "Y Axis with K,M,B,T abbreviations",
      // Enable K,M,B,T abbreviations for large labels
      labelProvider: new CustomNumericLabelProvider({
        customFormat: "KMBT",
        labelPrefix: "$",
        labelPostfix: " USD",
        labelPrecision: 2,
        labelFormat: ENumericFormat.SignificantFigures
      }),
      visibleRange: new NumberRange(1, 1e12)
    }
  },
  xAxes: {
    type: EAxisType.NumericAxis,
    options: {
      axisTitle: "X Axis with comma separators",
      // Enable comma formatting for large labels
      labelProvider: new CustomNumericLabelProvider({ customFormat: "Commas", labelPrecision: 1 }),
      visibleRange: new NumberRange(0, 1e10)
    }
  },
  modifiers: [
    { type: EChart2DModifierType.MouseWheelZoom },
    { type: EChart2DModifierType.ZoomPan }
  ]
});

This results in the following output:

  • When the property formatOptions = "Default", default numeric formatting is chosen
  • When the property formatOptions = "Commas", numbers are formatted with comma separators, e.g. 1,000,000
  • When the property formatOptions = "KMBT", large numbers are formatted as 1k, 1M, 1B
<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
// #region ExampleA
const {
  NumericLabelProvider
} = SciChart;

// A custom class which inherits NumericLabelProvider for advanced numeric formatting
// You can also inherit DateLabelProvider for date formatting
class CustomNumericLabelProvider extends NumericLabelProvider {

  customFormat = "Commas"; // Options are "Default", "Commas" and "KMBT"

  // Options accepts values from ILabel2DOptions or 'customFormat' e.g.
  // { customFormat: "Commas", labelPrefix: "$", labelPrecision: 2 } or { customFormat: "KMBT" }
  constructor(options) {
    super(options);
    this.customFormat = options?.customFormat ?? "Commas";
  }

  // Called for each label
  formatLabel(dataValue) {
    if (this.customFormat === "Default") {
      return super.formatLabel(dataValue);
    } else if (this.customFormat === "Commas") {
      // Format numbers using the default format, but with commas e.g. 1,000,000
      return this.formatNumberWithCommas(dataValue);
    } else if (this.customFormat === "KMBT") {
      // Format large numbers with K, M, B abbreviations e.g. 1.5M
      return this.formatNumberKMBT(dataValue);
    }
  }

  // Called for each tooltip/cursor label
  formatCursorLabel(dataValue) {
    return this.formatLabel(dataValue);
  }

  // Formats a label with commas e.g. 1000000 becomes 1,000,000
  formatNumberWithCommas(dataValue) {
    const labelValue = super.formatLabel(dataValue);
    const commasRegex = /\B(?=(\d{3})+(?!\d))/g;
    const output = labelValue.replace(commasRegex, ",");

    // Log what happened for educational purposes
    console.log(`formatNumberWithCommas: ${dataValue} => ${labelValue} => ${output}`);
    return output;
  }

  // Returns a number formatted as K, M, B, T for thousands, millions, billions, trillions
  formatNumberKMBT(dataValue) {
    // formatLabel applies decimal, significant figure formatting and adds prefix, postfix
    let originalLabel = super.formatLabel(dataValue);
    let result = originalLabel;
    // Now we need to inject K, M, B, T into the label before the postfix

    // e.g. formatLabel(1000000) with prefix="$", postfix="USD" = "$1000000 USD" => "$1M USD"
    if (dataValue >= 1_000_000_000_000) {
      result = super.formatLabel(dataValue / 1_000_000_000_000).replace(this.postfix, "T" + this.postfix);
    } else if (dataValue >= 1_000_000_000) {
      result = super.formatLabel(dataValue / 1_000_000_000).replace(this.postfix, "B" + this.postfix);
    } else if (dataValue >= 1_000_000) {
      result = super.formatLabel(dataValue / 1_000_000).replace(this.postfix, "M" + this.postfix);
    } else if (dataValue >= 1_000) {
      result = super.formatLabel(dataValue / 1_000).replace(this.postfix, "K" + this.postfix);
    }

    // Log what happened for educational purposes
    console.log(`formatNumberKMBT: ${dataValue} => ${originalLabel} => ${result}`);

    return result
  }

}
// #endregion

async function labelProviderClass(divElementId) {
  const {
    SciChartSurface,
    NumericAxis,
    LogarithmicAxis,
    SciChartJsNavyTheme,
    NumberRange,
    TextAnnotation,
    ENumericFormat,
    ECoordinateMode,
    EHorizontalAnchorPoint,
    ZoomPanModifier,
    MouseWheelZoomModifier
  } = SciChart;

  const addChartTitle = (sciChartSurface, titleText, subTitleText) => {
    // Note: we will be improving this shortly in scichart.js v3.1
    sciChartSurface.annotations.add(new TextAnnotation({
      text: titleText,
      x1: 0.5, y1: 0.5,
      yCoordShift: -50,
      xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative,
      horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
      opacity: 0.5,
      fontSize: 32,
      fontWeight: "Bold",
      textColor: "White",
    }));
    sciChartSurface.annotations.add(new TextAnnotation({
      text: subTitleText,
      x1: 0.5, y1: 0.5,
      xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative,
      horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
      opacity: 0.4,
      fontSize: 17,
      textColor: "White",
    }));
  };

  // or, for npm, import { SciChartSurface, ... } from "scichart"

  const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme()
  });

  addChartTitle(sciChartSurface, "Custom LabelProvider Class Example", "Shows how to custom format numeric labels using SciChart.js");

  // #region ExampleB

  // Apply the custom labelprovider we created before to different axis

  sciChartSurface.yAxes.add(new LogarithmicAxis(wasmContext, {
    axisTitle: "Y Axis with K,M,B,T abbreviations",
    // Enable K,M,B,T abbreviations for large labels
    labelProvider: new CustomNumericLabelProvider({
      customFormat: "KMBT",
      labelPrefix: "$",
      labelPostfix: " USD",
      labelPrecision: 2,
      labelFormat: ENumericFormat.SignificantFigures
    }),
    visibleRange: new NumberRange(1, 1e12)
  }));

  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, {
    axisTitle: "X Axis with comma separators",
    // Enable comma formatting for large labels
    labelProvider: new CustomNumericLabelProvider({ customFormat: "Commas", labelPrecision: 1 }),
    visibleRange: new NumberRange(0, 1e10)
  }));
  // #endregion

  // For the example, we add zooming, panning and an annotation so you can see how dates react on zoom.
  sciChartSurface.chartModifiers.add(new ZoomPanModifier(), new MouseWheelZoomModifier());
};

labelProviderClass("scichart-root");





async function builderExample(divElementId) {
  const {
    chartBuilder,
    ENumericFormat,
    EThemeProviderType,
    NumberRange,
    EAxisType,
    EChart2DModifierType
  } = SciChart;

  // or, for npm, import { chartBuilder, ... } from "scichart"

  // #region ExampleC
  const { wasmContext, sciChartSurface } = await chartBuilder.build2DChart(divElementId, {
    surface: { theme: { type: EThemeProviderType.Dark } },
    yAxes: {
      type: EAxisType.LogarithmicAxis,
      options: {
        axisTitle: "Y Axis with K,M,B,T abbreviations",
        // Enable K,M,B,T abbreviations for large labels
        labelProvider: new CustomNumericLabelProvider({
          customFormat: "KMBT",
          labelPrefix: "$",
          labelPostfix: " USD",
          labelPrecision: 2,
          labelFormat: ENumericFormat.SignificantFigures
        }),
        visibleRange: new NumberRange(1, 1e12)
      }
    },
    xAxes: {
      type: EAxisType.NumericAxis,
      options: {
        axisTitle: "X Axis with comma separators",
        // Enable comma formatting for large labels
        labelProvider: new CustomNumericLabelProvider({ customFormat: "Commas", labelPrecision: 1 }),
        visibleRange: new NumberRange(0, 1e10)
      }
    },
    modifiers: [
      { type: EChart2DModifierType.MouseWheelZoom },
      { type: EChart2DModifierType.ZoomPan }
    ]
  });
  // #endregion
};



// Uncomment this to use the builder example
  //builderExample("scichart-root");


  

The above example showcases how to apply custom or complex formatting to labels in SciChart.js.

Any formatting (dynamic or static) that you can think of can be applied using the LabelProvider API.

Custom labelproviders can then be set on individual X,Y axis of any type.

 

See Also