Public
Edited
Mar 14, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
drawSingleRidge(data[0],[">80% Männer",
"60-80% Männer",
"40-60% Männer/Frauen",
"60-80% Frauen",
">80% Frauen"],"Geschlechterzusammensetzung")
Insert cell
drawChart([">80% Männer",
"60-80% Männer",
"40-60% Männer/Frauen",
"60-80% Frauen",
">80% Frauen"],"Geschlechterzusammensetzung")
Insert cell
drawChart(["25 Jahre und jünger",
"26-50 Jahre",
"51-75 Jahre",
"76-100 Jahre",
"Über 100 Jahre",],"Alter")
Insert cell
drawChart(["Keine",
"1-30%",
"31-60%",
"61-100%"], "Junge")
Insert cell
drawChart([
"Eine Sportart",
"2-5 Sportarten",
"Über 5 Sportarten"], "Sportangebot")
Insert cell
dataSeries = [
{ variable: "Sportangebot", cats: [
"Eine Sportart",
"2-5 Sportarten",
"Über 5 Sportarten"]
},
{ variable: "Geschlechterzusammensetzung", cats: [">80% Männer",
"60-80% Männer",
"40-60% Männer/Frauen",
"60-80% Frauen",
">80% Frauen"] },
{ variable: "Junge", cats: ["Keine",
"1-30%",
"31-60%",
"61-100%"] },
{ variable: "Sportangebot", cats: [
"Eine Sportart",
"2-5 Sportarten",
"Über 5 Sportarten"] },
]
Insert cell
mutable currVariable = 0
Insert cell
Insert cell
function showVariable(v,d,i) {
mutable currVariable = dataSeries.indexOf(d)
}
Insert cell
Insert cell
mbCats = ["Kinder", "Jugendliche", "Erwachsene Aktivmitglieder mit Lizenz", "Erwachsene Aktivmitglieder ohne Lizenz"]
Insert cell
infrastrukturCats = ["öffentliche", "vereinseigene", "private", "keine"]
Insert cell
mitgliederbeitraege = data.map(d => {
return {name: d.abbr, ...mbCats.reduce((p,c, i) => {
p[c] = d.Mitgliederbeitraege.length !== mbCats.length ? null : d.Mitgliederbeitraege[i];
return p;
}, [])}
})
Insert cell
infrastruktur = data.map(d => {
return {name: d.abbr, ...infrastrukturCats.reduce((p,c,i) => {
p[c] = d.Infrastruktur[i];
return p;
},[])
}
})
Insert cell
Insert cell
Plot.plot({
height: 1000,
marks: [
Plot.rectX(data, Plot.stackY({
y: "ArbeitsaufwandProEhrenamt",
x2: "EhrenamtlicheAemterProVerein",
fill: "orange",
//sort: {y:"-y"},
insetTop: 0.2,
insetBottom: 0.2,
order: "EhrenamtlicheAemterProVerein"
},)),
Plot.textX(data, Plot.stackY({
x: 0,
insetLeft:4,
y: "ArbeitsaufwandProEhrenamt",
text: (d) => d.abbr, dy: 0,
lineAnchor: "middle", textAnchor: "start",
insetTop: 0.2,
insetBottom: 0.2,
order: "EhrenamtlicheAemterProVerein"
},)),
//Plot.text(data, {x: 0, y: "ArbeitsaufwandProEhrenamt", text: (d) => d.abbr, dy: -6, lineAnchor: "bottom"}),

//Plot.text(data, {x: 0, y: "EhrenamtlicheAemterProVerein", text: (d) => d.abbr }),

]
})

Insert cell
Plot.plot({
height: 1000,
marks: [
Plot.rectX(data, Plot.stackY({
y: "EhrenamtlicheAemterProVerein",
x2: "ArbeitsaufwandProEhrenamt",
fill: "red",
//sort: {y:"-y"},
insetTop: 0.2,
insetBottom: 0.2,
order: "ArbeitsaufwandProEhrenamt"
},)),
Plot.textX(data, Plot.stackY({
x: 0,
insetLeft:4,
y: "EhrenamtlicheAemterProVerein",
text: (d) => d.abbr, dy: 0,
lineAnchor: "middle", textAnchor: "start",
insetTop: 0.2,
insetBottom: 0.2,
order: "ArbeitsaufwandProEhrenamt"
},)),
//Plot.text(data, {x: 0, y: "ArbeitsaufwandProEhrenamt", text: (d) => d.abbr, dy: -6, lineAnchor: "bottom"}),

//Plot.text(data, {x: 0, y: "EhrenamtlicheAemterProVerein", text: (d) => d.abbr }),

]
})

Insert cell
Plot.plot({
marks: [
Plot.rectX(data, {x: "ArbeitsaufwandProEhrenamt", y: "abbr", fill: "green", sort: {y: "-x"}}),

]
})
Insert cell
Plot.plot({
marks: [
Plot.rectX(data, {x: "ArbeitsaufwandProEhrenamt", y: "abbr", fill: "green", sort: {y: "-x"}}),

]
})
Insert cell
Plot.plot({
color: {
domain: infrastrukturCats,
range: ["green","orange", "red", "#DDD"],
legend: true
},
marks: [
Plot.dot(infrastruktur, {x: infrastrukturCats[0], y: "name", fill: "green"}),
Plot.dot(infrastruktur, {x: infrastrukturCats[1], y: "name", fill: "orange"}),
Plot.dot(infrastruktur, {x: infrastrukturCats[2],y: "name", fill: "red"}),
Plot.dot(infrastruktur, {x: infrastrukturCats[3], y: "name", fill: "#DDD"}),
]
})

Insert cell
verbandSymbol = (data) => {

return html`<div style="width: 12rem; height: 12rem; color: white; aspect-ratio: 1; position: relative; transition: transform 1s ease-in; transform: rotate(${currVariable*90}deg)">
<div style="background: #333; position: absolute; inset: 4rem; width:4rem; height:4rem; line-height: 100%; display: flex; justify-content: center; align-items: center; border-radius: 0.25rem">
<span style="font-size: 10px;text-align:center">${data.abbr}</span>
${dataSeries.map((s,i) => html`
<div style="position: absolute; width:100%; height:100%; transform: rotate(${i*90}deg)">
<div style="position: absolute; bottom:100%; height:auto">
${drawSingleRidge(data,s.cats, s.variable)}
</div>
</div>
`)}


</div>`
}
Insert cell
xy = Plot.normalizeX("sum", {x: "population", y: "state", z: "state"})
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.rectX(diffVereine.filter(d => !isNaN(d.diffAmount)), {x: "diffAmount", y: "abbr", fill: d => d.diffAmount > 0 ? "green" : "red", sort: {y: "-x"}}),
]
})
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.rectX(diffVereine, {x: "diffEhrenamtlicheAemterProVerein", y: "abbr", fill: d => d.diffEhrenamtlicheAemterProVerein > 0 ? "green" : "red", sort: {y: "-x"}}),
]
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.rectX(diffVereine, {x: "diffGroesseGross", y: "abbr", fill: d => d.diffGroesseGross > 0 ? "green" : "red", sort: {y: "-x"}}),
]
})
Insert cell
missingIn2016 = diffVereine.filter(d => isNaN(d.diff)).map(d => d.abbr)
Insert cell
missingIn2022 = data_2016.filter(d => !data.map(dd => dd.abbr).includes(d.abbr)).map(d => d.abbr)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vereineByAufwand2022 = data.reduce((p,c) => {
Array.from({length: Math.round(c.totalVereine[0] * c.EhrenamtlicheAemterProVerein[0] / 100)}).forEach((o,i) => {
p.push({ name: c.abbr, size: c.ArbeitsaufwandProEhrenamt[0] * c.ArbeitsaufwandProEhrenamt[0] })
})
return p;
},[]).map((o,i) => ({ ...o, id:i }))
Insert cell
Insert cell
Pack(vereineByAufwand2022, {
path: (d) => d.name+"/"+d.id, // e.g. flare/animate/Easing
//label: (d) => [d.name].join(" – ") ,//.split(".").pop(), // display text
value: (d) => d?.size, //d?.size, // area of each circle
title: (d, n) => [n.id,n.value.toLocaleString()].join("\n"), // hover text
width,
fill: "pink",
opacity: 0.2,
height: 900
})
Insert cell
Insert cell
Pack(data.map((o,i) => ({...o, id:i})), {
path: (d) => d.abbr, // e.g. flare/animate/Easing
label: (d) => [d.abbr].join(" – ") ,//.split(".").pop(), // display text
value: (d) => d?.ArbeitsaufwandProEhrenamt[0], //d?.size, // area of each circle
//title: (d, n) => [n.id,n.value.toLocaleString()].join("\n"), // hover text
width,
fill: "pink",
opacity: 0.2,
height: 900
})
Insert cell
Insert cell
VereinsgroessenProVerbandChart = {
// Specify the dimensions of the chart.
const width = 928;
const height = width;
const margin = 1; // to avoid clipping the root circle stroke

// Specify the number format for values.
const format = d3.format(",d");


// Compute the hierarchy from the JSON data; recursively sum the
// values for each node; sort the tree by descending value; lastly
// apply the pack layout.
const value = (d) => d?.type === "l" ? 40 : d?.type ==="m" ? 10 : 5;
const root = d3.stratify().path((d,n) => d.name+"/"+d.id)(vereineBySize2022);
// return root;
root.sum(d => Math.max(0, value(d))).sort((a, b) => {
return b.value - a.value
});
const pack = d3.pack()
.size([width - margin*2, height - margin*2])
.padding(2)
(root);
// Create the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-margin, -margin, width, height])
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;")
.attr("text-anchor", "middle");

// Place each node according to the layout’s x and y values.
const node = svg.append("g")
.selectAll()
.data(root.descendants().filter(d => !d.children))
.join("g")
.attr("transform", d => `translate(${d.x},${d.y})`);

// Add a title.
//node.append("title")
// .text(d => `${d.name}\n${format(d.value)}`);

// Add a filled or stroked circle.
node.append("circle")
.attr("fill", "green")
//.attr("stroke", d => "#bbb")
.attr("r", d => d.r);

const enclosings = svg.append("g")
.selectAll()
.data(root.descendants().filter(d => d.children && d.parent))
.join("g")
.attr("transform", d => `translate(${d.x},${d.y})`);
// Add a label to leaf nodes.
const text = enclosings
.append("text")
.attr("style", "font-weight:bold")
.text(d => d.id.slice(1));
//.attr("clip-path", d => `circle(${d.r})`);


return svg.node();
}
Insert cell
vereineByEhrenaemter = data.reduce((p,c) => {
/*const aemter = (c.EhrenamtlicheAemterProVerein[0] || 0) + (c.EntschaedigteAemterProVerein[0] || 0);
const ehrenamt_ratio = c.EhrenamtlicheAemterProVerein[0] / aemter;
const entsch_ratio = 1 - ehrenamt_ratio;*/
const nofEntschaedigte = Math.round(c.totalVereine[0] * (c.EntschaedigteAemterProVerein[0] || 0));
const nofEhrenamtliche = Math.round(c.totalVereine[0] * (c.EhrenamtlicheAemterProVerein[0] || 0))
Array.from({length: Math.round(nofEntschaedigte/100)}).forEach((o,i) => {
p.push({ name: c.abbr, type:'entschaedigtes amt', nofEntschaedigte })
})
Array.from({length: Math.round(nofEhrenamtliche/100)}).forEach((o,i) => {
p.push({ name: c.abbr, type:'ehrenamt', nofEhrenamtliche })
})
return p;
},[]).map((o,i) => ({ ...o, id:i }))
Insert cell
import {Pack} from "@d3/pack"
Insert cell
Insert cell
Insert cell
Pack(data, {
path: (d) => d.abbr, // e.g. flare/animate/Easing
label: (d) => [d.abbr, d.totalVereine[1]].join('\n'), // display text
value: (d) => { return d?.totalVereine[1] }, // area of each circle
//title: (d, n) => d?.data?.name, // hover text
width,
fill: "red",
padding: 10,
height: 400
})
Insert cell
import {beeswarmForce} from "72cd479f14e9b994"
Insert cell
createYBeeswarm = (data, opts) => {
//return d3.extent(diffVereine, accY);
const r = d3.scaleSqrt(
[100, 1000],
[1, Math.sqrt(opts.width * opts.height) / 30]
);
return beeswarmForce()
.y(d => opts.scale(opts.y(d)))
.x(width / 2)
.r((d) => opts.r(d) + opts.gap)
//.r(d => 1 + r(d.size))

}
Insert cell
{
const height= 800;
const width = 800;
const r = () => 15;
const y = (d) => d["diffEhrenamtlicheAemterProVerein"];
const scale = d3.scaleLinear(
d3.extent(diffVereine, y),
[0, height]
);
const beeswarm = createYBeeswarm(diffVereine, {
y,
r,
scale,
height,
width,
gap: 2
})
return beeswarm(diffVereine);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
const g = svg.append("g")
//.attr("transform", `translate(${[margin.left, margin.top]})`);
g.append("g")
.call(d3.axisLeft(scale).tickSizeOuter(0))
.attr("transform", `translate( ${width },0)`);
g.selectAll("circle")
.data(beeswarm(diffVereine))
.join("circle")
.attr("stroke", "black")
.attr("fill-opacity", 0.8)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", r);
return svg.node();
}
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