Public
Edited
Apr 12, 2023
Insert cell
Insert cell
data = FileAttachment("gate-data@1.json").json({ typed: true })
Insert cell
dat = data.dat
Insert cell
scores = _.map(dat, (d) => d.score)
Insert cell
residues = _.map(dat, (d) => d.residue)
Insert cell
Table(dat, {
format: {
score: sparkbar(d3.max(dat, (d) => d.score))
}
})
Insert cell
Insert cell
Table = Inputs.table // deprecated alias
Insert cell
Plot.plot({
caption: "Score distribution in residue",
x: {
label: "Residue"
},
marks: [Plot.ruleY([0]), Plot.barY(dat, { x: "residue", y: "score" })]
})
Insert cell
chart = {
const svg = d3.select(DOM.svg(width, height));

const x = d3
.scaleBand()
.domain(dat.map((d) => d.residue))
.range([margin.left, width - margin.right])
.padding(0.1);

const y = d3
.scaleBand()
.domain(["gate"])
.range([margin.top, height - margin.bottom])
.padding(0.1);

const z = d3.scaleSequential(d3.interpolateOrRd).domain([0, d3.max(scores)]);

const xAxis = (g) =>
g
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.attr("y", 0)
.attr("x", -9)
.attr("dy", ".35em")
.attr("transform", "rotate(270)")
.style("text-anchor", "end")
.style("fill", "#777");

const yAxis = (g) =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSize(0).tickPadding(4))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.style("fill", "#777");

svg.append("g").call(xAxis);
svg.append("g").call(yAxis);

const serie = svg
.append("g")
.selectAll("g")
.data([{ key: "gate", dat }])
.enter()
.append("g")
.attr("transform", (d) => `translate(0,${y(d.key) + 1})`);

const tip = d3tip()
.attr("class", "d3-tip")
.html((d, x) => x.residue);

svg.call(tip);

serie
.append("g")
.selectAll("rect")
.data((d) => d.dat)
.enter()
.append("rect")
.attr("fill", (d) => z(d.score))
.attr("x", (d, i) => x(residues[i]))
.attr("y", 0)
.attr("height", y.bandwidth())
.attr("width", x.bandwidth())
.on("mouseover", tip.show)
.on("mouseout", tip.hide);

return svg.node();
}
Insert cell
chart2 = {
const svg = d3.select(DOM.svg(width, height));

const x = d3
.scaleBand()
.domain(dat.map((d) => d.residue))
.range([margin.left, width - margin.right])
.padding(0.1);

const y = d3
.scaleBand()
.domain(["gate"])
.range([margin.top, height - margin.bottom])
.padding(0.1);

const z = d3.scaleSequential(d3.interpolateOrRd).domain([0, d3.max(scores)]);

const xAxis = (g) =>
g
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.attr("y", 0)
.attr("x", -9)
.attr("dy", ".35em")
.attr("transform", "rotate(270)")
.style("text-anchor", "end")
.style("fill", "#777");

const yAxis = (g) =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSize(0).tickPadding(4))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.style("fill", "#777");

svg.append("g").call(xAxis);
svg.append("g").call(yAxis);

const serie = svg
.append("g")
.selectAll("g")
.data([{ key: "gate", dat }])
.enter()
.append("g")
.attr("transform", (d) => `translate(0,${y(d.key) + 1})`);

const tip = d3tip()
.attr("class", "d3-tip")
.html((d, x) => x.residue);

svg.call(tip);

serie
.append("g")
.selectAll("rect")
.data((d) => d.dat)
.enter()
.append("rect")
.attr("fill", (d) => z(d.score))
.attr("x", (d, i) => x(residues[i]))
.attr("y", 0)
.attr("height", y.bandwidth())
.attr("width", x.bandwidth())
.on("mouseover", tip.show)
.on("mouseout", tip.hide);

return svg.node();
}
Insert cell
{
const a = _.chunk(dat, 50);
return a;
}
Insert cell
height = 80
Insert cell
{
return seqChart(data);
}
Insert cell
coreData = FileAttachment("core-data.json").json()
Insert cell
{
return seqChart(coreData);
}
Insert cell
function seqChart(data) {
const dat = _.chunk(data.dat, 50);
const svg = d3.select(DOM.svg(width, height * dat.length + margin.bottom));
const scores = _.map(data.dat, (d) => d.score);

dat.forEach((chunk, index) => {
const residues = chunk.map((d) => d.residue);

const x = d3
.scaleBand()
.domain(residues)
.range([margin.left, 18 * chunk.length])
.padding(0.1);

const y = d3
.scaleBand()
.domain([data.name])
.range([margin.top, height - margin.bottom])
.padding(0.1);

const z = d3
.scaleSequential(d3.interpolateOrRd)
.domain([0, d3.max(scores)]);

const xAxis = (g) =>
g
.attr("transform", `translate(0, ${height * index + 50})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.attr("y", 0)
.attr("x", -9)
.attr("dy", ".35em")
.attr("transform", "rotate(270)")
.style("text-anchor", "end")
.style("fill", "#444");

const yAxis = (g) =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSize(0).tickPadding(4))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.style("fill", "#444");

svg.append("g").call(xAxis);

if (index === 0) {
svg.append("g").call(yAxis);
}

const serie = svg
.append("g")
.selectAll("g")
.data([{ key: data.name, dat: chunk }])
.enter()
.append("g")
.attr(
"transform",
(d) => `translate(0,${y(d.key) + 1 + height * index})`
);

const tip = d3tip()
.attr("class", "d3-tip")
.html((d, x, i) => x.residue);

svg.call(tip);

serie
.append("g")
.selectAll("rect")
.data((d) => d.dat)
.enter()
.append("rect")
.attr("fill", (d) => z(d.score))
.attr("x", (d, i) => x(residues[i]))
.attr("y", 0)
.attr("height", y.bandwidth())
.attr("width", x.bandwidth())
.on("mouseover", tip.show)
.on("mouseout", tip.hide);
});

return svg.node();
}
Insert cell
{
return chainChart([data, coreData, data, coreData]);
}
Insert cell
function chainChart(data) {
const length = _.reduce(data, (sum, n) => sum + n.dat.length / 50, 0);
const svgHeight = height * (length + 2.2);
const svg = d3.select(DOM.svg(width, svgHeight));
const scores = _.flatMap(data, (d) => _.map(d.dat, (d) => d.score));
const z = d3.scaleSequential(d3.interpolateOrRd).domain([0, d3.max(scores)]);

let accHeight = 0;

data.forEach((data, idx) => {
const dat = _.chunk(data.dat, 50);

dat.forEach((chunk, index) => {
const residues = chunk.map((d) => d.residue);

const x = d3
.scaleBand()
.domain(residues)
.range([margin.left, 18 * chunk.length])
.padding(0.1);

const y = d3
.scaleBand()
.domain([data.name])
.range([margin.top, height - margin.bottom])
.padding(0.1);

const xAxis = (g) =>
g
.attr("transform", `translate(0, ${accHeight + height * index + 50})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.attr("y", 0)
.attr("x", -9)
.attr("dy", ".35em")
.attr("transform", "rotate(270)")
.style("text-anchor", "end")
.style("fill", "#444");

const yAxis = (g) =>
g
.attr("transform", `translate(${margin.left},${accHeight})`)
.call(d3.axisLeft(y).tickSize(0).tickPadding(4))
.call((g) => g.select(".domain").remove())
.selectAll("text")
.style("fill", "#444");

svg.append("g").call(xAxis);

if (index === 0) {
svg.append("g").call(yAxis);
}

const serie = svg
.append("g")
.selectAll("g")
.data([{ key: data.name, dat: chunk }])
.enter()
.append("g")
.attr(
"transform",
(d) => `translate(0,${y(d.key) + 1 + accHeight + height * index})`
);

const tip = d3tip()
.attr("class", "d3-tip")
.html((d, x, i) => x.residue + ": " + x.score.toFixed(2));

svg.call(tip);

function check(e, d) {
const prefix = d.residue.substring(0, 3);
// console.log(prefix);

const parent = e.target.parentElement.parentElement.parentElement;

// console.log(parent);
svg
.selectAll("rect")
.attr("stroke", (f) =>
prefix && f.residue && f.residue.startsWith(prefix)
? "black"
: undefined
);
}

serie
.append("g")
.selectAll("rect")
.data((d) => d.dat)
.enter()
.append("rect")
.attr("fill", (d) => z(d.score))
.attr("x", (d, i) => x(residues[i]))
.attr("y", 0)
.attr("height", y.bandwidth())
.attr("width", x.bandwidth())
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
.on("click", (e, d) => check(e, d));
});

accHeight += height * dat.length;
});

// Legend
const legendBins = [...Array(9).keys()].map((x) =>
d3.quantile([0, d3.max(scores)], x * 0.1)
);

const legendElementWidth = 56;

const legendHeight = 20;

const legend = svg
.append("g")
.attr("transform", (d) => `translate(${margin.left}, ${svgHeight - 100})`);

legend
.selectAll("rect")
.data(legendBins)
.enter()
.append("rect")
.attr("x", (d, i) => legendElementWidth * i)
.attr("y", height - 2 * legendHeight)
.attr("width", legendElementWidth)
.attr("height", legendHeight)
.style("fill", (d) => z(d));

legend
.selectAll("text")
.data(legendBins)
.enter()
.append("text")
.text((d) => "≥ " + d.toFixed(1))
.attr("x", (d, i) => legendElementWidth * i)
.attr("y", height - legendHeight / 2)
.style("font-size", "9pt")
.style("font-family", "Consolas, courier")
.style("fill", "#aaa");

return svg.node();
}
Insert cell
margin = ({ top: 30, right: 30, bottom: 30, left: 80 })
Insert cell
d3tip = require("https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.js")
Insert cell
html`<link rel="stylesheet" href="//rawgithub.com/Caged/d3-tip/master/examples/example-styles.css">`
Insert cell
_ = require("lodash")
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