Published
Edited
Jul 15, 2021
Insert cell
md`# 카드 클러스터, 새로운 카드 클러스터 생성하기(2회차 데모회 테스트)`
Insert cell
chart = {
let delaunay = [];
let voronoi;
let viewCoord = { scale: 1, offx: 0, offy: 0 };
let dragStartEventPos = [0, 0];
let dragStartCardPos = [0, 0];

function viewposTrans(event) {
return {
x:
dragStartCardPos[0] +
(event.x - dragStartEventPos[0]) / viewCoord.scale,
y:
dragStartCardPos[1] + (event.y - dragStartEventPos[1]) / viewCoord.scale
};
}

const simulation = d3
.forceSimulation(extSentences)
.force("x", d3.forceX(d => x(d.sx)).strength(0.5))
.force("y", d3.forceY(d => y(d.sy)).strength(0.5))
.force("collide", d3.forceCollide(cardHeight * 0.8).strength(0.5));
simulation.tick(100);

const rootEl = d3.select(
html`<div style="position: relative; width: ${width}px; height: ${height}px;"></div>`
);

const node = rootEl
.append("div")
.attr("id", "view")
.style("width", `${width}`)
.style("height", `${height}`)
.selectAll("div.card")
.data(extSentences)
.join("div")
.attr("class", d => `card ${d._cluster}`)
.style("position", "absolute")
.style("will-change", "transform")
.style(
"transform",
d => `translate(${d.x - cardWidth / 2}px, ${d.y - cardHeight / 2}px)`
)
.style("width", `${cardWidth}px`)
.style("height", `${cardHeight}px`)
.style("background-color", d => getClusterColor(d._cluster))
.style('box-shadow', '2px 2px 8px #0004')
.style('box-sizing', 'border-box')
.style('padding', '0.5em')
.style('overflow', 'hidden')
.style('font-size', '4px')
.style('cursor', 'grab')
.style("transition", "opacity 0.5s ease-out")
.text((d, i) => d._text);

const rootSvg = rootEl
.append("svg")
.attr("viewBox", `${[0, 0, width, height]}`)
.attr("class", "rootSvg");

const mesh = rootSvg
.append("path")
.attr("fill", "none")
.attr("class", "mesh")
.attr("stroke", "black");

const cell = rootEl
.select("svg")
.append("g")
.attr("fill", "none")
.attr("pointer-events", "all")
.selectAll("path")
.data(extSentences)
.join("path")
.attr("class", "cell");

function updatePartition() {
delaunay = d3.Delaunay.from(extSentences, d => d.x, d => d.y);
voronoi = delaunay.voronoi([0, 0, width, height]);
// d3.select(".rootSvg")
// .selectAll('path.mesh')
// .data([1])
// .join("path")
// .attr("fill", "none")
// .attr("class", "mesh")
// .attr("stroke", "black")
// .attr("d", voronoi.render());

// d3.selectAll(".cell")
// .data(extSentences)
// .attr("stroke", "black")
// .attr("d", (d, i) => voronoi.renderCell(i));
}

function findLabel(px, py) {
const index = delaunay.find(px, py);
const sentence = extSentences[index];

const distance =
((x.invert(px) - sentence.sx) ** 2 + (y.invert(py) - sentence.sy) ** 2) **
0.5;

return distance < 0.1
? sentence._cluster
: d3.max(extSentences, d => d._cluster) + 1;
}

const drag = simulation => {
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.1).restart();
node.filter(p => p === d).raise();

// scale에 따른 position
dragStartEventPos = [event.x, event.y];
dragStartCardPos = [d.x, d.y];
const viewpos = viewposTrans(event);
d.fx = viewpos.x;
d.fy = viewpos.y;
}

function dragged(event, d) {
const viewpos = viewposTrans(event);

d.fx = viewpos.x;
d.fy = viewpos.y;

console.log(viewpos.x)
const oldColor = getClusterColor(d._cluster);
const curColor = getClusterColor(findLabel(viewpos.x, viewpos.y));
d3.select(this).style(
"background",
`linear-gradient(135deg, ${oldColor} 30%, ${curColor} 70%
)`
);
}

function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
const viewpos = viewposTrans(event);
d.sx = x.invert(viewpos.x);
d.sy = y.invert(viewpos.y);
d.fx = null;
d.fy = null;

d._cluster = findLabel(event.x, event.y);
d3.select(this).style("background", getClusterColor(d._cluster));

updatePartition();
simulation.nodes(extSentences);
}

return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
};

let transform = d3.zoomIdentity.scale(0.5);

function zoomed(transform) {
d3.select("g").attr("transform", "translate(" + transform.x + "," + transform.y + ") scale(" + transform.k + ")");

d3.select("#view")
.style('transform-origin', `${transform.x}px ${transform.y}px`)
.style(
"transform",
`scale(${transform.k}) translate(${transform.x}px,${transform.y}px) `
);
viewCoord.scale = transform.k;
viewCoord.offx = transform.x;
viewCoord.offy = transform.y;
}

const zoom = d3
.zoom()
.scaleExtent([0.5, 3])
.on('zoom', ({ transform }) => zoomed(transform));

rootEl.call(zoom);
node.call(drag(simulation));
node.call(updatePartition);

simulation.on("tick", () => {
node.style("transform", d => {
let transform = `translate(${d.x - cardWidth / 2}px, ${d.y -
cardHeight / 2}px)`;
return transform;
});
});

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

return rootEl.node();
}
Insert cell
Insert cell
Insert cell
keywordList = analyzed.keywords
Insert cell
Insert cell
Insert cell
function cosinesim(A, B) {
let dotproduct = 0;
let mA = 0;
let mB = 0;
for (let i = 0; i < A.length; i++) {
// here you missed the i++
dotproduct += A[i] * B[i];
mA += A[i] * A[i];
mB += B[i] * B[i];
}
mA = Math.sqrt(mA);
mB = Math.sqrt(mB);
var similarity = dotproduct / (mA * mB); // here you needed extra brackets
return similarity;
}
Insert cell
Insert cell
Insert cell
scale = 0.5
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function getClusterColor(cluster) {
return colors[cluster % colors.length];
}
Insert cell
Insert cell
rawTable.view()
Insert cell
rawTable = aq.from(raw)
Insert cell
raw = [...tsv].slice(1)
Insert cell
colTypes = Object.values(tsv[0])
Insert cell
analyzed = {
const n = 20;
const nc = 3;
const keys = Object.keys(tsv[0]);
console.log(keys);
const textcols = colTypes
.map((c, i) => (c === "text" ? keys[i] : null))
.filter(d => d !== null);
return analyzeTable(n, nc, "complete", textcols);
}
Insert cell
async function analyzeTable(n, nc, method, textcols) {
const url = 'https://an-engine.pxd.systems/analyze-table'
const params = `n=${n}&nc=${nc}&method=${method}&textcols=${encodeURIComponent(textcols.join(","))}`
const res = await fetch(`${url}?${params}`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(raw),
})
return res.json()
}
Insert cell
tsv = await d3.tsv(googleSheet)
Insert cell
googleSheet = "https://docs.google.com/spreadsheets/d/e/2PACX-1vTeavK2VcoS8eq7tbsXoqizYXlOuPHxFq_SBxF8DwsTysFxloAysoN1ZldtNWKajrW9n680vc2pK74A/pub?gid=856602323&single=true&output=tsv"
Insert cell
Insert cell
import {aq, op} from '@uwdata/arquero'
Insert cell
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