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

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