Public
Edited
Feb 10
Insert cell
# Two Plots: Same x-axis, different y-axis
Insert cell
{
const topPlot = Plot.plot({
marginLeft: 120,
x: { axis: null, type:"band" },
width: 1200,
height: 300,
marks: [
Plot.dot(data, {
x: "week",
y: "aval",
r: 5,
fill: d => d3.scaleSequential(d3.interpolateGreys).domain([0, d3.max(data, d => d.ID)])(d.ID),
tip: true,
title: d => {
// Collect all overlapping data points at the same (week, aval) position
const overlappingData = data.filter(point => point.week === d.week && point.aval === d.aval);
return overlappingData.map(d => `ID: ${d.ID}, Value: ${d.aval}`).join('\n');
},
}),
Plot.lineY(data, {
x: 'week',
y: 'aval',
stroke: d => d3.scaleSequential(d3.interpolateGreys).domain([0, d3.max(data, d => d.ID)])(d.ID)
}),
]
});

const bottomPlot = Plot.plot({
marginLeft: 120,
x: topPlot.scale("x"), // reuses the same x scale
width: 1200,
marks: [
Plot.cell(
time,
{
x: "week",
y: d => d.param === "Collector Initials" || d.param === "Time Collected" ? d.param : "param1", // Use categorical y-axis for specific params
fill: d => {
if (d.param === "Time Collected") {
return isBeforeNoon(d.aval) <= 12 ? 'white' : 'red';
} else if (d.param === "Collector Initials") {
return colorScale(d.aval);
}
},
inset: 2,
stroke: d => d.param === "Time Collected" && isBeforeNoon(d.aval) <= 12 ? 'grey' : 'white',
strokeWidth: 1,
r: 10,
title: d => `${d.param === "Time Collected" ? `Time: ${d.aval}` : `Collector Initials: ${d.aval}`}`,
tip: true,
}
),
]
});

return html`${topPlot}${bottomPlot}`;
}
Insert cell
colorScale = d3.scaleOrdinal().domain('MHW').range('purple');
Insert cell
Insert cell
function isBeforeNoon(timeStr) {
const [hours, minutes] = timeStr.split(':').map(Number);
return hours < 12 || (hours === 12 && minutes === 0);
}
Insert cell
data = [
{
"week": -4.142857142857143,
"aval": 3,
"ID": 'Top'
},
{
"week": -2,
"aval": 3,
"ID": 'Top'
},
{
"week": -1,
"aval": 3,
"ID": 'Top'
},
{
"week": 11.85714285714286,
"aval": 3,
"ID": 'Top'
},
{
"week": 24,
"aval": 2,
"ID": 'Top'
},
{
"week": 26.14285714285714,
"aval": 1,
"ID": 'Top'
},
{
"week": 35.85714285714285,
"aval": 1,
"ID": 'Top'
},
{
"week": -4.142857142857143,
"aval": 12,
"ID": 'Bottom'
},
{
"week": -2,
"aval": 11,
"ID": 'Bottom'
},
{
"week": -1,
"aval": 11,
"ID": 'Bottom'

},
{
"week": 11.85714285714286,
"aval": 14,
"ID": 'Bottom'

},
{
"week": 24,
"aval": 10,
"ID": 'Bottom'

},
{
"week": 26.14285714285714,
"aval": 10,
"ID": 'Bottom'

},
{
"week": 35.85714285714285,
"aval": 11,
"ID": 'Bottom'
},
{
"week": -4.142857142857143,
"aval": 11,
"ID": 'Middle'
},
{
"week": -2,
"aval": 12,
"ID": 'Middle'
},
{
"week": -1,
"aval": 12,
"ID": 'Middle'
},
{
"week": 11.85714285714286,
"aval": 12,
"ID": 'Middle'
},
{
"week": 24,
"aval": 11,
"ID": 'Middle'
},
{
"week": 26.14285714285714,
"aval": 11,
"ID": 'Middle'
},
{
"week": 35.85714285714285,
"aval": 11,
"ID": 'Middle'
}
]
Insert cell
time = [
{
"param": "Collector Initials",
"aval": "MHW",
"week": -2.857142857142857
},
{
"param": "Time Collected",
"aval": "10:10",
"week": -0.8571428571428571
},
{
"param": "Collector Initials",
"aval": "MHW",
"week": -0.7142857142857143
},
{
"param": "Time Collected",
"aval": "09:33",
"week": 12
},
{
"param": "Collector Initials",
"aval": "MHW",
"week": 24.57142857142857
},
{
"param": "Time Collected",
"aval": "12:30",
"week": 35.42857142857143
},
{
"param": "Collector Initials",
"aval": "MHW",
"week": 46.28571428571428
},
{
"param": "Time Collected",
"aval": "12:49",
"week": -2.857142857142857
},
{
"param": "Collector Initials",
"aval": "MHW",
"week": -0.8571428571428571
},
{
"param": "Time Collected",
"aval": "09:20",
"week": -0.7142857142857143
},
{
"param": "Collector Initials",
"aval": "MHW",
"week": 12
},
{
"param": "Time Collected",
"aval": "14:26",
"week": 24.57142857142857
},
{
"param": "Collector Initials",
"aval": "MHW",
"week": 35.42857142857143
},
{
"param": "Time Collected",
"aval": "12:49",
"week": 46.28571428571428
}
]
Insert cell
yScaleCategorical = d3.scaleBand() // Categorical scale for 'Collector Initials' and 'Time Collected'
.domain(['Collector Initials', 'Time Collected', 10, 2, 1])
.range([300, 0]);
Insert cell
yScaleNumeric = d3.scaleLinear()
.domain([1,2,10]) // Numeric domain, ensures the order 1, 2, 10
.range([300, 0]);
Insert cell
yScaleCombined = d3.scaleBand()
.domain([10, 2, 1, 'Collector Initials', 'Time Collected'].sort((a, b) => (typeof a === 'number' && typeof b === 'number') ? b- a : 0))
.range([300, 0]);
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more