Public
Edited
Nov 17, 2023
1 star
Insert cell
Insert cell
// image of output from visuals created below
diagrammaticWritingVisDist = FileAttachment("diagrammatic-writing-vis-dist.jpg").image()
Insert cell
Insert cell
data = new Map( [
['space', [
x(1, 1), x(3, 2), x(4, 3), x(5, 1), x(7, 1), x(8, 1), x(10, 1), x(11, 8),
x(12, 7), x(12, 2, 'worldspace'),x(12, 1, 'wordspace'),
// '…from falling into the surrounding worldspace. White space differentiates wordspace from worldspace'
x(14, 4), x(15, 3), x(16, 1), x(18, 2), x(19, 6), x(20, 4), x(21, 2), x(22, 1), x(23, 5),
x(24, 3), x(25, 1), x(26, 1), x(27, 2), x(28, 1), x(29, 3),
x(31, 1), x(31, 1, 'worldspace'),x(31, 1, 'wordspace'),
]],
['gutter', [
x(12, 1), x(18, 1), x(23, 2), x(25, 1), x(30, 1)
]],
['text', [
x(3, 1), x(4, 1), x(7, 7), x(8, 8), x(9, 2), x(11, 2), x(12, 2), x(13, 3), x(14, 3), x(15, 15)
,x(16, 1), x(17, 13), x(18, 6), x(19, 5), x(21, 5), x(22, 5), x(23, 5),x(24, 7),
x(25, 4), x(26, 6), x(27, 5), x(29, 3), x(30, 1), x(31, 2)
]],
['paratext', [
x(30, 1), x(11, 2)
]],
['textual', [
x(17, 2), x(20, 1),x(21, 1), x(22, 2), x(23, 1), x(25, 1)
]],
['intertextual', [
x(25, 1)
]],
['dialogue', [
x(8, 1), x(17, 1), x(18, 1), x(20, 2), x(23, 1), x(25, 10), x(28, 1), x(30, 1),
]],
['discourse', [
x(7, 1), x(16, 2), x(18, 2), x(20, 2), x(23, 1), x(24, 2)
]],
['play', [
x(9, 1), x(11, 2), x(16, 2), x(17, 1), x(25, 1), x(27, 1), x(28, 1)
]],
])
Insert cell
Insert cell
data
Insert cell
// view summary of data
summaryView = data.get(selectWord).map(array => {
const page = array[0]; // all same number
const amount = array.length;
return {
page: page,
count: amount
}
})
Insert cell
summaryView
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell

createArray(data, selectWord).filter(d => d.value === selectPage)
Insert cell
Insert cell
Insert cell
Insert cell
dataKeys = [ ...data.keys()]
Insert cell
Insert cell
Insert cell
viewof bins = Inputs.range([2, 35], {label: "Bins", step: 1, value: 5})
Insert cell
Plot.plot({
marks: [
Plot.rectY(createArray(data, selectWord), Plot.binX({y: "count"}, {x: {thresholds: bins -1, value: "value"}})),
Plot.ruleY([0])
]
})
Insert cell
// create a function to create an array of the counts
// use this as a visual representation of the words per page for ease of checking
// page and how many times it repeats
function x (page, count, word) { // use word as a mark to if it differs
const array = d3.range(count).map(d => page);
// return an array of the same numbers representing the counts on the book page
// i.e. [4, 4, 4, 4, 4] // shows on page 4, five times
return [...array];
}
Insert cell
Select a data source…
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
createArray = function(data, string) {
const array = _.flattenDeep(data.get(string)).map(d => {
return {
label: string,
value: d
}
});
return array;
}
Insert cell
createArray(data, 'space')
Insert cell
beeSwarm(createArray(data, 'space'))
Insert cell
beeSwarm(createArray(data, 'gutter'), {
height: 100
})
Insert cell
spaceData = createArray(data, 'space').concat(createArray(data, 'gutter'))
Insert cell
// combine space and gutter
beeSwarm(spaceData, {
height: 150,
words: ['gutter'] // colour the gutter words
})
Insert cell
beeSwarm(spaceData, {
height: 250,
words: ['paratext', 'textual', 'intertextual'],
radius: 8,
letter: 'S',
words: ['gutter'],
circle: false
})
Insert cell
beeSwarm(createArray(data, 'text'), {
height: 200,
words: ['textual']
})
Insert cell
textData = createArray(data, 'text').concat(
createArray(data, 'paratext'),
createArray(data, 'textual'),
createArray(data, 'intertextual')
)
Insert cell
beeSwarm(textData, {
height: 200,
words: ['paratext', 'textual', 'intertextual']
})
Insert cell
beeSwarm(textData, {
height: 320,
words: ['paratext', 'textual', 'intertextual'],
radius: 8,
letter: 'T',
circle: false
})
Insert cell
dialogueData = createArray(data, 'dialogue').concat(
createArray(data, 'play'),
createArray(data, 'discourse')
)
Insert cell
beeSwarm(dialogueData, {
height: 200,
words: ['play', 'discourse']
})
Insert cell
beeSwarm(dialogueData, {
height: 250,
words: ['play', 'discourse'],
radius: 8,
letter: 'D',
circle: false
})
Insert cell
Insert cell
Plot.plot({
r: {range: [0, 14]},
marks: [
Plot.dot(dialogueData, Plot.binX({r: "count"}, {x: "value"}))
]
})
Insert cell
// using this one for design layout — no data meaning
beeSwarm(
d3.range(-4, 33).map(d => {return {value: d, label: d}}), {
height: 50,
fillCircleDefault: 'yellow'
})
Insert cell
function beeSwarm (data, { // https://observablehq.com/@d3/beeswarm/2
width = 928,
height = 150,
words = [], // ['word1', 'word2'] // have a way of colouring differing words
fillCircle = ['#e4bd0b', '#de3d83', '#0098d8'],
marginTop = 20,
marginRight = 20,
marginBottom = 20,
marginLeft = 20,
radius = 4,
fontSize = 16,
padding = 1.5,
xDomain = [-4, 32],
fillCircleDefault = '#000',
letter = null, // show a letter, the pass in one 'a',
circle = true // boolean to render a circle
} = {}) {

// get unique values for ticks
const tickValues = _.uniq(data.map(d => d.value))
const x = d3.scaleLinear()
.domain(xDomain)
.range([marginLeft, width - marginRight]);

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0).tickValues(tickValues));
if (circle) {
svg.append("g")
.selectAll()
.data(dodge(data, {radius: radius * 2 + padding, x: d => x(d.value)}))
.join("circle")
.attr("cx", d => d.x)
.attr("cy", d => height - marginBottom - radius - padding - d.y)
.attr("r", radius)
.attr('fill', d => {
console.log('word', d.data.label)
if (_.includes(words, d.data.label)) {
console.log('word index', _.indexOf(words, d.data.label));
console.log('fill', fillCircle[_.indexOf(words, d.data.label)])
return fillCircle[_.indexOf(words, d.data.label)];
} else {
return fillCircleDefault;
}
})
.append("title")
.text(d => d.label);
}

if (letter) {
svg.append("g")
.selectAll()
.data(dodge(data, {radius: radius * 2 + padding, x: d => x(d.value)}))
.join("text")
.attr("x", d => d.x)
.attr("y", d => height - marginBottom - radius - padding - d.y)
.attr('dx', -5)
.attr("font-size", fontSize)
.attr("font-weight", 600)
.attr('fill', d => {
console.log('word', d.data.label)
if (_.includes(words, d.data.label)) {
console.log('word index', _.indexOf(words, d.data.label));
console.log('fill', fillCircle[_.indexOf(words, d.data.label)])
return fillCircle[_.indexOf(words, d.data.label)];
} else {
return '#000';
}
})
.text(letter);
}

return svg.node();
}
Insert cell
dialogueData
Insert cell
beeSwarmAltText(dialogueData)
Insert cell
function beeSwarmAltText (data, {
letter = 'A',
pause = '-',
change = '|',
xDomain = [-4, 32]
} = {}) {

const pages = data.map(d => d.value);

const baseAxis = d3.range(xDomain[0], xDomain[1]);

const output = baseAxis.map(d => {
const x = _.includes(pages, d) ? null : `${change} ${pause} `;
return {
label: d,
value: (x === null) ? `${change} ${pages.filter(page => page === d).map(() => letter).join("")} ` : x
}
})
return output.map(d => d.value).join("");
}
Insert cell
import {dodge} from "@d3/beeswarm/2"
Insert cell
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<link href="https://unpkg.com/basscss@8.0.2/css/basscss.min.css" rel="stylesheet">
<style>
text {
font-family:'Space Mono',monospace;
}
</style>
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more