Отображать только целые значения вдоль оси y в d3.js, когда диапазон равен 0-1 или 0-2

Я использую 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 вычисляется из 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

В основном то, что я хочу, состоит только в 1 тике, когда yStackMax составляет 1, 2 тика, когда он равен 2, но он также должен работать, если yStackMax составляет 12 или 1 000 000

Ответ 1

Короткий ответ. Вы можете динамически устанавливать количество тиков. Установите для параметра 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>

Я знаю, что это немного не по теме, но мне обычно нравится приводить примеры/решения. Обратите внимание на дополнительные материалы форматирования в качестве бонуса к реальной проблеме.

Ответ 2

Если вы запрашиваете определенное количество тиков (через axis.ticks()), то d3 попытается дать вам много тиков - но попытается использовать красивые значения. Это не имеет никакого отношения к вашим данным.

Ваши решения должны использовать tickFormat, как и вы, для округления всех значений до целочисленных значений, запрашивать только один тик, как ответил Юве, или явно установить значения тика с помощью axis.tickValues ​​([...]), который будет довольно легко использоваться в сочетании с d3.range

rangeRound не поможет в этом случае, потому что он относится к диапазону вывода шкалы, который в этом случае является смещением пикселя для построения графика: между 0 и высотой.

Ответ 3

Отключение 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"))

Ответ 4

Или просто пусть тики, как есть, и "спрятать" десятичные числа

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

Ответ 5

Вот код:

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