当范围为0-1或0-2时,仅在d3.js中沿y轴显示整个值

时间:2012-11-22 12:53:42

标签: javascript d3.js

我正在使用d3js来显示网站视图的实时表示。为此,我使用堆栈布局,目前我通过JSON更新我的数据集。

当y轴上仅显示1或2个视图时,其与图表中的视图量动态相关,轴标签为:1 => 0, 0.2, 0.4, 0.6, 0.8, 1,轴标签为:2 => 0, 0.5, 1, 1.5, 2这对我的数据集毫无意义,因为它显示了一个页面的视图,而你不能有一半的视图。

我在d3js中有一个线性刻度,我的y轴基于

var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height]); 

根据the documentation of rangeRound(),我应该只从这个尺度中获得全部价值。为了绘制我的轴,我使用:

var y_axis = svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(0,0)")
    .call(y_inverted.axis = d3.svg.axis()
        .scale(y_inverted)
        .orient("left")
        .ticks(5));

因为它是实时应用程序,所以我每秒都会通过调用:

来更新它
function update(){
    y_inverted.domain([yStackMax, 0]);
    y_axis.transition()
        .duration(interval)
        .ease("linear")
        .call(y_inverted.axis);
}

yStackMax是从stacklayout计算出来的,据我所知,用于y值的数据只包含整数。

var yStackMax = d3.max(layers, function(layer) {
    return d3.max(layer, function(d) {
        return d.y0 + d.y;
    });
});

我已经尝试了几件事来为我的y轴获得一个合适的值。

d3.svg.axis()
    .scale(y_inverted)
    .orient("left")
    .ticks(5).tickFormat(d3.format(",.0f"))

给我最近的沙发,但它仍显示0, 0, 0, 1, 1, 1

基本上我想要的是yStackMax为1时只有1个刻度,当它为2时有2个刻度,但如果yStackMax为12或1,000,000

它也应该有效

5 个答案:

答案 0 :(得分:12)

简短回答:您可以动态设置刻度数。将其设置为1以仅显示两个刻度标签:

var maxTicks = 5, minTicks = 1;
if (yStackMax < maxTicks) {
    y_axis.ticks(minTicks)
}
else {
    y_axis.ticks(maxTicks)
}

长答案(稍微偏离主题): 在玩你的例子时,我想出了一个相当“完整的解决方案”来解决你的所有格式问题。随意使用它:)

var svg = d3.select("#svg")
var width = svg.attr("width")
var height = svg.attr("height")
var yStackMax = 100000
var interval = 500
var maxTicks = 5
var minTicks = 1

var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height])
var defaultFormat = d3.format(",.0f")

var format = defaultFormat

var y_axis = d3.svg.axis()
    .scale(y_inverted)
    .orient("left")
    .ticks(minTicks)
    .tickFormat(doFormat)

var y_axis_root;
var decimals = 0;

function countDecimals(v){
    var test = v, count = 0;
    while(test > 10) {
        test /= 10
        count++;
    }
    return count;
}

function doFormat(d,i){
    return format(d,i)
}

function init(){
    y_axis_root = svg.append("g")
        .attr("class", "y axis")
        // I modified your example to move the axis to a visible part of the screen
        .attr("transform", "translate(150,0)")
        .call(y_axis)
}

// custom formatting functions:
function toTerra(d) { return (Math.round(d/10000000000)/100) + "T" }
function toGiga(d)  { return (Math.round(d/10000000)/100) + "G" }
function toMega(d)  { return (Math.round(d/10000)/100) + "M" }
function toKilo(d)  { return (Math.round(d/10)/100) + "k" }

// the factor is just for testing and not needed if based on real world data
function update(factor){
    factor = (factor) || 0.1;
    yStackMax*=factor
    decimals = countDecimals(yStackMax)
    console.log("yStackMax decimals:",decimals, factor)
    if     (yStackMax < maxTicks) {
        format = defaultFormat
        y_axis.ticks(minTicks)
    }
    else {
        y_axis.ticks(maxTicks)
        if     (decimals < 3 ) format = defaultFormat
        else if(decimals < 6 ) format = toKilo
        else if(decimals < 9 ) format = toMega
        else if(decimals < 12) format = toGiga
        else                   format = toTerra
    }

    y_inverted.domain([yStackMax, 0]);
    y_axis_root.transition()
        .duration(interval)
        .ease("linear")
        .call(y_axis);
}

init()
setTimeout(update, 200)
setTimeout(update, 400)
setTimeout(update, 600)

您可以与此html代码段一起试用:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script>
    </head>

    <body>
        <div><svg id="svg" width="200" height="300"></svg></div>
        <script src="axis.js"></script>
        <button id="button1" onclick="update(10)">+</button>
        <button id="button2" onclick="update(0.1)">-</button>
    </body>
</html>

我知道这有点偏离主题,但我通常喜欢提供运行示例/解决方案。将额外的格式化内容视为实际问题的奖励。

答案 1 :(得分:5)

如果你要求一定数量的刻度(通过axis.ticks()),那么d3会尝试给你很多刻度 - 但会尝试使用漂亮的值。它与您的数据无关。

您的解决方案是使用tickFormat,就像您一样,将所有值四舍五入为整数值,仅在Juve回答时请求一个滴答,或者使用axis.tickValues([...])明确设置滴答值,这将非常容易使用与d3.range

一起使用

rangeRound在这种情况下无济于事,因为它与比例的输出范围有关,在这种情况下是在0和高度之间绘制的像素偏移。

答案 2 :(得分:4)

脱离Superboggly的回答,这对我有用。首先,我使用y.domain().slice(-1)[0]从y域获得最大(最大)数字,然后使用d3.range()构建了一个滴答值数组...

  var y_max = y.domain().slice(-1)[0]
  var yAxis = d3.svg.axis()
      .scale(y)
      .tickValues(d3.range(y_max+1))
      .tickFormat(d3.format(",.0f"))

答案 3 :(得分:3)

或者只是让它们保持原样并且&#34;隐藏&#34;十进制数

d3.svg.axis()
    .scale(y_inverted)
    .orient("left")
    .ticks(5).tickFormat(function(d) {
              if (d % 1 == 0) {
                return d3.format('.f')(d)
              } else {
                return ""
              }
            });

答案 4 :(得分:1)

以下是代码:

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(d3.format(".2s"));