plotly.js icon indicating copy to clipboard operation
plotly.js copied to clipboard

Bug report & feature request: Formatting numbers with natural language suffixes, k(ilo), M(illion), B(illion)

Open soerenwolfers opened this issue 3 years ago • 1 comments

Plotly doesn't currently let me format numbers, e.g. in hover templates, in "human" or "business" form, by which I mean essentially SI representation but with the suffix replacements k->k(ilo) M->M(illion), G->B(illion), T->T(trillion),.... (by pure luck, this is really just one necessary replacement in the range I've listed).

The reason seems to be that plotly relies on d3-format, which claims to "format numbers for human consumption" but misses the one format non-technically trained humans can actually consume at ease.

However, oddly plotly does use the "human readable" form by default for the X and Y columns in default hover displays, even when they're included in the hover templates manually as %{y}, but not for custom data included via %{customdata[0]} (see the MWE below). This in some sense makes this a bug: You want to include two columns of data in the same way, using the same format specifier, but you get different results.

Instead of waiting on d3, I would like to make this feature request: I believe plotly should default for custom data to the same formatting it uses by default elsewhere (including axes labels), unless a format specifier is explicitly listed, thus increasing consistency.

Example: Despite using text=y and hovertemplate ='%{y}<br>%{text}', I get different representations:

import plotly.graph_objects as go
y = [2, 100000, 2000000, 2000000000]
fig = go.Figure(go.Scatter(
    x = [1,2,3,4],
    y = y,
    text = y,
    hovertemplate ='%{y}<br>%{text}'
))
fig.show()

image

Similar requests elsewhere: https://github.com/plotly/plotly.py/issues/1222

https://stackoverflow.com/questions/68005050/b-billions-instead-of-g-giga-in-python-plotly-customdata-si-prefix-d3

https://stackoverflow.com/questions/17037023/how-to-get-localizable-or-customizable-si-codes-with-d3-format

https://community.plotly.com/t/custom-si-unit-prefixes/29739/4

https://stackoverflow.com/questions/40774677/d3-formatting-tick-value-to-show-b-billion-instead-of-g-giga

https://github.com/d3/d3/issues/2241

https://stackoverflow.com/questions/64313173/how-to-hover-number-formatting-to-k-or-m-accordingly-in-r-plotly-pie-charts-and

https://github.com/d3/d3-format/issues/71

https://github.com/d3/d3-format/pull/81

https://github.com/d3/d3-format/pull/96

soerenwolfers avatar Jan 21 '22 19:01 soerenwolfers

One option is to selectively override d3.format in your application, adding rules your customer base would prefer. The below code overrides d3.format to define a whimsical custom format , and fall back to standard d3.format otherwise

var _d3format = d3.format

d3.format = function(fmt) {
  var getMagnitude = function(v) { return Math.floor(Math.log10(v)/3)}
  var prefixes = ['do','di','re','ri','mi','fa','fi','so','si','la','li','ti','do']
  var getPrefix = function(mag) { return prefixes[mag]}

  if (fmt == 'solfege') {
    return function(val) {
      var str = _d3format('s')(val)
      var mag = getMagnitude(val)          
      if (mag>4 && mag<4+prefixes.length) {
        var base = str.slice(0,3)
        var pre = getPrefix(mag-4)
        return base + ' '+ pre + "zillions"
      } 
      else return _d3format("0.3s")(val)
    } 
  }
  else return _d3format(fmt)
}

e.g. d3.format('solfege')(3403940323489323092394829839240) yields "340 fizillions"

Of course, be careful if your code is to be embedded in other code, where polluting d3 might cause issues with other libraries

pietersv avatar Jun 17 '22 02:06 pietersv