Public
Edited
Jan 11, 2023
Insert cell
Insert cell
viewof color = Inputs.select(["Select a topic", "tenure", "health", "welsh", "ethnicity", "economic", "household", "religion", "care", "disability", "hours", "marital", "population", "agemed", "national"], {value: "steelblue", label: ""})
Insert cell
function spark() {
let st = ``
for (let i = 0; i < arr.length; i++) {
st = st+arr[i] +`<br>`
}
return st
}
Insert cell
mutable arr = s[wo]
Insert cell
// flare.children.filter(e => e.children.some(f => f.children.some(g => g.name=="adj_noun")))
Insert cell
function clicked(d) {
let p = d.path[0].__data__
console.log('p ', p)
let d1 = p
for (let i = 0; i < 6; i++) {
if (d1['depth'] > 1) {
d1 = d1['parent']
}
}
d1 = d1['data']['name']
console.log('d1', d1)

let d2 = p
for (let i = 0; i < 6; i++) {
if (d2['depth'] > 2) {
d2 = d2['parent']
}
}
d2 = d2['data']['name']
console.log('d2', d2)
let d3 = p
for (let i = 0; i < 6; i++) {
if (d3['depth'] > 3) {
d3 = d3['parent']
}
}
d3 = d3['data']['name']
console.log('d3', d3)

let alt1
console.log('d2', d2)
if (Object.keys(chains).includes(d2)) {

console.log('chain chain chain')
alt1 = chains[d2][0]
}
console.log('chain', alt1)

mutable gen1 = data[topicswitch[d1][0]]['general_sf']
mutable gen2 = data[topicswitch[d1][1]]['general_sf']

mutable verb_past_samp = data[d1][d2]['verb_past_samp']
let fd2 = adjswitch[d1][0]

console.log("data",data)
console.log("d1",d1)
console.log('fd2', fd2)

mutable adj_noun = data[d1][fd2]['adj_noun']
// If "Back" is clicked
if (p.data.name=="Back") {
mutable flare = dtrans(data)
}
else if ((ar.includes(p.data.name))&(p.height!=0)) {
mutable flare = {name: "Back", children: mutable flare.children.filter(child => child.name==p.data.name)}
}
else if (((subar.includes(p.data.name))|("subhead"==p.data.name))&(p.height!=0)) {
let temp = mutable flare
if (p.depth>2) {
temp = temp.children[0].children.filter(child => child.name==p.parent.data.name)
temp = temp[0].children.filter(child => child.name==p.data.name)
} else {
temp = temp.children[0].children.filter(child => child.name==p.data.name)
}
mutable flare = {name: "Back", children: temp}
}
// Is d the last node on the line?
else if (p.height==0) {
mutable va = p.data.name
mutable wo = p.parent.data.name

if (alt1) {
mutable va2 = data[d1][alt1][d3]
}
}
// Is d the second last node
else if (p.height==1) {
mutable wo = p.data.name
mutable va = p.children[0].data.name
if (!swi) {
if (p.depth==2) {
mutable flare = filtgen(flare, p.data.name)
} else if (p.depth==4) {
// mutable flare = filtgen4(flare, p.data.name)
}
else {
mutable flare = filt(flare, p.data.name)
}
} else {
mutable swi = false
mutable flare = dtrans(data)
}
}
}
Insert cell
s = ({
adj_noun: [
`<p>The percentage of <b>${va}</b> is increasing faster here than in any other local authority district across Wales.</p>`,
`<p>This borough saw England's largest rise in the proportion of <b>${va}</b>.</p>`,
`<p>The proportion of <b>${va}</b> fell at a similar rate to the figure for the whole of the South East (from 7.7% in 2011 to 6.6% in 2021).</p>`,
`<p>Across the South East, Test Valley (from 33.2% in 2011 to 38.5% in 2021) saw the next greatest rise in the proportion of <b>${va}</b>, while the regional figure went up from 28.9% to 30.9%.</p>`,
`<p>The proportion of <b>${va}</b> is growing at the third fastest rate of all local authority districts across England, while the percentage of <b>${va}</b> is falling faster than anywhere else in the region.</p>`,
`<p>The proportion of <b>${va}</b> has been growing at a similar rate to the figure across the North West and across England.</p>`
],
adj_noun_er: [
`<p>The number of <b>${va}</b> increased from about 570 to about 890 (from 1.0% to 1.5%).</p>`
],
adj_noun_s: [
`<p>During this period, the district overtook nine areas, including the Isle of Anglesey and Conwy to become the Welsh local authority with the fourth-highest percentage of <b>${va}</b>.</p>`,
`<p>In Test Valley, the proportion of <b>${va}</b> stayed close to 4.6% between the last two censuses.</p>`
],
verb_past: [
`The percentage of Test Valley residents <b>${va}</b> was 21.3% in 2021 (up from 18.9 in 2011).`,
],
sample: [
`<p class="context">Chart title:</p><p><b>${va}</b> in Test Valley rose from 59% to 62%, while ${va2} decreased from 11.0% to 10.6%.</p>`,
`<b>${va}</b> increased in Test Valley, but at the second slowest rate of any local authority area across England.`
],
sample_title: [
`<p class="context">Chart title:</p><p><b>${va}</b> in Test Valley decreased by 6.9 percentage points</p>`,
],
clausal_modifier: [
`<p>The percentage of <b>${va}</b> increased from 4.0% to 5.7%.</p>`,
`<p>The percentage of <b>${va}</b> remained at 8.3%.</p>`,
],
label: [
`<p class="context">Chart label:</p><p><b>${va}</b></p>`
],
profile: [
`<p><b>${va}</b> in the decade leading up to the most recent census.</p>`
],
topic: [
`<p><b>${va}</b> in Test Valley rose from 50.7% to 51.6%, while <b>${va}</b> remained stable at around 2.5%.</p>`,
`<p><b>${va}</b> fell in Test Valley, but at the slowest rate of any local authority across England.</p>`,
],
general: [
`<p>Despite the change in <b>${va}</b>, Test Valley had the South East's second-highest proportion of ${adj_noun}.</p>`
],
general_sf: [
`<p>At the same time there were changes in <b>${va}</b>, ${gen1} and ${gen2}.</p>`,
`<p>Data from the census also show there were changes in <b>${va}</b> and ${gen2}.</p>`,
],
synonym: [
`<p>Census 2021 data also show a <b>${va}</b>.</p>`
],
measure: [
`<p class="context">Chart subtitle:</p><p>The percentage of <b>${va}</b> in Test Valley, the South East and England that that own their home, March 2011, March 2021.</p>`
],
measure_s: [
`<p>In 2021, just over one in six (17.9%) <b>${va}</b> ${verb_past_samp}, compared with 9.9% in 2011.</p>`
],
verb: [
`<p>The percentage <b>${va}</b> decreased 26% to 21%.</p>`,
`<p>In Test Valley, the proportion <b>${va}</b> went up from 22.4% in 2011 to 39.3% in 2021.</p>`
],
verb_er: [
`<p>The number of people in Test Valley <b>${va}</b> decreased from just over 58,000 in 2001 to just under 59,000 in 2011. However, as a percentage of the total population, this represented a decrease from 97% to 96%.</p>`
],
verb_past_samp: [
`<p class="context">Chart title:</p><p>Around 5.8% of residents <b>${va}</b> in Test Valley</p>`,
`<p>In 2021, about 3 in 10 (31%) <b>${va}</b>, compared with 24% in 2011.</p>`,
`<p>In 2021, just under one in two (51.2%) people in Test Valley <b>${va}</b>, compared with 48.1% in 2011</p>`,
`<p>Around 51.2% of Test Valley residents <b>${va}</b> in 2021, up from 48.1% in 2011</p>`,
`<p>The percentage that <b>${va}</b> remained at 33.7%.</p>`
],
verb_past_samp_er: [
`<p>In 2021, 21.6% of Test Valley residents <b>${va}</b>, up from about 150 in 2011 (14.5%).</p>`
],
pos: [
`<p>Subheading: <b>${va}</b></p>`,
],
neg: [
`<p>Subheading: <b>${va}</b></p>`,
],
neu: [
`<p>Subheading: <b>${va}</b></p>`,
],
buc: [
`<p>Subheading: <b>${va}</b></p>`,
]
})
Insert cell
sent = html`<style>span.context {color: #b0aeae;} p.context {color: #b0aeae;}</style><div style="padding: 5% 20%;">${spark()}</div>`
Insert cell
chart = Tree(flare, {
label: d => d.name,
title: (d, n) => `${n.ancestors().reverse().map(d => d.data.name).join(".")}`, // hover text
link: (d) => d.children
? clicked(d)
: clicked(d),
width: 1600
})
Insert cell
data = FileAttachment("topics@5.json").json()
Insert cell
data['tenure']
Insert cell
mutable gen1 = ``
Insert cell
mutable gen2 = ``
Insert cell
mutable verb_past_samp = ``
Insert cell
mutable adj_noun = ``
Insert cell
va2
Insert cell
color
Insert cell
clickedInput(color)
Insert cell
function clickedInput(d) {
if (d != "Select a topic") {
if ((d != flare['children'][0]['name'])| (d != flare['children'][flare['children'].length-1]['name'])) {

mutable flare = dtrans(data)
mutable flare = {
name: "Back",
children: flare['children'].filter(e => e.name == color)
}
}
}
}
Insert cell
adjswitch = ({
'tenure': ['Private rented'],
'welsh': ['Can speak Welsh'],
'health': ['Age-standardised proportion: Very good health'],
'ethnicity': ['White'],
'national': [
'English and British only identity'
],
'economic': [
'Economically active (excluding full-time students): In employment'
],
'household': [
'Single family household: Married or civil partnership couple: No children'
],
'religion': ['Christian'],
'care': [
'Age-standardised proportion: Provides 50 or more hours unpaid care a week'
],
'disability': [
'Age-standardised proportion: Disabled under the Equality Act: Day-to-day activities limited a little'
],
'hoursworked': [
'Part-time: 15 hours or less worked'
],
'marital': [
'Never married and never registered a civil partnership'
],
})
Insert cell
topicswitch = ({
'tenure': ['health','welsh'],
'welsh': ['tenure','health'],
'health': ['ethnicity','tenure'],
'ethnicity': ['economic','tenure'],
'national': ['economic', 'tenure'],
'economic': ['household','tenure'],
'household': ['economic','tenure'],
'religion': ['care','tenure'],
'care': ['disability','tenure'],
'disability': ['care','tenure'],
'children': ['hoursworked','household'],
'hoursworked': ['marital','houeshold'],
'marital': ['disability','household'],
'population': ['agemed','household'],
'agemed': ['population','household'],
})
Insert cell
chains = ({
'Very good or good health': [
'Very bad or bad health', 'Fair health'
],
'Very bad or bad health': [
'Very good or good health', 'Fair health'
],
'White': [
'Black, Black British, Black Welsh, Caribbean or African', 'Asian, Asian British or Asian Welsh'
],
'Black, Black British, Black Welsh, Caribbean or African': [
'White', 'Asian, Asian British or Asian Welsh'
],
'Asian, Asian British or Asian Welsh': [
'White', 'Black, Black British, Black Welsh, Caribbean or African'
],
'Private rented': [
'Rented from council or Local Authority', 'Owns outright or with a mortgage or loan'
],
'Rented from council or Local Authority': [
'Private rented', 'Owns outright or with a mortgage or loan'
],
'Owns outright or with a mortgage or loan': [
'Private rented', 'Rented from council or Local Authority'
],
'Economically inactive and a full-time student': [
'Economically active and a full-time student: In employment', 'Economically active and a full-time student: Unemployed', 'Economically inactive (excluding full-time students)'
],
'Economically inactive (excluding full-time students)': [
'Economically active (excluding full-time students): In employment', 'Economically active (excluding full-time students): Unemployed', 'Economically inactive and a full-time student'
],
'Economically active (excluding full-time students): In employment': [
'Economically active (excluding full-time students): Unemployed', 'Economically inactive (excluding full-time students)', 'Economically inactive and a full-time student'
],
'Economically active (excluding full-time students): Unemployed': [
'Economically active (excluding full-time students): In employment', 'Economically inactive (excluding full-time students)', 'Economically inactive and a full-time student'
],
'Single family household: Married or civil partnership couple: No children': [
'Single family household: Cohabiting couple family : No children', 'Single family household: Married or civil partnership couple: Dependent children'
],
'Single family household: Cohabiting couple family : No children': [
'Single family household: Married or civil partnership couple: No children', 'Single family household: Cohabiting couple family : With dependent children'
],
'Single family household: Married or civil partnership couple: Dependent children': [
'Single family household: Cohabiting couple family : With dependent children', 'Single family household: Married or civil partnership couple: No children'
],
'Single family household: Cohabiting couple family : With dependent children': ['Single family household: Married or civil partnership couple: Dependent children', 'Single family household: Cohabiting couple family : No children'
],
'Single family household: Lone parent family : With dependent children': [
'Single family household: Married or civil partnership couple: Dependent children', 'Single family household: Cohabiting couple family : With dependent children'
],
'Never married and never registered a civil partnership': [
'Married or in a registered civil partnership', 'Divorced or civil partnership dissolved'
],
'Married or in a registered civil partnership': [
'Never married and never registered a civil partnership', 'Divorced or civil partnership dissolved'
],
'Divorced or civil partnership dissolved': [
'Married or in a registered civil partnership', 'Never married and never registered a civil partnership'
],
'Age-standardised proportion: Provides 19 or less hours unpaid care a week': [
'Age-standardised proportion: Provides 20 to 49 hours unpaid care a week', 'Age-standardised proportion: Provides no unpaid care'
],
'Age-standardised proportion: Provides 20 to 49 hours unpaid care a week': [
'Age-standardised proportion: Provides 19 or less hours unpaid care a week', 'Age-standardised proportion: Provides no unpaid care'
],
'Age-standardised proportion: Provides 50 or more hours unpaid care a week': [
'Age-standardised proportion: Provides 20 to 49 hours unpaid care a week', 'Age-standardised proportion: Provides 19 or less hours unpaid care a week'
],


'Part-time: 15 hours or less worked': [
'Full-time: 49 or more hours worked', 'Full-time: 31 to 48 hours worked'
],
'Full-time: 49 or more hours worked': [
'Part-time: 15 hours or less worked', 'Full-time: 31 to 48 hours worked'
],

'Age-standardised proportion: Disabled under the Equality Act: Day-to-day activities limited a little': [
'Age-standardised proportion: Disabled under the Equality Act: Day-to-day activities limited a lot'
],
'Age-standardised proportion: Disabled under the Equality Act: Day-to-day activities limited a lot': [
'Age-standardised proportion: Disabled under the Equality Act: Day-to-day activities limited a little'
],

'British only identity': [
'English only identity', 'English and British only identity'
],
'Welsh only identity': [
'Welsh and British only identity', 'British only identity'
],
'Welsh and British only identity': [
'Welsh only identity', 'British only identity'
],
'English only identity': [
'English and British only identity', 'British only identity'
],
'English and British only identity': [
'English only identity', 'British only identity'
],
'Non-UK identity only': [
'UK identity and non-UK identity', 'British only identity'
],
})
Insert cell
mutable swi = false
Insert cell
// ft = filtgen4(flare, 'pos')
Insert cell
// flare.children.filter(e => {
// console.log("G", e.children.some(f => f.name == 'synonym'))
// typeof e.children == "object"
// return true
// })
Insert cell
// flare.children.filter(e => { e.children.some(f => {
// console.log("EEF", f)
// f.children.some(g => {
// console.log("Gee", g)
// if (g.children) {
// g.children.some(h => {
// console.log("ACH", h.name=="pos")
// return h.name=="pos"
// })
// }
// }
// )
// return true
// }
// )})
Insert cell
function filtgen4(flare, w) {
let flareT = flare
console.log("FLARE", flareT)

let flare2 = flareT.children.filter(e => e.children.some(f => { f.children.some(g => { if (g.children) {
g.children.some(h => { h.name==h.name })}})}))

// let flare2 = flareT.children.filter(e => e.children.some(f => f.children.some(g => (g.children)? g.children.some(h => ('h.children', h.children)):console.log("F", f))))
flareT.children = flare2
// console.log("flareT", flareT)
for (let i = 0; i < flareT.children.length; i++) {
// let flare1 = flareT.children[i].children.filter(e => e.children.some(f => f.name == w))
// flareT.children[i].children = flare1
// for (let j = 0; j < flareT.children[i].children.length; j++) {
// let flare0 = flareT.children[i].children[j].children.filter(e => e.name == w)
// flareT.children[i].children[j].children = flare0
// }
}
console.log("FILT2", flareT)
mutable swi = true
return flareT
}
Insert cell
mutable flare = dtrans(data)
Insert cell
function filtgen(flare, w) {
let flareT = flare
console.log("FLARE", flareT)
let flare2 = flareT.children.filter(e => e.children.some(f => f.name==w))
flareT.children = flare2
for (let i = 0; i < flareT.children.length; i++) {
let flare1 = flareT.children[i].children.filter(e => e.name == w)
flareT.children[i].children = flare1
}
console.log("FILT2", flareT)
mutable swi = true
return flareT
}
Insert cell
function filt(flare, w) {
let flareT = flare
console.log("FLARE", flareT)

let flare2 = flareT.children.filter(e => e.children.some(f => f.children.some(g => g.name==w)))
flareT.children = flare2
for (let i = 0; i < flareT.children.length; i++) {
let flare1 = flareT.children[i].children.filter(e => e.children.some(f => f.name == w))
flareT.children[i].children = flare1
for (let j = 0; j < flareT.children[i].children.length; j++) {
let flare0 = flareT.children[i].children[j].children.filter(e => e.name == w)
flareT.children[i].children[j].children = flare0
}
}
console.log("FILT2", flareT)
mutable swi = true
return flareT
}
Insert cell
function filterTree(topics, find) {
let chilT = []
topics.children.forEach((e) => {
e.children.forEach((f) => {
f.children.forEach((g) => {
if (g.name==find) {
chilT.push(e)
let h = {}
h.name = e.name
}
})
})
})
chilT = [...new Set(chilT)]
return topics
} // children[6].children[4].children[1].name
Insert cell
mutable va = ``
Insert cell
mutable va2 = ``
Insert cell
mutable wo = ``
Insert cell
ar = ['agemed', 'population', 'matiral', 'hoursworked', 'children', 'care', 'religion', 'tenure', 'health', 'ethnicity', 'economic', 'household']
Insert cell
subar = ['Hindu',
'Buddhist',
'1to19hoursWeek',
'white',
'self-employed',
'fair',
'Kids',
'inactive',
'noCare',
'NonDepKids',
'Jewish',
'Female1-15',
'employee',
'Single',
'Married',
'Sikh',
'bad',
'black',
'Christian',
'unemployed',
'OnePerson',
'Seperated',
'asian',
'owned',
'good',
'Cohabiting',
'Male1-15',
'rented_private',
'student',
'Muslim',
'40PlushoursWeek',
'rented_social',
'NoKids',
'Male49plus',
'Noreligion',
'20to49hoursWeek',
'Female49plus',
'LoneParent']
Insert cell
function proc(d) {
return d.path[0].__data__.data.name
}
Insert cell
function iter(data) {
let out
if (typeof data == "object") {
out = []
for (var key in data) {
out.push({name: key, children: iter(data[key])})
}
} else {
out = [{name: data, size: 1000}]
}
return out
}
Insert cell
function dtrans(data) {
let y = (
{
name: "Topics",
children: iter(data)
})
return y
}
Insert cell
Insert cell
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/tree
function Tree(data, { // data is either tabular (array of objects) or hierarchy (nested objects)
path, // as an alternative to id and parentId, returns an array identifier, imputing internal nodes
id = Array.isArray(data) ? d => d.id : null, // if tabular data, given a d in data, returns a unique identifier (string)
parentId = Array.isArray(data) ? d => d.parentId : null, // if tabular data, given a node d, returns its parent’s identifier
children, // if hierarchical data, given a d in data, returns its children
tree = d3.tree, // layout algorithm (typically d3.tree or d3.cluster)
sort, // how to sort nodes prior to layout (e.g., (a, b) => d3.descending(a.height, b.height))
label, // given a node d, returns the display name
title, // given a node d, returns its hover text
link, // given a node d, its link (if any)
linkTarget = "_blank", // the target attribute for links (if any)
width = 640, // outer width, in pixels
height, // outer height, in pixels
r = 3, // radius of nodes
padding = 1, // horizontal padding for first and last column
fill = "#999", // fill for nodes
fillOpacity, // fill opacity for nodes
stroke = "#555", // stroke for links
strokeWidth = 2, // stroke width for links
strokeOpacity = 0.4, // stroke opacity for links
strokeLinejoin, // stroke line join for links
strokeLinecap, // stroke line cap for links
halo = "#fff", // color of label halo
haloWidth = 3, // padding around the labels
} = {}) {

// If a path accessor is specified, we can impute the internal nodes from the slash-
// separated path; otherwise, the tabular data must include the internal nodes, not
// just leaves. TODO https://github.com/d3/d3-hierarchy/issues/33
if (path != null) {
const D = d3.map(data, d => d);
const I = d3.map(data, path).map(d => (d = `${d}`).startsWith("/") ? d : `/${d}`);
const paths = new Set(I);
for (const path of paths) {
const parts = path.split("/");
while (parts.pop(), parts.length) {
const path = parts.join("/") || "/";
if (paths.has(path)) continue;
paths.add(path), I.push(path), D.push(null);
}
}
id = (_, i) => I[i];
parentId = (_, i) => I[i] === "/" ? "" : I[i].slice(0, I[i].lastIndexOf("/")) || "/";
data = D;
}

// If id and parentId options are specified (perhaps implicitly via the path option),
// use d3.stratify to convert tabular data to a hierarchy; otherwise we assume that
// the data is specified as an object {children} with nested objects (a.k.a. the
// “flare.json” format), and use d3.hierarchy.
const root = id == null && parentId == null
? d3.hierarchy(data, children)
: d3.stratify().id(id).parentId(parentId)(data);

// Compute labels and titles.
const descendants = root.descendants();
const L = label == null ? null : descendants.map(d => label(d.data, d));

// Sort the nodes.
if (sort != null) root.sort(sort);

// Compute the layout.
const dx = 10;
const dy = width / (root.height + padding);
tree().nodeSize([dx, dy])(root);

// Center the tree.
let x0 = Infinity;
let x1 = -x0;
root.each(d => {
if (d.x > x1) x1 = d.x;
if (d.x < x0) x0 = d.x;
});

// Compute the default height.
if (height === undefined) height = x1 - x0 + dx * 2;

const svg = d3.create("svg")
.attr("viewBox", [200-dy * padding / 2, x0 - dx, width, height])
.attr("width", width+300)
.attr("height", height)
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "sans-serif")
.attr("font-size", 16);

svg.append("g")
.attr("fill", "none")
.attr("stroke", stroke)
.attr("stroke-opacity", strokeOpacity)
.attr("stroke-linecap", strokeLinecap)
.attr("stroke-linejoin", strokeLinejoin)
.attr("stroke-width", strokeWidth)
.selectAll("path")
.data(root.links())
.join("path")
.attr("d", d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x));


const node = svg.append("g")
.selectAll("a")
.data(root.descendants())
.join("a")
.on("click", link == null ? null : d => link(d))
.attr("transform", d => `translate(${d.y},${d.x})`)
.style('cursor', 'pointer');



node.append("circle")
.attr("fill", d => d.children ? fill : stroke)
.attr("r", r);

if (title != null) node.append("title")
.text(d => title(d.data, d));

if (L) node.append("text")
.attr("dy", "0.32em")
.attr("x", d => d.children ? -6 : 6)
.attr("text-anchor", d => d.children ? "end" : "start")
.text((d, i) => L[i])
.call(text => text.clone(true))
.attr("fill", "none")
.attr("stroke", halo)
.attr("stroke-width", haloWidth);

return svg.node();
}
Insert cell
import {howto} from "@d3/example-components"
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