13

我正在使用 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]); 

根据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 是根据堆栈布局计算的,据我所知,用于 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

基本上我想要的是在 1 时只有 1 个刻度yStackMax,在 2 时只有 2 个刻度,但如果yStackMax是 12 或 1,000,000 ,它也应该工作

4

5 回答 5

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>

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

于 2012-11-23T15:08:48.723 回答
5

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

您的解决方案是像您一样使用 tickFormat 将所有值四舍五入为整数值,只要求 Juve 回答的一个刻度,或者使用axis.tickValues([...])显式设置刻度值,这会很漂亮易于与d3.range结合使用

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

于 2012-11-26T14:36:55.113 回答
4

脱离 Superboggly 的回答,这对我有用。首先,我从 y 域中获得了最大(最大)数y.domain().slice(-1)[0],然后我使用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"))
于 2012-12-27T22:58:16.427 回答
3

或者只是让刻度保持原样并“隐藏”十进制数字

d3.svg.axis()
    .scale(y_inverted)
    .orient("left")
    .ticks(5).tickFormat(function(d) {
              if (d % 1 == 0) {
                return d3.format('.f')(d)
              } else {
                return ""
              }
            });
于 2015-06-15T08:05:56.213 回答
1

这是代码:

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(d3.format(".2s"));
于 2015-08-04T16:34:02.497 回答