тики диапазона масштабирования d3 v4

#d3.js

#d3.js

Вопрос:

У меня есть данные, подобные следующим

дата, значения
2016-10-01,10
2016-10-02,20
2016-10-03,30
2016-10-04,5
2016-10-05,50
2016-10-06,2
2016-10-07,7
2016-10-08,17

и я генерирую столбчатую диаграмму, используя следующий код

 var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 800 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;

var parseDate = d3.timeParse("%Y-%m-%d");

var x = d3.scaleBand().range([0, width]);

var y = d3.scaleLinear().range([height, 0]);

var xAxis = d3.axisBottom(x);

var yAxis = d3.axisLeft(y);

var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Month of "   d.date   ":</strong> <span style='color:red'>"   d.value   " sales</span>";
})

var svg = d3.select("#barg").append("svg")
.attr("width", width   margin.left   margin.right)
.attr("height", height   margin.top   margin.bottom)
.append("g")
.attr("transform", 
      "translate("   margin.left   ","   margin.top   ")");
svg.call(tip);
data = d3.csvParse(d3.select("pre#data2").text());
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value =  d.value;
});

x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);

svg.append("g")
  .attr("transform", "translate(0,"   height   ")")
  .call(xAxis)
  .selectAll("text")
  .style("text-anchor", "end")
  .attr("dx", "-.8em")
  .attr("dy", "-.55em")
  .attr("transform", "rotate(-90)" )

svg.append("g")
  .call(yAxis)
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", ".71em")
  .style("text-anchor", "end")
  .text("Value ($)");

svg.selectAll(".bar")
  .data(data)
  .enter().append("rect")
  .attr("class", "bar")
  .attr("x", function(d) { return x(d.date); })
  .attr("width", x.bandwidth() - 5)
  .attr("y", function(d) { return y(d.value); })
  .attr("height", function(d) { return height - y(d.value); })
  .on('mouseover', tip.show)
  .on('mouseout', tip.hide)
  

Итак, проблема, с которой я сталкиваюсь, заключается в том, что у меня есть порядковые данные, но для большой мощности (например, 120 точек данных) На оси x слишком много тиков. Я пробовал несколько вещей, подобных tickValues , но когда я использую это, все мои точки отсчета по оси x отображаются друг на друге. В идеале я хотел бы 10 тиковых точек или около того, когда мощность высока. Есть идеи?

Ответ №1:

Это можно сделать с помощью tickValues indeed. Например, в этой демонстрации у нас 200 значений, поэтому ось абсолютно переполнена:

 var svg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 100);

var data = d3.range(200);

var xScale = d3.scaleBand()
  .domain(data.map(function(d){ return d}))
  .range([10, 490]);

var xAxis = d3.axisBottom(xScale);

var gX = svg.append("g").call(xAxis);  
 <script src="https://d3js.org/d3.v4.min.js"></script>  

Теперь тот же код, использующий tickValues :

 var svg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 100);

var data = d3.range(200);

var xScale = d3.scaleBand()
  .domain(data.map(function(d){ return d}))
  .range([10, 490]);

var xAxis = d3.axisBottom(xScale)
  .tickValues(xScale.domain().filter(function(d,i){ return !(i%10)}));

var gX = svg.append("g").call(xAxis);  
 <script src="https://d3js.org/d3.v4.min.js"></script>  

В этом последнем фрагменте tickValues использует оператор остатка, чтобы показывать только 1 из каждых 10 тактов:

 .tickValues(xScale.domain().filter(function(d,i){ 
    return !(i%10)
}));
  

Комментарии:

1. Я пытаюсь использовать ваш код для адаптации моего. Вы знаете, почему все мои значения тиков находятся друг над другом по оси x?

2. Мне кажется, что вы используете дату JS, но вы не используете временную шкалу. Попробуйте удалить d.date = parseDate(d.date); и посмотрите, что произойдет.

3. На самом деле, если вы не возражаете, что я создал jsFiddle , не могли бы вы взглянуть на это?

4. Ах, подождите, я, кажется, теперь понял это. Я удалил функцию parseDate и добавил <code>return d.date</code> в домен. Спасибо, теперь он отлично работает!

5. я продолжаю получать значения Tick, это не функция??

Ответ №2:

Вот общее решение этой проблемы с использованием tickFormat(...) . Мы можем определить минимально допустимую ширину для наших тиков, затем пропускать каждый n-й тик на основе этого минимума.

 d3
    .axisBottom(xScale)
    .tickFormat((t, i) => {
        const MIN_WIDTH = 30;
        let skip = Math.round(MIN_WIDTH * data.length / chartWidth);
        skip = Math.max(1, skip);

        return (i % skip === 0) ? t : null;
    });
  

let skip = ... является перестановкой неравенства ChartWidth / (NumTicks / N) > MinWidth . Здесь N представлен тик «размер шага», поэтому мы утверждаем, что ширина каждого n-го тика больше минимально допустимой ширины. Если мы изменим неравенство для решения N , мы сможем определить, сколько тиков нужно пропустить, чтобы достичь желаемой ширины.

Комментарии:

1. Это очень полезный фрагмент кода! Теперь проблема для меня заключается в том, чтобы динамически определять MIN_WIDTH с учетом разных меток оси и разных размеров шрифта. И как измерить ширину текста, который еще не был отрисован… Пока я буду придерживаться константы, но слишком большое значение может отбросить слишком много меток.