Public
Edited
Mar 24
Paused
Importers
5 stars
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
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
matrixChart = showMatrix ? vl
.markCircle({ tooltip: true })
.encode(
vl
.y()
.fieldO("source.name")
.sort(vl.fieldO("source.cluster"))
.axis(null)
.axis(filteredNetwork.nodes.length > 200 ? null : {}),
vl
.x()
.fieldO("target.name")
.sort(vl.fieldO("source.cluster"))
.axis(filteredNetwork.nodes.length > 100 ? null : {}),
vl.size().fieldQ("value"),
vl.color().fieldN("source.cluster")
)
.data(
filteredNetwork.links.map((l) => ({
...l,
source: dAuthorsId.get(l.source),
target: dAuthorsId.get(l.target)
}))
)
.width(width - 200)
.height(width - 200)
.render() : htl.html`Please enable show matrix`
Insert cell
networkTypes = [
"Co-Authorship",
"Citation"
//"computed now from papers"
]
Insert cell
function clearFilteredAuthors() {
viewof selectedAuthors.value = [];
viewof selectedAuthors.dispatchEvent(new Event("input"));
viewof minLinkValue.value = selectedNetwork === "Citation" ? 15 : 0;
viewof minLinkValue.dispatchEvent(new Event("input"));
}
Insert cell
function showKhouryVis() {
viewof selectedAuthors.value = khouryVisProfessors;
viewof selectedAuthors.dispatchEvent(new Event("input"));
viewof selectedNetwork.value = "Co-Authorship";
viewof selectedNetwork.dispatchEvent(new Event("input"));
viewof minLinkValue.value = 0;
viewof minLinkValue.dispatchEvent(new Event("input"));
}
Insert cell
function showMostCoauthorship() {
viewof selectedAuthors.value = [];
viewof selectedAuthors.dispatchEvent(new Event("input"));
viewof selectedNetwork.value = "Co-Authorship";
viewof selectedNetwork.dispatchEvent(new Event("input"));
viewof minLinkValue.value = 15;
viewof minLinkValue.dispatchEvent(new Event("input"));
}
Insert cell
function showMostCited() {
viewof selectedAuthors.value = [];
viewof selectedAuthors.dispatchEvent(new Event("input"));
viewof minLinkValue.value = 30;
viewof minLinkValue.dispatchEvent(new Event("input"));
viewof selectedNetwork.value = "Citation";
viewof selectedNetwork.dispatchEvent(new Event("input"));
}
Insert cell
khouryVisProfessors = [
"Enrico Bertini",
"John Alexis Guerra Gómez",
"Lace M. K. Padilla",
"Cody Dunne",
"Michelle A. Borkin",
"Michelle Borkin",
"Michael Correll",
"Melanie Tory",
// "Caglar Yildirim",
// "Yifan Hu"
]
Insert cell
import {vl} from "@vega/vega-lite-api-v5"
Insert cell
r = d3
.scaleSqrt()
.domain([0, d3.max(network.nodes.map((d) => d.value))])
.range([1, maxR])
Insert cell
w = d3
.scaleLinear()
.domain([0, d3.max(network.links.map((l) => l.value))])
.range([0.1, 10])
Insert cell
ns = d3
.scaleLinear()
.domain([0, network.nodes.length])
.range([-70, -0.1])
Insert cell
colors = d3.schemeCategory10.concat(d3.schemeAccent)
// d3.quantize(d3.interpolateTurbo, dClusters.size)
Insert cell
dClusters = d3.group(network.nodes, d=> d.cluster)
Insert cell
dAuthorsName = d3.group(network.nodes, d=> d.name)
Insert cell
dAuthorsId = new Map(network.nodes.map((d,i) => [d.index,d]))
Insert cell
network = {
switch (selectedNetwork) {
case "Citation":
return citationNetwork;
case "computed now from papers":
return networkComputed;
default:
return coAuthorshipNetwork;
}
}
Insert cell
filteredNetwork = filterNetwork(network, {
edgelessNodes,
nodeId: (d) => d.index,
nodesMap: dAuthorsId,
egoDistance,
egoPoint5,
byNodes: (n) =>
selectedClusters.length === 0 || selectedClusters.includes("" + n.cluster),
byLinks: (l) =>
l.value >= minLinkValue &&
(selectedAuthors.length === 0 ||
selectedAuthors.includes(dAuthorsId.get(l.source).name) ||
selectedAuthors.includes(dAuthorsId.get(l.target).name))
})
Insert cell
function filterNetwork(
network,
{
nodeId = (d) => d.id,
byNodes = null,
byLinks = null,
edgelessNodes = false, // Should we allow nodes without edges?
nodesMap,
egoDistance = 1,
egoPoint5 = true // find connections between filtered connections
} = {}
) {
nodesMap = nodesMap
? nodesMap
: new Map(network.nodes.map((d, i, all) => [nodeId(d, i, all), d]));
let res = {
nodes: [...network.nodes],
links: [...network.links]
};
//
function filterNodesFromLinks(res) {
const ids = [...new Set(res.links.map((l) => [l.source, l.target]).flat())];
res.nodes = ids.map((id) => nodesMap.get(id)).filter((d) => d); // undefined when the node is not visible anymore;
return res;
}

function filterLinksFromNodes({ nodes, links }, { filterBoth = true } = {}) {
const ids = new Set(nodes.map(nodeId));
let filteredLinks = links;
links = links
.filter(({ source, target }) =>
filterBoth
? ids.has(source) && ids.has(target)
: ids.has(source) || ids.has(target)
)
.map((l) => ({ ...l }));
return { nodes, links };
}

// Filter nodes
if (byNodes) {
res.nodes = res.nodes.filter(byNodes).map((n) => ({ ...n }));
}

res = filterLinksFromNodes(res, { filterBoth: true });

// Then links
if (byLinks) {
res.links = res.links.filter(byLinks).map((l) => ({
...l
}));
// console.log("Filter Links", res);
// Remove edgeless nodes
if (!edgelessNodes) {
res = filterNodesFromLinks(res, { filterBoth: true });
}
}

if (egoPoint5) {
// 0.5
res = filterLinksFromNodes(
{ nodes: res.nodes, links: network.links },
{ filterBoth: true }
);
res = filterNodesFromLinks(res, { filterBoth: true });
}

for (
let currentDistance = 1;
currentDistance < egoDistance;
currentDistance += 1
) {
res = filterLinksFromNodes(
{ nodes: res.nodes, links: network.links },
{ filterBoth: false }
);
res = filterNodesFromLinks(res, { filterBoth: true });

if (egoPoint5) {
// 0.5
res = filterLinksFromNodes(
{ nodes: res.nodes, links: network.links },
{ filterBoth: true }
);
res = filterNodesFromLinks(res, { filterBoth: true });
}
}

return res;
}
Insert cell
coAuthorshipNetwork = FileAttachment("coauthorshipNetwork2024_full.json")
.json()
.then((res) => {
res.nodes.map((d) => (d.name = d.name.replace(/\d/g, '').trim())); // there is a weird 001 in the data 🤷🏼
return res;
})
Insert cell
citationNetwork = FileAttachment("citationsNetwork2024_full.json")
.json()
.then((res) => {
res.nodes.map((d) => (d.name = d.name.replace(/\d/g, '').trim())); // there is a weird 001 in the data 🤷🏼
return res;
})
Insert cell
viewof selectedPapers = navio(papers)
Insert cell
papers = selectedNetwork === "computed now from papers"
? FileAttachment("IEEE VIS papers 1990-2024 - Main dataset.csv").csv({
typed: true
})
: [{ id: 1 }]
Insert cell
viewof unrollAttr = Inputs.select( ["AuthorNames", "InternalReferences"], {label: "Unroll papers by"})
Insert cell
viewof papersUnrolled = aq.from(selectedPapers)
.derive({name: aq.escape(d => op.split(d[unrollAttr], ";"))})
.unroll("name")
.view()
Insert cell
clusters = d3
.groups(network.nodes, (d) => d.cluster)
.sort((a, b) => d3.descending(a[1].length, b[1].length))
.map((d) => `${d[0]}: ${d[1].map((d) => d.name).join(", ")} `)
Insert cell
selectedClusters = selectedClustersObjs.map(d => d.split(": ")[0])
Insert cell
networkComputed = {
if (selectedNetwork !== "computed now from papers")
return { nodes: [], links: [] };

const network = tableToNetwork(papersUnrolled.objects(), {
nodesBy: "name",
matchBy: "DOI"
});
network.nodes.map((n) => {
n.index = n.id;
n.name = n.id;
n.value = n.degree;
n.cluster = 1;
delete n.id;
delete n.degree;
});

network.links.map((l) => {
l.value = l.count;
delete l.count;
});
return network;
}
Insert cell
allPapers = FileAttachment("IEEE VIS papers 1990-2024 - Main dataset.csv").csv({
typed: true
})
Insert cell
allPapers
.filter((d) => d.AuthorNames && d.AuthorNames.indexOf("Cody Dunne") !== -1)
.map((d) => `${d.AuthorNames}- ${d.Title}`)
Insert cell
OnClickLink("Click me", {
label: "clickme",
onClick: () => {
console.log("testing");
}
})
Insert cell
function OnClickButton(label, opts) {
const btn = Inputs.button(label, opts);
if (opts.onClick) {
btn.addEventListener("click", opts.onClick);
}

return btn;
}
Insert cell
Insert cell
height = 600
Insert cell
import {tableToNetwork} from "@john-guerra/table-to-network"
Insert cell
import {aq, op} from "@uwdata/arquero"
Insert cell
import {navio} from "@john-guerra/navio"
Insert cell
import { ForceGraph } from "@john-guerra/force-directed-graph"
Insert cell
import {searchCheckbox} from "@john-guerra/search-checkbox"
Insert cell
import {multiAutoSelect} from "@john-guerra/multi-auto-select"
Insert cell
import {PersistInput} from "@john-guerra/persist-input"
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