Public
Edited
Jul 11, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
d3.select('#wrapper2').selectAll("svg").remove()

// combine data
const data = [...adlb, ...advs, ...adae]

// canvas
const wrapper = d3.select("#wrapper2")
.append("svg")
.attr("width", dimensions.width)
.attr("height", dimensions.height)

const bounds = wrapper.append("g")
.style("transform", `translate(${
dimensions.margin.left
}px, ${
dimensions.margin.top
}px)`)

// scales
const radiusScale = d3.scaleSqrt()
.domain([1, maxCount])
.range([5, 20])

// marks

const forceData = beeswarmForce(data, xScale, radiusScale)

const dots = bounds.selectAll("dot")
.data(forceData)
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.r)
.attr('fill', d => {
if (d.fill === 'labs') {
return '#969696'
} else if (d.fill === 'vitals') {
return '#FD8D3C'
} else {
return '#9E9AC8'
}
})
.on('mousemove', onMousemove)
.on('mouseout', onMouseOut)

function onMousemove(event, d){
const x = event.x
const y = event.y

toolTip
.style('left', `${x}px`)
.style('top', `${y}px`)
.style('display', 'block')
.html(
`<span>Day: ${d.ADY}</span>
</br>
<span>Count: ${d.count}</span>
`
)
d3.select(event.target).style("cursor", "pointer")
}

function onMouseOut(event, d){
toolTip.style("display", "none")

d3.select(event.target).style("cursor", "default");
}

// axes
const xAxisGenerator = d3.axisBottom()
.scale(xScale)
.tickValues(Array.from(tickData.keys()))
.tickFormat((d, i) => tickData.get(d))

const xAxis = bounds.append("g")
.call(xAxisGenerator)
.style("transform", `translateY(${dimensions.boundedHeight}px)`)

xAxis.selectAll("text")
.attr('transform', 'rotate(-45)')
.attr('text-anchor', 'end')
.attr('dx', '-1em')
}
Insert cell
Insert cell
<div id="wrapper3"></div>
Insert cell
Insert cell
Insert cell
Insert cell
toolTip = d3.select("body").append("div").attr("class", "toolTip")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
xAccessor = d => d.ADY
Insert cell
colorAcccessor = d => d.count
Insert cell
xScale = d3.scaleLinear()
.domain(d3.extent(advs, xAccessor)) // in this case advs b/c the range is larger. will need a way to determine this
.range([0, dimensions.boundedWidth])
Insert cell
labsColorScale = d3.scaleSequential()
.domain(d3.extent(adlb, d => d.count))
.interpolator(t => d3.interpolateBlues(t + 0.5)) // use only the upper 1/2 of the blues scale b/c lower 1/2 is too light to see
Insert cell
vitalsColorScale = d3.scaleSequential()
.domain(d3.extent(advs, d => d.count))
.interpolator(t => d3.interpolateReds(t + 0.5))
Insert cell
aesColorScale = d3.scaleSequential()
.domain(d3.extent(adae, d => d.count))
.interpolator(t => d3.interpolatePurples(t + 0.5))
Insert cell
nudge = (x) => {
const random = d3.randomInt(4, 6)
return x + random()
}
Insert cell
maxCount = {
const maxCountAes = d3.max(adae.map(d => d.count))
const maxCountLabs = d3.max(adlb.map(d => d.count))
const maxCountVitals = d3.max(advs.map(d => d.count))

return d3.max([maxCountAes, maxCountLabs, maxCountVitals])
}
Insert cell
adlb = {
const raw = await FileAttachment("adlb.json").json()

const filtered = raw
.filter(d => d.USUBJID === '01-701-1192')
// .filter(d => d.ADY >= 0)
.filter(d => d.LBNRIND !== 'NORMAL')

const adlbRolled = d3.rollup(filtered, d => d.length, d => d.ADY)

let out = []
for (const [key, value] of adlbRolled.entries()) {
out.push({
ADY: key,
count: value,
series: 'labs'
})
}

return out
}
Insert cell
advs = {
const raw = await FileAttachment("advs.json").json()

const filtered = raw
.filter(d => d.USUBJID === '01-701-1192')
// .filter(d => d.ADY >= 0)
.filter(d => d.ANRIND !== 'NORMAL')

const advsRolled = d3.rollup(filtered, d => d.length, d => d.ADY)

let out = []
for (const [key, value] of advsRolled.entries()) {
out.push({
ADY: key,
count: value,
// data: filtered.filter(d => d.ADY === key) // so we can put whatever we want in the tooltips
series: 'vitals'
})
}

return out
}
Insert cell
adae = {
const raw = await FileAttachment("adae.json").json()

const filtered = raw
.filter(d => d.USUBJID === '01-701-1192')
.filter(d => typeof d.AESTDY !== 'undefined')

const adaeRolled = d3.rollup(filtered, d => d.length, d => d.AESTDY)

let out = []
for (const [key, value] of adaeRolled.entries()) {
out.push({
ADY: key, // really AESTDY but this is to keep things consistent across domains
count: value,
series: 'adverse events'
})
}

return out
}
Insert cell
tickData = {

const raw = await FileAttachment("adlb.json").json()

const filtered = raw
.filter(d => d.USUBJID === '01-701-1192')
// .filter(d => d.ADY >= 0)
.filter(d => d.LBNRIND !== 'NORMAL')
.map(d => {
return {
ADY: d.ADY,
VISIT: d.VISIT
}
})
.sort((a, b) => d3.ascending(a.ADY, b.ADY))
return d3.rollup(filtered, d => d[0].VISIT, d => d.ADY) // first arg is reducer, 2nd is group
}
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more