d3.timeMonth на оси показывает каждую дату, а не каждый месяц

#javascript #svg #d3.js

#javascript #svg #d3.js

Вопрос:

Я новичок в D3, и уже несколько дней безуспешно пытаюсь устранить эту проблему. Я не уверен, что попробовать дальше.

У меня есть набор данных JSON с ежедневными данными, и я пытаюсь создать столбчатую диаграмму с одним столбиком в день. Все хорошо. Однако у меня возникли проблемы с x -axis . Я бы хотел x , чтобы у -axis были отметки и метки только в начале каждого месяца. Это как если d3.timeMonth бы каждая точка данных считалась новым месяцем:

введите описание изображения здесь

Я настроил x -axis как scaleBand , потому что каждый раз, когда я пытался настроить его как scaleTime , столбцы отображались как огромные перекрывающиеся столбцы. Однако непосредственно перед настройкой x -axis я напечатал свои данные в журнале консоли, и они выглядят правильно отформатированными как даты.

 const data = [
  {
    date_facet: '2020-08-31',
    published: 2,
    not_published: 0,
  },
  {
    date_facet: '2020-09-01',
    published: 0,
    not_published: 0,
  },
  {
    date_facet: '2020-09-02',
    published: 1,
    not_published: 0,
  },
  {
    date_facet: '2020-09-03',
    published: 1,
    not_published: 0,
  },
  {
    date_facet: '2020-09-04',
    published: 0,
    not_published: 0,
  },
  {
    date_facet: '2020-09-05',
    published: 0,
    not_published: 0,
  },
];

// set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 80,
    left: 40
  },
  width = 450 - margin.left - margin.right,
  height = 350 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#graph")
  .append("svg")
  .attr("viewBox", '0 0 450 350')
  .append("g")
  .attr("transform",
    "translate("   margin.left   ","   margin.top   ")");

// parse the date / time
var parseTime = d3.timeParse("%Y-%m-%d");

// format the data
data.forEach(function(d) {
  d.date_facet = parseTime(d.date_facet);
  d.published =  d.published;
});

// order the data
data.sort(function(a, b) {
  return a["date_facet"] - b["date_facet"];
})

// X axis
var x = d3.scaleBand()
  .range([0, width])
  .domain(data.map(function(d) {
    return d.date_facet;
  }))
  .padding(0.2);

// Y axis
var y = d3.scaleLinear()
  .range([height, 0])
  .domain([0, d3.max(data, function(d) {
    return Math.max(d.published);
  })   4]);

// Add X axis, ticks and labels
svg.append("g")
  .attr("class", "axis axis-minor")
  .attr("transform", "translate(0,"   height   ")")
  .call(d3.axisBottom(x)
    .ticks(d3.timeMonth.every(1))
    .tickFormat(d3.timeFormat("%b")))
  .selectAll("text")
  .style("text-anchor", "end")
  .attr("dx", "-.8em")
  .attr("dy", ".15em")
  .attr("transform", "rotate(-45)");


svg.append("g")
  .call(d3.axisLeft(y));

// Bars
svg.selectAll("mybar")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", function(d) {
    return x(d.date_facet);
  })
  .attr("width", x.bandwidth())
  .attr("height", function(d) {
    return height - y(d.published);
  })
  .attr("y", function(d) {
    return y(d.published);
  })  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="graph"></div>  

Ответ №1:

Поскольку вы используете scaleBand , все значения считаются категориальными. Я имею в виду, что они похожи на метки, такие как «шар», «оранжевый», «круг». Просто термины, совершенно не связанные друг с другом. Это контрастирует со временем или числами, где вы можете сказать, что одно значение больше другого, или одно ближе к A, чем к B.

Измените значения на scaleTime вместо:

 const data = [
  {
    date_facet: '2020-08-31',
    published: 2,
    not_published: 0,
  },
  {
    date_facet: '2020-09-01',
    published: 0,
    not_published: 0,
  },
  {
    date_facet: '2020-09-02',
    published: 1,
    not_published: 0,
  },
  {
    date_facet: '2020-09-03',
    published: 1,
    not_published: 0,
  },
  {
    date_facet: '2020-09-04',
    published: 0,
    not_published: 0,
  },
  {
    date_facet: '2020-09-05',
    published: 0,
    not_published: 0,
  },
];

// set the dimensions and margins of the graph
var margin = {
    top: 10,
    right: 30,
    bottom: 80,
    left: 40
  },
  width = 450 - margin.left - margin.right,
  height = 350 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#graph")
  .append("svg")
  .attr("viewBox", '0 0 450 350')
  .append("g")
  .attr("transform",
    "translate("   margin.left   ","   margin.top   ")");

// parse the date / time
var parseTime = d3.timeParse("%Y-%m-%d");

// format the data
data.forEach(function(d) {
  d.date_facet = parseTime(d.date_facet);
  d.published =  d.published;
});

// order the data
data.sort(function(a, b) {
  return a["date_facet"] - b["date_facet"];
})


// Extend the domain by 12 hours on each side to account for the bar widths
var xDomain = d3.extent(data.map(function(d) {
  return d.date_facet;
}));
// Deep copy the date objects to make sure you can make safe modifications
xDomain = [new Date(xDomain[0]), new Date(xDomain[1])];
xDomain[0].setHours(xDomain[0].getHours() - 12);
xDomain[1].setHours(xDomain[1].getHours()   12);

// X axis
var x = d3.scaleTime()
  .range([0, width])
  .domain(xDomain);

var xDomainInDays = (x.domain()[1] - x.domain()[0]) / (1000 * 60 * 60 * 24);
var xBarWidth = width / xDomainInDays;
var padding = 0.2;

// Y axis
var y = d3.scaleLinear()
  .range([height, 0])
  .domain([0, d3.max(data, function(d) {
    return Math.max(d.published);
  })   4]);

// Add X axis, ticks and labels
svg.append("g")
  .attr("class", "axis axis-minor")
  .attr("transform", "translate(0,"   height   ")")
  .call(d3.axisBottom(x)
    .ticks(d3.timeMonth.every(1))
    .tickFormat(d3.timeFormat("%b")))
  .selectAll("text")
  .style("text-anchor", "end")
  .attr("dx", "-.8em")
  .attr("dy", ".15em")
  .attr("transform", "rotate(-45)");


svg.append("g")
  .call(d3.axisLeft(y));

// Bars
svg.selectAll("mybar")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", function(d) {
    // Get the x coordinate
    // Then shift by half of xBarWidth so the middle of the bar is at the tick
    // Then apply half of the padding (other half at the other side)
    return x(d.date_facet) - (xBarWidth / 2)   (padding / 2) * xBarWidth;
  })
  // Make the bar "padding * xBarWidth" thinner so it applies the padding correctly
  .attr("width", xBarWidth - padding * xBarWidth)
  .attr("height", function(d) {
    return height - y(d.published);
  })
  .attr("y", function(d) {
    return y(d.published);
  })  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="graph"></div>  

Это связано с некоторыми сложностями.

  • Вам нужно самостоятельно рассчитать ширину полосы. Я сделал это, проверив размер домена и размер диапазона, поэтому я нашел ширину, доступную для каждой панели таким образом;
  • Галочка будет у левого края каждой строки. Если вы хотите центрировать его (что я и сделал здесь), вам нужно поиграть с заполнением и центрировать полосу на галочке;
  • Теперь полоса будет немного превышать ось. Вы можете увеличить домен на 12 часов в обоих направлениях, это решает проблему.