How to set stroke color based on bin aggregated value

I have a plot with two marks, one line and one dot, as below:


    this.chart = Plot.plot({
      height: 750,
      width: 1200,
      x: {interval: "month", ticks: 'month'},
      y: {grid: true, domain: [30, 100]},
      style: {
        backgroundColor: '#eeeeee',
        fontSize: '14'
      },
      color: {legend: true},
      marks: [
        Plot.lineY(
          this.data,
          Plot.binX<LineYOptions>({y: 'mean'}, {
            x: 'ord_datetime',
            y: 'metric',
            interval: 'month',
            tip: true,
          })
        ),
        Plot.dot(this.data,
            Plot.binX<LineYOptions>({y: 'mean'}, {
              x: 'ord_datetime',
              y: 'metric',
              interval: 'month',
              stroke: d => d.metric > 50 ? 'green': 'red',
              strokeWidth: 9,
            })
          ),
          }
        )
      ]
    });

But since I’m using a bin for the dot, I don’t know how to evaluate the mean value and not all the individual values. When I try something like what I have above, it has unintended effects in that it shows the mark in a different position than if I remove it, I suppose because it’s affecting the underlying aggregate value somehow.

How can I base the color logic on the aggregate, monthly, binned value, rather than for individual row values?

You need to include stroke in the outputs of the bin transform (the first argument, where you’re currently only generating y).

But in order to do this correctly, you need to set the input to the bin transform for the stroke channel to be the same metric column, rather than computing literal colors. You can achieve the same color encoding more idiomatically by using a threshold color scale.

Here’s a live example adapted to a public dataset and simplified slightly:

Plot.plot({
  y: { grid: true },
  color: {
    legend: true,
    type: "threshold",
    domain: [0],
    range: ["#3ca951", "#ff725c"]
  },
  marks: [
    Plot.lineY(
      gistemp,
      Plot.binX({ y: "mean" }, { x: "Date", y: "Anomaly", tip: true })
    ),
    Plot.dot(
      gistemp,
      Plot.binX(
        { y: "mean", stroke: "mean" },
        { x: "Date", y: "Anomaly", stroke: "Anomaly", strokeWidth: 8 }
      )
    )
  ]
})

For this dataset, I’ve used a threshold of 0 by setting the domain to [0]. If you want a threshold of 50, you can set the domain to [50] instead.

Notebook:

This is exactly what I needed, thanks! I didn’t make the connection about the stroke output being there also.