github like activity diagram with some modifications

I have a simple github like activity diagram which I would like to modify a bit.
However I am not sure what is feasible and in which direction I should look.
Maybe someone can guide me here?

The current diagram is simple:

 Plot.plot({
    marginLeft: 80,
    width: width,
    color: {scheme: "YlGnBu"},
    facet: {
      data: data,
      y: "category",
      label: "",
      marginLeft: 70
    },
    marks: [
      Plot.barX(data, Plot.binX({fill: "count"}, {x: {value: "date", thresholds: d3.utcWeek}}))
    ]
  })

Working example

Some modifications I would like to make:

  • Can the category name be replaced with an alternate text? As these are facets, tickFormat does not work. And even better: Can the category name be replaced with a pictogram = a SVG image?
  • Is it possible to print the overall sum of events for a row right next to the diagram?
  • Sometimes there is very little data which results in a lot of white cells which blend in to the white background of the page. I am thinking of making empty cells grey. Is it easily possible to modify a color scheme? Or are there better alternatives? Maybe a border around the plot is enough.

Thanks

You can specify the tickFormat option on the fy scale.

Plot.plot({
  width,
  color: { scheme: "YlGnBu" },
  fy: { tickFormat: (x) => `${x}`.toUpperCase() },
  facet: {
    data,
    y: "category",
    label: null,
    marginLeft: 90
  },
  marks: [
    Plot.barX(
      data,
      Plot.binX({ fill: "count" }, { x: "date", thresholds: d3.utcWeek })
    )
  ]
})

Alternatively, and probably simpler, you can forgo faceting here by instead using a y value for your barX. Then you can use the tickFormat option on the y scale if desired.

Plot.plot({
  marginLeft: 90,
  width,
  color: { scheme: "YlGnBu" },
  y: { label: null },
  marks: [
    Plot.barX(
      data,
      Plot.binX(
        { fill: "count" },
        { x: "date", y: "category", thresholds: d3.utcWeek }
      )
    )
  ]
})

If you want to show the empty bins, you can set the filter option on the bin transform to null.

Plot.plot({
  marginLeft: 90,
  width,
  color: { scheme: "YlGnBu" },
  y: { label: null },
  marks: [
    Plot.barX(
      data,
      Plot.binX(
        { fill: "count", filter: null },
        { x: "date", y: "category", thresholds: d3.utcWeek }
      )
    )
  ]
})

Alternatively you could have a dedicated set of just the empty bins in gray:

Plot.plot({
  marginLeft: 90,
  width,
  color: { scheme: "YlGnBu" },
  y: { label: null },
  marks: [
    Plot.barX(
      data,
      Plot.binX(
        { filter: (x) => x.length === 0 },
        { x: "date", y: "category", thresholds: d3.utcWeek, fill: "#eee" }
      )
    ),
    Plot.barX(
      data,
      Plot.binX(
        { fill: "count" },
        { x: "date", y: "category", thresholds: d3.utcWeek }
      )
    )
  ]
})

This is trickier… typically you’d do it by constructing another Plot, and then showing them side-by-side in the cell. I don’t think there’s an easy way to do this with a single Plot but maybe someone else knows.

For the counts, you could use a group transform:

Plot.plot({
  marginLeft: 70,
  width,
  color: { scheme: "YlGnBu" },
  y: { axis: null },
  marks: [
    Plot.barX(
      data,
      Plot.binX(
        { filter: (x) => x.length === 0 },
        { x: "date", y: "category", thresholds: d3.utcWeek, fill: "#eee" }
      )
    ),
    Plot.barX(
      data,
      Plot.binX(
        { fill: "count" },
        { x: "date", y: "category", thresholds: d3.utcWeek }
      )
    ),
    Plot.text(
      data,
      Plot.selectFirst({
        x: d3.min(data, (d) => d.date),
        y: "category",
        z: "category",
        text: d => d.category?.toUpperCase(),
        textAnchor: "end",
        dx: -10
      })
    ),
    Plot.text(
      data,
      Plot.groupY(
        { text: "count" },
        {
          x: d3.max(data, (d) => d.date),
          y: "category",
          textAnchor: "end",
          dx: 32
        }
      )
    )
  ]
})

Finally, to create svgs instead of text labels, you can replace the corresponding Plot.text by a Plot.image mark.

Again I am absolutely blown away by the quality of the answers here.
The diagram now looks exactly like I envisioned and I learned more about how things work.

Thank you!

2 Likes