2

I have a chart that is tracking file size. I want to set up an axis (Y) that displays file size with a proper Mebibyte suffix ex: KiB, MiB, GiB etc.

Now, I am feeding in data in bytes so theoretically a simple .tickFormat() method followed by a d3.format() should do the trick. Unfortunately that's not the case, since file size is actually multiples of 1024 (or 2^20) and not typical SI 10^6. What that means is that my line starts to plot but the axis is all weird.

Take for example a file size of 587108352 and if round that up to nearest MB i get 560MiB (using the 1024 (2^20) multiplier).

How can I setup my axis to work with that?

I tried this:

yAxis = d3.axisLeft(y)
     .tickFormat(function (d) {
     return d3.format(scope.formatOptions.specifier)(d * scope.formatOptions.multiplier) + scope.formatOptions.suffix;
     });

Where format options object is:

vm.FormatObjectSize = {
        specifier: ".0s", // decimal notation with an SI prefix, rounded to significant digits.
        multiplier: 1, // none
        suffix: "b" // bytes
    };

That doesn't exactly work too well:

enter image description here

konrad
  • 3,544
  • 4
  • 36
  • 75
  • the Sugar.js library has a method to generalize the number to a string for you: https://sugarjs.com/docs/#/Number/bytes – theGleep Oct 05 '17 at 22:12

1 Answers1

3

You can use tickFormat with a function to convert the file size. Here, I'm using the function provided in this answer, which I named fileSize.

So, your tickFormat should be:

.tickFormat(function(d){
    return fileSize(d)
 }

Here is a demo, going from 0 to 587108352:

var scale = d3.scaleLinear()
  .domain([0, 587108352])
  .range([20, 470]);

var svg = d3.select("svg");

var gX = svg.append("g").attr("transform", "translate(0,50)")
  .call(d3.axisBottom(scale).tickValues(scale.ticks(5).concat(scale.domain())).tickFormat(function(d) {
    return fileSize(d)
  }))

function fileSize(bytes) {
  var thresh = 1024;
  if (Math.abs(bytes) < thresh) {
    return bytes + ' B';
  }
  var units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
  var u = -1;
  do {
    bytes /= thresh;
    ++u;
  } while (Math.abs(bytes) >= thresh && u < units.length - 1);
  return bytes.toFixed(1) + ' ' + units[u];
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="100"></svg>

As you can see, the last value for 587108352 is 559.9 Mebibytes, which is the correct value.

By the way, your question's title is a bit incorrect: what you want is actually KiB, MiB, GiB etc, not Kb, Mb, Gb...

Also, a file/storage size can be provided using either MB/GB/TB/etc or MiB/GiB/TiB/etc, it depends on the case. For instance, HD manufacturers normally use decimal (1000), while RAM manufacturers normally use 1024.

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171