-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update docs: datavis survey analysis
- Loading branch information
Showing
43 changed files
with
2,052 additions
and
538 deletions.
There are no files selected for viewing
Binary file not shown.
1,079 changes: 1,079 additions & 0 deletions
1,079
_file/data/datavis-survey/scopes-001.d44da6f0.json
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>` | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}) | ||
} | ||
|
Oops, something went wrong.