Public
Edited
Feb 28
Insert cell
Insert cell
Plot.plot({
height: 20,
width: 600,
marks: [
Plot.axisX(d3.ticks(0, 1, 10), {tickSize: 0})
],
transform: d3.zoom().translateExtent([[0, 0], [600, 20]])
})
Insert cell
mutable datum = ({
})
Insert cell
Plot.plot({
width: width,
height: data.length * 15,
symbol: { type: "ae", legend: true },
x: {
type: "band",
inset: 10,
ticks: 10,
label: null
},
y: {
//inset: 5,
grid: false,
fill: "white",
stroke: "white",
label: null
},
facet: {
label: null
},
marginRight: 0,
marginLeft: 70,
symbol: { type: "categorical", legend: true },
marks: [
Plot.axisY({
anchor: "right",
}),
Plot.axisFy({
anchor: "left", // Places the axis on the left
label: "Subject ID", // Sets the axis label
labelAnchor: "top"
}),
Plot.frame({
// 'zebra' frame fill based on frame index thanks @fil!
render: (index, scales, values, dimensions, context, next) =>
index.fi % 2 ? next(index, scales, values, dimensions, context) : null,
fill: "rgb(250,250,250)"
}),
Plot.ruleY(
data,
scalefreeY({
x1: "start_day_var",
x2: "x_end",
fy: "subject",
stroke: d => colorScale(d.ae),
})
),
Plot.dot(
data,
scalefreeY({
x: "time",
y: "line_id",
fy: "subject",
fill: d => colorScale(d.ae),
stroke: d => colorScale(d.ae),
symbol: d => d.ongoing ? arrowheadshape : (d.AESER_NEW === "Serious Event" ? starshape : 'circle'),
title: d => `Time: ${d.time} \nGrade: ${d.ae}`,
tip: true
})
),
Plot.dot(
data,
scalefreeY({
x: "time",
y: "line_id",
fy: "subject",
fill: 'transparent',
stroke: 'none',
r: 20,
render: (index, scales, values, dimensions, context, next) => {
const g = next(index, scales, values, dimensions, context, next);
const children = d3.select(g).selectChildren();
children
.on("click", function (event, i) {
mutable datum = data[i];
})
return g;
}
})
)
]
})
Insert cell
arrowheadshape = ({
draw: (context, size) => {
context.lineJoin = "round"; // Round the corners where the lines meet
context.lineCap = "round"; // Round the ends of the lines

// Start drawing the left-facing arrow with two lines
context.moveTo(0, 0); // Start at the center
context.lineTo(-5, -5); // First line (left-up diagonal)
context.moveTo(0, 0); // Start a new line from the center
context.lineTo(-5, 5); // Second line (left-down diagonal)
}
})
Insert cell
starshape = ({
draw: (context, size) => {
context.lineJoin = "round";
context.lineCap = "round";

// Vertical line
context.moveTo(0, -5);
context.lineTo(0, 5);

// Horizontal line
context.moveTo(-5, 0);
context.lineTo(5, 0);

// Diagonal line (top-left to bottom-right)
context.moveTo(-4, -4);
context.lineTo(4, 4);

// Diagonal line (top-right to bottom-left)
context.moveTo(4, -4);
context.lineTo(-4, 4);
}
});
Insert cell
colorScale = d3.scaleOrdinal()
.domain(['grade 1', 'grade 2', 'grade 3', 'grade 4'])
.range(['green', 'gold', 'orange', 'red']);
Insert cell
scalefreeY = (options) =>
Plot.initializer(options, (data, facets, channels, scales, dimensions) => {
const {
y: { value: Y, scale }
} = channels;
for (const index of facets) {
const y = d3
.scalePoint(d3.sort(Array.from(index, (i) => Y[i])), [
dimensions.marginTop,
dimensions.height - dimensions.marginBottom
])
.padding(0.5);
for (const i of index) Y[i] = y(Y[i]);
}
return { data, facets, channels: { y: { value: Y } } };
})
Insert cell
scalefreeY2 = (options) =>
Plot.initializer(options, (data, facets, channels, scales, dimensions) => {
const {
y: { value: Y }
} = channels;

// Compute max `line_id` per `fy` (subject)
const maxLineByFacet = new Map();
for (const index of facets) {
maxLineByFacet.set(
index,
d3.max(index, (i) => Y[i]) // Find max `line_id` per subject
);
}

// Compute facet-specific heights
const baseHeight = 30; // Minimum height per subject
const heightPerLine = 15; // Height increment per line_id
const facetHeights = new Map();
for (const [index, maxLine] of maxLineByFacet) {
facetHeights.set(index, baseHeight + maxLine * heightPerLine);
}

// Compute cumulative heights to position facets correctly
let cumulativeHeight = dimensions.marginTop;
const subjectOffsets = new Map();
for (const [index, height] of facetHeights) {
subjectOffsets.set(index, cumulativeHeight);
cumulativeHeight += height;
}

// Adjust Y scale per subject (`fy`) with correct height range
for (const index of facets) {
const maxLine = maxLineByFacet.get(index); // Get max line_id for this subject
const yOffset = subjectOffsets.get(index); // Get y-position offset

// Define scaleLinear per subject
const yScale = d3.scaleLinear()
.domain([1, maxLine]) // Scale from 1 to max `line_id`
.range([yOffset + heightPerLine, yOffset]); // Adjust range with offset

// Map each Y value to its new scaled position
for (const i of index) Y[i] = yScale(Y[i]);
}

return { data, facets, channels: { y: { value: Y } }, dimensions: { height: cumulativeHeight } };
});

Insert cell
data = [
{subject: 'Subject 1', ae: 'grade 1', time: 1, line_id: 1, AESER_NEW: 'Serious Event'},
{subject: 'Subject 1', ae: 'grade 1', time: 5, line_id: 1, ongoing: true},

{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 2},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 2},
{subject: 'Subject 1', ae: 'grade 4', time: 1, line_id: 3, AESER_NEW: 'Serious Event'},
{subject: 'Subject 1', ae: 'grade 4', time: 5, line_id: 3, ongoing: true},
{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 4},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 4},
{subject: 'Subject 1', ae: 'grade 1', time: 1, line_id: 5, AESER_NEW: 'Serious Event'},
{subject: 'Subject 1', ae: 'grade 1', time: 5, line_id: 5, ongoing: true},
{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 6},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 6},
{subject: 'Subject 1', ae: 'grade 4', time: 1, line_id: 7, AESER_NEW: 'Serious Event'},
{subject: 'Subject 1', ae: 'grade 4', time: 5, line_id: 7, ongoing: true},
{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 8},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 8},
{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 9},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 9},
{subject: 'Subject 1', ae: 'grade 1', time: 1, line_id: 10, AESER_NEW: 'Serious Event'},
{subject: 'Subject 1', ae: 'grade 1', time: 5, line_id: 10, ongoing: true},
{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 11},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 11},
{subject: 'Subject 1', ae: 'grade 4', time: 1, line_id: 12, AESER_NEW: 'Serious Event'},
{subject: 'Subject 1', ae: 'grade 4', time: 5, line_id: 12, ongoing: true},
{subject: 'Subject 1', ae: 'grade 2', time: 2, line_id: 13},
{subject: 'Subject 1', ae: 'grade 2', time: 7, line_id: 13},

{subject: 'Subject 2', ae: 'grade 2', time: 1, line_id: 1},
{subject: 'Subject 2', ae: 'grade 2', time: 18, line_id: 1},
{subject: 'Subject 2', ae: 'grade 1', time: 1, line_id: 2},
{subject: 'Subject 2', ae: 'grade 1', time: 1130, line_id: 2},

{subject: 'Subject 3', ae: 'grade 4', time: 1, line_id: 1},
{subject: 'Subject 3', ae: 'grade 4', time: 5, line_id: 1},

{subject: 'Subject 4', ae: 'grade 1', time: 1, line_id: 1},
{subject: 'Subject 4', ae: 'grade 2', time: 2, line_id: 2},
{subject: 'Subject 4', ae: 'grade 2', time: 7, line_id: 2},
{subject: 'Subject 4', ae: 'grade 3', time: 6, line_id: 3},
{subject: 'Subject 4', ae: 'grade 3', time: 12, line_id: 3, ongoing: true},
];

Insert cell
Plot.plot({
style: {paddingTop: 30, paddingBottom: 30},
width: 600,
marginLeft: 30,
height: 200,
x: { axis: null, type: "band" },
y: {
domain: [0, 1.79],
type: 'linear'
},
marks: [
Plot.dot(temp1, {
x: "week",
y: "aval",
r: d => d.reasonval === '' ? 5 : 20,
fill: d => d.reasonval === '' | d.reasonval === undefined ? 'black' : 'rgb(164, 17, 8)',
tip: true,
title: d => d.tooltip
}),
Plot.lineY(temp1, {
x: 'week',
y: 'aval',
stroke: 'black',
})
]
});
Insert cell
temp1 = [
{
"week": -1.142857142857143,
"aval": 0.79,
"reasnval": "",
"tooltip": "Patient Identifier: 404-1401\nDate: 19808\nStudy Day: -8\nStudy Week: -1.14285714285714\nVisit: 1\nValue: 0.79\nBaseline Value: 0.79\nChange From Baseline: 0\nReason Not Valid: "
},
{
"week": 12.14285714285714,
"aval": 0.68,
"reasnval": "",
"tooltip": "Patient Identifier: 404-1401\nDate: 19900\nStudy Day: 85\nStudy Week: 12.1428571428571\nVisit: 3\nValue: 0.68\nBaseline Value: 0.79\nChange From Baseline: -0.11\nReason Not Valid: "
},
{
"week": 24,
"aval": 0.7,
"reasnval": "",
"tooltip": "Patient Identifier: 404-1401\nDate: 19983\nStudy Day: 168\nStudy Week: 24\nVisit: 4\nValue: 0.7\nBaseline Value: 0.79\nChange From Baseline: -0.0900000000000001\nReason Not Valid: "
}
]
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