Public
Edited
Jan 29, 2022
1 fork
Importers
Insert cell
md`# Force Layout for Candidat`
Insert cell
Insert cell
html`${viewof group_choice}`
Insert cell
T = require('@tidyjs/tidy@2.4.2/dist/umd/tidy.min.js')

Insert cell
clean("Star (higher the better)", filtered_company_data)
Insert cell
clean= (selection, data) =>{
let clean_data=[]
if (selection=="Seniority"){
clean_data=tidy(data, arrange([T.fixedOrder('Seniority', ["Leadership","Manager","Lead","Individual Contributor"])]));}
else if (selection=="Star (higher the better)"){
clean_data=tidy(data, arrange([T.fixedOrder('Star (higher the better)', [5,4,3,2,1,0,"N/A"])]));}
else if (selection=="gender"){
clean_data=tidy(data, arrange([T.fixedOrder('gender', ["Female","Male"])]));}
else if (selection=="Ethnically Diverse"){
clean_data=tidy(data, arrange([T.fixedOrder('Ethnically Diverse', ["Yes","No"])]));}
return clean_data
}
Insert cell
Insert cell
Insert cell
force={
return html`<div class="demo">
<style>
div.demo{
position:relative
}
div.toolTip {
position: absolute;
display: none;
min-width: 30px;
max-width: 240px;
border-radius: 4px;
height: auto;
background: rgba(250,250,250, 0.9);
border: 1px solid #DDD;
padding: 4px 8px;
font-size: .85rem;
text-align: center;
z-index: 1000;
}
</style>
</div>`
}
Insert cell
previews(profiles)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
height=300
Insert cell
char_width=width
Insert cell
chart = {
d3.select(".demo").selectAll("svg").remove();
const svg = d3.select(".demo").append("svg") .attr("width",char_width).attr("height", height)
// var svg = d3.create('svg')
// .attr("width",width)
// .attr("height", 600);

let toolTip = d3.select(".demo").append("div").attr("class", "toolTip")

var board= svg.append("g").attr("transform","translate(30,0)")
let options=Array.from(new Set(filtered_company_data.map(d=>d["gender"])))
var colorScale = d3.scaleOrdinal().domain(options).range(["#ff0000","#626681","#FFC100","#9FB40F","#76523B","#DAD5B5","#0E8E89","#E19348","#F383A2","#247FEA"])
// let colorScale= d3.scaleOrdinal().domain(["Female","Male"]).range(["#ff0000","#434343"])

var yScale=d3.scalePoint().domain(options).range([80, height-120]).padding(0.5)
function ticked() {
bubbles.selectAll("circle").attr("cx", function(d) {return d.x;}).attr("cy", function(d) { return d.y;})
}
var legendOrdinal = d3legend.legendColor()
.title(`${group_choice} of Candidates`)
.titleWidth(300)
.shape("path", d3.symbol().type(d3.symbolCircle).size(150)())
.shapePadding(10)
.scale(colorScale);
board.append("g")
.attr("class", "legendSymbol")
.attr("transform", "translate(20, 20)");
d3.select(".legendTitle").select("tspan").attr("dy","0.7em")
d3.select(".legendSymbol").call(legendOrdinal)

var simulation = d3.forceSimulation()
.nodes(filtered_company_data)
.force('charge', d3.forceManyBody().strength(-12))
.force("x", d3.forceX(char_width/2).strength(0.015))
.force("y", d3.forceY().y(d=>yScale(d["gender"])).strength(0.5))
.force('collision', d3.forceCollide().radius(18))
.on("tick", ticked)

var bubbles = board.selectAll("g.node").data(filtered_company_data).join("g").attr("class", "node");
const defs = bubbles.append("defs");
defs
.append('pattern')
.attr("id", d=>d["Public LinkedIn URL"].replace(/[^0-9a-z]/gi, ''))
.attr("width", 1)
.attr("height", 1)
.append("svg:image")
.attr("xlink:href", (d,i) => profiles[i]=="error"?"https://i.postimg.cc/0NKhB9qw/person.jpg":d.Image)
.attr("width", 30)
.attr("height", 30)
.attr("preserveAspectRatio", "xMidYMid slice");

bubbles.append("a")
.attr("xlink:href", d=>d["Public LinkedIn URL"])
.attr("target","_blank")
.append("circle")
.attr("class", "profile-circle")
.attr("fill", (d,i) => `url(#${d["Public LinkedIn URL"].replace(/[^0-9a-z]/gi, '')})`)
.attr("stroke", d=>colorScale(d["gender"]))
.attr("stroke-width", 3)
.attr("r", 15)
.on('mousemove', nodeMouseOver).on('mouseout', nodeMouseOut );
// .append("title")
// .text(d => d["Full Name"])

function nodeMouseOver(event, d){
const x = d.x + 30;
const y = d.y + 30;
// <img src=${d.Image} alt=${d["Full Name"]}></img>
// const i= filtered_company_data.map(u=>u["Public LinkedIn URL"]).indexof(d["id"])
// const url_use= (profiles[i]=="error")?"https://i.postimg.cc/0NKhB9qw/person.jpg":d.Image
toolTip
.style("left", `calc(10px + ${x}px)`)
.style("top", `calc(10px + ${y}px)`)
.style("display", "block")
.html(`
<h5>Name: <strong>${d["First Name"]} ${d["Last Name"]}</strong></h5>
<div style="border: solid 1px #e8e8e8; border-radius: 4px; box-sizing: border-box; width: 100%; padding-top: 62.5%; background-size: contain; background-repeat: no-repeat; background-position: center; background-image: url(${
d.Image
});"></div>

<p>Gender: <strong>${d["gender"]}</strong></p>
<p>Star: <strong>${d["Star (higher the better)"]}</strong></p>
<p>Seniority: <strong>${d["Seniority"]}</strong></p> `);

toolTip.selectChildren().style("display","block")
// Optional cursor change on target
d3.select(event.target).style("cursor", "pointer");
// Optional highlight effects on target
d3.select(this)
// .transition()
.attr('stroke', '#A8234E')
.attr('stroke-width', 3);
}
function nodeMouseOut(event, d){
// Hide tooltip on mouse out
toolTip.style("display", "none"); // Hide toolTip
// Optional cursor change removed
d3.select(event.target).style("cursor", "default");
// Optional highlight removed
d3.select(event.target)
.attr('stroke', d=>colorScale(d.gender))
.attr('stroke-width', 3);
}

invalidation.then(() => simulation.stop());
return Object.assign(svg.node(), {
update(selection) {
let data_use=clean(selection,filtered_company_data)
let options_new=Array.from(new Set(data_use.map(d=>d[selection])))
if (selection=="gender"){options_new=["Female","Male"]}
let colorScale_new = d3.scaleOrdinal().domain(options_new).range(["#ff0000","#626681","#FFC100","#9FB40F","#76523B","#DAD5B5","#0E8E89","#E19348","#F383A2","#247FEA"])

let yScale_new=d3.scalePoint().domain(options_new).range([80, height-120]).padding(0.5)
legendOrdinal.scale(colorScale_new)
d3.select(".legendSymbol").call(legendOrdinal)
function new_nodeMouseOut(event, d){
// Hide tooltip on mouse out
toolTip.style("display", "none"); // Hide toolTip
// Optional cursor change removed
d3.select(event.target).style("cursor", "default");
// Optional highlight removed
d3.select(event.target)
.attr('stroke', d=>colorScale_new(d[selection]))
.attr('stroke-width', 3)
}
simulation.force("y", d3.forceY(d=>yScale_new(d[selection])).strength(0.2))
const t = d3.transition()
.duration(750)
.ease(d3.easeLinear);

bubbles.selectAll("circle").transition(t).attr("stroke", function(d) {return colorScale_new(d[selection]);})
bubbles.selectAll("circle").on("mousemove", nodeMouseOver).on("mouseout", new_nodeMouseOut)

simulation.alpha(0.5).alphaTarget(0.3).alphaDecay(0.02).restart();
}
})
}

Insert cell
// toolTip = d3.select("body").append("div").attr("class", "toolTip")
Insert cell
Insert cell
Insert cell
Insert cell
filtered_company_data[0]
Insert cell
loadImage(filtered_company_data[0])
Insert cell
Insert cell
function loadImage3(bird) {
var img = new Image();
img.src = bird['image'];

return Promises.delay(
200,
new Promise((resolve, reject) => {
img.decode().then(() => resolve([img, bird]));
// img.onerror = resolve([img, bird]);
img.onerror = resolve("error");
})
);
}
Insert cell
Insert cell
profiles3=Promise.all(data_test.map(bird => loadImage3(bird)))
Insert cell
Insert cell
Insert cell
Insert cell
data_test=FileAttachment("data_test.csv").csv({typed:true})
Insert cell
d3legend=require('d3-svg-legend')
Insert cell
loadImage3({img:"https://i.postimg.cc/0NKhB9qw/person.jpg"})
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