Public
Edited
Jan 31, 2023
1 fork
1 star
Insert cell
Insert cell
viewof charge = Inputs.range([1000, 10000], {
value: 3000,
step: 1000,
label: "charge"
})
Insert cell
chart = {
const links = data.links.map((d) => Object.create(d));
const nodes = data.nodes.map((d) => Object.create(d));
// await new Promise((eLink(nodes.links).strength(0.05).distance(100));
const simulation = d3
.forceSimulation(nodes)
.force(
"link",
d3.forceLink(links).id((d) => d.id)
)
.force("charge", d3.forceManyBody().strength(-charge))
.force("x", d3.forceX())
.force("y", d3.forceY());

const svg = d3
.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height])
.style("font", "12px sans-serif");

// Per-type markers, as they don't inherit styles.
svg
.append("defs")
.selectAll("marker")
.data(nodes.map((n) => n.id))
.join("marker")
.attr("id", (d) => `arrow-${d}`)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -2)
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("orient", "auto")
.append("path")
.attr("fill", "grey")
.attr("d", "M0,-5L10,0L0,5");

const link = svg
.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.selectAll("path")
.data(links)
.join("path")
.attr("stroke", (d) => skillColor(d.Label));

link
.attr("stroke-width", 2)
.attr(
"marker-end",
(d) => `url(${new URL(`#arrow-${d.source.id}`, location)})`
);

// const nodeDefs = svg.append("defs").attr("id", "imgdefs");
// const imgpattern = nodeDefs
// .append("pattern")
// .attr("id", "imgpattern")
// .attr("height", 1)
// .attr("width", 1)
// .attr("x", "0")
// .attr("y", "0");
// imgpattern
// .append("image")
// .attr("x", 0)
// .attr("y", 0)
// .attr("height", 40)
// .attr("width", 40)
// .attr(
// "xlink:href",
// "https://cdn.lorem.space/images/face/.cache/150x150/daniil-lobachev-u3pi6HhSYew-unsplash.jpg"
// );
const node = svg
.append("g")
.attr("fill", "currentColor")
.attr("stroke-linecap", "round")
.attr("stroke-linejoin", "round")
.selectAll("g")
.data(nodes)
.attr("id", (d) => `g${d.index}`)
.join("g")
.call(drag(simulation));

node
.append("circle")
.attr("stroke", "white")
.attr("stroke-width", 1)
.attr("r", 20)
// .attr("fill", (d) => "url(#imgpattern)")
.attr("stroke-width", (d) => (d.nodeWeight + 2) / 2);
// .attr("fill", (d) => nodeColor(d.id));
// .attr("stroke-width", (d) => (d.nodeWeight + 2) / 2)
// .attr("fill", )

node
.append("text")
.attr("x", 20)
.attr("y", "0.31em")
.text((d) => d.id)
.clone(true)
.lower()
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 3);
node
.append("image")
.attr('xlink:href', d=>d.img)
.attr("height", 25)
.attr("width", 25)
.attr("transform", "translate(-13,-13)")
.style("border-radius", 5);
// svg.selectAll("g.node").style({"border-radius": "50%"});

simulation.on("tick", () => {
link.attr("d", linkArc);
node.attr("transform", (d) => `translate(${d.x},${d.y})`);
});

invalidation.then(() => simulation.stop());

return svg.node();
}
Insert cell
nodeColor = d3.scaleOrdinal(
data.nodes.map((n) => n.id),
d3.schemeTableau10
)
Insert cell
skillColor = d3.scaleOrdinal(skills, d3.schemeCategory10)
Insert cell
skills = Array.from(new Set(links.map((l) => l.Label)))
Insert cell
function linkArc(d) {
const r = Math.hypot(d.target.x - d.source.x, d.target.y - d.source.y);
return `
M${d.source.x},${d.source.y}
A${r},${r} 0 0,1 ${d.target.x},${d.target.y}
`;
}
Insert cell
drag = (simulation) => {
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}

function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}

return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
Insert cell
data = ({
nodes: nodes_skills.map((n) => {
n.img = "https://cdn.lorem.space/images/face/.cache/150x150/daniil-lobachev-u3pi6HhSYew-unsplash.jpg";
return n;
}),
links
})
Insert cell
nodes_skills = FileAttachment("nodes_skills.json").json()
Insert cell
Insert cell
// ExistingSkillsBarChart = BarChart(demandsFreqData, {
// x: d => d.skill,
// y: d => d.frequency,
// // xDomain: d3.groupSort(alphabet, ([d]) => -d.frequency, d => d.letter), // sort by descending frequency
// // yFormat: "%",
// // yLabel: "↑ Frequency",
// width : 1000,
// // height: 500,
// color: "steelblue"
// })
Insert cell
existingSkills = {
const skillsToShare = Array.from(skillsharedRaw.flatMap(e=>e.SkillsToShare), e=>e?.trim());
const existingSkillsFreq = ({});
for(const skill of skillsToShare){
// existingSkillsFreq[skill] = skillsToShare[skill]? skillsToShare[skill] + 1:1;
demandsFreq[skill] = demandsFreq[skill]? demandsFreq[skill] + 1:1;
}
return existingSkillsFreq;
}
Insert cell
Array.from(skillsharedRaw.flatMap(e=>e.SkillsToShare))
Insert cell
demandSkillsBarChart = BarChart(demandsFreqData, {
x: d => d.skill,
y: d => d.frequency,
// xDomain: d3.groupSort(alphabet, ([d]) => -d.frequency, d => d.letter), // sort by descending frequency
// yFormat: "%",
// yLabel: "↑ Frequency",
width : 1000,
// height: 500,
color: "steelblue"
})
Insert cell
demandsFreqData = {
const demandsFreqData = [];
for(const skill of Object.keys(demandsFreq)){
const tempObject = ({});
tempObject["skill"] = skill;
tempObject["frequency"] = demandsFreq[skill];
demandsFreqData.push(tempObject);
}
return demandsFreqData.sort((a,b)=>(b.frequency-a.frequency));
}
Insert cell
demandsFreq = {
const demandsFreq = ({});
for(const skill of demandedSkills){
demandsFreq[skill] = demandsFreq[skill]? demandsFreq[skill] + 1:1;
}
return demandsFreq
}
Insert cell
demandedSkills =
Array.from(skillsharedRaw.flatMap(e=>e.SkillsToLearn))
Insert cell
skillsharedRaw = {
const ssr = skillsharedataset.sheet(0, { headers: true, typed:true })
const skillsharedRaw = ssr.map(e=>
{
e.SkillsToShare = e["Skills to Share"]?e["Skills to Share"]?.split(","):[];
e.SkillsToLearn = e["Skills to Learn"]?.split(",");
return e;
});
return skillsharedRaw;
}
Insert cell
skillsharedataset = FileAttachment("skillsharedataset.xlsx").xlsx()
Insert cell
height = (charge + 1000) / 7
Insert cell
import {BarChart} from "@d3/bar-chart"
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