d3js-创建自定义刻度-正对数和负对数

d3js - Create custom scale - Positive and negative logarithm

本文关键字:创建 自定义 d3js-      更新时间:2023-10-27

我目前正在进行一个d3项目,我正在尝试显示具有大量正值和负值的条形图。

我在网上看到了一个使用d3.scale.sqrt()或显示两个对数刻度的漫游,但我想知道我是否可以创建自己的刻度。

我想到的是负值的对数刻度、[-e,e]之间的线性刻度和正值的常规对数刻度的混合。看起来可能是这样的:http://img15.hostingpics.net/pics/12746197ln.png

y = -log(-x)  if x < -e
y = x/e       if -e <= x <= e
y = log(x)    if x > e

你认为这可能吗?我还创建了一个JSFiddle来总结它。

谢谢,

以下是我找到的一个解决方案:JSFiddle

这可能是一种奇怪的方式,但我认为它正在发挥作用,如果你有任何关于改进的建议,不要犹豫。我想我犯了错误,尤其是在日志和记号上。

这是我基于d3.js本身创建的函数。

(function() {
  scalepnlog = {
    init: function(){
      return d3_scale_pnlog(d3.scale.linear().domain([ 0, 1 ]), [ 1, 10 ]);
    }
  }
  function d3_scaleExtent(domain) {
    var start = domain[0], stop = domain[domain.length - 1];
    return start < stop ? [ start, stop ] : [ stop, start ];
  }
  var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
    floor: function(x) {
      return -Math.ceil(-x);
    },
    ceil: function(x) {
      return -Math.floor(-x);
    }
  };
  function sign(x){
    return x >= 0 ? 1:-1;
  }

  function d3_scale_pnlog(linear, domain) {
    function pnlog(x) {
      return (x >= Math.E || x <= -Math.E) ? sign(x)*Math.log(Math.abs(x)) : x/Math.E;
    }
    function pnpow(x) {
      return (x >= 1 || x <= -1 )? sign(x)*Math.pow(Math.E,Math.abs(x)) : Math.E*x;
    }
    function scale(x) {
      return linear(pnlog(x));
    }
    scale.invert = function(x) {
      return pnpow(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      linear.domain((domain = x.map(Number)).map(pnlog));
      return scale;
    };
    scale.nice = function() {
      var niced = d3_scale_nice(domain.map(pnlog), positive ? Math : d3_scale_logNiceNegative);
      linear.domain(niced);
      domain = niced.map(pow);
      return scale;
    };
    scale.ticks = function() {
      var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(pnlog(u)), j = Math.ceil(pnlog(v)), n = 10 % 1 ? 2 : 10;
      if (isFinite(j - i)) {
        for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pnpow(i) * k);
        ticks.push(pnpow(i));
        for (i = 0; ticks[i] < u; i++) {}
        for (j = ticks.length; ticks[j - 1] > v; j--) {}
        ticks = ticks.slice(i, j);
      }
      return ticks;
    };
    scale.tickFormat = function(n, format) {
      if (!arguments.length) return d3_scale_logFormat;
      if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
      var k = Math.max(1, 10 * n / scale.ticks().length);
      return function(d) {
        var i = d / pnpow(Math.round(pnlog(d)));
        if (i * 10 < 10 - .5) i *= 10;
        return i <= k ? format(d) : "";
      };
    };
    scale.copy = function() {
      return d3_scale_pnlog(linear.copy(), domain);
    };
    return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
  }
})();

我真的不知道自己在做什么,但基本上,我创建了pnlogpnpow,它们是相互的,并添加了所需的不同d3函数,直到它工作为止。

它们在这里:

 function pnlog(x) {
   return (x >= Math.E || x <= -Math.E) ? sign(x)*Math.log(Math.abs(x)) : x/Math.E;
 }

function pnpow(x) {
  return (x >= 1 || x <= -1 )? sign(x)*Math.pow(Math.E,Math.abs(x)) : Math.E*x;
}