Published
Edited
May 15, 2021
1 star
Insert cell
md`# 小组会议发言分布 (using force-layout and topic distribution are generated by Biterm-Topic-Model)`
Insert cell
{
const paddingTop = 100
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("font", "10px sans-serif");
const g = svg.append("g")
.attr("transform",`translate(0,${paddingTop})`)
const simulation = d3.forceSimulation(circles)
.force("x", d3.forceX(d => d.x))
.force("y", d3.forceY(d => d.y))
.force("collide", d3.forceCollide(d => d.r).strength(0.3));
const bar = g.append("g")
.selectAll("g")
.data(agendas)
.join("g")
.attr("class",(d,i) => `agenda-group-${i}`)
.on("mouseover",(evt,d,i)=>mouseOver(evt,d,i))
.on("mouseout",mouseOut)
bar.append("rect")
.attr("width",15)
.attr("height",(d,i) => -yScaleBar(dialogs.filter(d => d.agenda === i).length) )
.attr("y", (d,i) => yScaleBar(dialogs.filter(d => d.agenda === i).length))
.attr("x",(d,i) => 50 * i)
.attr("fill","grey")
.attr("opacity",0.5)
g.append("g")
.call(axisRight)
g.append("g")
.call(axisBottom)
const storyBoard = g.append("g")
.attr("class","storyBoard")
const nodes = storyBoard.selectAll("g")
.data(circles)
.join("g")
.attr("class",d => `node ${d.topic == undefined ? "":"node-topic-"+d.topic} ${d.agenda == undefined ? "":"node-agenda-"+d.agenda}`)
.attr("transform",d=>`translate(${d.x},${d.y})`)
nodes.selectAll("path")
.data(d=>d.arcs)
.join("path")
.attr("d",d=>arc(d))
.attr("fill-opacity", d=>{
if(d.value === 2)
return 0.3
else
return 0.85
})
.attr("fill",d=>{
if(d.value === 2)
return "grey"
else
return color[d.index]
})
simulation.on("tick", () => {
nodes
.transition()
.delay((d, i) => d.x)
.ease(d3.easeLinear)
.attr("transform",d=>`translate(${d.x},${d.y})`)
});
simulation.on("end",()=>{
bar.append("g")
.selectAll("path")
.data((d,i) => circles.filter(d=>d.agenda != undefined && d.agenda === i))
.join("path")
.attr("class",d=>`link link-${d.index}`)
.transition()
.duration(2000)
.attr("d",d => lineCurveTo ([d.agenda * 50 + 7.5,0],[d.x,d.y]))
.attr("stroke","grey")
.attr("stroke-opacity",0.3)
.attr("fill","none")
})
// const hullG = g.append("g")
// .attr("class","hullG")
// const hulls = hullG
// .selectAll('path')
// .data(agendas.map((a,i) => {
// return {
// cluster: i,
// nodes: circles.filter(d => (d.agenda != undefined && d.agenda === i))
// };
// }).filter(d=> d.cluster === 0))
// .join('path')
// .attr('d', d => lineClose(d3.polygonHull(hullPoints(d.nodes))))
// .attr('fill', function(d) { return color[d.cluster]})
// .attr('opacity', 0.4)
return svg.node()

}
Insert cell
agendas.map((a,i) => {
return {
cluster: i,
nodes: circles.filter(d => (d.agenda != undefined && d.agenda === i))
};
})
Insert cell
md`## Function`
Insert cell
mouseOver = function(evt,d){
d3.selectAll(".link")
.attr("stroke", "grey")
.attr("stroke-opacity", 0.1);

d3.selectAll(`.agenda-group-${agendas.indexOf(d)} .link`)
.attr("stroke", "orange")
.attr("stroke-opacity", 1);
d3.selectAll(".node")
.attr("opacity", 0.1);
d3.selectAll(`.node-agenda-${agendas.indexOf(d)}`)
.attr("opacity", 0.85);
}
Insert cell
mouseOut = _ => {
d3.selectAll(".link")
.attr("stroke", "grey")
.attr("stroke-opacity", 0.3);
d3.selectAll(".node")
.attr("opacity", 0.85);
}
Insert cell
hullPoints = function (data) {
let pointArr = [];
const padding = 0;
data.forEach(d => {
const pad = d.r + padding;
pointArr = pointArr.concat([
[d.x - pad, d.y - pad],
[d.x - pad, d.y + pad],
[d.x + pad, d.y - pad],
[d.x + pad, d.y + pad]
]);
});
return pointArr;
}
Insert cell
lineCurveTo = (a,b) => {
const p = d3.path()
p.moveTo(...a)
p.bezierCurveTo(a[0],a[1]+50,b[0],b[1],...b)
return p.toString()
}
Insert cell
lineClose = d3.line().curve(d3.curveBasisClosed)
Insert cell
xScale = d3.scaleLinear()
.domain([0,dialogs[dialogs.length-1].endTime+10])
.range([0+padding,width-padding])
Insert cell
yScale = d3.scalePoint()
.domain(roles)
.range([0+padding,width-padding])
.round(true)
.padding(0.5)
Insert cell
yScaleBar = d3.scaleLinear()
.domain([0,40])
.range([0,-100])
Insert cell
formatSecond = value => value+"s"
Insert cell
format = (value)=>{
let min = Math.floor(value/60)
let second = value % 60
return "" + min + "min"+second+"s"
}
Insert cell
axisBottom = svg => svg.call(d3.axisBottom(xScale)
.tickFormat(formatSecond)
.tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.attr("font-size",5))

Insert cell
axisRight = svg => svg.call(d3
.axisRight(yScale)
.tickSize(width)
.tickSizeOuter(0)
)
.call(g => g.selectAll(".tick line")
.attr("stroke-opacity",0.5)
.attr("stroke-dasharray","2,2"))
.call(g => g.selectAll(".tick text")
.attr("x",4)
.attr("dy",-4))
Insert cell
arc = d3.arc()
.startAngle(d => d.startAngle)
.endAngle(d => d.endAngle)
.innerRadius(0)

Insert cell
md`## Configuration`
Insert cell
color = d3.schemeCategory10
Insert cell
width = 450
Insert cell
height = width *2
Insert cell
topicNum = 3
Insert cell
padding = 20
Insert cell
md`## Data`
Insert cell
dialogs = FileAttachment("ES2002a(dt)(3btm)(ag).json").json()
Insert cell
circles = dialogs.map(v=>{
let c = {}
let start = xScale(v.startTime)
let end = xScale(v.endTime)
if(v.topic || v.topic === 0){
c.arcs = d3.pie().sort(null)(v.dis)//sort(null)意思是不排序,数据按照输入的顺序排列
}
else
{
c.arcs = d3.pie()([2])
}
if(v.agenda != undefined)
c.agenda = v.agenda
c.r = (Math.floor((end-start)/2)+4)
c.arcs.forEach(a=>a.outerRadius = c.r)

c.x = start+c.r
c.y = yScale(v.role)
return c
})
Insert cell
agendas = ['Introduce yourself to the other members including your name and job',
'Check the meeting agenda in the email',
'Familiar with meeting room equipment like whiteboard, microphones, projectors',
'draw favourite animal and sum up your favourite characteristics of it',
'Discuss the project finances and selling prices',
'Discuss various features to consider in making the remote controller.']
Insert cell
roles = {
return [...new Set(dialogs.map(v=>v.role))]
}
Insert cell
md`## Reference`
Insert cell
d3 = require("d3@6")
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