Skip to content

Commit

Permalink
update docs: datavis survey analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
enjalot committed May 3, 2024
1 parent 3d7fc9f commit 56c86c2
Show file tree
Hide file tree
Showing 43 changed files with 2,052 additions and 538 deletions.
Binary file not shown.
1,079 changes: 1,079 additions & 0 deletions _file/data/datavis-survey/scopes-001.d44da6f0.json

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions _file/index.e65534ab.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

/* HEADER STYLES */
#observablehq-header a[href] {
color: inherit;
}

#observablehq-header a[target="_blank"] {
display: flex;
align-items: center;
gap: 0.25rem;
text-decoration: none;
}

#observablehq-header a[target="_blank"]:hover span {
text-decoration: underline;
}

#observablehq-header a[target="_blank"]::after {
content: "↗";
}

@container not (min-width: 640px) {
.hide-if-small {
display: none;
}
}


/* DOCUMENTATION STYLES */
.pageshot {
width: 60%;
}
.screenshot {
width: 55%;
margin: 0 24px;
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3);
margin-bottom: 16px;
}

@container not (min-width: 640px) {
.pageshot {
width: 100%;
}
.screenshot {
width: 100%;
}
}


.static-table input[type=checkbox] {
display: none;
}

/* HULL COMPONENT */
.hull {
display: block;
}

/* TOOLTIP STYLES */
.tooltip {
position: absolute;
pointer-events: none;
z-index: 1000;
background-color: white;
padding: 8px;
border-radius: 4px;
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3);
}

.cluster-content {
display: flex;
flex-direction: row;
}
.cluster-plot {
background-color: white;
padding: 4px;
border-radius: 4px;
margin: 6px;
width: 300px;
max-width: 300px;
display: inline-block;
}
.cluster-description {
margin: 6px;
padding: 4px;

}

@container not (min-width: 640px) {
}
56 changes: 56 additions & 0 deletions _import/components/clusterCard.3a640b4d.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {html} from "../../_npm/[email protected]/_esm.js";
import * as Inputs from "../../_observablehq/stdlib/inputs.js";
import * as Plot from "../../_npm/@observablehq/[email protected]/_esm.js";

export function clusterCard(cluster, description, da, scope) {
const cda = da.filter(d => d.cluster == cluster)
return html`<div class="grid grid-cols-1">
<div class="card">
<h2>Cluster ${cluster}: ${scope.cluster_labels_lookup[cluster].label}</h2>
<h3>${cda.length} rows</h3>
<div class="cluster-content">
<div class="cluster-plot">
${
Plot.plot({
marks: [
Plot.dot(da, {
x: "x",
y: "y",
fill: "lightgray",
}),
Plot.dot(da, {
filter: d => d.cluster == cluster,
x: "x",
y: "y",
fill: "cluster",
})
],
width: 300,
height: 300,
color: { scheme: "cool" },
y: { axis: null},
x: { axis: null }
})
}
</div>
<div class="cluster-description">
${description}
</div>
</div>
<div class="static-table">
${Inputs.table(cda, {
columns: [
"DataVizNotUnderstood",
"Role",
"YearsDataVizExperience",
],
width: {
"DataVizNotUnderstood": "60%",
"YearsDataVizExperience": "100px"
},
rows: 12
})}
</div>
</div>`
}

92 changes: 92 additions & 0 deletions _import/components/hull.532abe46.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { create, select } from "../../_npm/[email protected]/_esm.js";
import { transition } from "../../_npm/[email protected]/_esm.js";
import { line, curveLinearClosed, curveCatmullRomClosed } from "../../_npm/[email protected]/_esm.js";
import { easeCubicInOut, easeExpOut } from "../../_npm/[email protected]/_esm.js";
import { scaleLinear, scaleSequential } from "../../_npm/[email protected]/_esm.js";
import { extent, range } from "../../_npm/[email protected]/_esm.js";
import { rgb } from "../../_npm/[email protected]/_esm.js";

{/* <path class="hull" d="M0.657,-0.781L0.635,-0.784L0.484,-0.715L0.444,-0.685L0.483,-0.686Z" style="fill: darkgray; stroke-width: 0.004; opacity: 0.75;"></path> */}
{/* <path class="hull" d="M0.657,-0.781L0.635,-0.784L0.484,-0.715L0.444,-0.685L0.483,-0.686Z" style="fill: darkgray; stroke: black; stroke-width: 0.4; opacity: 0.75;"></path> */}
{/* <path class="hull" d="M0.657,-0.781L0.635,-0.784L0.484,-0.715L0.444,-0.685L0.483,-0.686Z" style="fill: none; stroke: black; stroke-width: 0.4; opacity: 0.75;"></path> */}

export function hull(hulls, {
// TODO: make this not rerender every time
// svg = document.createElement("svg"),
width,
height,
xd = [-1, 1],
yd = [-1, 1],
x = (d) => d.x,
y = (d) => d.y,
strokeWidth = 2,
fill = "none",
stroke = "black",
duration = 1000,
delay = 0,
ease = easeCubicInOut
} = {}) {

const svgs = create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; display: block;")


const xScaleFactor = width / (xd[1] - xd[0]);
const yScaleFactor = height / (yd[1] - yd[0]);

// Calculate translation to center the drawing at (0,0)
// This centers the view at (0,0) and accounts for the SVG's inverted y-axis
const xOffset = width / 2 - (xScaleFactor * (xd[1] + xd[0]) / 2);
const yOffset = height / 2 + (yScaleFactor * (yd[1] + yd[0]) / 2);

// Calculate a scaled stroke width
const scaledStrokeWidth = strokeWidth / Math.sqrt(xScaleFactor * yScaleFactor) / 2;

const g = svgs.append("g")
g.attr('transform', `translate(${xOffset}, ${yOffset}) scale(${xScaleFactor}, ${yScaleFactor})`);

const draw = line()
.x(d => d.x)
.y(d => -d.y)
// .curve(curveCatmullRomClosed);
.curve(curveLinearClosed);

let sel = g.selectAll("path.hull")
.data(hulls)

const exit = sel.exit()
// .transition()
// .duration(duration)
// .delay(delay)
// .ease(easeExpOut)
.style("opacity", 0)
.remove()

const enter = sel.enter()
.append("path")
.classed("hull", true)
.attr("d", draw)
.style("fill", fill)
.style("stroke", stroke)
.style("stroke-width", scaledStrokeWidth)
// .style("opacity", 0.)
// .transition()
// .delay(delay + 100)
// .duration(duration - 100)
// .ease(easeExpOut)
.style("opacity", 0.75)

const update = sel
// .transition()
// .duration(duration)
// .delay(delay)
// .ease(easeCubicInOut)
.style("opacity", 0.75)
.attr("d", draw)


return svgs.node()
}
127 changes: 127 additions & 0 deletions _import/components/scatter.e6a504c8.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// import * as Plot from "npm:@observablehq/plot";
import createScatterplot from "../../_npm/[email protected]/_esm.js";
import { scaleLinear, scaleSequential } from "../../_npm/[email protected]/_esm.js";
import { interpolateViridis, interpolateTurbo, interpolateCool } from "../../_npm/[email protected]/_esm.js";
import { extent, range } from "../../_npm/[email protected]/_esm.js";
import { rgb } from "../../_npm/[email protected]/_esm.js";

const scatterCache = new WeakMap();

export function scatter(data, {
canvas = document.createElement("canvas"),
width,
height,
x = (d) => d.x,
y = (d) => d.y,
color = null,
size = null,
colorInterpolator = interpolateCool,
pointSize = 3,
pointOpacity = 0.75
} = {}) {
let scatterplot;
if(!scatterCache.has(canvas)) {
// create the scatterplot
const scatterSettings = {
canvas,
width,
height,
pointColorHover: [0.1, 0.1, 0.1, 0.5],
pointSize,
pointOpacity,
xScale: scaleLinear().domain([-1, 1]),
yScale: scaleLinear().domain([-1, 1]),
}
scatterplot = createScatterplot(scatterSettings);
scatterCache.set(canvas, scatterplot)
canvas.value = {
xd: [-1, 1],
yd: [-1, 1],
selected: [],
hovered: [],
width,
height
}

scatterplot.subscribe(
"view",
({ camera, view, xScale: xs, yScale: ys }) => {
let xd = xs.domain();
let yd = ys.domain();
canvas.value.xd = xd
canvas.value.yd = yd
canvas.dispatchEvent(new Event("input"));
}
);
scatterplot.subscribe("select", ({ points }) => {
canvas.value.selected= points
canvas.dispatchEvent(new Event("input"));
});
scatterplot.subscribe("deselect", () => {
canvas.value.selected = []
canvas.dispatchEvent(new Event("input"));
});
scatterplot.subscribe("pointOver", (pointIndex) => {
canvas.value.hovered = [pointIndex]
canvas.dispatchEvent(new Event("input"));
});
scatterplot.subscribe("pointOut", (pointIndex) => {
canvas.value.hovered = []
canvas.dispatchEvent(new Event("input"));
});
canvas.scatter = scatterplot
} else {
// update the scatterplot
scatterplot = scatterCache.get(canvas)
scatterplot.set({ width, height });
canvas.value.width = width
canvas.value.height = height
canvas.dispatchEvent(new Event("input"));
}

const points = data.map(d => [
x(d), y(d), color && color(d), size && size(d)
].filter(x => x !== null))

if(color) {
scatterplot.set({colorBy: 'valueA'})
// determine if the values in ordinal or continuous
const valueSet = new Set(points.map(p => p[2]));
const isOrdinal = [...valueSet].every(val => typeof val === 'string' || (typeof val === 'number' && !Number.isInteger(val) && val % 1 !== 0));
if (isOrdinal) {
// create a color for each unique value
const uniqueValues = [...valueSet].sort();
const colorScale = scaleSequential(colorInterpolator)
.domain([0, uniqueValues.length]);
const pointColor = uniqueValues.map((u,i) => rgb(colorScale(i)).hex())

// update the points to use the index of the unique value (for regl-scatterplot)
points.forEach(p => p[2] = uniqueValues.indexOf(p[2]))

scatterplot.set({ pointColor });
} else {

//make sure the points scale from 0 to 1 for regl-scatter
const values = [...valueSet]
const scale = scaleLinear().domain(extent(values)).range([0, 1])
points.forEach(p => p[2] = scale(p[2]))

let r = range(0, 100)
const colorScale = scaleSequential(colorInterpolator)
.domain([0, 100]);
const pointColor = r.map(i => rgb(colorScale(i)).hex())

scatterplot.set({ pointColor }); // getContinuousColorScale is a hypothetical function
}
}
if(size) {
if(color) {
scatterplot.set({sizeBy: 'valueB'})
} else {
scatterplot.set({sizeBy: 'valueA'})
}
}

scatterplot.draw(points, { transition: false });
return canvas
}
27 changes: 27 additions & 0 deletions _import/components/tooltip.10184b2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { create } from "../../_npm/[email protected]/_esm.js"
import { scaleLinear } from "../../_npm/[email protected]/_esm.js"


export function tooltip({
} = {}) {
const tooltip = create("div")
.attr("class", "tooltip")

function show(p, map, html) {
tooltip.html(html)
tooltip.style("display", "block")
let x = scaleLinear().domain(map.xd).range([0, map.width])
let y = scaleLinear().domain(map.yd).range([map.height, 0])
tooltip.style("left", `${x(p.x) + 10}px`)
tooltip.style("top", `${y(p.y)}px`)
}
function hide() {
tooltip.style("display", "none")
}

return Object.assign(tooltip.node(), {
show,
hide
})
}

Loading

0 comments on commit 56c86c2

Please sign in to comment.