Menu

Time of day plotEdit

0:00
4:00
8:00
12:00
16:00
20:00
24:00
2018-07-28
2018-07-27
2018-07-26
2018-07-25
2018-07-24
2018-07-23
2018-07-22

A scatter plot with an scaleBand for the y-scale to bucket them by day and a scaleTime for the x-scale. The only real fancy part of this plot is modifying the input data to be in "seconds since start of day" and generating the yDomain as every day between the min and max values, not just days for which we have values. This lets us see days in between that have no data.

<script>
  import { LayerCake, ScaledSvg, Html, calcExtents } from 'layercake';
  import { timeDay } from 'd3-time';
  import { scaleBand, scaleTime } from 'd3-scale';

  import AxisX from './components/AxisX.html.svelte';
  import AxisY from './components/AxisY.html.svelte';
  import Scatter from './components/Scatter.html.svelte';

  import data from './data/days.csv';

  const xKey = 'seconds';
  const yKey = 'day';

  const r = 4;
  const padding = 2;

  const daysTransformed = data.map(d => {
    const parts = d.timestring.split('T');
    const time = parts[1].replace('Z', '').split(':').map(q => +q);
    d[xKey] = time[0] * 60 * 60 + time[1] * 60 + time[2];
    d[yKey] = parts[0];
    return d;
  });

  /* --------------------------------------------
   * Generate a range of days in between the min and max
   * in case we are missing any in our data so we can show empty days for them
   */
  const extents = calcExtents(daysTransformed, [
    { field: 'x', accessor: d => d.timestring }
  ]);

  const minDate = extents.x[0].split('T')[0].split('-').map(d => +d);
  const maxDate = extents.x[1].split('T')[0].split('-').map(d => +d);

  const allDays = timeDay.range(new Date(Date.UTC(minDate[0], minDate[1] - 1, minDate[2])), new Date(Date.UTC(maxDate[0], maxDate[1] - 1, maxDate[2] + 1)))
    .map(d => d.toISOString().split('T')[0]).sort().reverse();

</script>

<style>
  .chart-container {
    width: 100%;
    height: 100%;
  }
</style>

<div class="chart-container">
  <LayerCake
    ssr={true}
    percentRange={true}
    padding={{ top: 0, right: 15, bottom: 20, left: 75 }}
    x={xKey}
    y={yKey}
    xDomain={[0, 24 * 60 * 60]}
    yDomain={allDays}
    xScale={scaleTime()}
    yScale={scaleBand().paddingInner([0.05]).round(true)}
    xPadding={[padding, padding]}
    data={daysTransformed}
  >
    <Html>
      <AxisX
        ticks={[0, 4, 8, 12, 16, 20, 24].map(d => d * 60 * 60)}
        formatTick={d => `${Math.floor(d / 60 / 60)}:00`}
      />
      <AxisY/>
      <Scatter
        {r}
        fill={'rgba(255, 204, 0, 0.75)'}
      />
    </Html>
  </LayerCake>
</div>