One suggestion, rather than using cells.each() for creating the circles, you can chain the join as you would without each:
From this
cells.each(function (d) {
d3.select(this)
.selectAll("circle")
.data((d) => d.yields)
.join("circle")
.attr("fill", "none")
.attr("stroke", (d) => color(d.year))
.attr("stroke-width", "1.5")
.attr("cx", (d) => x(d.yield))
.attr("cy", (d) => y(d.variety))
.attr("r", 3);
});
To this
cells.selectAll("circle")
.data((d) => d.yields)
.join("circle")
.attr("fill", "none")
.attr("stroke", (d) => color(d.year))
.attr("stroke-width", "1.5")
.attr("cx", (d) => x(d.yield))
.attr("cy", (d) => y(d.variety))
.attr("r", 3);