Public
Edited
Jan 4, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = {
mutable cleanlist = []
file === null ? yield parseNewick(await FileAttachment("Nam_Dinh_virus_ml_tree_midpoint@1.newick").text()) : yield parseNewick(await file.text())
}
Insert cell
text_data = {
file === null
? yield FileAttachment("Nam_Dinh_virus_ml_tree_midpoint@1.newick").text()
: yield file.text();
}
Insert cell
Insert cell
Insert cell
Insert cell
uniqueVirus = {
var uniqueVirus = [];
if (cleanlist.length > 0) {
cleanlist.reduce((previousValue, currentValue) => {
uniqueVirus.indexOf(currentValue.Virus) === -1
? uniqueVirus.push(currentValue.Virus)
: null;
});
}
yield uniqueVirus;
}
Insert cell
// https://github.com/jasondavies/newick.js
function parseNewick(a) {
for (
var e = [], r = {}, s = a.split(/\s*(;|\(|\)|,|:)\s*/), t = 0;
t < s.length;
t++
) {
var n = s[t];
switch (n) {
case "(":
var c = {};
(r.branchset = [c]), e.push(r), (r = c);
break;
case ",":
var c = {};
e[e.length - 1].branchset.push(c), (r = c);
break;
case ")":
r = e.pop();
break;
case ":":
break;
default:
var h = s[t - 1];
")" == h || "(" == h || "," == h
? (r.name = n)
: ":" == h && (r.length = parseFloat(n));
}
}
return r;
}
Insert cell
Insert cell
//function for building cleanlist
function checkinsertion(dict) {
for (let index = 0; index < mutable cleanlist.length; ++index) {
if (mutable cleanlist[index].Record === dict[0] && mutable cleanlist[index].Virus === dict[1] && mutable cleanlist[index].Code === dict[2]){
return false;
}
}
return true;
}
Insert cell
Insert cell
<style type = "text/css">
.phylotree-container rect{
cursor: default;
overflow-x: auto;
}
.container {
display:flex;
justify-content:center;
}

input#search-bar {
padding: 10px;
border: 1px solid grey;
margin-top: 8px;
margin-left: 35px;
margin-bottom: 10px;
font-size: 17px;
border-radius: 20px;
}
.search {
width: 30%;
min-width: 30%;
}
#tbody {
height: 800px;
overflow-y: auto;
overflow-x: hidden;
margin-bottom: 100px;
}
table td, table th{
text-align: center;
}

table th, table td {
border-bottom-width: 1px;
border-bottom-color: lightgray;
border-bottom-style: solid;
padding-top: 0.2rem;
padding-bottom: 0.2rem;
}

tr[display = false]{
display: none;
}
.link--active {
stroke: #000 !important;
stroke-width: 1.5px;
}
.link-extension--active {
stroke-opacity: .6;
}
.label--active {
font-weight: bold;
}

button{
margin-right:10px;
border-radius: 20px;
align-items: center;
appearance: none;
background-color: #FCFCFD;
border-radius: 4px;
border-width: 0;
box-shadow: rgba(45, 35, 66, 0.4) 0 2px 4px, rgba(45, 35, 66, 0.3) 0 7px 13px -3px, #D6D6E7 0 -3px 0 inset;
box-sizing: border-box;
color: #36395A;
cursor: pointer;
display: inline-flex;
font-family: "JetBrains Mono", monospace;
height: 48px;
justify-content: center;
line-height: 1;
list-style: none;
overflow: hidden;
padding-left: 16px;
padding-right: 16px;
position: relative;
text-align: left;
text-decoration: none;
transition: box-shadow .15s, transform .15s;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
white-space: nowrap;
will-change: box-shadow, transform;
font-size: 18px;
}
button:focus {
box-shadow: #D6D6E7 0 0 0 1.5px inset, rgba(45, 35, 66, 0.4) 0 2px 4px, rgba(45, 35, 66, 0.3) 0 7px 13px -3px, #D6D6E7 0 -3px 0 inset;
}
button:hover {
box-shadow: rgba(45, 35, 66, 0.4) 0 4px 8px, rgba(45, 35, 66, 0.3) 0 7px 13px -3px, #D6D6E7 0 -3px 0 inset;
transform: translateY(-2px);
}
button:active {
box-shadow: #D6D6E7 0 3px 7px inset;
transform: translateY(2px);
}
html:fullscreen {
height: 100%;
overflow: hidden;
/* Chrome adds a black backdrop in fullscreen. */
background-color: #fff;
}
html:fullscreen body {
height: 100%;
margin: 0;
padding: 14px;
overflow: auto;
box-sizing: border-box;
width: 100%;
}
/* Disable Observable's cell positioning, because we can't see the editors anyway. */
html:fullscreen .observablehq-root > .observablehq {
margin-bottom: 0 !important;
}
</style>
Insert cell
Insert cell
stylesheet = html`<link rel='stylesheet'
href='https://unpkg.com/phylotree@1.0.0-alpha.24/dist/phylotree.css' />`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const root = d3.hierarchy(data, d => d.branchset)
.sum(d => d.branchset ? 0 : 1)
.sort((a, b) => (a.value - b.value) || d3.ascending(a.data.length, b.data.length));

cluster(root);
setRadius(root, root.data.length = 0, innerRadius / maxLength(root));
setColor(root);

const svg = d3.create("svg")
.attr("viewBox", [-outerRadius, -outerRadius, width, width])
.attr("font-family", "sans-serif")
.attr("font-size", 10);

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

const linkExtension = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.25)
.selectAll("path")
.data(root.links().filter(d => !d.target.children))
.join("path")
.each(function(d) { d.target.linkExtensionNode = this; })
.attr("d", linkExtensionConstant);

const link = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(root.links())
.join("path")
.each(function(d) { d.target.linkNode = this; })
.attr("d", linkConstant)
.attr("stroke", d => d.target.color);

svg.append("g")
.selectAll("text")
.data(root.leaves())
.join("text")
.attr("dy", ".31em")
.attr("font-size", "10px")
.attr("transform", d => `rotate(${d.x - 90}) translate(${innerRadius + 4},0)${d.x < 180 ? "" : " rotate(180)"}`)
.attr("text-anchor", d => d.x < 180 ? "start" : "end")
.attr("node-id", (d, i) => i)
.text(function(d){
var list = d.data.name.replaceAll("_", " ").slice(1,-1).split('|');
if (checkinsertion(list)){
if (list.length === 3){ cleanlist.push({Record: list[0], Virus: list[1], Code: list[2]})}
else {cleanlist.push({Record: list[0], Virus: list[1]})}}
else{clearTimeout(InitialTimeOut);}
return d.data.name;
})
.text(d=> d.data.name.replaceAll("_", " ").slice(1,-1))
.on("mouseover", mouseovered(true))
.on("mouseout", mouseovered(false));

function update(checked) {
const t = d3.transition().duration(750);
linkExtension.transition(t).attr("d", checked ? linkExtensionVariable : linkExtensionConstant);
link.transition(t).attr("d", checked ? linkVariable : linkConstant);
}

function mouseovered(active) {
return function(event, d) {
d3.select(this).attr('highlighted', active);
d3.select(this).classed("label--active", active);
d3.select(d.linkExtensionNode).classed("link-extension--active", active).raise();
active ? highlightTr(d3.select(this).attr("node-id")) : unhighlightTr(d3.select(this).attr("node-id"));
do d3.select(d.linkNode).classed("link--active", active).raise();
while (d = d.parent);
};
}

return Object.assign(svg.node(), {update});
}
Insert cell
Table = html`
<div id="tbody">
<table id="table">
<thead>
<tr>
<th>&nbsp</th>
<th>Record</th>
<th>Virus</th>
${checkCodeExist() ? "<th>Code</th>" : ""}
</tr>
</thead>
<tbody></tbody>
</div>
</table>
`
Insert cell
Insert cell
Tablerows = {
d3.select(Table).select("tbody").select("tr").remove();
const tr = d3
.select(Table)
.select("tbody")
.selectAll("tr")
.data(cleanlist)
.enter()
.append("tr")
.attr("data-id", (d, i) => i)
.attr("display", true)
.attr("highlighted", false)
.on("mouseover", function (event, d) {
var node_id = d3.select(this).attr("data-id");
highlightTr(parseInt(node_id));
d3.select(`[node-id='${node_id}']`).dispatch("mouseover");
})
.on("mouseout", function (event, d) {
var node_id = d3.select(this).attr("data-id");
unhighlightTr(parseInt(node_id));
d3.select(`[node-id='${node_id}']`).dispatch("mouseout");
});
tr.selectAll("td")
.data((d) => ["", d.Record, d.Virus, d.Code])
.enter()
.append("td")
.attr("class", (d, i) => (i > 0 ? "" : null))
.text((d) => {
return d;
});
}
Insert cell
Insert cell
function highlightTr(index) {
const tds = d3.selectAll(`#table tr[data-id="${index}"]`)
tds.attr('highlighted', true);
tds.style("background-color", "#ffff99")
}
Insert cell
function unhighlightTr(index) {
const tds = d3.selectAll(`#table tr[data-id="${index}"]`)
tds.attr('highlighted', false);
tds.style("background-color", "white")
}
Insert cell
Insert cell
//function for checking whether the third column(Code) exists in data
function checkCodeExist(params) {
var exist = false
if (cleanlist.length > 0){
for (let index = 0; index < cleanlist.length; ++index) {
cleanlist[index].Code != undefined ? exist = true : null;
}
}
return exist;
}
Insert cell
Insert cell
OnSearchTextChange = {
d3.select("#search-bar").on("input", (e) => {
clearAllHighlighted();
e.target.value.length > 0 ? findMatch(e.target.value) : null;
}
)
}
Insert cell
function clearAllHighlighted(){
d3.selectAll(`tr`).attr("display", true);
d3.selectAll("[highlighted=true]").dispatch("mouseout");
}
Insert cell
function findMatch(text) {
text = text.toLowerCase();
for (let index = 0; index < cleanlist.length; ++index) {
var words = Object.values(cleanlist[index]);
for (var i in words){
if (words[i].toLowerCase().indexOf(text) >=0){
var node = d3.select(`[node-id = '${index.toString()}']`);
node.dispatch("mouseover");
break;
}
}
}
d3.selectAll("tr[highlighted=false]").attr("display", false);
}

Insert cell
Insert cell
Insert cell
InitialTimeOut = setTimeout(function name() {
mutable cleanlist = cleanlist;
}, 200)
Insert cell
Insert cell
Insert cell
update = chart.update(showLength)
Insert cell
cluster = d3.cluster()
.size([360, innerRadius])
.separation((a, b) => 1)
Insert cell
color = d3.scaleOrdinal()
.domain(uniqueVirus)
.range(d3.schemeCategory10)
Insert cell
// Compute the maximum cumulative length of any node in the tree.
function maxLength(d) {
return d.data.length + (d.children ? d3.max(d.children, maxLength) : 0);
}
Insert cell
// Set the radius of each node by recursively summing and scaling the distance from the root.
function setRadius(d, y0, k) {
d.radius = (y0 += d.data.length) * k;
if (d.children) d.children.forEach(d => setRadius(d, y0, k));
}
Insert cell
// Set the color of each node by recursively inheriting.
function setColor(d) {
var name = d.data.name.replaceAll("_", " ").slice(1,-1).split('|')[1];
d.color = color.domain().indexOf(name) >= 0 ? color(name) : d.parent ? d.parent.color : null;
if (d.children) d.children.forEach(setColor);
}
Insert cell
legend = svg => {
const g = svg
.selectAll("g")
.data(color.domain())
.join("g")
.attr("transform", (d, i) => `translate(${-outerRadius},${-outerRadius + i * 20})`);

g.append("rect")
.attr("width", 18)
.attr("height", 18)
.attr("fill", color);

g.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", "0.35em")
.text(d => d);
}
Insert cell
function linkVariable(d) {
return linkStep(d.source.x, d.source.radius, d.target.x, d.target.radius);
}
Insert cell
function linkConstant(d) {
return linkStep(d.source.x, d.source.y, d.target.x, d.target.y);
}
Insert cell
function linkExtensionVariable(d) {
return linkStep(d.target.x, d.target.radius, d.target.x, innerRadius);
}
Insert cell
function linkExtensionConstant(d) {
return linkStep(d.target.x, d.target.y, d.target.x, innerRadius);
}
Insert cell
function linkStep(startAngle, startRadius, endAngle, endRadius) {
const c0 = Math.cos(startAngle = (startAngle - 90) / 180 * Math.PI);
const s0 = Math.sin(startAngle);
const c1 = Math.cos(endAngle = (endAngle - 90) / 180 * Math.PI);
const s1 = Math.sin(endAngle);
return "M" + startRadius * c0 + "," + startRadius * s0
+ (endAngle === startAngle ? "" : "A" + startRadius + "," + startRadius + " 0 0 " + (endAngle > startAngle ? 1 : 0) + " " + startRadius * c1 + "," + startRadius * s1)
+ "L" + endRadius * c1 + "," + endRadius * s1;
}
Insert cell
width = 970
Insert cell
outerRadius = width / 2.11
Insert cell
innerRadius = outerRadius - 180
Insert cell
Insert cell
function matchMedia(query) {
return Generators.observe(notify => {
const list = window.matchMedia(query);
const onchange = e => { notify(e.matches) };
list.addEventListener('change', onchange);
notify(list.matches);
return () => list.removeEventListener('change', onchange);
});
}
Insert cell
width_between_600_and_1000px = matchMedia('(min-width: 600px) and (max-width: 1000px)')
Insert cell
is_fullscreen = matchMedia('(display-mode: fullscreen)')
Insert cell
Insert cell
Insert cell
mutable radial = true
Insert cell
Insert cell
tree = new phylotree.phylotree(text_data)
Insert cell
laddertree = tree.render({
height: phyloheight,
width: phylowidth,
"left-right-spacing": "fit-to-size",
"top-bottom-spacing": "fit-to-size",
"internal-names": true,
"node-styler": colorNodesByName
})
Insert cell
bootstrapNodes = tree.getInternals()
Insert cell
colorNodesByName = function (element, data) {
if (_.includes(tree.getInternals(), data)) {
element.style("stroke", "black");
}
}
Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
Insert cell
phylotree = require('phylotree@1.0.10')
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