Why is there a last useless bin in d3js scaleTime histogram?

Viewed 73

I have a list of dates and i tried to have bins representing each days of the week, in order to plot an histogram. The result is somewhat what i wanted, but the last bin have x0=x1, and thus will always be empty. Why is the bin array of 8 elements while i wanted 7 ?

I tried to read the documentation but it seems there is the same issue in provided examples

Here is the code i used:

  const monday = new Date(2021, 4, 3)  // Monday 3 May 2021, midnight
  const next_monday =  new Date(2021, 4, 10)

  const scale = d3.scaleTime()
      .domain([monday, next_monday])

  const bins = d3.histogram()
      .domain(scale.domain())
      .thresholds(scale.ticks(7))

  console.log(
      bins([
          new Date(2021, 4, 3, 15), // 15h, monday
          new Date(2021, 4, 9, 15) // 15h, sunday, end of week
      ])
  )
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

What results looks like:

0: Array [ Date Mon May 03 2021 15:00:00 GMT+0200 (heure d’été d’Europe centrale) ]
    x0: Date Mon May 03 2021 00:00:00 GMT+0200 (heure d’été d’Europe centrale)
​​    x1: Date Tue May 04 2021 00:00:00 GMT+0200 (heure d’été d’Europe centrale)
​​1: Array []
​2: Array []
​3: Array []
​4: Array []
​5: Array []
​6: Array [ Date Sun May 09 2021 15:00:00 GMT+0200 (heure d’été d’Europe centrale) ]
​7: Array []
​​length: 0
​​    x0: Date Mon May 10 2021 00:00:00 GMT+0200 (heure d’été d’Europe centrale)
​​    x1: Date Mon May 10 2021 00:00:00 GMT+0200 (heure d’été d’Europe centrale)
    !!! x1=x0 !!! Why is this bin added ?
​​    <prototype>: Array []
​
length: 8
1 Answers

I have 8 time points, scale.ticks(7) is returning an array of 8 elements.

scale.ticks(7) doesn't necessarily return an array with 7 ticks: "The specified count is only a hint; the scale may return more or fewer values depending on the domain." from the docs. The scale prioritizes "sensible values (such as every day at midnight)"

That midnight part is throwing off your ticks: it's a natural spot for a tick giving you 8 ticks, one on each Monday. Instead you can modify your domain so that you avoid that issue:

 const next_monday =  new Date(2021, 4, 10)-1  // just before midnight.

Ticks are guaranteed to be within the domain, so you shouldn't see that tick:

const monday = new Date(2021, 4, 3)  // Monday 3 May 2021, midnight
  const next_monday =  new Date(2021, 4, 10)-1

  const scale = d3.scaleTime()
      .domain([monday, next_monday])

  const bins = d3.histogram()
      .domain(scale.domain())
      .thresholds(scale.ticks(7))

  console.log(
      bins([
          new Date(2021, 4, 3, 15), // 15h, monday
          new Date(2021, 4, 9, 15) // 15h, sunday, end of week
      ])
  )
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Related